@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.cjs +80 -50
- package/dist/cli.js +66 -36
- package/dist/index.cjs +85 -53
- package/dist/index.d.cts +7 -2
- package/dist/index.d.ts +7 -2
- package/dist/index.js +72 -40
- package/package.json +2 -2
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
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
5560
|
+
import { join as join5 } from "path";
|
|
5531
5561
|
function externalStorePath(provider, home = homedir2()) {
|
|
5532
|
-
return provider === "claude" ?
|
|
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
|
|
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(
|
|
6678
|
-
this.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(
|
|
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
|
|
6788
|
+
import { join as join7 } from "path";
|
|
6759
6789
|
var DEFAULT_LIMIT = 200;
|
|
6760
6790
|
var MAX_LIMIT = 2e3;
|
|
6761
|
-
function readAuditRecords(
|
|
6762
|
-
if (!existsSync12(
|
|
6791
|
+
function readAuditRecords(auditDir2, query = {}) {
|
|
6792
|
+
if (!existsSync12(auditDir2)) return [];
|
|
6763
6793
|
let files;
|
|
6764
6794
|
try {
|
|
6765
|
-
files = readdirSync2(
|
|
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(
|
|
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
|
|
6836
|
+
import { join as join8 } from "path";
|
|
6807
6837
|
var AuditWriter = class {
|
|
6808
|
-
constructor(
|
|
6809
|
-
this.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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
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(
|
|
7553
|
-
const auditPruneSweeper = new AuditPruneSweeper(
|
|
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 =
|
|
7675
|
+
const full = join11(seg, candidate);
|
|
7646
7676
|
if (existsSync15(full)) return full;
|
|
7647
7677
|
}
|
|
7648
7678
|
return null;
|