@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/index.js CHANGED
@@ -38,6 +38,7 @@ var CodexOAuthSessionStore = class {
38
38
  ttlMs;
39
39
  sessions = /* @__PURE__ */ new Map();
40
40
  activeSessionId = null;
41
+ aborters = /* @__PURE__ */ new Map();
41
42
  /** Whether a codex sign-in is currently in flight (port 1455 held). */
42
43
  isBusy() {
43
44
  this.sweep();
@@ -49,13 +50,22 @@ var CodexOAuthSessionStore = class {
49
50
  const sessionId = crypto.randomBytes(24).toString("base64url");
50
51
  this.sessions.set(sessionId, { status: "pending", createdAt: Date.now() });
51
52
  this.activeSessionId = sessionId;
52
- return sessionId;
53
+ const controller = new AbortController();
54
+ this.aborters.set(sessionId, controller);
55
+ return { sessionId, signal: controller.signal };
53
56
  }
54
57
  /** Settle a flow (done/error) + free the active slot. */
55
58
  settle(sessionId, status, error) {
56
59
  const prior = this.sessions.get(sessionId);
57
60
  this.sessions.set(sessionId, { status, error, createdAt: prior?.createdAt ?? Date.now() });
58
61
  if (this.activeSessionId === sessionId) this.activeSessionId = null;
62
+ this.aborters.delete(sessionId);
63
+ }
64
+ cancel(sessionId) {
65
+ if (!this.sessions.has(sessionId)) return false;
66
+ this.aborters.get(sessionId)?.abort();
67
+ this.settle(sessionId, "error", "login: cancelled");
68
+ return true;
59
69
  }
60
70
  /** Read a flow's status (token-free), or null when unknown/expired. */
61
71
  get(sessionId) {
@@ -84,13 +94,13 @@ function handleCodexOAuthStart(deps) {
84
94
  );
85
95
  }
86
96
  const { authUrl, codeVerifier, state } = codexOAuth.generateAuthParams();
87
- const sessionId = deps.codexSessions.begin();
88
- void runCodexLoopback(sessionId, codeVerifier, state, deps);
97
+ const { sessionId, signal } = deps.codexSessions.begin();
98
+ void runCodexLoopback(sessionId, codeVerifier, state, signal, deps);
89
99
  return { status: 200, body: { authUrl, sessionId } };
90
100
  }
91
- async function runCodexLoopback(sessionId, codeVerifier, state, deps) {
101
+ async function runCodexLoopback(sessionId, codeVerifier, state, signal, deps) {
92
102
  try {
93
- const code = await deps.codexAwaitLoopback(state);
103
+ const code = await deps.codexAwaitLoopback(state, void 0, signal);
94
104
  const result = await codexOAuth.exchangeCodeForTokens(
95
105
  { authorizationCode: code, codeVerifier, state },
96
106
  deps.oauthExchangeFetch
@@ -112,6 +122,10 @@ async function runCodexLoopback(sessionId, codeVerifier, state, deps) {
112
122
  deps.codexSessions.settle(sessionId, "error", reason);
113
123
  }
114
124
  }
125
+ function handleCodexOAuthCancel(sessionId, deps) {
126
+ if (!deps.codexSessions.cancel(sessionId)) return err(404, "unknown or expired codex sign-in session");
127
+ return { status: 200, body: { ok: true } };
128
+ }
115
129
  function handleCodexOAuthStatus(sessionId, deps) {
116
130
  const s = deps.codexSessions.get(sessionId);
117
131
  if (!s) return err(404, "unknown or expired codex sign-in session");
@@ -1874,12 +1888,16 @@ function preserveWebhookSecrets(incoming, current) {
1874
1888
  }
1875
1889
 
1876
1890
  // src/audit/auditRuntime.ts
1891
+ import { join as join3 } from "path";
1877
1892
  import { setAuditCaptureConfig, setAuditSink } from "@omnicross/core/pipeline/auditSink";
1893
+ import { setUpstreamTracePath } from "@omnicross/core/pipeline/upstreamTrace";
1878
1894
  var writer = null;
1879
1895
  var sweeper = null;
1880
- function setAuditRuntime(w, s) {
1896
+ var auditDir = "";
1897
+ function setAuditRuntime(w, s, dir) {
1881
1898
  writer = w;
1882
1899
  sweeper = s;
1900
+ auditDir = dir;
1883
1901
  }
1884
1902
  function applyAuditConfig(config) {
1885
1903
  const enabled = config?.enabled === true && writer !== null;
@@ -1891,9 +1909,11 @@ function applyAuditConfig(config) {
1891
1909
  sweeper.configure(config);
1892
1910
  sweeper.start();
1893
1911
  }
1912
+ setUpstreamTracePath(config.captureBodies ? join3(auditDir, "upstream-trace.jsonl") : null);
1894
1913
  } else {
1895
1914
  setAuditCaptureConfig(null);
1896
1915
  setAuditSink(null);
1916
+ setUpstreamTracePath(null);
1897
1917
  if (sweeper) {
1898
1918
  if (config) sweeper.configure(config);
1899
1919
  sweeper.dispose();
@@ -1903,9 +1923,11 @@ function applyAuditConfig(config) {
1903
1923
  function resetAuditRuntimeForTests() {
1904
1924
  setAuditCaptureConfig(null);
1905
1925
  setAuditSink(null);
1926
+ setUpstreamTracePath(null);
1906
1927
  if (sweeper) sweeper.dispose();
1907
1928
  writer = null;
1908
1929
  sweeper = null;
1930
+ auditDir = "";
1909
1931
  }
1910
1932
 
1911
1933
  // src/billing/billingRuntime.ts
@@ -3520,6 +3542,10 @@ async function handleAccounts(req, res, method, rest, deps) {
3520
3542
  const result = handleCodexOAuthStatus(rest[2], deps);
3521
3543
  return writeJson2(res, result.status, result.body);
3522
3544
  }
3545
+ if (method === "DELETE" && rest[0] === "codex" && rest[1] === "oauth" && rest[2]) {
3546
+ const result = handleCodexOAuthCancel(rest[2], deps);
3547
+ return writeJson2(res, result.status, result.body);
3548
+ }
3523
3549
  if (method === "PUT" || method === "POST" || method === "DELETE") {
3524
3550
  const providerId = asSubscriptionProviderId(rest[0]);
3525
3551
  if (!providerId) {
@@ -3859,7 +3885,7 @@ async function handleUiStatic(req, res, urlPath, uiDist) {
3859
3885
  }
3860
3886
 
3861
3887
  // src/admin/version.ts
3862
- var DAEMON_VERSION = true ? "0.1.3" : "0.0.0-dev";
3888
+ var DAEMON_VERSION = true ? "0.1.5" : "0.0.0-dev";
3863
3889
 
3864
3890
  // src/admin/AdminServer.ts
3865
3891
  var LOOPBACK_ADDR = "127.0.0.1";
@@ -4114,7 +4140,7 @@ var DEFAULT_TIMEOUT_MS = 5 * 6e4;
4114
4140
  function pageHtml(message) {
4115
4141
  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>`;
4116
4142
  }
4117
- function awaitLoopbackCode(expectedState, timeoutMs = DEFAULT_TIMEOUT_MS) {
4143
+ function awaitLoopbackCode(expectedState, timeoutMs = DEFAULT_TIMEOUT_MS, signal) {
4118
4144
  return new Promise((resolve, reject) => {
4119
4145
  let settled = false;
4120
4146
  const finish = (server2, fn) => {
@@ -4148,6 +4174,12 @@ function awaitLoopbackCode(expectedState, timeoutMs = DEFAULT_TIMEOUT_MS) {
4148
4174
  res.end(pageHtml("Login complete."));
4149
4175
  finish(server, () => resolve(code));
4150
4176
  });
4177
+ const abort = () => finish(server, () => reject(new Error("login: cancelled")));
4178
+ if (signal?.aborted) {
4179
+ abort();
4180
+ return;
4181
+ }
4182
+ signal?.addEventListener("abort", abort, { once: true });
4151
4183
  server.on("error", (err5) => {
4152
4184
  if (settled) return;
4153
4185
  settled = true;
@@ -4235,21 +4267,21 @@ function createPoolKeysLoader(getProviderRow, autoDisabled) {
4235
4267
  }
4236
4268
 
4237
4269
  // src/commands/paths.ts
4238
- import { dirname as dirname2, join as join3 } from "path";
4270
+ import { dirname as dirname2, join as join4 } from "path";
4239
4271
  function defaultVouchersPath(configPath) {
4240
- return join3(dirname2(configPath), "vouchers.json");
4272
+ return join4(dirname2(configPath), "vouchers.json");
4241
4273
  }
4242
4274
  function defaultPricingPath(configPath) {
4243
- return join3(dirname2(configPath), "pricing.json");
4275
+ return join4(dirname2(configPath), "pricing.json");
4244
4276
  }
4245
4277
  function defaultUsageEventsPath(configPath) {
4246
- return join3(dirname2(configPath), "usage-events.jsonl");
4278
+ return join4(dirname2(configPath), "usage-events.jsonl");
4247
4279
  }
4248
4280
  function defaultAuditDir(configPath) {
4249
- return join3(dirname2(configPath), "audit");
4281
+ return join4(dirname2(configPath), "audit");
4250
4282
  }
4251
4283
  function defaultBillingDir(configPath) {
4252
- return join3(dirname2(configPath), "billing");
4284
+ return join4(dirname2(configPath), "billing");
4253
4285
  }
4254
4286
 
4255
4287
  // src/ports/ConfigFileProviderConfigSource.ts
@@ -5335,9 +5367,9 @@ function findDuplicateCredentialIds(accounts) {
5335
5367
  // src/ports/external-cli-credentials.ts
5336
5368
  import { existsSync as existsSync8, readFileSync as readFileSync8 } from "fs";
5337
5369
  import { homedir as homedir2 } from "os";
5338
- import { join as join4 } from "path";
5370
+ import { join as join5 } from "path";
5339
5371
  function externalStorePath(provider, home = homedir2()) {
5340
- return provider === "claude" ? join4(home, ".claude", ".credentials.json") : join4(home, ".codex", "auth.json");
5372
+ return provider === "claude" ? join5(home, ".claude", ".credentials.json") : join5(home, ".codex", "auth.json");
5341
5373
  }
5342
5374
  function decodeJwtExpiryMs(token) {
5343
5375
  try {
@@ -6456,7 +6488,7 @@ var AccountHealthSweeper = class {
6456
6488
 
6457
6489
  // src/audit/AuditPruneSweeper.ts
6458
6490
  import { existsSync as existsSync11, readdirSync, unlinkSync } from "fs";
6459
- import { join as join5 } from "path";
6491
+ import { join as join6 } from "path";
6460
6492
 
6461
6493
  // src/audit/auditFiles.ts
6462
6494
  var AUDIT_FILE_RE = /^audit-(\d{4})-(\d{2})-(\d{2})\.jsonl$/;
@@ -6482,8 +6514,8 @@ function auditFileDateMs(fileName) {
6482
6514
  var DAY_MS = 24 * 60 * 6e4;
6483
6515
  var SWEEP_INTERVAL_MS2 = 60 * 6e4;
6484
6516
  var AuditPruneSweeper = class {
6485
- constructor(auditDir, logger, config, intervalMs = SWEEP_INTERVAL_MS2, now = Date.now) {
6486
- this.auditDir = auditDir;
6517
+ constructor(auditDir2, logger, config, intervalMs = SWEEP_INTERVAL_MS2, now = Date.now) {
6518
+ this.auditDir = auditDir2;
6487
6519
  this.logger = logger;
6488
6520
  this.config = config;
6489
6521
  this.intervalMs = intervalMs;
@@ -6539,7 +6571,7 @@ var AuditPruneSweeper = class {
6539
6571
  const dateMs = auditFileDateMs(file);
6540
6572
  if (dateMs === null || dateMs >= cutoff) continue;
6541
6573
  try {
6542
- unlinkSync(join5(this.auditDir, file));
6574
+ unlinkSync(join6(this.auditDir, file));
6543
6575
  removed += 1;
6544
6576
  } catch (error) {
6545
6577
  this.logger.warn("[AuditPruneSweeper] failed to unlink expired audit file", {
@@ -6563,14 +6595,14 @@ var AuditPruneSweeper = class {
6563
6595
 
6564
6596
  // src/audit/auditReader.ts
6565
6597
  import { existsSync as existsSync12, readdirSync as readdirSync2, readFileSync as readFileSync11 } from "fs";
6566
- import { join as join6 } from "path";
6598
+ import { join as join7 } from "path";
6567
6599
  var DEFAULT_LIMIT = 200;
6568
6600
  var MAX_LIMIT = 2e3;
6569
- function readAuditRecords(auditDir, query = {}) {
6570
- if (!existsSync12(auditDir)) return [];
6601
+ function readAuditRecords(auditDir2, query = {}) {
6602
+ if (!existsSync12(auditDir2)) return [];
6571
6603
  let files;
6572
6604
  try {
6573
- files = readdirSync2(auditDir).filter((f) => AUDIT_FILE_RE.test(f));
6605
+ files = readdirSync2(auditDir2).filter((f) => AUDIT_FILE_RE.test(f));
6574
6606
  } catch {
6575
6607
  return [];
6576
6608
  }
@@ -6581,7 +6613,7 @@ function readAuditRecords(auditDir, query = {}) {
6581
6613
  for (const file of files.sort().reverse()) {
6582
6614
  let raw;
6583
6615
  try {
6584
- raw = readFileSync11(join6(auditDir, file), "utf8");
6616
+ raw = readFileSync11(join7(auditDir2, file), "utf8");
6585
6617
  } catch {
6586
6618
  continue;
6587
6619
  }
@@ -6611,10 +6643,10 @@ function isAuditRecord(value) {
6611
6643
 
6612
6644
  // src/audit/AuditWriter.ts
6613
6645
  import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync4 } from "fs";
6614
- import { join as join7 } from "path";
6646
+ import { join as join8 } from "path";
6615
6647
  var AuditWriter = class {
6616
- constructor(auditDir, logger, defer = (fn) => setTimeout(fn, 0)) {
6617
- this.auditDir = auditDir;
6648
+ constructor(auditDir2, logger, defer = (fn) => setTimeout(fn, 0)) {
6649
+ this.auditDir = auditDir2;
6618
6650
  this.logger = logger;
6619
6651
  this.defer = defer;
6620
6652
  }
@@ -6647,7 +6679,7 @@ var AuditWriter = class {
6647
6679
  mkdirSync4(this.auditDir, { recursive: true });
6648
6680
  this.dirEnsured = true;
6649
6681
  }
6650
- const file = join7(this.auditDir, auditFileName(record.ts));
6682
+ const file = join8(this.auditDir, auditFileName(record.ts));
6651
6683
  appendFileSync2(file, JSON.stringify(record) + "\n", "utf8");
6652
6684
  }
6653
6685
  };
@@ -6655,7 +6687,7 @@ var AuditWriter = class {
6655
6687
  // src/billing/BillingPublisher.ts
6656
6688
  import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync5 } from "fs";
6657
6689
  import { createHmac } from "crypto";
6658
- import { join as join8 } from "path";
6690
+ import { join as join9 } from "path";
6659
6691
  import { fetchUpstream as fetchUpstream4 } from "@omnicross/core/pipeline/upstreamFetch";
6660
6692
 
6661
6693
  // src/billing/billingFiles.ts
@@ -6726,7 +6758,7 @@ var BillingPublisher = class {
6726
6758
  */
6727
6759
  appendNow(event) {
6728
6760
  this.ensureDir();
6729
- const file = join8(this.billingDir, billingFileName(event.ts));
6761
+ const file = join9(this.billingDir, billingFileName(event.ts));
6730
6762
  appendFileSync3(file, JSON.stringify(event) + "\n", "utf8");
6731
6763
  }
6732
6764
  /**
@@ -6776,7 +6808,7 @@ var BillingPublisher = class {
6776
6808
  markDelivered(event) {
6777
6809
  try {
6778
6810
  this.ensureDir();
6779
- const file = join8(this.billingDir, deliveredFileName(event.ts));
6811
+ const file = join9(this.billingDir, deliveredFileName(event.ts));
6780
6812
  appendFileSync3(file, JSON.stringify({ id: event.id, deliveredAt: this.now() }) + "\n", "utf8");
6781
6813
  } catch (error) {
6782
6814
  this.logger.warn("[BillingPublisher] failed to append delivery marker", {
@@ -6793,7 +6825,7 @@ var BillingPublisher = class {
6793
6825
 
6794
6826
  // src/billing/billingReader.ts
6795
6827
  import { existsSync as existsSync13, readdirSync as readdirSync3, readFileSync as readFileSync12 } from "fs";
6796
- import { join as join9 } from "path";
6828
+ import { join as join10 } from "path";
6797
6829
  function readBillingLedger(billingDir) {
6798
6830
  const view = { events: [], deliveredIds: /* @__PURE__ */ new Set() };
6799
6831
  if (!existsSync13(billingDir)) return view;
@@ -6830,7 +6862,7 @@ function readBillingStatus(billingDir) {
6830
6862
  function parseLines(dir, file) {
6831
6863
  let raw;
6832
6864
  try {
6833
- raw = readFileSync12(join9(dir, file), "utf8");
6865
+ raw = readFileSync12(join10(dir, file), "utf8");
6834
6866
  } catch {
6835
6867
  return [];
6836
6868
  }
@@ -7271,7 +7303,7 @@ function buildDaemon(config, paths) {
7271
7303
  // lines through the injected logger (honors level/format/file sink).
7272
7304
  logger
7273
7305
  });
7274
- const auditDir = defaultAuditDir(paths.configPath);
7306
+ const auditDir2 = defaultAuditDir(paths.configPath);
7275
7307
  const billingDir = defaultBillingDir(paths.configPath);
7276
7308
  const adminServer = new AdminServer({
7277
7309
  configPath: paths.configPath,
@@ -7312,7 +7344,7 @@ function buildDaemon(config, paths) {
7312
7344
  // the app polls the token-free status. A test seam (`paths.codexAwaitLoopback`)
7313
7345
  // can inject a mock so no real port is bound.
7314
7346
  codexSessions: new CodexOAuthSessionStore(),
7315
- codexAwaitLoopback: paths.codexAwaitLoopback ?? ((state, timeoutMs) => awaitLoopbackCode(state, timeoutMs)),
7347
+ codexAwaitLoopback: paths.codexAwaitLoopback ?? ((state, timeoutMs, signal) => awaitLoopbackCode(state, timeoutMs, signal)),
7316
7348
  // Migration pack (app-parity child 6, design D2/D3) — the concrete credential
7317
7349
  // store provides BOTH the full DECRYPTED read (`getFullConfig`, export) and
7318
7350
  // the multi-account append (`appendProviderAccount`, import re-encrypts at-
@@ -7347,7 +7379,7 @@ function buildDaemon(config, paths) {
7347
7379
  // date-rotated audit store. Bound to the store dir here so the AdminServer
7348
7380
  // carries no path/store coupling. Records hold IP/UA/bodies → admin-only,
7349
7381
  // NEVER unauth, NEVER on `/health`. Routed in `AdminServer` (not `adminApi.ts`).
7350
- auditReader: (query) => readAuditRecords(auditDir, query),
7382
+ auditReader: (query) => readAuditRecords(auditDir2, query),
7351
7383
  // billing-event-stream: the AUTHED `GET /admin/api/billing-status` returns the
7352
7384
  // secret-free total/delivered/pending counts of the durable ledger.
7353
7385
  billingStatusReader: () => readBillingStatus(billingDir)
@@ -7357,9 +7389,9 @@ function buildDaemon(config, paths) {
7357
7389
  fetchImpl: (url, init) => fetchUpstream6(url, init)
7358
7390
  });
7359
7391
  setWebhookRuntime(webhookDispatcher, getSharedAccountHealth2());
7360
- const auditWriter = new AuditWriter(auditDir, logger);
7361
- const auditPruneSweeper = new AuditPruneSweeper(auditDir, logger, DEFAULT_AUDIT_CONFIG);
7362
- setAuditRuntime(auditWriter, auditPruneSweeper);
7392
+ const auditWriter = new AuditWriter(auditDir2, logger);
7393
+ const auditPruneSweeper = new AuditPruneSweeper(auditDir2, logger, DEFAULT_AUDIT_CONFIG);
7394
+ setAuditRuntime(auditWriter, auditPruneSweeper, auditDir2);
7363
7395
  const billingPublisher = new BillingPublisher(billingDir, logger);
7364
7396
  const billingRetrySweeper = new BillingRetrySweeper(
7365
7397
  billingDir,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@omnicross/daemon",
3
- "version": "0.1.3",
4
- "description": "Omnicross standalone daemon the bare-Node embedder of @omnicross/core with an admin HTTP API + dashboard.",
3
+ "version": "0.1.5",
4
+ "description": "Omnicross standalone daemon 鈥?the bare-Node embedder of @omnicross/core with an admin HTTP API + dashboard.",
5
5
  "license": "MIT",
6
6
  "author": "Sayo (https://github.com/Dumoedss)",
7
7
  "homepage": "https://github.com/Dumoedss/omnicross#readme",