@omnicross/daemon 0.1.3 → 0.1.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.
package/dist/cli.js CHANGED
@@ -911,7 +911,7 @@ async function keysRevoke(db, id) {
911
911
  // src/commands/launch.ts
912
912
  import { spawn as spawn2 } from "child_process";
913
913
  import { existsSync as existsSync15 } from "fs";
914
- import { delimiter as delimiter2, join as join10 } from "path";
914
+ import { delimiter as delimiter2, join as join11 } from "path";
915
915
  import { parseArgs as parseArgs3 } from "util";
916
916
  import {
917
917
  buildChatCliLaunchConfig as buildChatCliLaunchConfig2,
@@ -960,6 +960,7 @@ var CodexOAuthSessionStore = class {
960
960
  ttlMs;
961
961
  sessions = /* @__PURE__ */ new Map();
962
962
  activeSessionId = null;
963
+ aborters = /* @__PURE__ */ new Map();
963
964
  /** Whether a codex sign-in is currently in flight (port 1455 held). */
964
965
  isBusy() {
965
966
  this.sweep();
@@ -971,13 +972,22 @@ var CodexOAuthSessionStore = class {
971
972
  const sessionId = crypto.randomBytes(24).toString("base64url");
972
973
  this.sessions.set(sessionId, { status: "pending", createdAt: Date.now() });
973
974
  this.activeSessionId = sessionId;
974
- return sessionId;
975
+ const controller = new AbortController();
976
+ this.aborters.set(sessionId, controller);
977
+ return { sessionId, signal: controller.signal };
975
978
  }
976
979
  /** Settle a flow (done/error) + free the active slot. */
977
980
  settle(sessionId, status, error) {
978
981
  const prior = this.sessions.get(sessionId);
979
982
  this.sessions.set(sessionId, { status, error, createdAt: prior?.createdAt ?? Date.now() });
980
983
  if (this.activeSessionId === sessionId) this.activeSessionId = null;
984
+ this.aborters.delete(sessionId);
985
+ }
986
+ cancel(sessionId) {
987
+ if (!this.sessions.has(sessionId)) return false;
988
+ this.aborters.get(sessionId)?.abort();
989
+ this.settle(sessionId, "error", "login: cancelled");
990
+ return true;
981
991
  }
982
992
  /** Read a flow's status (token-free), or null when unknown/expired. */
983
993
  get(sessionId) {
@@ -1006,13 +1016,13 @@ function handleCodexOAuthStart(deps) {
1006
1016
  );
1007
1017
  }
1008
1018
  const { authUrl, codeVerifier, state } = codexOAuth.generateAuthParams();
1009
- const sessionId = deps.codexSessions.begin();
1010
- void runCodexLoopback(sessionId, codeVerifier, state, deps);
1019
+ const { sessionId, signal } = deps.codexSessions.begin();
1020
+ void runCodexLoopback(sessionId, codeVerifier, state, signal, deps);
1011
1021
  return { status: 200, body: { authUrl, sessionId } };
1012
1022
  }
1013
- async function runCodexLoopback(sessionId, codeVerifier, state, deps) {
1023
+ async function runCodexLoopback(sessionId, codeVerifier, state, signal, deps) {
1014
1024
  try {
1015
- const code = await deps.codexAwaitLoopback(state);
1025
+ const code = await deps.codexAwaitLoopback(state, void 0, signal);
1016
1026
  const result = await codexOAuth.exchangeCodeForTokens(
1017
1027
  { authorizationCode: code, codeVerifier, state },
1018
1028
  deps.oauthExchangeFetch
@@ -1034,6 +1044,10 @@ async function runCodexLoopback(sessionId, codeVerifier, state, deps) {
1034
1044
  deps.codexSessions.settle(sessionId, "error", reason);
1035
1045
  }
1036
1046
  }
1047
+ function handleCodexOAuthCancel(sessionId, deps) {
1048
+ if (!deps.codexSessions.cancel(sessionId)) return err(404, "unknown or expired codex sign-in session");
1049
+ return { status: 200, body: { ok: true } };
1050
+ }
1037
1051
  function handleCodexOAuthStatus(sessionId, deps) {
1038
1052
  const s = deps.codexSessions.get(sessionId);
1039
1053
  if (!s) return err(404, "unknown or expired codex sign-in session");
@@ -2220,12 +2234,16 @@ function preserveWebhookSecrets(incoming, current) {
2220
2234
  }
2221
2235
 
2222
2236
  // src/audit/auditRuntime.ts
2237
+ import { join as join4 } from "path";
2223
2238
  import { setAuditCaptureConfig, setAuditSink } from "@omnicross/core/pipeline/auditSink";
2239
+ import { setUpstreamTracePath } from "@omnicross/core/pipeline/upstreamTrace";
2224
2240
  var writer = null;
2225
2241
  var sweeper = null;
2226
- function setAuditRuntime(w, s) {
2242
+ var auditDir = "";
2243
+ function setAuditRuntime(w, s, dir) {
2227
2244
  writer = w;
2228
2245
  sweeper = s;
2246
+ auditDir = dir;
2229
2247
  }
2230
2248
  function applyAuditConfig(config) {
2231
2249
  const enabled = config?.enabled === true && writer !== null;
@@ -2237,9 +2255,11 @@ function applyAuditConfig(config) {
2237
2255
  sweeper.configure(config);
2238
2256
  sweeper.start();
2239
2257
  }
2258
+ setUpstreamTracePath(config.captureBodies ? join4(auditDir, "upstream-trace.jsonl") : null);
2240
2259
  } else {
2241
2260
  setAuditCaptureConfig(null);
2242
2261
  setAuditSink(null);
2262
+ setUpstreamTracePath(null);
2243
2263
  if (sweeper) {
2244
2264
  if (config) sweeper.configure(config);
2245
2265
  sweeper.dispose();
@@ -3852,6 +3872,10 @@ async function handleAccounts(req, res, method, rest, deps) {
3852
3872
  const result = handleCodexOAuthStatus(rest[2], deps);
3853
3873
  return writeJson2(res, result.status, result.body);
3854
3874
  }
3875
+ if (method === "DELETE" && rest[0] === "codex" && rest[1] === "oauth" && rest[2]) {
3876
+ const result = handleCodexOAuthCancel(rest[2], deps);
3877
+ return writeJson2(res, result.status, result.body);
3878
+ }
3855
3879
  if (method === "PUT" || method === "POST" || method === "DELETE") {
3856
3880
  const providerId = asSubscriptionProviderId(rest[0]);
3857
3881
  if (!providerId) {
@@ -4191,7 +4215,7 @@ async function handleUiStatic(req, res, urlPath, uiDist) {
4191
4215
  }
4192
4216
 
4193
4217
  // src/admin/version.ts
4194
- var DAEMON_VERSION = true ? "0.1.3" : "0.0.0-dev";
4218
+ var DAEMON_VERSION = true ? "0.1.5" : "0.0.0-dev";
4195
4219
 
4196
4220
  // src/admin/AdminServer.ts
4197
4221
  var LOOPBACK_ADDR = "127.0.0.1";
@@ -4446,7 +4470,7 @@ var DEFAULT_TIMEOUT_MS = 5 * 6e4;
4446
4470
  function pageHtml(message) {
4447
4471
  return `<!doctype html><meta charset="utf-8"><title>omnicross login</title><body style="font-family:sans-serif;padding:2rem"><h2>${message}</h2><p>You can close this window and return to the terminal.</p></body>`;
4448
4472
  }
4449
- function awaitLoopbackCode(expectedState, timeoutMs = DEFAULT_TIMEOUT_MS) {
4473
+ function awaitLoopbackCode(expectedState, timeoutMs = DEFAULT_TIMEOUT_MS, signal) {
4450
4474
  return new Promise((resolve, reject) => {
4451
4475
  let settled = false;
4452
4476
  const finish = (server2, fn) => {
@@ -4480,6 +4504,12 @@ function awaitLoopbackCode(expectedState, timeoutMs = DEFAULT_TIMEOUT_MS) {
4480
4504
  res.end(pageHtml("Login complete."));
4481
4505
  finish(server, () => resolve(code));
4482
4506
  });
4507
+ const abort = () => finish(server, () => reject(new Error("login: cancelled")));
4508
+ if (signal?.aborted) {
4509
+ abort();
4510
+ return;
4511
+ }
4512
+ signal?.addEventListener("abort", abort, { once: true });
4483
4513
  server.on("error", (err5) => {
4484
4514
  if (settled) return;
4485
4515
  settled = true;
@@ -5527,9 +5557,9 @@ function findDuplicateCredentialIds(accounts) {
5527
5557
  // src/ports/external-cli-credentials.ts
5528
5558
  import { existsSync as existsSync8, readFileSync as readFileSync9 } from "fs";
5529
5559
  import { homedir as homedir2 } from "os";
5530
- import { join as join4 } from "path";
5560
+ import { join as join5 } from "path";
5531
5561
  function externalStorePath(provider, home = homedir2()) {
5532
- return provider === "claude" ? join4(home, ".claude", ".credentials.json") : join4(home, ".codex", "auth.json");
5562
+ return provider === "claude" ? join5(home, ".claude", ".credentials.json") : join5(home, ".codex", "auth.json");
5533
5563
  }
5534
5564
  function decodeJwtExpiryMs(token) {
5535
5565
  try {
@@ -6648,7 +6678,7 @@ var AccountHealthSweeper = class {
6648
6678
 
6649
6679
  // src/audit/AuditPruneSweeper.ts
6650
6680
  import { existsSync as existsSync11, readdirSync, unlinkSync } from "fs";
6651
- import { join as join5 } from "path";
6681
+ import { join as join6 } from "path";
6652
6682
 
6653
6683
  // src/audit/auditFiles.ts
6654
6684
  var AUDIT_FILE_RE = /^audit-(\d{4})-(\d{2})-(\d{2})\.jsonl$/;
@@ -6674,8 +6704,8 @@ function auditFileDateMs(fileName) {
6674
6704
  var DAY_MS = 24 * 60 * 6e4;
6675
6705
  var SWEEP_INTERVAL_MS2 = 60 * 6e4;
6676
6706
  var AuditPruneSweeper = class {
6677
- constructor(auditDir, logger, config, intervalMs = SWEEP_INTERVAL_MS2, now = Date.now) {
6678
- this.auditDir = auditDir;
6707
+ constructor(auditDir2, logger, config, intervalMs = SWEEP_INTERVAL_MS2, now = Date.now) {
6708
+ this.auditDir = auditDir2;
6679
6709
  this.logger = logger;
6680
6710
  this.config = config;
6681
6711
  this.intervalMs = intervalMs;
@@ -6731,7 +6761,7 @@ var AuditPruneSweeper = class {
6731
6761
  const dateMs = auditFileDateMs(file);
6732
6762
  if (dateMs === null || dateMs >= cutoff) continue;
6733
6763
  try {
6734
- unlinkSync(join5(this.auditDir, file));
6764
+ unlinkSync(join6(this.auditDir, file));
6735
6765
  removed += 1;
6736
6766
  } catch (error) {
6737
6767
  this.logger.warn("[AuditPruneSweeper] failed to unlink expired audit file", {
@@ -6755,14 +6785,14 @@ var AuditPruneSweeper = class {
6755
6785
 
6756
6786
  // src/audit/auditReader.ts
6757
6787
  import { existsSync as existsSync12, readdirSync as readdirSync2, readFileSync as readFileSync12 } from "fs";
6758
- import { join as join6 } from "path";
6788
+ import { join as join7 } from "path";
6759
6789
  var DEFAULT_LIMIT = 200;
6760
6790
  var MAX_LIMIT = 2e3;
6761
- function readAuditRecords(auditDir, query = {}) {
6762
- if (!existsSync12(auditDir)) return [];
6791
+ function readAuditRecords(auditDir2, query = {}) {
6792
+ if (!existsSync12(auditDir2)) return [];
6763
6793
  let files;
6764
6794
  try {
6765
- files = readdirSync2(auditDir).filter((f) => AUDIT_FILE_RE.test(f));
6795
+ files = readdirSync2(auditDir2).filter((f) => AUDIT_FILE_RE.test(f));
6766
6796
  } catch {
6767
6797
  return [];
6768
6798
  }
@@ -6773,7 +6803,7 @@ function readAuditRecords(auditDir, query = {}) {
6773
6803
  for (const file of files.sort().reverse()) {
6774
6804
  let raw;
6775
6805
  try {
6776
- raw = readFileSync12(join6(auditDir, file), "utf8");
6806
+ raw = readFileSync12(join7(auditDir2, file), "utf8");
6777
6807
  } catch {
6778
6808
  continue;
6779
6809
  }
@@ -6803,10 +6833,10 @@ function isAuditRecord(value) {
6803
6833
 
6804
6834
  // src/audit/AuditWriter.ts
6805
6835
  import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync4 } from "fs";
6806
- import { join as join7 } from "path";
6836
+ import { join as join8 } from "path";
6807
6837
  var AuditWriter = class {
6808
- constructor(auditDir, logger, defer = (fn) => setTimeout(fn, 0)) {
6809
- this.auditDir = auditDir;
6838
+ constructor(auditDir2, logger, defer = (fn) => setTimeout(fn, 0)) {
6839
+ this.auditDir = auditDir2;
6810
6840
  this.logger = logger;
6811
6841
  this.defer = defer;
6812
6842
  }
@@ -6839,7 +6869,7 @@ var AuditWriter = class {
6839
6869
  mkdirSync4(this.auditDir, { recursive: true });
6840
6870
  this.dirEnsured = true;
6841
6871
  }
6842
- const file = join7(this.auditDir, auditFileName(record.ts));
6872
+ const file = join8(this.auditDir, auditFileName(record.ts));
6843
6873
  appendFileSync2(file, JSON.stringify(record) + "\n", "utf8");
6844
6874
  }
6845
6875
  };
@@ -6847,7 +6877,7 @@ var AuditWriter = class {
6847
6877
  // src/billing/BillingPublisher.ts
6848
6878
  import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync5 } from "fs";
6849
6879
  import { createHmac } from "crypto";
6850
- import { join as join8 } from "path";
6880
+ import { join as join9 } from "path";
6851
6881
  import { fetchUpstream as fetchUpstream4 } from "@omnicross/core/pipeline/upstreamFetch";
6852
6882
 
6853
6883
  // src/billing/billingFiles.ts
@@ -6918,7 +6948,7 @@ var BillingPublisher = class {
6918
6948
  */
6919
6949
  appendNow(event) {
6920
6950
  this.ensureDir();
6921
- const file = join8(this.billingDir, billingFileName(event.ts));
6951
+ const file = join9(this.billingDir, billingFileName(event.ts));
6922
6952
  appendFileSync3(file, JSON.stringify(event) + "\n", "utf8");
6923
6953
  }
6924
6954
  /**
@@ -6968,7 +6998,7 @@ var BillingPublisher = class {
6968
6998
  markDelivered(event) {
6969
6999
  try {
6970
7000
  this.ensureDir();
6971
- const file = join8(this.billingDir, deliveredFileName(event.ts));
7001
+ const file = join9(this.billingDir, deliveredFileName(event.ts));
6972
7002
  appendFileSync3(file, JSON.stringify({ id: event.id, deliveredAt: this.now() }) + "\n", "utf8");
6973
7003
  } catch (error) {
6974
7004
  this.logger.warn("[BillingPublisher] failed to append delivery marker", {
@@ -6985,7 +7015,7 @@ var BillingPublisher = class {
6985
7015
 
6986
7016
  // src/billing/billingReader.ts
6987
7017
  import { existsSync as existsSync13, readdirSync as readdirSync3, readFileSync as readFileSync13 } from "fs";
6988
- import { join as join9 } from "path";
7018
+ import { join as join10 } from "path";
6989
7019
  function readBillingLedger(billingDir) {
6990
7020
  const view = { events: [], deliveredIds: /* @__PURE__ */ new Set() };
6991
7021
  if (!existsSync13(billingDir)) return view;
@@ -7022,7 +7052,7 @@ function readBillingStatus(billingDir) {
7022
7052
  function parseLines(dir, file) {
7023
7053
  let raw;
7024
7054
  try {
7025
- raw = readFileSync13(join9(dir, file), "utf8");
7055
+ raw = readFileSync13(join10(dir, file), "utf8");
7026
7056
  } catch {
7027
7057
  return [];
7028
7058
  }
@@ -7463,7 +7493,7 @@ function buildDaemon(config, paths) {
7463
7493
  // lines through the injected logger (honors level/format/file sink).
7464
7494
  logger
7465
7495
  });
7466
- const auditDir = defaultAuditDir(paths.configPath);
7496
+ const auditDir2 = defaultAuditDir(paths.configPath);
7467
7497
  const billingDir = defaultBillingDir(paths.configPath);
7468
7498
  const adminServer = new AdminServer({
7469
7499
  configPath: paths.configPath,
@@ -7504,7 +7534,7 @@ function buildDaemon(config, paths) {
7504
7534
  // the app polls the token-free status. A test seam (`paths.codexAwaitLoopback`)
7505
7535
  // can inject a mock so no real port is bound.
7506
7536
  codexSessions: new CodexOAuthSessionStore(),
7507
- codexAwaitLoopback: paths.codexAwaitLoopback ?? ((state, timeoutMs) => awaitLoopbackCode(state, timeoutMs)),
7537
+ codexAwaitLoopback: paths.codexAwaitLoopback ?? ((state, timeoutMs, signal) => awaitLoopbackCode(state, timeoutMs, signal)),
7508
7538
  // Migration pack (app-parity child 6, design D2/D3) — the concrete credential
7509
7539
  // store provides BOTH the full DECRYPTED read (`getFullConfig`, export) and
7510
7540
  // the multi-account append (`appendProviderAccount`, import re-encrypts at-
@@ -7539,7 +7569,7 @@ function buildDaemon(config, paths) {
7539
7569
  // date-rotated audit store. Bound to the store dir here so the AdminServer
7540
7570
  // carries no path/store coupling. Records hold IP/UA/bodies → admin-only,
7541
7571
  // NEVER unauth, NEVER on `/health`. Routed in `AdminServer` (not `adminApi.ts`).
7542
- auditReader: (query) => readAuditRecords(auditDir, query),
7572
+ auditReader: (query) => readAuditRecords(auditDir2, query),
7543
7573
  // billing-event-stream: the AUTHED `GET /admin/api/billing-status` returns the
7544
7574
  // secret-free total/delivered/pending counts of the durable ledger.
7545
7575
  billingStatusReader: () => readBillingStatus(billingDir)
@@ -7549,9 +7579,9 @@ function buildDaemon(config, paths) {
7549
7579
  fetchImpl: (url, init) => fetchUpstream6(url, init)
7550
7580
  });
7551
7581
  setWebhookRuntime(webhookDispatcher, getSharedAccountHealth2());
7552
- const auditWriter = new AuditWriter(auditDir, logger);
7553
- const auditPruneSweeper = new AuditPruneSweeper(auditDir, logger, DEFAULT_AUDIT_CONFIG);
7554
- setAuditRuntime(auditWriter, auditPruneSweeper);
7582
+ const auditWriter = new AuditWriter(auditDir2, logger);
7583
+ const auditPruneSweeper = new AuditPruneSweeper(auditDir2, logger, DEFAULT_AUDIT_CONFIG);
7584
+ setAuditRuntime(auditWriter, auditPruneSweeper, auditDir2);
7555
7585
  const billingPublisher = new BillingPublisher(billingDir, logger);
7556
7586
  const billingRetrySweeper = new BillingRetrySweeper(
7557
7587
  billingDir,
@@ -7642,7 +7672,7 @@ function buildCliSpawnPlan(opts) {
7642
7672
  function resolveInPathDefault(candidate) {
7643
7673
  const segments = (process.env["PATH"] ?? "").split(delimiter2).filter(Boolean);
7644
7674
  for (const seg of segments) {
7645
- const full = join10(seg, candidate);
7675
+ const full = join11(seg, candidate);
7646
7676
  if (existsSync15(full)) return full;
7647
7677
  }
7648
7678
  return null;