doer-agent 0.2.0 → 0.2.2

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.
Files changed (2) hide show
  1. package/dist/agent.js +101 -13
  2. package/package.json +1 -1
package/dist/agent.js CHANGED
@@ -357,6 +357,20 @@ function writeTaskStream(taskId, stream, chunk) {
357
357
  function writeTaskUpload(taskId, message) {
358
358
  process.stdout.write(`[doer-agent][task=${taskId}][upload] ${message}\n`);
359
359
  }
360
+ function writeRpcStream(requestId, stream, chunk) {
361
+ const target = stream === "stdout" ? process.stdout : process.stderr;
362
+ const lines = chunk.replace(/\r/g, "\n").split("\n");
363
+ for (let i = 0; i < lines.length; i += 1) {
364
+ const line = lines[i];
365
+ if (line.length === 0 && i === lines.length - 1) {
366
+ continue;
367
+ }
368
+ target.write(`[doer-agent][rpc=${requestId}][${stream}] ${line}\n`);
369
+ }
370
+ }
371
+ function writeRpcStatus(requestId, message) {
372
+ process.stdout.write(`[doer-agent][rpc=${requestId}][status] ${message}\n`);
373
+ }
360
374
  function isLikelyNatsAuthError(error) {
361
375
  const message = (error instanceof Error ? error.message : String(error)).toLowerCase();
362
376
  return (message.includes("auth")
@@ -753,10 +767,15 @@ function normalizeShellRpcRequest(args) {
753
767
  if (requestAgentId !== args.agentId) {
754
768
  throw new Error("agent id mismatch");
755
769
  }
770
+ const kind = args.request.kind === "apply_patch" ? "apply_patch" : "shell";
756
771
  const command = typeof args.request.command === "string" ? args.request.command.trim() : "";
757
- if (!command) {
772
+ const patch = typeof args.request.patch === "string" ? args.request.patch : "";
773
+ if (kind === "shell" && !command) {
758
774
  throw new Error("missing command");
759
775
  }
776
+ if (kind === "apply_patch" && !patch.trim()) {
777
+ throw new Error("missing patch");
778
+ }
760
779
  const responseSubject = typeof args.request.responseSubject === "string" ? args.request.responseSubject.trim() : "";
761
780
  if (!responseSubject) {
762
781
  throw new Error("missing responseSubject");
@@ -764,7 +783,35 @@ function normalizeShellRpcRequest(args) {
764
783
  const cwd = typeof args.request.cwd === "string" && args.request.cwd.trim() ? args.request.cwd.trim() : null;
765
784
  const timeoutRaw = Number(args.request.timeoutMs);
766
785
  const timeoutMs = Number.isFinite(timeoutRaw) ? Math.max(1000, Math.min(Math.floor(timeoutRaw), 300000)) : 30000;
767
- return { requestId, command, cwd, timeoutMs, responseSubject };
786
+ return {
787
+ kind,
788
+ requestId,
789
+ command: kind === "shell" ? command : null,
790
+ patch: kind === "apply_patch" ? patch : null,
791
+ cwd,
792
+ timeoutMs,
793
+ responseSubject,
794
+ runtimeEnvPatch: normalizeEnvPatch(args.request.runtimeEnvPatch),
795
+ codexAuthBundle: normalizeShellRpcCodexAuthBundle(args.request.codexAuth),
796
+ };
797
+ }
798
+ function normalizeShellRpcCodexAuthBundle(value) {
799
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
800
+ return null;
801
+ }
802
+ const row = value;
803
+ const authJson = typeof row.authJson === "string" ? row.authJson : null;
804
+ if (!authJson) {
805
+ return null;
806
+ }
807
+ return {
808
+ taskId: typeof row.taskId === "string" ? row.taskId : undefined,
809
+ authMode: row.authMode === "oauth" ? "oauth" : row.authMode === "api_key" ? "api_key" : undefined,
810
+ issuedAt: typeof row.issuedAt === "string" ? row.issuedAt : undefined,
811
+ expiresAt: typeof row.expiresAt === "string" ? row.expiresAt : undefined,
812
+ authJson,
813
+ apiKey: typeof row.apiKey === "string" || row.apiKey === null ? row.apiKey : undefined,
814
+ };
768
815
  }
769
816
  function publishShellRpcResponse(args) {
770
817
  args.nc.publish(args.responseSubject, shellRpcCodec.encode(JSON.stringify(args.payload)));
@@ -779,28 +826,61 @@ async function handleShellRpcMessage(args) {
779
826
  const request = normalizeShellRpcRequest({ request: payload, agentId: args.agentId });
780
827
  requestId = request.requestId;
781
828
  responseSubject = request.responseSubject;
829
+ const startedAtMs = Date.now();
782
830
  const shellPath = resolveShellPath();
783
831
  const taskWorkspace = resolveTaskWorkspace(request.cwd);
784
- const runtimeBinPath = path.join(AGENT_PROJECT_DIR, "runtime/bin");
785
- const taskPath = [runtimeBinPath, process.env.PATH || ""].filter(Boolean).join(path.delimiter);
786
- const child = spawn(request.command, {
832
+ const codexAuth = await prepareCodexAuthBundle(request.codexAuthBundle);
833
+ const baseTaskEnvPatch = {
834
+ ...request.runtimeEnvPatch,
835
+ ...(codexAuth?.envPatch ?? {}),
836
+ WORKSPACE: taskWorkspace,
837
+ };
838
+ const taskGitEnv = await prepareTaskGitEnv({
787
839
  cwd: taskWorkspace,
788
- shell: shellPath,
789
- detached: process.platform !== "win32",
790
- env: {
791
- ...process.env,
792
- WORKSPACE: taskWorkspace,
793
- PATH: taskPath,
794
- },
795
- stdio: ["ignore", "pipe", "pipe"],
840
+ baseEnvPatch: baseTaskEnvPatch,
796
841
  });
842
+ const runtimeBinPath = path.join(AGENT_PROJECT_DIR, "runtime/bin");
843
+ const taskPath = [runtimeBinPath, process.env.PATH || ""].filter(Boolean).join(path.delimiter);
844
+ const child = request.kind === "apply_patch"
845
+ ? spawn("apply_patch", {
846
+ cwd: taskWorkspace,
847
+ detached: process.platform !== "win32",
848
+ env: {
849
+ ...process.env,
850
+ ...baseTaskEnvPatch,
851
+ ...taskGitEnv.envPatch,
852
+ PATH: taskPath,
853
+ DOER_AGENT_TOKEN: args.agentToken,
854
+ },
855
+ stdio: ["pipe", "pipe", "pipe"],
856
+ })
857
+ : spawn(request.command ?? "", {
858
+ cwd: taskWorkspace,
859
+ shell: shellPath,
860
+ detached: process.platform !== "win32",
861
+ env: {
862
+ ...process.env,
863
+ ...baseTaskEnvPatch,
864
+ ...taskGitEnv.envPatch,
865
+ PATH: taskPath,
866
+ DOER_AGENT_TOKEN: args.agentToken,
867
+ },
868
+ stdio: ["ignore", "pipe", "pipe"],
869
+ });
870
+ if (request.kind === "apply_patch") {
871
+ child.stdin?.write(request.patch ?? "");
872
+ child.stdin?.end();
873
+ }
874
+ writeRpcStatus(requestId, `started kind=${request.kind} cwd=${taskWorkspace} shell=${request.kind === "shell" ? shellPath : "apply_patch"}`);
797
875
  child.stdout.setEncoding("utf8");
798
876
  child.stderr.setEncoding("utf8");
799
877
  child.stdout.on("data", (chunk) => {
800
878
  stdout += chunk;
879
+ writeRpcStream(requestId, "stdout", chunk);
801
880
  });
802
881
  child.stderr.on("data", (chunk) => {
803
882
  stderr += chunk;
883
+ writeRpcStream(requestId, "stderr", chunk);
804
884
  });
805
885
  let timedOut = false;
806
886
  const timeout = setTimeout(() => {
@@ -832,6 +912,7 @@ async function handleShellRpcMessage(args) {
832
912
  ...(timedOut ? { error: `Command timed out after ${request.timeoutMs}ms` } : {}),
833
913
  },
834
914
  });
915
+ writeRpcStatus(requestId, `${timedOut ? "timed_out" : "completed"} exitCode=${result.exitCode ?? "null"} signal=${result.signal ?? "null"} durationMs=${Date.now() - startedAtMs}`);
835
916
  }
836
917
  catch (error) {
837
918
  const message = error instanceof Error ? error.message : String(error);
@@ -850,6 +931,7 @@ async function handleShellRpcMessage(args) {
850
931
  },
851
932
  });
852
933
  }
934
+ writeRpcStatus(requestId, `failed error=${message}`);
853
935
  writeAgentError(`shell rpc failed requestId=${requestId} error=${message}`);
854
936
  }
855
937
  }
@@ -866,6 +948,7 @@ function subscribeToShellRpc(args) {
866
948
  msg,
867
949
  jetstream: args.jetstream,
868
950
  agentId: args.agentId,
951
+ agentToken: args.agentToken,
869
952
  });
870
953
  },
871
954
  });
@@ -1042,6 +1125,9 @@ async function prepareTaskCodexAuth(args) {
1042
1125
  writeAgentError(`task=${args.taskId} codex auth sync skipped: ${message}`);
1043
1126
  return null;
1044
1127
  });
1128
+ return await prepareCodexAuthBundle(bundle);
1129
+ }
1130
+ async function prepareCodexAuthBundle(bundle) {
1045
1131
  if (!bundle || typeof bundle.authJson !== "string") {
1046
1132
  return null;
1047
1133
  }
@@ -1390,6 +1476,7 @@ async function main() {
1390
1476
  jetstream,
1391
1477
  userId,
1392
1478
  agentId: initialAgentId,
1479
+ agentToken,
1393
1480
  });
1394
1481
  for (const pendingTaskId of pendingTaskIds) {
1395
1482
  await waitForAvailableSlot();
@@ -1535,6 +1622,7 @@ async function main() {
1535
1622
  jetstream,
1536
1623
  userId,
1537
1624
  agentId: refreshedAgentId,
1625
+ agentToken,
1538
1626
  });
1539
1627
  for (const pendingTaskId of pendingTaskIds) {
1540
1628
  await waitForAvailableSlot();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "doer-agent",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Reverse-polling agent runtime for doer",
5
5
  "type": "module",
6
6
  "main": "dist/agent.js",