doer-agent 0.3.4 → 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 +127 -31
  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 };
@@ -3560,16 +3631,18 @@ function normalizeShellRpcCodexAuthBundle(value) {
3560
3631
  }
3561
3632
  const row = value;
3562
3633
  const authJson = typeof row.authJson === "string" ? row.authJson : null;
3563
- 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) {
3564
3637
  return null;
3565
3638
  }
3566
3639
  return {
3567
3640
  taskId: typeof row.taskId === "string" ? row.taskId : undefined,
3568
- authMode: row.authMode === "oauth" ? "oauth" : row.authMode === "api_key" ? "api_key" : undefined,
3641
+ authMode,
3569
3642
  issuedAt: typeof row.issuedAt === "string" ? row.issuedAt : undefined,
3570
3643
  expiresAt: typeof row.expiresAt === "string" ? row.expiresAt : undefined,
3571
- authJson,
3572
- apiKey: typeof row.apiKey === "string" || row.apiKey === null ? row.apiKey : undefined,
3644
+ authJson: authJson ?? undefined,
3645
+ apiKey,
3573
3646
  };
3574
3647
  }
3575
3648
  async function postJson(url, body) {
@@ -3734,27 +3807,49 @@ async function checkCancelRequested(args) {
3734
3807
  const response = await getJson(`${args.serverBaseUrl}/api/agent/tasks/${encodeURIComponent(args.taskId)}/events?${query.toString()}`);
3735
3808
  return Boolean(response.task?.cancelRequested);
3736
3809
  }
3737
- async function prepareTaskCodexAuth(args) {
3738
- 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
+ }
3739
3818
  return {
3740
- envPatch: {},
3819
+ envPatch,
3741
3820
  cleanup: async () => { },
3742
3821
  meta: {
3743
- codexAuthSource: "agent_local",
3744
- 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,
3745
3829
  },
3746
3830
  };
3747
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
+ }
3748
3841
  async function prepareCodexAuthBundle(bundle) {
3749
- void bundle;
3750
- return {
3751
- envPatch: {},
3752
- cleanup: async () => { },
3753
- meta: {
3754
- codexAuthSource: "agent_local",
3755
- codexAuthSynced: false,
3756
- },
3757
- };
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
+ });
3758
3853
  }
3759
3854
  async function prepareCommandExecution(args) {
3760
3855
  const shellPath = resolveShellPath();
@@ -3768,6 +3863,7 @@ async function prepareCommandExecution(args) {
3768
3863
  DOER_USER_ID: args.userId,
3769
3864
  DOER_AGENT_TASK_ID: args.taskId,
3770
3865
  ...buildAgentSettingsEnvPatch(localAgentSettings),
3866
+ ...args.runtimeEnvPatch,
3771
3867
  ...(codexAuth?.envPatch ?? {}),
3772
3868
  WORKSPACE: taskWorkspace,
3773
3869
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "doer-agent",
3
- "version": "0.3.4",
3
+ "version": "0.3.5",
4
4
  "description": "Reverse-polling agent runtime for doer",
5
5
  "type": "module",
6
6
  "main": "dist/agent.js",