workplane 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,6 @@
1
1
  # workplane
2
2
 
3
+ [![website](https://img.shields.io/badge/website-mindstaq.github.io%2Fworkplane-3dd6c6)](https://mindstaq.github.io/workplane/)
3
4
  [![npm version](https://img.shields.io/npm/v/workplane)](https://www.npmjs.com/package/workplane)
4
5
  [![status](https://img.shields.io/badge/status-beta-orange)](https://github.com/MindStaq/workplane)
5
6
  [![license](https://img.shields.io/npm/l/workplane)](https://github.com/MindStaq/workplane/blob/main/packages/workplane/LICENSE)
package/dist/cli.js CHANGED
@@ -111,11 +111,14 @@ function usage() {
111
111
  " task submit shell --command <cmd> [--repo <url>] [--branch <b>] [--cwd <path>]",
112
112
  " task submit aider --prompt <text> --repo <url> [--model <m>] [--test-command <cmd>]",
113
113
  " task submit inference --model <m> --prompt <text> [--requires ollama]",
114
- " task submit harness --harness <codex|claude-code> --repo <url> --prompt <text>",
114
+ " task submit harness --harness <codex|claude-code> --repo <url> --prompt <text> [--interactive]",
115
115
  " tasks [--status <status>]",
116
116
  " task show|logs|retry|cancel <taskId>",
117
117
  " runs [--task-id <id>] [--status <status>]",
118
118
  " run show <runId>",
119
+ " run input <runId> --stdin <text>",
120
+ " run input <runId> --signal <SIGTERM|SIGKILL|SIGINT>",
121
+ " run input <runId> --resize <COLSxROWS>",
119
122
  " logs <runId|taskId>",
120
123
  " artifacts <runId>"
121
124
  ].join("\n")
@@ -201,6 +204,7 @@ async function submitHarness(args) {
201
204
  const model = valueArg(args, "--model");
202
205
  const branch = valueArg(args, "--branch");
203
206
  const testCommand = valueArg(args, "--test-command");
207
+ const interactive = args.includes("--interactive") || void 0;
204
208
  const requires = parseCsv(valueArg(args, "--requires") ?? `${harness},git`);
205
209
  const task = await httpJson("/tasks", {
206
210
  method: "POST",
@@ -209,7 +213,7 @@ async function submitHarness(args) {
209
213
  kind: "agent.run",
210
214
  adapter: harness,
211
215
  requires,
212
- payload: { prompt, repo, model, branch, testCommand }
216
+ payload: { prompt, repo, model, branch, testCommand, interactive }
213
217
  }
214
218
  });
215
219
  printJson(task);
@@ -304,6 +308,36 @@ async function main() {
304
308
  printJson(runs);
305
309
  return;
306
310
  }
311
+ if (command === "run" && subcommand === "input") {
312
+ const runId = rest[0];
313
+ if (!runId) {
314
+ throw new Error("run id is required");
315
+ }
316
+ const inputArgs = rest.slice(1);
317
+ const stdinData = valueArg(inputArgs, "--stdin");
318
+ const signal = valueArg(inputArgs, "--signal");
319
+ const resize = valueArg(inputArgs, "--resize");
320
+ let body;
321
+ if (stdinData !== void 0) {
322
+ body = { kind: "stdin", payload: { data: stdinData } };
323
+ } else if (signal !== void 0) {
324
+ if (signal !== "SIGTERM" && signal !== "SIGKILL" && signal !== "SIGINT") {
325
+ throw new Error("--signal must be SIGTERM, SIGKILL, or SIGINT");
326
+ }
327
+ body = { kind: "signal", payload: { signal } };
328
+ } else if (resize !== void 0) {
329
+ const match = /^(\d+)x(\d+)$/.exec(resize);
330
+ if (!match) {
331
+ throw new Error("--resize must be in COLSxROWS format, e.g. 220x50");
332
+ }
333
+ body = { kind: "resize", payload: { cols: Number(match[1]), rows: Number(match[2]) } };
334
+ } else {
335
+ throw new Error("one of --stdin, --signal, or --resize is required");
336
+ }
337
+ const result = await httpJson(`/runs/${runId}/input`, { method: "POST", operator: true, body });
338
+ printJson(result);
339
+ return;
340
+ }
307
341
  if (command === "run" && subcommand === "show") {
308
342
  const runId = rest[0];
309
343
  if (!runId) {
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../../core/src/env.ts","../../core/src/config.ts","../../core/src/auth.ts","../../core/src/http-client.ts","../../cli/src/index.ts"],"sourcesContent":["import { config as dotenvConfig } from \"dotenv\";\n\nlet envLoaded = false;\n\nexport function loadLocalEnv(): void {\n if (envLoaded) {\n return;\n }\n\n dotenvConfig({ path: \".env\" });\n dotenvConfig({ path: \".env.local\", override: true });\n\n envLoaded = true;\n}\n\n","import { z } from \"zod\";\nimport { loadLocalEnv } from \"./env.js\";\n\nloadLocalEnv();\n\nconst serverEnvSchema = z.object({\n DATABASE_URL: z.string().min(1),\n WORKPLANE_SERVER_PORT: z\n .string()\n .optional()\n .default(\"8787\")\n .transform((value) => Number.parseInt(value, 10)),\n WORKPLANE_NODE_TOKEN: z.string().optional(),\n WORKPLANE_OPERATOR_TOKEN: z.string().optional(),\n WORKPLANE_ENV_ALLOWLIST: z.string().optional(),\n});\n\nconst nodeEnvSchema = z.object({\n WORKPLANE_SERVER_URL: z.string().url().optional().default(\"http://localhost:8787\"),\n WORKPLANE_NODE_NAME: z.string().optional().default(\"local-node-1\"),\n WORKPLANE_NODE_ID: z.string().optional(),\n WORKPLANE_NODE_CAPABILITIES: z\n .string()\n .optional()\n .default(\"shell,git,node,typescript,aider,ollama,codex,claude-code\"),\n WORKPLANE_NODE_TOKEN: z.string().optional(),\n WORKPLANE_POLL_INTERVAL_MS: z\n .string()\n .optional()\n .default(\"3000\")\n .transform((value) => Number.parseInt(value, 10)),\n WORKPLANE_ENV_ALLOWLIST: z.string().optional(),\n});\n\nexport interface ServerConfig {\n databaseUrl: string;\n port: number;\n nodeToken?: string;\n operatorToken?: string;\n envAllowlist: string[];\n}\n\nexport interface NodeConfig {\n serverUrl: string;\n nodeName: string;\n nodeId?: string;\n nodeCapabilities: string[];\n nodeToken?: string;\n pollIntervalMs: number;\n envAllowlist: string[];\n}\n\nexport function loadServerConfig(env: NodeJS.ProcessEnv = process.env): ServerConfig {\n const parsed = serverEnvSchema.parse(env);\n\n return {\n databaseUrl: parsed.DATABASE_URL,\n port: parsed.WORKPLANE_SERVER_PORT,\n nodeToken: parsed.WORKPLANE_NODE_TOKEN,\n operatorToken: parsed.WORKPLANE_OPERATOR_TOKEN,\n envAllowlist: parseCsv(parsed.WORKPLANE_ENV_ALLOWLIST ?? \"\"),\n };\n}\n\nexport function loadNodeConfig(env: NodeJS.ProcessEnv = process.env): NodeConfig {\n const parsed = nodeEnvSchema.parse(env);\n\n return {\n serverUrl: parsed.WORKPLANE_SERVER_URL,\n nodeName: parsed.WORKPLANE_NODE_NAME,\n nodeId: parsed.WORKPLANE_NODE_ID,\n nodeCapabilities: parseCsv(parsed.WORKPLANE_NODE_CAPABILITIES),\n nodeToken: parsed.WORKPLANE_NODE_TOKEN,\n pollIntervalMs: parsed.WORKPLANE_POLL_INTERVAL_MS,\n envAllowlist: parseCsv(parsed.WORKPLANE_ENV_ALLOWLIST ?? \"\"),\n };\n}\n\nexport function parseCsv(value: string): string[] {\n return value\n .split(\",\")\n .map((entry) => entry.trim())\n .filter(Boolean);\n}\n","import type { IncomingMessage } from \"node:http\";\n\nexport function parseBearerToken(authorizationHeader: string | undefined): string | undefined {\n if (!authorizationHeader) {\n return undefined;\n }\n const match = /^Bearer\\s+(.+)$/i.exec(authorizationHeader.trim());\n return match?.[1];\n}\n\nexport function isAuthorizedBearer(provided: string | undefined, expected: string | undefined): boolean {\n if (!expected) {\n return true;\n }\n if (!provided) {\n return false;\n }\n return provided === expected;\n}\n\nexport function getRequestBearer(req: IncomingMessage): string | undefined {\n const header = req.headers.authorization;\n const value = Array.isArray(header) ? header[0] : header;\n return parseBearerToken(value);\n}\n\nexport function authHeaders(token: string | undefined): Record<string, string> {\n if (!token) {\n return {};\n }\n return { authorization: `Bearer ${token}` };\n}\n","import { authHeaders } from \"./auth.js\";\n\nexport interface WorkplaneHttpOptions {\n method?: string;\n body?: unknown;\n nodeToken?: string;\n operatorToken?: string;\n}\n\nexport function buildWorkplaneHeaders(options: WorkplaneHttpOptions = {}): Record<string, string> {\n const token = options.nodeToken ?? options.operatorToken;\n return {\n \"content-type\": \"application/json\",\n ...authHeaders(token),\n };\n}\n\nexport async function workplaneFetch<T>(\n serverUrl: string,\n path: string,\n options: WorkplaneHttpOptions = {},\n): Promise<T> {\n const response = await fetch(`${serverUrl}${path}`, {\n method: options.method ?? \"GET\",\n headers: buildWorkplaneHeaders(options),\n body: options.body ? JSON.stringify(options.body) : undefined,\n });\n\n if (!response.ok) {\n const text = await response.text();\n throw new Error(`${response.status} ${response.statusText} from ${path}: ${text}`);\n }\n\n return (await response.json()) as T;\n}\n","import { loadLocalEnv } from \"../../core/src/env.js\";\nimport { parseCsv } from \"../../core/src/config.js\";\nimport { workplaneFetch } from \"../../core/src/http-client.js\";\n\nloadLocalEnv();\n\nconst serverUrl = process.env.WORKPLANE_SERVER_URL ?? \"http://localhost:8787\";\nconst operatorToken = process.env.WORKPLANE_OPERATOR_TOKEN;\n\nasync function httpJson<T>(\n path: string,\n options?: { method?: string; body?: Record<string, unknown>; operator?: boolean },\n): Promise<T> {\n return workplaneFetch<T>(serverUrl, path, {\n method: options?.method,\n body: options?.body,\n operatorToken: options?.operator ? operatorToken : undefined,\n });\n}\n\ninterface RunListResponse {\n runs: Array<{ id: string }>;\n}\n\nfunction valueArg(args: string[], flag: string): string | undefined {\n const index = args.indexOf(flag);\n if (index === -1) {\n return undefined;\n }\n return args[index + 1];\n}\n\nfunction printJson(payload: unknown): void {\n process.stdout.write(`${JSON.stringify(payload, null, 2)}\\n`);\n}\n\nasync function resolveRunIdFromTaskId(taskId: string): Promise<string> {\n const runs = await httpJson<RunListResponse>(`/runs?taskId=${encodeURIComponent(taskId)}`);\n const runId = runs.runs[0]?.id;\n if (!runId) {\n throw new Error(`no runs found for task ${taskId}`);\n }\n return runId;\n}\n\nasync function showLogsById(id: string): Promise<void> {\n const runId = id.startsWith(\"task_\") ? await resolveRunIdFromTaskId(id) : id;\n const logs = await httpJson(`/runs/${runId}/logs`);\n printJson(logs);\n}\n\nfunction usage(): void {\n process.stdout.write(\n [\n \"workplane CLI\",\n \"\",\n \"Env: WORKPLANE_SERVER_URL, WORKPLANE_OPERATOR_TOKEN (for task submit/retry/cancel)\",\n \"\",\n \"Commands:\",\n \" server start / node start # use pnpm dev:server / dev:node\",\n \" task submit shell --command <cmd> [--repo <url>] [--branch <b>] [--cwd <path>]\",\n \" task submit aider --prompt <text> --repo <url> [--model <m>] [--test-command <cmd>]\",\n \" task submit inference --model <m> --prompt <text> [--requires ollama]\",\n \" task submit harness --harness <codex|claude-code> --repo <url> --prompt <text>\",\n \" tasks [--status <status>]\",\n \" task show|logs|retry|cancel <taskId>\",\n \" runs [--task-id <id>] [--status <status>]\",\n \" run show <runId>\",\n \" logs <runId|taskId>\",\n \" artifacts <runId>\",\n ].join(\"\\n\"),\n );\n}\n\nasync function submitShell(args: string[]): Promise<void> {\n const command = valueArg(args, \"--command\");\n if (!command) {\n throw new Error(\"--command is required\");\n }\n\n const requires = parseCsv(valueArg(args, \"--requires\") ?? \"shell\");\n const repo = valueArg(args, \"--repo\");\n const branch = valueArg(args, \"--branch\");\n const cwd = valueArg(args, \"--cwd\");\n const task = await httpJson(\"/tasks\", {\n method: \"POST\",\n operator: true,\n body: {\n kind: \"shell.exec\",\n adapter: \"shell\",\n requires,\n payload: { command, repo, branch, cwd },\n },\n });\n\n printJson(task);\n}\n\nasync function submitAider(args: string[]): Promise<void> {\n const prompt = valueArg(args, \"--prompt\");\n if (!prompt) {\n throw new Error(\"--prompt is required\");\n }\n\n const model = valueArg(args, \"--model\");\n const repo = valueArg(args, \"--repo\");\n if (!repo) {\n throw new Error(\"--repo is required\");\n }\n const branch = valueArg(args, \"--branch\");\n const testCommand = valueArg(args, \"--test-command\");\n const requires = parseCsv(valueArg(args, \"--requires\") ?? \"git,aider\");\n const task = await httpJson(\"/tasks\", {\n method: \"POST\",\n operator: true,\n body: {\n kind: \"agent.run\",\n adapter: \"aider\",\n requires,\n payload: { prompt, model, repo, branch, testCommand },\n },\n });\n\n printJson(task);\n}\n\nasync function submitInference(args: string[]): Promise<void> {\n const model = valueArg(args, \"--model\");\n const prompt = valueArg(args, \"--prompt\");\n if (!model || !prompt) {\n throw new Error(\"--model and --prompt are required\");\n }\n\n const adapter = valueArg(args, \"--adapter\") ?? \"ollama\";\n const requires = parseCsv(valueArg(args, \"--requires\") ?? adapter);\n const inputFile = valueArg(args, \"--input-file\");\n\n const task = await httpJson(\"/tasks\", {\n method: \"POST\",\n operator: true,\n body: {\n kind: \"inference.batch\",\n adapter,\n requires,\n payload: { model, prompt, inputFile },\n },\n });\n\n printJson(task);\n}\n\nasync function submitHarness(args: string[]): Promise<void> {\n const harness = valueArg(args, \"--harness\");\n const prompt = valueArg(args, \"--prompt\");\n const repo = valueArg(args, \"--repo\");\n if (!harness || !prompt || !repo) {\n throw new Error(\"--harness, --prompt, and --repo are required\");\n }\n\n if (harness !== \"codex\" && harness !== \"claude-code\") {\n throw new Error(\"--harness must be codex or claude-code\");\n }\n\n const model = valueArg(args, \"--model\");\n const branch = valueArg(args, \"--branch\");\n const testCommand = valueArg(args, \"--test-command\");\n const requires = parseCsv(valueArg(args, \"--requires\") ?? `${harness},git`);\n\n const task = await httpJson(\"/tasks\", {\n method: \"POST\",\n operator: true,\n body: {\n kind: \"agent.run\",\n adapter: harness,\n requires,\n payload: { prompt, repo, model, branch, testCommand },\n },\n });\n\n printJson(task);\n}\n\nasync function main(): Promise<void> {\n const [command, subcommand, ...rest] = process.argv.slice(2);\n\n if (!command) {\n usage();\n return;\n }\n\n if (command === \"server\" && subcommand === \"start\") {\n process.stdout.write(\"Use: pnpm dev:server\\n\");\n return;\n }\n\n if (command === \"node\" && subcommand === \"start\") {\n process.stdout.write(\"Use: pnpm dev:node\\n\");\n return;\n }\n\n if (command === \"task\" && subcommand === \"submit\") {\n const adapter = rest[0];\n const adapterArgs = rest.slice(1);\n if (adapter === \"shell\") {\n await submitShell(adapterArgs);\n return;\n }\n if (adapter === \"aider\") {\n await submitAider(adapterArgs);\n return;\n }\n if (adapter === \"inference\") {\n await submitInference(adapterArgs);\n return;\n }\n if (adapter === \"harness\") {\n await submitHarness(adapterArgs);\n return;\n }\n throw new Error(`unsupported adapter submit: ${adapter}`);\n }\n\n if (command === \"tasks\") {\n const status = valueArg([subcommand, ...rest].filter(Boolean) as string[], \"--status\");\n const tasks = await httpJson(status ? `/tasks?status=${encodeURIComponent(status)}` : \"/tasks\");\n printJson(tasks);\n return;\n }\n\n if (command === \"task\" && subcommand === \"show\") {\n const taskId = rest[0];\n if (!taskId) {\n throw new Error(\"task id is required\");\n }\n const task = await httpJson(`/tasks/${taskId}`);\n printJson(task);\n return;\n }\n\n if (command === \"task\" && subcommand === \"retry\") {\n const taskId = rest[0];\n if (!taskId) {\n throw new Error(\"task id is required\");\n }\n const task = await httpJson(`/tasks/${taskId}/retry`, { method: \"POST\", operator: true });\n printJson(task);\n return;\n }\n\n if (command === \"task\" && subcommand === \"logs\") {\n const taskId = rest[0];\n if (!taskId) {\n throw new Error(\"task id is required\");\n }\n await showLogsById(taskId);\n return;\n }\n\n if (command === \"task\" && subcommand === \"cancel\") {\n const taskId = rest[0];\n if (!taskId) {\n throw new Error(\"task id is required\");\n }\n const task = await httpJson(`/tasks/${taskId}/cancel`, { method: \"POST\", operator: true });\n printJson(task);\n return;\n }\n\n if (command === \"runs\") {\n const taskId = valueArg(rest, \"--task-id\");\n const status = valueArg(rest, \"--status\");\n const query = new URLSearchParams();\n if (taskId) {\n query.set(\"taskId\", taskId);\n }\n if (status) {\n query.set(\"status\", status);\n }\n const runs = await httpJson(query.size > 0 ? `/runs?${query.toString()}` : \"/runs\");\n printJson(runs);\n return;\n }\n\n if (command === \"run\" && subcommand === \"show\") {\n const runId = rest[0];\n if (!runId) {\n throw new Error(\"run id is required\");\n }\n const run = await httpJson(`/runs/${runId}`);\n printJson(run);\n return;\n }\n\n if (command === \"logs\") {\n const id = subcommand;\n if (!id) {\n throw new Error(\"run id or task id is required\");\n }\n await showLogsById(id);\n return;\n }\n\n if (command === \"artifacts\") {\n const runId = subcommand;\n if (!runId) {\n throw new Error(\"run id is required\");\n }\n const artifacts = await httpJson(`/runs/${runId}/artifacts`);\n printJson(artifacts);\n return;\n }\n\n usage();\n}\n\nmain().catch((error) => {\n process.stderr.write(`${String(error)}\\n`);\n process.exit(1);\n});\n"],"mappings":"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","names":["serverUrl"]}
1
+ {"version":3,"sources":["../../core/src/env.ts","../../core/src/config.ts","../../core/src/auth.ts","../../core/src/http-client.ts","../../cli/src/index.ts"],"sourcesContent":["import { config as dotenvConfig } from \"dotenv\";\n\nlet envLoaded = false;\n\nexport function loadLocalEnv(): void {\n if (envLoaded) {\n return;\n }\n\n dotenvConfig({ path: \".env\" });\n dotenvConfig({ path: \".env.local\", override: true });\n\n envLoaded = true;\n}\n\n","import { z } from \"zod\";\nimport { loadLocalEnv } from \"./env.js\";\n\nloadLocalEnv();\n\nconst serverEnvSchema = z.object({\n DATABASE_URL: z.string().min(1),\n WORKPLANE_SERVER_PORT: z\n .string()\n .optional()\n .default(\"8787\")\n .transform((value) => Number.parseInt(value, 10)),\n WORKPLANE_NODE_TOKEN: z.string().optional(),\n WORKPLANE_OPERATOR_TOKEN: z.string().optional(),\n WORKPLANE_ENV_ALLOWLIST: z.string().optional(),\n});\n\nconst nodeEnvSchema = z.object({\n WORKPLANE_SERVER_URL: z.string().url().optional().default(\"http://localhost:8787\"),\n WORKPLANE_NODE_NAME: z.string().optional().default(\"local-node-1\"),\n WORKPLANE_NODE_ID: z.string().optional(),\n WORKPLANE_NODE_CAPABILITIES: z\n .string()\n .optional()\n .default(\"shell,git,node,typescript,aider,ollama,codex,claude-code\"),\n WORKPLANE_NODE_TOKEN: z.string().optional(),\n WORKPLANE_POLL_INTERVAL_MS: z\n .string()\n .optional()\n .default(\"3000\")\n .transform((value) => Number.parseInt(value, 10)),\n WORKPLANE_ENV_ALLOWLIST: z.string().optional(),\n});\n\nexport interface ServerConfig {\n databaseUrl: string;\n port: number;\n nodeToken?: string;\n operatorToken?: string;\n envAllowlist: string[];\n}\n\nexport interface NodeConfig {\n serverUrl: string;\n nodeName: string;\n nodeId?: string;\n nodeCapabilities: string[];\n nodeToken?: string;\n pollIntervalMs: number;\n envAllowlist: string[];\n}\n\nexport function loadServerConfig(env: NodeJS.ProcessEnv = process.env): ServerConfig {\n const parsed = serverEnvSchema.parse(env);\n\n return {\n databaseUrl: parsed.DATABASE_URL,\n port: parsed.WORKPLANE_SERVER_PORT,\n nodeToken: parsed.WORKPLANE_NODE_TOKEN,\n operatorToken: parsed.WORKPLANE_OPERATOR_TOKEN,\n envAllowlist: parseCsv(parsed.WORKPLANE_ENV_ALLOWLIST ?? \"\"),\n };\n}\n\nexport function loadNodeConfig(env: NodeJS.ProcessEnv = process.env): NodeConfig {\n const parsed = nodeEnvSchema.parse(env);\n\n return {\n serverUrl: parsed.WORKPLANE_SERVER_URL,\n nodeName: parsed.WORKPLANE_NODE_NAME,\n nodeId: parsed.WORKPLANE_NODE_ID,\n nodeCapabilities: parseCsv(parsed.WORKPLANE_NODE_CAPABILITIES),\n nodeToken: parsed.WORKPLANE_NODE_TOKEN,\n pollIntervalMs: parsed.WORKPLANE_POLL_INTERVAL_MS,\n envAllowlist: parseCsv(parsed.WORKPLANE_ENV_ALLOWLIST ?? \"\"),\n };\n}\n\nexport function parseCsv(value: string): string[] {\n return value\n .split(\",\")\n .map((entry) => entry.trim())\n .filter(Boolean);\n}\n","import type { IncomingMessage } from \"node:http\";\n\nexport function parseBearerToken(authorizationHeader: string | undefined): string | undefined {\n if (!authorizationHeader) {\n return undefined;\n }\n const match = /^Bearer\\s+(.+)$/i.exec(authorizationHeader.trim());\n return match?.[1];\n}\n\nexport function isAuthorizedBearer(provided: string | undefined, expected: string | undefined): boolean {\n if (!expected) {\n return true;\n }\n if (!provided) {\n return false;\n }\n return provided === expected;\n}\n\nexport function getRequestBearer(req: IncomingMessage): string | undefined {\n const header = req.headers.authorization;\n const value = Array.isArray(header) ? header[0] : header;\n return parseBearerToken(value);\n}\n\nexport function authHeaders(token: string | undefined): Record<string, string> {\n if (!token) {\n return {};\n }\n return { authorization: `Bearer ${token}` };\n}\n","import { authHeaders } from \"./auth.js\";\n\nexport interface WorkplaneHttpOptions {\n method?: string;\n body?: unknown;\n nodeToken?: string;\n operatorToken?: string;\n}\n\nexport function buildWorkplaneHeaders(options: WorkplaneHttpOptions = {}): Record<string, string> {\n const token = options.nodeToken ?? options.operatorToken;\n return {\n \"content-type\": \"application/json\",\n ...authHeaders(token),\n };\n}\n\nexport async function workplaneFetch<T>(\n serverUrl: string,\n path: string,\n options: WorkplaneHttpOptions = {},\n): Promise<T> {\n const response = await fetch(`${serverUrl}${path}`, {\n method: options.method ?? \"GET\",\n headers: buildWorkplaneHeaders(options),\n body: options.body ? JSON.stringify(options.body) : undefined,\n });\n\n if (!response.ok) {\n const text = await response.text();\n throw new Error(`${response.status} ${response.statusText} from ${path}: ${text}`);\n }\n\n return (await response.json()) as T;\n}\n","import { loadLocalEnv } from \"../../core/src/env.js\";\nimport { parseCsv } from \"../../core/src/config.js\";\nimport { workplaneFetch } from \"../../core/src/http-client.js\";\n\nloadLocalEnv();\n\nconst serverUrl = process.env.WORKPLANE_SERVER_URL ?? \"http://localhost:8787\";\nconst operatorToken = process.env.WORKPLANE_OPERATOR_TOKEN;\n\nasync function httpJson<T>(\n path: string,\n options?: { method?: string; body?: Record<string, unknown>; operator?: boolean },\n): Promise<T> {\n return workplaneFetch<T>(serverUrl, path, {\n method: options?.method,\n body: options?.body,\n operatorToken: options?.operator ? operatorToken : undefined,\n });\n}\n\ninterface RunListResponse {\n runs: Array<{ id: string }>;\n}\n\nfunction valueArg(args: string[], flag: string): string | undefined {\n const index = args.indexOf(flag);\n if (index === -1) {\n return undefined;\n }\n return args[index + 1];\n}\n\nfunction printJson(payload: unknown): void {\n process.stdout.write(`${JSON.stringify(payload, null, 2)}\\n`);\n}\n\nasync function resolveRunIdFromTaskId(taskId: string): Promise<string> {\n const runs = await httpJson<RunListResponse>(`/runs?taskId=${encodeURIComponent(taskId)}`);\n const runId = runs.runs[0]?.id;\n if (!runId) {\n throw new Error(`no runs found for task ${taskId}`);\n }\n return runId;\n}\n\nasync function showLogsById(id: string): Promise<void> {\n const runId = id.startsWith(\"task_\") ? await resolveRunIdFromTaskId(id) : id;\n const logs = await httpJson(`/runs/${runId}/logs`);\n printJson(logs);\n}\n\nfunction usage(): void {\n process.stdout.write(\n [\n \"workplane CLI\",\n \"\",\n \"Env: WORKPLANE_SERVER_URL, WORKPLANE_OPERATOR_TOKEN (for task submit/retry/cancel)\",\n \"\",\n \"Commands:\",\n \" server start / node start # use pnpm dev:server / dev:node\",\n \" task submit shell --command <cmd> [--repo <url>] [--branch <b>] [--cwd <path>]\",\n \" task submit aider --prompt <text> --repo <url> [--model <m>] [--test-command <cmd>]\",\n \" task submit inference --model <m> --prompt <text> [--requires ollama]\",\n \" task submit harness --harness <codex|claude-code> --repo <url> --prompt <text> [--interactive]\",\n \" tasks [--status <status>]\",\n \" task show|logs|retry|cancel <taskId>\",\n \" runs [--task-id <id>] [--status <status>]\",\n \" run show <runId>\",\n \" run input <runId> --stdin <text>\",\n \" run input <runId> --signal <SIGTERM|SIGKILL|SIGINT>\",\n \" run input <runId> --resize <COLSxROWS>\",\n \" logs <runId|taskId>\",\n \" artifacts <runId>\",\n ].join(\"\\n\"),\n );\n}\n\nasync function submitShell(args: string[]): Promise<void> {\n const command = valueArg(args, \"--command\");\n if (!command) {\n throw new Error(\"--command is required\");\n }\n\n const requires = parseCsv(valueArg(args, \"--requires\") ?? \"shell\");\n const repo = valueArg(args, \"--repo\");\n const branch = valueArg(args, \"--branch\");\n const cwd = valueArg(args, \"--cwd\");\n const task = await httpJson(\"/tasks\", {\n method: \"POST\",\n operator: true,\n body: {\n kind: \"shell.exec\",\n adapter: \"shell\",\n requires,\n payload: { command, repo, branch, cwd },\n },\n });\n\n printJson(task);\n}\n\nasync function submitAider(args: string[]): Promise<void> {\n const prompt = valueArg(args, \"--prompt\");\n if (!prompt) {\n throw new Error(\"--prompt is required\");\n }\n\n const model = valueArg(args, \"--model\");\n const repo = valueArg(args, \"--repo\");\n if (!repo) {\n throw new Error(\"--repo is required\");\n }\n const branch = valueArg(args, \"--branch\");\n const testCommand = valueArg(args, \"--test-command\");\n const requires = parseCsv(valueArg(args, \"--requires\") ?? \"git,aider\");\n const task = await httpJson(\"/tasks\", {\n method: \"POST\",\n operator: true,\n body: {\n kind: \"agent.run\",\n adapter: \"aider\",\n requires,\n payload: { prompt, model, repo, branch, testCommand },\n },\n });\n\n printJson(task);\n}\n\nasync function submitInference(args: string[]): Promise<void> {\n const model = valueArg(args, \"--model\");\n const prompt = valueArg(args, \"--prompt\");\n if (!model || !prompt) {\n throw new Error(\"--model and --prompt are required\");\n }\n\n const adapter = valueArg(args, \"--adapter\") ?? \"ollama\";\n const requires = parseCsv(valueArg(args, \"--requires\") ?? adapter);\n const inputFile = valueArg(args, \"--input-file\");\n\n const task = await httpJson(\"/tasks\", {\n method: \"POST\",\n operator: true,\n body: {\n kind: \"inference.batch\",\n adapter,\n requires,\n payload: { model, prompt, inputFile },\n },\n });\n\n printJson(task);\n}\n\nasync function submitHarness(args: string[]): Promise<void> {\n const harness = valueArg(args, \"--harness\");\n const prompt = valueArg(args, \"--prompt\");\n const repo = valueArg(args, \"--repo\");\n if (!harness || !prompt || !repo) {\n throw new Error(\"--harness, --prompt, and --repo are required\");\n }\n\n if (harness !== \"codex\" && harness !== \"claude-code\") {\n throw new Error(\"--harness must be codex or claude-code\");\n }\n\n const model = valueArg(args, \"--model\");\n const branch = valueArg(args, \"--branch\");\n const testCommand = valueArg(args, \"--test-command\");\n const interactive = args.includes(\"--interactive\") || undefined;\n const requires = parseCsv(valueArg(args, \"--requires\") ?? `${harness},git`);\n\n const task = await httpJson(\"/tasks\", {\n method: \"POST\",\n operator: true,\n body: {\n kind: \"agent.run\",\n adapter: harness,\n requires,\n payload: { prompt, repo, model, branch, testCommand, interactive },\n },\n });\n\n printJson(task);\n}\n\nasync function main(): Promise<void> {\n const [command, subcommand, ...rest] = process.argv.slice(2);\n\n if (!command) {\n usage();\n return;\n }\n\n if (command === \"server\" && subcommand === \"start\") {\n process.stdout.write(\"Use: pnpm dev:server\\n\");\n return;\n }\n\n if (command === \"node\" && subcommand === \"start\") {\n process.stdout.write(\"Use: pnpm dev:node\\n\");\n return;\n }\n\n if (command === \"task\" && subcommand === \"submit\") {\n const adapter = rest[0];\n const adapterArgs = rest.slice(1);\n if (adapter === \"shell\") {\n await submitShell(adapterArgs);\n return;\n }\n if (adapter === \"aider\") {\n await submitAider(adapterArgs);\n return;\n }\n if (adapter === \"inference\") {\n await submitInference(adapterArgs);\n return;\n }\n if (adapter === \"harness\") {\n await submitHarness(adapterArgs);\n return;\n }\n throw new Error(`unsupported adapter submit: ${adapter}`);\n }\n\n if (command === \"tasks\") {\n const status = valueArg([subcommand, ...rest].filter(Boolean) as string[], \"--status\");\n const tasks = await httpJson(status ? `/tasks?status=${encodeURIComponent(status)}` : \"/tasks\");\n printJson(tasks);\n return;\n }\n\n if (command === \"task\" && subcommand === \"show\") {\n const taskId = rest[0];\n if (!taskId) {\n throw new Error(\"task id is required\");\n }\n const task = await httpJson(`/tasks/${taskId}`);\n printJson(task);\n return;\n }\n\n if (command === \"task\" && subcommand === \"retry\") {\n const taskId = rest[0];\n if (!taskId) {\n throw new Error(\"task id is required\");\n }\n const task = await httpJson(`/tasks/${taskId}/retry`, { method: \"POST\", operator: true });\n printJson(task);\n return;\n }\n\n if (command === \"task\" && subcommand === \"logs\") {\n const taskId = rest[0];\n if (!taskId) {\n throw new Error(\"task id is required\");\n }\n await showLogsById(taskId);\n return;\n }\n\n if (command === \"task\" && subcommand === \"cancel\") {\n const taskId = rest[0];\n if (!taskId) {\n throw new Error(\"task id is required\");\n }\n const task = await httpJson(`/tasks/${taskId}/cancel`, { method: \"POST\", operator: true });\n printJson(task);\n return;\n }\n\n if (command === \"runs\") {\n const taskId = valueArg(rest, \"--task-id\");\n const status = valueArg(rest, \"--status\");\n const query = new URLSearchParams();\n if (taskId) {\n query.set(\"taskId\", taskId);\n }\n if (status) {\n query.set(\"status\", status);\n }\n const runs = await httpJson(query.size > 0 ? `/runs?${query.toString()}` : \"/runs\");\n printJson(runs);\n return;\n }\n\n if (command === \"run\" && subcommand === \"input\") {\n const runId = rest[0];\n if (!runId) {\n throw new Error(\"run id is required\");\n }\n const inputArgs = rest.slice(1);\n const stdinData = valueArg(inputArgs, \"--stdin\");\n const signal = valueArg(inputArgs, \"--signal\");\n const resize = valueArg(inputArgs, \"--resize\");\n\n let body: Record<string, unknown>;\n if (stdinData !== undefined) {\n body = { kind: \"stdin\", payload: { data: stdinData } };\n } else if (signal !== undefined) {\n if (signal !== \"SIGTERM\" && signal !== \"SIGKILL\" && signal !== \"SIGINT\") {\n throw new Error(\"--signal must be SIGTERM, SIGKILL, or SIGINT\");\n }\n body = { kind: \"signal\", payload: { signal } };\n } else if (resize !== undefined) {\n const match = /^(\\d+)x(\\d+)$/.exec(resize);\n if (!match) {\n throw new Error(\"--resize must be in COLSxROWS format, e.g. 220x50\");\n }\n body = { kind: \"resize\", payload: { cols: Number(match[1]), rows: Number(match[2]) } };\n } else {\n throw new Error(\"one of --stdin, --signal, or --resize is required\");\n }\n\n const result = await httpJson(`/runs/${runId}/input`, { method: \"POST\", operator: true, body });\n printJson(result);\n return;\n }\n\n if (command === \"run\" && subcommand === \"show\") {\n const runId = rest[0];\n if (!runId) {\n throw new Error(\"run id is required\");\n }\n const run = await httpJson(`/runs/${runId}`);\n printJson(run);\n return;\n }\n\n if (command === \"logs\") {\n const id = subcommand;\n if (!id) {\n throw new Error(\"run id or task id is required\");\n }\n await showLogsById(id);\n return;\n }\n\n if (command === \"artifacts\") {\n const runId = subcommand;\n if (!runId) {\n throw new Error(\"run id is required\");\n }\n const artifacts = await httpJson(`/runs/${runId}/artifacts`);\n printJson(artifacts);\n return;\n }\n\n usage();\n}\n\nmain().catch((error) => {\n process.stderr.write(`${String(error)}\\n`);\n process.exit(1);\n});\n"],"mappings":"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","names":["serverUrl"]}
package/dist/node.js CHANGED
@@ -192,12 +192,24 @@ ${childError.message}`.trim() : stderr
192
192
  });
193
193
  });
194
194
  });
195
- const kill = () => {
196
- if (activeChild && !activeChild.killed) {
197
- activeChild.kill("SIGTERM");
195
+ const kill = (signal = "SIGTERM") => {
196
+ if (!activeChild || activeChild.killed) return;
197
+ activeChild.kill(signal);
198
+ if (signal === "SIGTERM") {
199
+ const escalation = setTimeout(() => {
200
+ if (activeChild && !activeChild.killed) {
201
+ activeChild.kill("SIGKILL");
202
+ }
203
+ }, 5e3);
204
+ escalation.unref();
205
+ }
206
+ };
207
+ const writeStdin = (data) => {
208
+ if (activeChild && !activeChild.killed && activeChild.stdin && !activeChild.stdin.destroyed) {
209
+ activeChild.stdin.write(data);
198
210
  }
199
211
  };
200
- return { exec, kill };
212
+ return { exec, kill, writeStdin };
201
213
  }
202
214
  async function ensureWorkspacePath(workspacePath, ...segments) {
203
215
  const fullPath = join2(workspacePath, ...segments);
@@ -205,6 +217,93 @@ async function ensureWorkspacePath(workspacePath, ...segments) {
205
217
  return fullPath;
206
218
  }
207
219
 
220
+ // ../node/src/pty-exec.ts
221
+ import * as pty from "node-pty";
222
+
223
+ // ../node/src/env.ts
224
+ function pickNodeEnv(allowlist) {
225
+ if (!allowlist || allowlist.length === 0) {
226
+ return { ...process.env };
227
+ }
228
+ const picked = {};
229
+ for (const key of allowlist) {
230
+ const value = process.env[key];
231
+ if (value !== void 0) {
232
+ picked[key] = value;
233
+ }
234
+ }
235
+ picked.PATH = process.env.PATH ?? "";
236
+ picked.HOME = process.env.HOME ?? "";
237
+ return picked;
238
+ }
239
+
240
+ // ../node/src/pty-exec.ts
241
+ var ANSI_RE = /\x1b(?:[@-Z\\-_]|\[[0-9;?]*[ -/]*[@-~]|\][^\x07\x1b]*(?:\x07|\x1b\\))/g;
242
+ function stripAnsi(text) {
243
+ return text.replace(ANSI_RE, "");
244
+ }
245
+ function createPtyExec(context) {
246
+ let activePty;
247
+ let killTimer;
248
+ const exec = async (command, args = [], options = {}) => new Promise((resolve2) => {
249
+ const env = {
250
+ ...pickNodeEnv(context.envAllowlist),
251
+ ...options.env ?? {},
252
+ TERM: "xterm-256color"
253
+ };
254
+ const terminal = pty.spawn(command, args, {
255
+ cwd: options.cwd ?? context.workspacePath,
256
+ env,
257
+ cols: 220,
258
+ rows: 50
259
+ });
260
+ activePty = terminal;
261
+ let output = "";
262
+ terminal.onData((chunk) => {
263
+ const stripped = stripAnsi(chunk);
264
+ output += stripped;
265
+ void context.log("stdout", stripped);
266
+ });
267
+ terminal.onExit(({ exitCode, signal: exitSignal }) => {
268
+ activePty = void 0;
269
+ if (killTimer !== void 0) {
270
+ clearTimeout(killTimer);
271
+ killTimer = void 0;
272
+ }
273
+ const terminated = exitSignal !== void 0 && exitSignal !== 0;
274
+ resolve2({
275
+ exitCode: terminated ? 1 : exitCode ?? 0,
276
+ stdout: output,
277
+ stderr: terminated ? `process terminated (signal ${exitSignal})` : ""
278
+ });
279
+ });
280
+ });
281
+ const kill = (signal = "SIGTERM") => {
282
+ if (!activePty) return;
283
+ activePty.kill(signal);
284
+ if (signal === "SIGTERM" && killTimer === void 0) {
285
+ killTimer = setTimeout(() => {
286
+ if (activePty) {
287
+ activePty.kill("SIGKILL");
288
+ activePty = void 0;
289
+ }
290
+ killTimer = void 0;
291
+ }, 5e3);
292
+ }
293
+ };
294
+ const writeStdin = (data) => {
295
+ if (activePty) {
296
+ activePty.write(data);
297
+ }
298
+ };
299
+ const ptyResize = (rows, cols) => {
300
+ if (activePty) {
301
+ activePty.resize(cols, rows);
302
+ }
303
+ };
304
+ return { exec, kill, writeStdin, ptyResize };
305
+ }
306
+
208
307
  // ../adapter-shell/src/index.ts
209
308
  import { join as join3 } from "path";
210
309
  import { writeFile } from "fs/promises";
@@ -305,13 +404,27 @@ function createHarnessAdapter(options) {
305
404
  return {
306
405
  name: options.name,
307
406
  kind: options.kind,
407
+ interactive: options.interactive,
408
+ terminalMode: options.terminalMode,
308
409
  async run(context, payload) {
309
410
  const cwd = repoPath(context.workspacePath);
310
411
  const branchName = payload.taskBranch ?? defaultTaskBranchName(context.runId);
311
412
  await createTaskBranch(context, branchName);
413
+ const binary = resolveBinary(options.binary);
414
+ if (payload.interactive && options.buildInteractiveArgs) {
415
+ const interactiveArgs = options.buildInteractiveArgs(payload);
416
+ const execPromise = context.exec(binary, interactiveArgs, { cwd });
417
+ await new Promise((resolve2) => setTimeout(resolve2, 300));
418
+ context.writeStdin?.(payload.prompt + "\n");
419
+ const result2 = await execPromise;
420
+ if (result2.exitCode !== 0) {
421
+ throw new Error(`${binary} failed with exit code ${result2.exitCode}`);
422
+ }
423
+ await captureGitDiff(context, options.name);
424
+ return;
425
+ }
312
426
  const extra = payload.extraArgs ?? options.defaultArgs ?? [];
313
427
  const promptArgs = options.buildArgs ? options.buildArgs(payload) : [...extra, payload.prompt];
314
- const binary = resolveBinary(options.binary);
315
428
  const result = await context.exec(binary, promptArgs, { cwd });
316
429
  if (result.exitCode !== 0) {
317
430
  throw new Error(`${binary} failed with exit code ${result.exitCode}`);
@@ -342,8 +455,9 @@ ${testResult.stderr}`, "utf8");
342
455
  var codexAdapter = createHarnessAdapter({
343
456
  name: "codex",
344
457
  kind: "agent.run",
458
+ interactive: true,
459
+ terminalMode: "stdio",
345
460
  binary: () => process.env.WORKPLANE_CODEX_BIN ?? "codex",
346
- defaultArgs: ["--approval-mode", "full-auto"],
347
461
  buildArgs: (payload) => {
348
462
  const args = [...payload.extraArgs ?? ["--approval-mode", "full-auto"]];
349
463
  if (payload.model) {
@@ -351,6 +465,13 @@ var codexAdapter = createHarnessAdapter({
351
465
  }
352
466
  args.push(payload.prompt);
353
467
  return args;
468
+ },
469
+ buildInteractiveArgs: (payload) => {
470
+ const args = [...payload.extraArgs ?? []];
471
+ if (payload.model) {
472
+ args.push("--model", payload.model);
473
+ }
474
+ return args;
354
475
  }
355
476
  });
356
477
 
@@ -358,14 +479,22 @@ var codexAdapter = createHarnessAdapter({
358
479
  var claudeCodeAdapter = createHarnessAdapter({
359
480
  name: "claude-code",
360
481
  kind: "agent.run",
482
+ interactive: true,
483
+ terminalMode: "pty",
361
484
  binary: () => process.env.WORKPLANE_CLAUDE_CODE_BIN ?? "claude",
362
485
  buildArgs: (payload) => {
363
486
  const args = ["-p", payload.prompt, "--output-format", "text"];
364
487
  if (payload.model) {
365
488
  args.push("--model", payload.model);
366
489
  }
367
- const extra = payload.extraArgs ?? [];
368
- return [...args, ...extra];
490
+ return [...args, ...payload.extraArgs ?? []];
491
+ },
492
+ buildInteractiveArgs: (payload) => {
493
+ const args = [];
494
+ if (payload.model) {
495
+ args.push("--model", payload.model);
496
+ }
497
+ return [...args, ...payload.extraArgs ?? []];
369
498
  }
370
499
  });
371
500
 
@@ -423,6 +552,20 @@ async function emitArtifact(config, runId, artifact) {
423
552
  ...nodeHttpOptions(config)
424
553
  });
425
554
  }
555
+ async function pollInputEvents(config, runId, afterSequence) {
556
+ const result = await workplaneFetch(
557
+ config.serverUrl,
558
+ `/runs/${runId}/input?afterSequence=${afterSequence}`,
559
+ nodeHttpOptions(config)
560
+ );
561
+ return result.events;
562
+ }
563
+ async function markDelivered(config, runId, sequence) {
564
+ await workplaneFetch(config.serverUrl, `/runs/${runId}/input/${sequence}/delivered`, {
565
+ method: "POST",
566
+ ...nodeHttpOptions(config)
567
+ });
568
+ }
426
569
  async function isCancelled(config, runId) {
427
570
  const run = await workplaneFetch(config.serverUrl, `/runs/${runId}`, nodeHttpOptions(config));
428
571
  if (run.status === "cancelled") {
@@ -486,39 +629,46 @@ async function executeAssignment(config, assignment) {
486
629
  );
487
630
  await updateStatus(config, runId, "running");
488
631
  const serverEnvAllowlist = config.envAllowlist.length > 0 ? config.envAllowlist : loadServerConfig().envAllowlist.length > 0 ? loadServerConfig().envAllowlist : void 0;
489
- const { exec, kill } = createCancellableExec({
632
+ const logFn = async (stream, message, stepName) => {
633
+ if (mirrorLogs) {
634
+ const prefix = `[run:${runId}] [${stream}]${stepName ? ` [${stepName}]` : ""} `;
635
+ if (stream === "stderr") {
636
+ process.stderr.write(`${prefix}${message}`);
637
+ } else {
638
+ process.stdout.write(`${prefix}${message}`);
639
+ }
640
+ }
641
+ await appendLogs(config, runId, [{ stream, message, stepName }]);
642
+ };
643
+ const isInteractive = task.payload.interactive === true;
644
+ const usePty = adapter.terminalMode === "pty" && isInteractive;
645
+ const stubContext = {
490
646
  runId,
491
647
  taskId: task.id,
492
648
  workspacePath: runWorkspace,
493
- log: async () => {
494
- },
649
+ log: logFn,
495
650
  exec: async () => ({ exitCode: 1, stdout: "", stderr: "not initialized" }),
496
651
  ensureWorkspace: async (...segments) => ensureWorkspacePath(runWorkspace, ...segments),
497
652
  emitArtifact: async () => {
498
653
  },
499
654
  envAllowlist: serverEnvAllowlist
500
- });
655
+ };
656
+ const handle = usePty ? createPtyExec(stubContext) : createCancellableExec(stubContext);
657
+ const { exec, kill, writeStdin } = handle;
658
+ const ptyResize = "ptyResize" in handle ? handle.ptyResize : void 0;
501
659
  const context = {
502
660
  runId,
503
661
  taskId: task.id,
504
662
  workspacePath: runWorkspace,
505
663
  envAllowlist: serverEnvAllowlist,
506
- log: async (stream, message, stepName) => {
507
- if (mirrorLogs) {
508
- const prefix = `[run:${runId}] [${stream}]${stepName ? ` [${stepName}]` : ""} `;
509
- if (stream === "stderr") {
510
- process.stderr.write(`${prefix}${message}`);
511
- } else {
512
- process.stdout.write(`${prefix}${message}`);
513
- }
514
- }
515
- await appendLogs(config, runId, [{ stream, message, stepName }]);
516
- },
664
+ log: logFn,
517
665
  exec,
518
666
  ensureWorkspace: async (...segments) => ensureWorkspacePath(runWorkspace, ...segments),
519
667
  emitArtifact: async (input) => {
520
668
  await emitArtifact(config, runId, input);
521
- }
669
+ },
670
+ writeStdin: adapter.interactive && isInteractive ? writeStdin : void 0,
671
+ ptyResize: adapter.interactive && isInteractive ? ptyResize : void 0
522
672
  };
523
673
  const cancelInterval = setInterval(() => {
524
674
  void isCancelled(config, runId).then((cancelled) => {
@@ -527,6 +677,30 @@ async function executeAssignment(config, assignment) {
527
677
  }
528
678
  });
529
679
  }, 2e3);
680
+ let lastInputSequence = 0;
681
+ let inputInterval;
682
+ if (adapter.interactive && isInteractive) {
683
+ inputInterval = setInterval(() => {
684
+ void pollInputEvents(config, runId, lastInputSequence).then(async (events) => {
685
+ for (const event of events) {
686
+ if (event.kind === "stdin") {
687
+ writeStdin(String(event.payload.data ?? ""));
688
+ } else if (event.kind === "signal") {
689
+ kill(String(event.payload.signal ?? "SIGTERM"));
690
+ } else if (event.kind === "resize" && ptyResize) {
691
+ const rows = Number(event.payload.rows ?? 50);
692
+ const cols = Number(event.payload.cols ?? 220);
693
+ ptyResize(rows, cols);
694
+ }
695
+ await markDelivered(config, runId, event.sequence);
696
+ lastInputSequence = Math.max(lastInputSequence, event.sequence);
697
+ }
698
+ }).catch((err) => {
699
+ process.stderr.write(`input poll error [run:${runId}]: ${String(err)}
700
+ `);
701
+ });
702
+ }, 500);
703
+ }
530
704
  try {
531
705
  await context.log("system", `starting adapter ${adapter.name}`, "run-adapter");
532
706
  const hasRepo = await checkoutRepoIfPresent(context, task.payload);
@@ -558,6 +732,9 @@ async function executeAssignment(config, assignment) {
558
732
  await updateStatus(config, runId, "failed", message);
559
733
  } finally {
560
734
  clearInterval(cancelInterval);
735
+ if (inputInterval !== void 0) {
736
+ clearInterval(inputInterval);
737
+ }
561
738
  kill();
562
739
  }
563
740
  }