doer-agent 0.3.2 → 0.3.5

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 +129 -32
  2. package/package.json +1 -1
package/dist/agent.js CHANGED
@@ -609,7 +609,6 @@ function createDefaultAgentSettingsConfig() {
609
609
  codex: {
610
610
  model: "gpt-5.4",
611
611
  authMode: "api_key",
612
- apiKey: null,
613
612
  },
614
613
  realtime: {
615
614
  model: process.env.OPENAI_REALTIME_MODEL?.trim() || "gpt-realtime",
@@ -687,7 +686,6 @@ function normalizeAgentSettingsConfig(value, fallback) {
687
686
  codex: {
688
687
  model: typeof codex.model === "string" && codex.model.trim() ? codex.model.trim() : base.codex.model,
689
688
  authMode: codex.authMode === "oauth" ? "oauth" : codex.authMode === "api_key" ? "api_key" : base.codex.authMode,
690
- apiKey: codex.apiKey === null ? null : normalizeNullableString(codex.apiKey) ?? base.codex.apiKey,
691
689
  },
692
690
  realtime: {
693
691
  model: typeof realtime.model === "string" && realtime.model.trim() ? realtime.model.trim() : base.realtime.model,
@@ -768,7 +766,6 @@ function toMaskedSecret(value) {
768
766
  return { has: true, masked: maskSecretPreview(value), length: value.length };
769
767
  }
770
768
  function toAgentSettingsPublic(config) {
771
- const codexKey = toMaskedSecret(config.codex.apiKey);
772
769
  const realtimeKey = toMaskedSecret(config.realtime.apiKey);
773
770
  const gitOauth = toMaskedSecret(config.git.oauthToken);
774
771
  const awsSecret = toMaskedSecret(config.aws.secretAccessKey);
@@ -784,9 +781,9 @@ function toAgentSettingsPublic(config) {
784
781
  codex: {
785
782
  model: config.codex.model,
786
783
  authMode: config.codex.authMode,
787
- hasApiKey: codexKey.has,
788
- apiKeyMasked: codexKey.masked,
789
- apiKeyLength: codexKey.length,
784
+ hasApiKey: false,
785
+ apiKeyMasked: null,
786
+ apiKeyLength: null,
790
787
  },
791
788
  realtime: {
792
789
  model: config.realtime.model,
@@ -874,7 +871,6 @@ function normalizeAgentSettingsPatch(value) {
874
871
  move("firstTurnPrompt", "general", "firstTurnPrompt");
875
872
  move("codexModel", "codex", "model");
876
873
  move("codexAuthMode", "codex", "authMode");
877
- move("codexApiKey", "codex", "apiKey");
878
874
  move("realtimeModel", "realtime", "model");
879
875
  move("realtimeVoice", "realtime", "voice");
880
876
  move("realtimeWakeName", "realtime", "wakeName");
@@ -915,9 +911,6 @@ async function resolveAgentSettingsConfig(args) {
915
911
  }
916
912
  function buildAgentSettingsEnvPatch(config) {
917
913
  const envPatch = {};
918
- if (config.codex.authMode === "api_key" && config.codex.apiKey) {
919
- envPatch.OPENAI_API_KEY = config.codex.apiKey;
920
- }
921
914
  if (config.git.enabled) {
922
915
  if (config.git.name)
923
916
  envPatch.GIT_AUTHOR_NAME = config.git.name;
@@ -1113,6 +1106,7 @@ async function startManagedRun(args) {
1113
1106
  userId: args.userId,
1114
1107
  taskId: args.runId,
1115
1108
  codexAuthBundle: args.codexAuthBundle,
1109
+ runtimeEnvPatch: args.runtimeEnvPatch,
1116
1110
  });
1117
1111
  const child = spawnManagedCodexCommand({
1118
1112
  codexArgs: args.codexArgs,
@@ -1312,6 +1306,62 @@ async function runLocalCodexCli(args, timeoutMs, envPatch) {
1312
1306
  });
1313
1307
  });
1314
1308
  }
1309
+ async function runLocalCodexCliWithInput(args, input, timeoutMs, envPatch) {
1310
+ const command = buildLocalCodexCliCommand(args);
1311
+ const workspaceRoot = workspaceRootOverride ?? (process.env.WORKSPACE?.trim() || process.cwd());
1312
+ const env = {
1313
+ ...process.env,
1314
+ ...(envPatch ?? {}),
1315
+ WORKSPACE: workspaceRoot,
1316
+ CODEX_HOME: resolveCodexHomePath(),
1317
+ };
1318
+ return await new Promise((resolve, reject) => {
1319
+ const child = spawn(command, {
1320
+ cwd: workspaceRoot,
1321
+ shell: resolveShellPath(),
1322
+ env,
1323
+ stdio: ["pipe", "pipe", "pipe"],
1324
+ });
1325
+ let stdout = "";
1326
+ let stderr = "";
1327
+ let done = false;
1328
+ let timedOut = false;
1329
+ child.stdout.setEncoding("utf8");
1330
+ child.stderr.setEncoding("utf8");
1331
+ child.stdout.on("data", (chunk) => {
1332
+ stdout += chunk;
1333
+ });
1334
+ child.stderr.on("data", (chunk) => {
1335
+ stderr += chunk;
1336
+ });
1337
+ child.stdin?.write(input);
1338
+ if (!input.endsWith("\n")) {
1339
+ child.stdin?.write("\n");
1340
+ }
1341
+ child.stdin?.end();
1342
+ const timer = setTimeout(() => {
1343
+ timedOut = true;
1344
+ sendSignalToTaskProcess(child, "SIGTERM");
1345
+ setTimeout(() => sendSignalToTaskProcess(child, "SIGKILL"), 1000);
1346
+ }, Math.max(500, timeoutMs));
1347
+ child.once("error", (error) => {
1348
+ if (done) {
1349
+ return;
1350
+ }
1351
+ done = true;
1352
+ clearTimeout(timer);
1353
+ reject(error);
1354
+ });
1355
+ child.once("exit", (code) => {
1356
+ if (done) {
1357
+ return;
1358
+ }
1359
+ done = true;
1360
+ clearTimeout(timer);
1361
+ resolve({ code, stdout, stderr, timedOut });
1362
+ });
1363
+ });
1364
+ }
1315
1365
  function buildSkillGeneratorPrompt(userPrompt) {
1316
1366
  return [
1317
1367
  "Create a Codex skill from the user's description.",
@@ -1663,6 +1713,20 @@ async function startLocalCodexLogin() {
1663
1713
  }
1664
1714
  throw new Error(normalized || `Codex login failed with code ${result.code ?? "null"}`);
1665
1715
  }
1716
+ async function loginLocalCodexWithApiKey(apiKey) {
1717
+ const result = await runLocalCodexCliWithInput(["login", "--with-api-key"], apiKey, 15000);
1718
+ const normalized = stripAnsi([result.stdout, result.stderr].filter(Boolean).join("\n")).trim();
1719
+ if ((result.code ?? 1) !== 0) {
1720
+ throw new Error(normalized || `Codex API key login failed with code ${result.code ?? "null"}`);
1721
+ }
1722
+ const status = await getLocalCodexLoginStatus().catch(() => null);
1723
+ return {
1724
+ loggedIn: status?.loggedIn === true,
1725
+ output: status?.output || normalized || "Logged in",
1726
+ verificationUri: null,
1727
+ userCode: null,
1728
+ };
1729
+ }
1666
1730
  async function logoutLocalCodexAuth() {
1667
1731
  if (pendingCodexDeviceAuth && pendingCodexDeviceAuth.child.exitCode === null) {
1668
1732
  sendSignalToTaskProcess(pendingCodexDeviceAuth.child, "SIGTERM");
@@ -1694,11 +1758,15 @@ function normalizeCodexAuthRpcRequest(args) {
1694
1758
  const responseSubject = typeof args.request.responseSubject === "string" ? args.request.responseSubject.trim() : "";
1695
1759
  const requestAgentId = typeof args.request.agentId === "string" ? args.request.agentId.trim() : "";
1696
1760
  const actionRaw = typeof args.request.action === "string" ? args.request.action.trim() : "";
1697
- const action = actionRaw === "start" || actionRaw === "logout" ? actionRaw : "status";
1761
+ const action = actionRaw === "start" || actionRaw === "logout" || actionRaw === "login_api_key" ? actionRaw : "status";
1762
+ const apiKey = typeof args.request.apiKey === "string" && args.request.apiKey.trim() ? args.request.apiKey.trim() : null;
1698
1763
  if (!requestId || !responseSubject || !requestAgentId || requestAgentId !== args.agentId) {
1699
1764
  throw new Error("invalid codex auth rpc request");
1700
1765
  }
1701
- return { requestId, responseSubject, action };
1766
+ if (action === "login_api_key" && !apiKey) {
1767
+ throw new Error("api key is required");
1768
+ }
1769
+ return { requestId, responseSubject, action, apiKey };
1702
1770
  }
1703
1771
  function publishCodexAuthRpcResponse(args) {
1704
1772
  args.nc.publish(args.responseSubject, codexAuthRpcCodec.encode(JSON.stringify(args.payload)));
@@ -1712,7 +1780,10 @@ async function handleCodexAuthRpcMessage(args) {
1712
1780
  requestId = request.requestId;
1713
1781
  responseSubject = request.responseSubject;
1714
1782
  let result = null;
1715
- if (request.action === "start") {
1783
+ if (request.action === "login_api_key") {
1784
+ result = await loginLocalCodexWithApiKey(request.apiKey ?? "");
1785
+ }
1786
+ else if (request.action === "start") {
1716
1787
  const status = await getLocalCodexLoginStatus();
1717
1788
  if (status.loggedIn) {
1718
1789
  result = { loggedIn: true, output: status.output };
@@ -2596,12 +2667,13 @@ async function executeFsRpc(args) {
2596
2667
  if (!uploadUrl || !agentId) {
2597
2668
  throw new Error("missing upload parameters");
2598
2669
  }
2670
+ const resolvedUploadUrl = new URL(uploadUrl, `${args.serverBaseUrl}/`).toString();
2599
2671
  const data = await readFile(abs);
2600
2672
  const fileName = path.basename(abs) || "file";
2601
2673
  const form = new FormData();
2602
2674
  form.append("file", new File([data], fileName));
2603
2675
  form.append("agentId", agentId);
2604
- const response = await fetch(uploadUrl, {
2676
+ const response = await fetch(resolvedUploadUrl, {
2605
2677
  method: "POST",
2606
2678
  headers: { Authorization: `Bearer ${args.agentToken}` },
2607
2679
  body: form,
@@ -3559,16 +3631,18 @@ function normalizeShellRpcCodexAuthBundle(value) {
3559
3631
  }
3560
3632
  const row = value;
3561
3633
  const authJson = typeof row.authJson === "string" ? row.authJson : null;
3562
- if (!authJson) {
3634
+ const authMode = row.authMode === "oauth" ? "oauth" : row.authMode === "api_key" ? "api_key" : undefined;
3635
+ const apiKey = typeof row.apiKey === "string" || row.apiKey === null ? row.apiKey : undefined;
3636
+ if (!authJson && authMode !== "api_key" && apiKey === undefined) {
3563
3637
  return null;
3564
3638
  }
3565
3639
  return {
3566
3640
  taskId: typeof row.taskId === "string" ? row.taskId : undefined,
3567
- authMode: row.authMode === "oauth" ? "oauth" : row.authMode === "api_key" ? "api_key" : undefined,
3641
+ authMode,
3568
3642
  issuedAt: typeof row.issuedAt === "string" ? row.issuedAt : undefined,
3569
3643
  expiresAt: typeof row.expiresAt === "string" ? row.expiresAt : undefined,
3570
- authJson,
3571
- apiKey: typeof row.apiKey === "string" || row.apiKey === null ? row.apiKey : undefined,
3644
+ authJson: authJson ?? undefined,
3645
+ apiKey,
3572
3646
  };
3573
3647
  }
3574
3648
  async function postJson(url, body) {
@@ -3733,27 +3807,49 @@ async function checkCancelRequested(args) {
3733
3807
  const response = await getJson(`${args.serverBaseUrl}/api/agent/tasks/${encodeURIComponent(args.taskId)}/events?${query.toString()}`);
3734
3808
  return Boolean(response.task?.cancelRequested);
3735
3809
  }
3736
- async function prepareTaskCodexAuth(args) {
3737
- void args;
3810
+ async function syncCodexAuthState(args) {
3811
+ const envPatch = {};
3812
+ const synced = false;
3813
+ if (args.authMode === "api_key") {
3814
+ if (args.apiKey) {
3815
+ envPatch.OPENAI_API_KEY = args.apiKey;
3816
+ }
3817
+ }
3738
3818
  return {
3739
- envPatch: {},
3819
+ envPatch,
3740
3820
  cleanup: async () => { },
3741
3821
  meta: {
3742
- codexAuthSource: "agent_local",
3743
- codexAuthSynced: false,
3822
+ codexAuthSource: args.source,
3823
+ codexAuthMode: args.authMode ?? null,
3824
+ codexAuthHasApiKey: Boolean(args.apiKey),
3825
+ codexAuthHasAuthJson: Boolean(args.authJson),
3826
+ codexAuthIssuedAt: args.issuedAt ?? null,
3827
+ codexAuthExpiresAt: args.expiresAt ?? null,
3828
+ codexAuthSynced: synced,
3744
3829
  },
3745
3830
  };
3746
3831
  }
3832
+ async function prepareTaskCodexAuth(args) {
3833
+ void args;
3834
+ return await syncCodexAuthState({
3835
+ source: "agent_local",
3836
+ authJson: null,
3837
+ issuedAt: null,
3838
+ expiresAt: null,
3839
+ });
3840
+ }
3747
3841
  async function prepareCodexAuthBundle(bundle) {
3748
- void bundle;
3749
- return {
3750
- envPatch: {},
3751
- cleanup: async () => { },
3752
- meta: {
3753
- codexAuthSource: "agent_local",
3754
- codexAuthSynced: false,
3755
- },
3756
- };
3842
+ if (!bundle) {
3843
+ return null;
3844
+ }
3845
+ return await syncCodexAuthState({
3846
+ source: "server_bundle",
3847
+ authMode: bundle.authMode,
3848
+ apiKey: bundle.apiKey,
3849
+ authJson: bundle.authJson ?? null,
3850
+ issuedAt: bundle.issuedAt ?? null,
3851
+ expiresAt: bundle.expiresAt ?? null,
3852
+ });
3757
3853
  }
3758
3854
  async function prepareCommandExecution(args) {
3759
3855
  const shellPath = resolveShellPath();
@@ -3767,6 +3863,7 @@ async function prepareCommandExecution(args) {
3767
3863
  DOER_USER_ID: args.userId,
3768
3864
  DOER_AGENT_TASK_ID: args.taskId,
3769
3865
  ...buildAgentSettingsEnvPatch(localAgentSettings),
3866
+ ...args.runtimeEnvPatch,
3770
3867
  ...(codexAuth?.envPatch ?? {}),
3771
3868
  WORKSPACE: taskWorkspace,
3772
3869
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "doer-agent",
3
- "version": "0.3.2",
3
+ "version": "0.3.5",
4
4
  "description": "Reverse-polling agent runtime for doer",
5
5
  "type": "module",
6
6
  "main": "dist/agent.js",