From 09da1c6019346d8464e73548a089b0d42b1bb1f7 Mon Sep 17 00:00:00 2001 From: Khaliq Date: Tue, 12 May 2026 22:06:58 +0200 Subject: [PATCH 1/4] feat(packages): host @workforce/daytona-runner in workforce MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Relocate @workforce/daytona-runner from the private cloud monorepo (where cloud#543 mistakenly placed it) into workforce — the canonical OSS publishing surface per the deploy-v1 codex spec. Sources copied verbatim from cloud/packages/daytona-runner/src/ (auth.ts, types.ts, runtime.ts, index.ts, runtime.test.ts). Zero @cloud/* leaks: the package was already extracted to be cloud-free. Package metadata aligned to workforce conventions: - Name preserved as @workforce/daytona-runner (cloud's pin still resolves once published) - Version reset to 0.1.0 (fresh major-zero, matches relay#844 policy) - ESM, tsconfig extends ../../tsconfig.base.json - publishConfig.provenance: true to match workforce's OSS posture - @daytonaio/sdk declared as a peer dep, ^0.148.0 (cloud parity) Mirrors AgentWorkforce/relay#844, which just relocated @agent-relay/events + @agent-relay/agent from cloud to relay for the same OSS/private split reason. Co-Authored-By: Claude Opus 4.7 (1M context) --- packages/daytona-runner/.gitignore | 2 + packages/daytona-runner/README.md | 40 ++++ packages/daytona-runner/package.json | 43 ++++ packages/daytona-runner/src/auth.ts | 53 +++++ packages/daytona-runner/src/index.ts | 21 ++ packages/daytona-runner/src/runtime.test.ts | 87 +++++++ packages/daytona-runner/src/runtime.ts | 246 ++++++++++++++++++++ packages/daytona-runner/src/types.ts | 44 ++++ packages/daytona-runner/tsconfig.json | 8 + pnpm-lock.yaml | 6 + 10 files changed, 550 insertions(+) create mode 100644 packages/daytona-runner/.gitignore create mode 100644 packages/daytona-runner/README.md create mode 100644 packages/daytona-runner/package.json create mode 100644 packages/daytona-runner/src/auth.ts create mode 100644 packages/daytona-runner/src/index.ts create mode 100644 packages/daytona-runner/src/runtime.test.ts create mode 100644 packages/daytona-runner/src/runtime.ts create mode 100644 packages/daytona-runner/src/types.ts create mode 100644 packages/daytona-runner/tsconfig.json diff --git a/packages/daytona-runner/.gitignore b/packages/daytona-runner/.gitignore new file mode 100644 index 00000000..1eae0cf6 --- /dev/null +++ b/packages/daytona-runner/.gitignore @@ -0,0 +1,2 @@ +dist/ +node_modules/ diff --git a/packages/daytona-runner/README.md b/packages/daytona-runner/README.md new file mode 100644 index 00000000..8a3f044e --- /dev/null +++ b/packages/daytona-runner/README.md @@ -0,0 +1,40 @@ +# @workforce/daytona-runner + +Daytona-backed `WorkflowRuntime` adapter for AgentWorkforce deploy workflows. This package owns the Daytona runtime implementation, auth helpers, and runtime contract types. + +## Install + +```sh +npm install @workforce/daytona-runner @daytonaio/sdk +``` + +`@daytonaio/sdk` is a peer dependency — consumers bring their own version (^0.148.0). + +## Usage + +```ts +import { Daytona } from '@daytonaio/sdk'; +import { + DaytonaRuntime, + resolveDaytonaAuthCredentials, +} from '@workforce/daytona-runner'; + +const auth = resolveDaytonaAuthCredentials({ + apiKey: process.env.DAYTONA_API_KEY, + jwtToken: process.env.DAYTONA_JWT_TOKEN, + organizationId: process.env.DAYTONA_ORGANIZATION_ID, +}); + +const daytona = new Daytona(auth); +const runtime = new DaytonaRuntime({ daytona }); + +const handle = await runtime.launch({ label: 'my-workflow' }); +const result = await runtime.exec(handle, 'node -e "console.log(\\"ok\\")"'); +await runtime.destroy(handle); +``` + +## Exports + +- `DaytonaRuntime` — the `WorkflowRuntime` implementation. +- `resolveDaytonaAuthCredentials` / `applyDaytonaAuthEnv` — Daytona auth helpers (apiKey vs JWT+org). +- `WorkflowRuntime`, `RuntimeHandle`, `LaunchOptions`, `ExecOptions`, `ExecResult`, `RuntimeCapabilities` — runtime contract types. diff --git a/packages/daytona-runner/package.json b/packages/daytona-runner/package.json new file mode 100644 index 00000000..1d3a48a9 --- /dev/null +++ b/packages/daytona-runner/package.json @@ -0,0 +1,43 @@ +{ + "name": "@workforce/daytona-runner", + "version": "0.1.0", + "description": "Daytona-backed WorkflowRuntime adapter for AgentWorkforce deploy workflows.", + "private": false, + "type": "module", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + }, + "files": [ + "dist", + "README.md", + "package.json" + ], + "repository": { + "type": "git", + "url": "https://github.com/AgentWorkforce/workforce", + "directory": "packages/daytona-runner" + }, + "publishConfig": { + "access": "public", + "provenance": true + }, + "scripts": { + "build": "tsc -p tsconfig.json", + "dev": "tsc -p tsconfig.json --watch --preserveWatchOutput", + "typecheck": "tsc -p tsconfig.json --noEmit", + "test": "tsc -p tsconfig.json && node --test dist/*.test.js", + "lint": "tsc -p tsconfig.json --noEmit" + }, + "dependencies": {}, + "peerDependencies": { + "@daytonaio/sdk": "^0.148.0" + }, + "devDependencies": { + "@daytonaio/sdk": "^0.148.0" + } +} diff --git a/packages/daytona-runner/src/auth.ts b/packages/daytona-runner/src/auth.ts new file mode 100644 index 00000000..94620916 --- /dev/null +++ b/packages/daytona-runner/src/auth.ts @@ -0,0 +1,53 @@ +export interface DaytonaAuthCredentials { + apiKey?: string; + jwtToken?: string; + organizationId?: string; +} + +export type ResolvedDaytonaAuthCredentials = + | { apiKey: string } + | { jwtToken: string; organizationId: string }; + +export function resolveDaytonaAuthCredentials( + credentials: DaytonaAuthCredentials, +): ResolvedDaytonaAuthCredentials { + const apiKey = credentials.apiKey?.trim(); + if (apiKey) { + return { apiKey }; + } + + const jwtToken = credentials.jwtToken?.trim(); + if (!jwtToken) { + throw new Error('Daytona auth is required in credential bundle'); + } + + const organizationId = credentials.organizationId?.trim(); + if (!organizationId) { + throw new Error('DAYTONA_ORGANIZATION_ID is required when using Daytona JWT auth'); + } + + return { jwtToken, organizationId }; +} + +export function applyDaytonaAuthEnv( + env: Record, + credentials: DaytonaAuthCredentials, +): void { + const resolved = resolveDaytonaAuthCredentials(credentials); + if ('apiKey' in resolved) { + env.DAYTONA_API_KEY = resolved.apiKey; + // Clear the JWT-mode env keys so a caller flipping from JWT to apiKey + // auth doesn't leave stale credentials in the bag that downstream + // consumers might read and prefer over the apiKey path. + delete env.DAYTONA_JWT_TOKEN; + delete env.DAYTONA_ORGANIZATION_ID; + return; + } + + env.DAYTONA_JWT_TOKEN = resolved.jwtToken; + env.DAYTONA_ORGANIZATION_ID = resolved.organizationId; + // Same logic in reverse: a JWT-auth caller should not see a lingering + // DAYTONA_API_KEY from an earlier mode that would silently take + // priority over the freshly-applied JWT. + delete env.DAYTONA_API_KEY; +} diff --git a/packages/daytona-runner/src/index.ts b/packages/daytona-runner/src/index.ts new file mode 100644 index 00000000..9b0fca0d --- /dev/null +++ b/packages/daytona-runner/src/index.ts @@ -0,0 +1,21 @@ +export { + DaytonaRuntime, + type DaytonaAttachedSandboxOptions, + type DaytonaRuntimeOptions, +} from './runtime.js'; + +export { + applyDaytonaAuthEnv, + resolveDaytonaAuthCredentials, + type DaytonaAuthCredentials, + type ResolvedDaytonaAuthCredentials, +} from './auth.js'; + +export type { + ExecOptions, + ExecResult, + LaunchOptions, + RuntimeCapabilities, + RuntimeHandle, + WorkflowRuntime, +} from './types.js'; diff --git a/packages/daytona-runner/src/runtime.test.ts b/packages/daytona-runner/src/runtime.test.ts new file mode 100644 index 00000000..47716ca1 --- /dev/null +++ b/packages/daytona-runner/src/runtime.test.ts @@ -0,0 +1,87 @@ +import { after, before, describe, it } from 'node:test'; +import assert from 'node:assert/strict'; +import { Daytona } from '@daytonaio/sdk'; + +import * as pkg from './index.js'; +import { DaytonaRuntime, applyDaytonaAuthEnv, resolveDaytonaAuthCredentials } from './index.js'; +import type { RuntimeHandle } from './index.js'; + +describe('public barrel', () => { + it('exports DaytonaRuntime as a class', () => { + assert.equal(typeof pkg.DaytonaRuntime, 'function'); + assert.equal(typeof DaytonaRuntime, 'function'); + }); + + it('exports resolveDaytonaAuthCredentials and applyDaytonaAuthEnv', () => { + assert.equal(typeof pkg.resolveDaytonaAuthCredentials, 'function'); + assert.equal(typeof resolveDaytonaAuthCredentials, 'function'); + assert.equal(typeof pkg.applyDaytonaAuthEnv, 'function'); + assert.equal(typeof applyDaytonaAuthEnv, 'function'); + }); + + it('resolveDaytonaAuthCredentials normalises an apiKey-only input', () => { + const resolved = resolveDaytonaAuthCredentials({ apiKey: 'sk-test' }); + assert.ok('apiKey' in resolved, 'expected apiKey-mode result'); + assert.equal(resolved.apiKey, 'sk-test'); + }); + + it('resolveDaytonaAuthCredentials rejects empty input', () => { + assert.throws(() => resolveDaytonaAuthCredentials({})); + }); + + it('applyDaytonaAuthEnv writes DAYTONA_API_KEY into the supplied env bag', () => { + const env: Record = {}; + applyDaytonaAuthEnv(env, { apiKey: 'sk-test' }); + assert.equal(env.DAYTONA_API_KEY, 'sk-test'); + }); +}); + +const daytonaApiKey = process.env.DAYTONA_API_KEY?.trim(); +const HAS_DAYTONA = Boolean(daytonaApiKey); +const SMOKE_LABEL = 'daytona-runner-smoke'; + +describe('DaytonaRuntime smoke', { concurrency: false }, () => { + let runtime: DaytonaRuntime | undefined; + let handle: RuntimeHandle | undefined; + + before(() => { + if (!HAS_DAYTONA) return; + const auth = resolveDaytonaAuthCredentials({ + apiKey: daytonaApiKey, + jwtToken: process.env.DAYTONA_JWT_TOKEN, + organizationId: process.env.DAYTONA_ORGANIZATION_ID, + }); + const daytona = new Daytona(auth); + runtime = new DaytonaRuntime({ daytona }); + }); + + after(async () => { + if (runtime && handle) { + try { + await runtime.destroy(handle); + } catch { + // best-effort cleanup; sandbox leaks surface via Daytona dashboard + } + } + }); + + it( + 'launches a sandbox, runs node -e, and destroys it', + { skip: HAS_DAYTONA ? false : 'DAYTONA_API_KEY is not set', timeout: 120_000 }, + async () => { + assert.ok(runtime, 'runtime should be initialised when DAYTONA_API_KEY is set'); + handle = await runtime.launch({ label: SMOKE_LABEL }); + const result = await runtime.exec(handle, "node -e 'console.log(\"ok\")'"); + assert.equal( + result.exitCode, + 0, + `expected exitCode 0, got ${result.exitCode}: ${result.output}`, + ); + assert.match( + result.output, + /\bok\b/, + `expected output to contain "ok", got: ${result.output}`, + ); + }, + ); +}); diff --git a/packages/daytona-runner/src/runtime.ts b/packages/daytona-runner/src/runtime.ts new file mode 100644 index 00000000..407a7547 --- /dev/null +++ b/packages/daytona-runner/src/runtime.ts @@ -0,0 +1,246 @@ +import type { Daytona, Sandbox } from '@daytonaio/sdk'; +import type { + ExecOptions, + ExecResult, + LaunchOptions, + RuntimeCapabilities, + RuntimeHandle, + WorkflowRuntime, +} from './types.js'; + +export interface DaytonaRuntimeOptions { + daytona: Daytona; + snapshot?: string; + defaultHomeDir?: string; +} + +export interface DaytonaAttachedSandboxOptions { + homeDir?: string; + workdir?: string; + owned?: boolean; +} + +interface RegisteredSandbox { + sandbox: Sandbox; + owned: boolean; +} + +export class DaytonaRuntime implements WorkflowRuntime { + readonly id = 'daytona'; + readonly capabilities: RuntimeCapabilities = { + pty: false, + snapshots: true, + isolation: 'strong', + persistentHandle: true, + streamingLogs: true, + }; + + private readonly sandboxes = new Map(); + private readonly daytona: Daytona; + private readonly snapshot?: string; + private readonly defaultHomeDir: string; + + constructor(options: DaytonaRuntimeOptions) { + this.daytona = options.daytona; + this.snapshot = options.snapshot; + this.defaultHomeDir = options.defaultHomeDir ?? '/home/daytona'; + } + + async launch(options: LaunchOptions = {}): Promise { + const sandbox = await this.createSandbox(options); + const homeDir = await this.resolveHomeDir(sandbox); + return this.registerSandbox(sandbox, { + owned: true, + homeDir, + workdir: options.workdir, + }); + } + + attachSandbox(sandbox: Sandbox, options: DaytonaAttachedSandboxOptions = {}): RuntimeHandle { + return this.registerSandbox(sandbox, { + owned: options.owned ?? false, + homeDir: options.homeDir, + workdir: options.workdir, + }); + } + + async exec(handle: RuntimeHandle, command: string, options: ExecOptions = {}): Promise { + const sandbox = this.requireSandbox(handle); + const result = await sandbox.process.executeCommand( + command, + options.cwd, + options.env, + this.msToSeconds(options.timeoutMs), + ); + + return { + output: result.result ?? '', + exitCode: result.exitCode ?? 0, + }; + } + + async uploadFile(handle: RuntimeHandle, source: string | Buffer, destination: string): Promise { + const sandbox = this.requireSandbox(handle); + if (typeof source === 'string') { + await sandbox.fs.uploadFile(source, destination); + return; + } + await sandbox.fs.uploadFile(source, destination); + } + + async downloadFile(handle: RuntimeHandle, source: string, destination?: string): Promise { + const sandbox = this.requireSandbox(handle); + if (destination) { + await sandbox.fs.downloadFile(source, destination); + return; + } + return sandbox.fs.downloadFile(source); + } + + async getHomeDir(handle: RuntimeHandle): Promise { + if (handle.homeDir) { + return handle.homeDir; + } + + const sandbox = this.requireSandbox(handle); + const homeDir = await this.resolveHomeDir(sandbox); + handle.homeDir = homeDir; + return homeDir; + } + + async destroy(handle: RuntimeHandle): Promise { + const entry = this.sandboxes.get(handle.id); + if (!entry) { + return; + } + + if (!entry.owned) { + // For attached (non-owned) sandboxes we never call the remote + // delete; just drop the local registration so the caller-managed + // resource isn't tracked here any more. + this.sandboxes.delete(handle.id); + return; + } + + const client = this.daytona as unknown as { + remove?: (sandbox: Sandbox) => Promise; + delete: (sandbox: Sandbox) => Promise; + }; + const remove = client.remove ?? client.delete; + // Order matters: do the remote delete *first*, and only drop the + // local map entry after it succeeds. If we dropped the entry first + // and the remote delete then failed, the handle id would be lost + // and the caller could not retry cleanup safely. + await remove.call(client, entry.sandbox); + this.sandboxes.delete(handle.id); + } + + private async createSandbox(options: LaunchOptions): Promise { + const params = this.buildCreateParams(options); + + if (this.snapshot) { + try { + return await this.daytona.create({ + snapshot: this.snapshot, + ...params, + }); + } catch (err) { + // Only fall back to a fresh sandbox when the snapshot itself is + // missing. Auth/network/quota errors should bubble — otherwise + // we silently mask real failures (a 401 ends up creating an + // unsnapshotted sandbox under whichever credentials worked). + if (!isSnapshotNotFoundError(err)) { + throw err; + } + } + } + + return this.daytona.create({ + language: 'typescript', + ...params, + }); + } + + private buildCreateParams(options: LaunchOptions): { + envVars?: Record; + name?: string; + } { + const envVars = options.env && Object.keys(options.env).length > 0 ? options.env : undefined; + const name = options.label?.trim() ? options.label.trim() : undefined; + + return { + ...(envVars ? { envVars } : {}), + ...(name ? { name } : {}), + }; + } + + private registerSandbox( + sandbox: Sandbox, + options: DaytonaAttachedSandboxOptions & { owned: boolean }, + ): RuntimeHandle { + const handle: RuntimeHandle = { + id: sandbox.id, + ...(options.homeDir ? { homeDir: options.homeDir } : {}), + ...(options.workdir ? { workdir: options.workdir } : {}), + }; + + this.sandboxes.set(handle.id, { + sandbox, + owned: options.owned, + }); + return handle; + } + + private requireSandbox(handle: RuntimeHandle): Sandbox { + const entry = this.sandboxes.get(handle.id); + if (!entry) { + throw new Error(`Runtime handle "${handle.id}" is no longer active`); + } + return entry.sandbox; + } + + private async resolveHomeDir(sandbox: Sandbox): Promise { + try { + const home = await sandbox.getUserHomeDir(); + if (home) { + return home; + } + } catch { + // fall through to default + } + + return this.defaultHomeDir; + } + + private msToSeconds(timeoutMs?: number): number | undefined { + if (!timeoutMs || timeoutMs <= 0) { + return undefined; + } + + return Math.max(1, Math.ceil(timeoutMs / 1000)); + } +} + +/** + * Heuristic: identify Daytona errors that indicate the snapshot we asked + * for doesn't exist (so falling back to a fresh sandbox is safe). We look + * at the HTTP status when the SDK surfaces one, plus a few well-known + * error-message shapes Daytona emits. Anything else propagates so the + * caller sees the original error (auth/network/quota/etc.). + */ +function isSnapshotNotFoundError(err: unknown): boolean { + if (!err || typeof err !== 'object') return false; + const candidate = err as { status?: unknown; statusCode?: unknown; message?: unknown; code?: unknown }; + const status = typeof candidate.status === 'number' + ? candidate.status + : typeof candidate.statusCode === 'number' + ? candidate.statusCode + : undefined; + if (status === 404) return true; + const message = typeof candidate.message === 'string' ? candidate.message.toLowerCase() : ''; + if (!message) return false; + return ( + message.includes('snapshot') && + (message.includes('not found') || message.includes('does not exist') || message.includes('no such')) + ); +} diff --git a/packages/daytona-runner/src/types.ts b/packages/daytona-runner/src/types.ts new file mode 100644 index 00000000..303afcc6 --- /dev/null +++ b/packages/daytona-runner/src/types.ts @@ -0,0 +1,44 @@ +export type IsolationLevel = 'none' | 'process' | 'strong'; + +export interface RuntimeCapabilities { + pty: boolean; + snapshots: boolean; + isolation: IsolationLevel; + persistentHandle: boolean; + streamingLogs: boolean; +} + +export interface LaunchOptions { + env?: Record; + label?: string; + workdir?: string; +} + +export interface RuntimeHandle { + id: string; + homeDir?: string; + workdir?: string; +} + +export interface ExecOptions { + cwd?: string; + env?: Record; + timeoutMs?: number; +} + +export interface ExecResult { + output: string; + exitCode: number; +} + +export interface WorkflowRuntime { + readonly id: string; + readonly capabilities: RuntimeCapabilities; + + launch(options?: LaunchOptions): Promise; + exec(handle: RuntimeHandle, command: string, options?: ExecOptions): Promise; + uploadFile(handle: RuntimeHandle, source: string | Buffer, destination: string): Promise; + downloadFile(handle: RuntimeHandle, source: string, destination?: string): Promise; + getHomeDir(handle: RuntimeHandle): Promise; + destroy(handle: RuntimeHandle): Promise; +} diff --git a/packages/daytona-runner/tsconfig.json b/packages/daytona-runner/tsconfig.json new file mode 100644 index 00000000..df59da57 --- /dev/null +++ b/packages/daytona-runner/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "dist" + }, + "include": ["src/**/*.ts"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9f7a133a..f8c3cbf9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -45,6 +45,12 @@ importers: specifier: ^9.4.0 version: 9.4.0 + packages/daytona-runner: + devDependencies: + '@daytonaio/sdk': + specifier: ^0.148.0 + version: 0.148.0(ws@8.20.0) + packages/deploy: dependencies: '@agentworkforce/persona-kit': From 73fe39a30aecb1d1714b454b2c91881abcb680fa Mon Sep 17 00:00:00 2001 From: Khaliq Date: Tue, 12 May 2026 22:50:15 +0200 Subject: [PATCH 2/4] chore(daytona-runner): rename to @agentworkforce scope The package was originally drafted under `@workforce/*` per the deploy-v1 codex spec, but that scope is unclaimed on npm and does not match any other published surface in this repo. Every other workforce-published package uses `@agentworkforce/*` (persona-kit, workload-router, cli, agentworkforce umbrella). Rename `@workforce/daytona-runner` -> `@agentworkforce/daytona-runner` and slot it into the existing publish workflow's lockstep allow-list so it ships at the same cadence as the rest of the surface. No new scope, no new OIDC trusted-publisher registration, no separate workflow track. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/publish.yml | 2 +- packages/daytona-runner/README.md | 6 +++--- packages/daytona-runner/package.json | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 53592b35..3ad69de8 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -89,7 +89,7 @@ jobs: # persona-kit is a leaf dep consumed by workload-router and cli, so it # must publish first. The top-level `agentworkforce` wrapper depends on # `@agentworkforce/cli`, so it must publish last. - echo "packages=persona-kit workload-router cli agentworkforce" >> "$GITHUB_OUTPUT" + echo "packages=persona-kit workload-router cli agentworkforce daytona-runner" >> "$GITHUB_OUTPUT" # Lockstep baseline heal. The workspace publishes every package at the # same version, so if any package's local version lags either its own diff --git a/packages/daytona-runner/README.md b/packages/daytona-runner/README.md index 8a3f044e..a1dd9a9c 100644 --- a/packages/daytona-runner/README.md +++ b/packages/daytona-runner/README.md @@ -1,11 +1,11 @@ -# @workforce/daytona-runner +# @agentworkforce/daytona-runner Daytona-backed `WorkflowRuntime` adapter for AgentWorkforce deploy workflows. This package owns the Daytona runtime implementation, auth helpers, and runtime contract types. ## Install ```sh -npm install @workforce/daytona-runner @daytonaio/sdk +npm install @agentworkforce/daytona-runner @daytonaio/sdk ``` `@daytonaio/sdk` is a peer dependency — consumers bring their own version (^0.148.0). @@ -17,7 +17,7 @@ import { Daytona } from '@daytonaio/sdk'; import { DaytonaRuntime, resolveDaytonaAuthCredentials, -} from '@workforce/daytona-runner'; +} from '@agentworkforce/daytona-runner'; const auth = resolveDaytonaAuthCredentials({ apiKey: process.env.DAYTONA_API_KEY, diff --git a/packages/daytona-runner/package.json b/packages/daytona-runner/package.json index 1d3a48a9..c388a13e 100644 --- a/packages/daytona-runner/package.json +++ b/packages/daytona-runner/package.json @@ -1,5 +1,5 @@ { - "name": "@workforce/daytona-runner", + "name": "@agentworkforce/daytona-runner", "version": "0.1.0", "description": "Daytona-backed WorkflowRuntime adapter for AgentWorkforce deploy workflows.", "private": false, From 96ba7121031a585ce81a3fa096becca8ba68d3e3 Mon Sep 17 00:00:00 2001 From: Khaliq Date: Tue, 12 May 2026 23:00:43 +0200 Subject: [PATCH 3/4] remove provenance --- packages/daytona-runner/package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/daytona-runner/package.json b/packages/daytona-runner/package.json index c388a13e..7c5078a8 100644 --- a/packages/daytona-runner/package.json +++ b/packages/daytona-runner/package.json @@ -23,8 +23,7 @@ "directory": "packages/daytona-runner" }, "publishConfig": { - "access": "public", - "provenance": true + "access": "public" }, "scripts": { "build": "tsc -p tsconfig.json", From dd25310f7a965d1ee7b166f2ee272b1a608bc0df Mon Sep 17 00:00:00 2001 From: Khaliq Date: Tue, 12 May 2026 23:06:00 +0200 Subject: [PATCH 4/4] chore(daytona-runner): publish before agentworkforce umbrella; add to release-notes order Move daytona-runner ahead of the agentworkforce umbrella in the publish list so the existing "agentworkforce must publish last" invariant (documented on lines 88-91) still holds. Also add daytona-runner to the build-release-notes packageOrder array at the matching position so its release-note entry sorts deterministically instead of falling to the front via Array.indexOf returning -1. Addresses CodeRabbit and Devin review feedback on #98. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/publish.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 3ad69de8..9066e8b6 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -89,7 +89,7 @@ jobs: # persona-kit is a leaf dep consumed by workload-router and cli, so it # must publish first. The top-level `agentworkforce` wrapper depends on # `@agentworkforce/cli`, so it must publish last. - echo "packages=persona-kit workload-router cli agentworkforce daytona-runner" >> "$GITHUB_OUTPUT" + echo "packages=persona-kit workload-router cli daytona-runner agentworkforce" >> "$GITHUB_OUTPUT" # Lockstep baseline heal. The workspace publishes every package at the # same version, so if any package's local version lags either its own @@ -689,7 +689,7 @@ jobs: // didn't publish an umbrella stamp (e.g. version: none re-runs). const releaseVersion = process.env.RELEASE_VERSION || canonicalVersion; - const packageOrder = ['persona-kit', 'workload-router', 'cli', 'agentworkforce']; + const packageOrder = ['persona-kit', 'workload-router', 'cli', 'daytona-runner', 'agentworkforce']; const entries = versionsRaw.trim().split(/\s+/).filter(Boolean).map((entry) => { const idx = entry.indexOf(':'); return { pkg: entry.slice(0, idx), ver: entry.slice(idx + 1) };