@odla-ai/cli 0.25.7 → 0.25.9
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/README.md +24 -0
- package/dist/bin.cjs +294 -53
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-5U3EXWCR.js → chunk-HXUVCUEM.js} +295 -54
- package/dist/chunk-HXUVCUEM.js.map +1 -0
- package/dist/index.cjs +294 -53
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/skills/odla/SKILL.md +4 -0
- package/dist/chunk-5U3EXWCR.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1080,11 +1080,6 @@ async function adminCommand(parsed) {
|
|
|
1080
1080
|
});
|
|
1081
1081
|
}
|
|
1082
1082
|
|
|
1083
|
-
// src/app-export.ts
|
|
1084
|
-
var import_node_fs5 = require("fs");
|
|
1085
|
-
var import_node_stream = require("stream");
|
|
1086
|
-
var import_promises = require("stream/promises");
|
|
1087
|
-
|
|
1088
1083
|
// src/config.ts
|
|
1089
1084
|
var import_node_fs4 = require("fs");
|
|
1090
1085
|
var import_node_path4 = require("path");
|
|
@@ -1393,7 +1388,90 @@ function bothTenants(cfg) {
|
|
|
1393
1388
|
return { sandbox: (0, import_apps.tenantIdFor)(cfg.app.id, "dev"), live: (0, import_apps.tenantIdFor)(cfg.app.id, "prod") };
|
|
1394
1389
|
}
|
|
1395
1390
|
|
|
1391
|
+
// src/agent-command.ts
|
|
1392
|
+
async function agentCommand(parsed, deps = {}) {
|
|
1393
|
+
const action = parsed.positionals[1];
|
|
1394
|
+
if (action !== "jobs" && action !== "retry") {
|
|
1395
|
+
throw new Error(`unknown agent action "${action ?? ""}". Try "odla-ai agent jobs --json".`);
|
|
1396
|
+
}
|
|
1397
|
+
assertArgs(parsed, ["config", "env", "state", "limit", "json", "token", "email"], action === "jobs" ? 2 : 3);
|
|
1398
|
+
if (action === "retry" && (parsed.options.state !== void 0 || parsed.options.limit !== void 0)) {
|
|
1399
|
+
throw new Error('--state and --limit are supported only by "agent jobs"');
|
|
1400
|
+
}
|
|
1401
|
+
const cfg = await loadProjectConfig(stringOpt(parsed.options.config) ?? "odla.config.mjs");
|
|
1402
|
+
const { env, tenant } = resolveTenant(cfg, stringOpt(parsed.options.env));
|
|
1403
|
+
const doFetch = deps.fetch ?? fetch;
|
|
1404
|
+
const out = deps.stdout ?? console;
|
|
1405
|
+
const credential2 = await getDeveloperToken(
|
|
1406
|
+
cfg,
|
|
1407
|
+
{
|
|
1408
|
+
configPath: cfg.configPath,
|
|
1409
|
+
token: stringOpt(parsed.options.token),
|
|
1410
|
+
email: stringOpt(parsed.options.email),
|
|
1411
|
+
open: false
|
|
1412
|
+
},
|
|
1413
|
+
doFetch,
|
|
1414
|
+
out
|
|
1415
|
+
);
|
|
1416
|
+
const base = `${cfg.dbEndpoint}/app/${encodeURIComponent(tenant)}/admin/agent-jobs`;
|
|
1417
|
+
const headers = { authorization: `Bearer ${credential2}` };
|
|
1418
|
+
if (action === "retry") {
|
|
1419
|
+
const id = parsed.positionals[2];
|
|
1420
|
+
const res2 = await doFetch(`${base}/${encodeURIComponent(id)}/retry`, { method: "POST", headers });
|
|
1421
|
+
const body2 = await readJson(res2);
|
|
1422
|
+
if (!res2.ok) throw new Error(`agent retry failed (${res2.status}): ${errorMessage(body2)}`);
|
|
1423
|
+
const result2 = { v: 1, appId: cfg.app.id, env, tenant, ...body2 };
|
|
1424
|
+
if (parsed.options.json === true) out.log(JSON.stringify(result2, null, 2));
|
|
1425
|
+
else out.log(`${tenant}: requeued ${id}`);
|
|
1426
|
+
return;
|
|
1427
|
+
}
|
|
1428
|
+
const state2 = stringOpt(parsed.options.state);
|
|
1429
|
+
const allowed = ["pending", "running", "succeeded", "dead_letter"];
|
|
1430
|
+
if (state2 && !allowed.includes(state2)) {
|
|
1431
|
+
throw new Error(`--state must be one of ${allowed.join(", ")}`);
|
|
1432
|
+
}
|
|
1433
|
+
const url = new URL(base);
|
|
1434
|
+
if (state2) url.searchParams.set("state", state2);
|
|
1435
|
+
const limit = numberOpt(parsed.options.limit, "--limit");
|
|
1436
|
+
if (limit) url.searchParams.set("limit", String(limit));
|
|
1437
|
+
const res = await doFetch(url, { headers });
|
|
1438
|
+
const body = await readJson(res);
|
|
1439
|
+
if (!res.ok) throw new Error(`agent jobs failed (${res.status}): ${errorMessage(body)}`);
|
|
1440
|
+
const result = { v: 1, appId: cfg.app.id, env, tenant, jobs: body.jobs ?? [], summary: body.summary };
|
|
1441
|
+
if (parsed.options.json === true) {
|
|
1442
|
+
out.log(JSON.stringify(result, null, 2));
|
|
1443
|
+
return;
|
|
1444
|
+
}
|
|
1445
|
+
const summary = body.summary;
|
|
1446
|
+
out.log(
|
|
1447
|
+
`${tenant}: ${summary?.pending ?? 0} pending, ${summary?.running ?? 0} running, ${summary?.deadLetter ?? 0} dead-letter, ${summary?.succeeded ?? 0} succeeded`
|
|
1448
|
+
);
|
|
1449
|
+
for (const job of body.jobs ?? []) {
|
|
1450
|
+
out.log(
|
|
1451
|
+
[job.state, job.id, job.triggerId, `${job.entityNs}/${job.entityId}`, `attempts=${job.attempts}`, job.lastErrorCode ?? ""].filter(Boolean).join(" ")
|
|
1452
|
+
);
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
async function readJson(res) {
|
|
1456
|
+
try {
|
|
1457
|
+
return await res.json();
|
|
1458
|
+
} catch {
|
|
1459
|
+
return {};
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
function errorMessage(body) {
|
|
1463
|
+
const error = body.error;
|
|
1464
|
+
if (typeof error === "string") return error;
|
|
1465
|
+
if (error && typeof error === "object" && typeof error.message === "string") {
|
|
1466
|
+
return error.message;
|
|
1467
|
+
}
|
|
1468
|
+
return "request failed";
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1396
1471
|
// src/app-export.ts
|
|
1472
|
+
var import_node_fs5 = require("fs");
|
|
1473
|
+
var import_node_stream = require("stream");
|
|
1474
|
+
var import_promises = require("stream/promises");
|
|
1397
1475
|
async function appExport(options) {
|
|
1398
1476
|
const cfg = await loadProjectConfig(options.configPath);
|
|
1399
1477
|
const out = options.stdout ?? console;
|
|
@@ -2371,6 +2449,7 @@ var CAPABILITIES = {
|
|
|
2371
2449
|
"apply Google Calendar booking config, then drive state-bound consent, status, discovery, and disconnect flows (bookings run live through the platform proxy)",
|
|
2372
2450
|
"validate integration contracts offline and smoke-test a provisioned db environment plus anonymous capability routes",
|
|
2373
2451
|
"read one versioned o11y status envelope spanning application RED, current live-sync freshness/load, the protected commit-to-visible canary, collector ingest/scheduler trust, provider-owned runtime metrics, and a bounded machine verdict",
|
|
2452
|
+
"inspect durable agent wakeups as a versioned JSON envelope and explicitly requeue one dead-lettered job with a scoped environment credential",
|
|
2374
2453
|
"run app-attributed hosted security discovery and independent validation without provider keys",
|
|
2375
2454
|
"connect/revoke source-read-only GitHub sources and drive commit-pinned hosted security jobs without PATs or provider keys",
|
|
2376
2455
|
"let admins manage system AI policy and credentials, inspect attributed usage, and read immutable admin changes through separate short-lived capability-only approvals"
|
|
@@ -4589,13 +4668,13 @@ async function createConversionRegistry(config) {
|
|
|
4589
4668
|
policies.set(policy.conversionId, Object.freeze(policy));
|
|
4590
4669
|
}
|
|
4591
4670
|
const outputCounts = /* @__PURE__ */ new Map();
|
|
4592
|
-
const
|
|
4671
|
+
const get = (id, kind) => {
|
|
4593
4672
|
const policy = policies.get(id);
|
|
4594
4673
|
if (!policy || policy.output.kind !== kind) throw new CamelError("conversion_rejected", "Conversion policy is missing or has the wrong output kind.");
|
|
4595
4674
|
return policy;
|
|
4596
4675
|
};
|
|
4597
4676
|
const checked = (source, id, kind) => {
|
|
4598
|
-
const policy =
|
|
4677
|
+
const policy = get(id, kind);
|
|
4599
4678
|
if (utf8Length(source.value) > policy.maximumSourceBytes) throw new CamelError("limit_exceeded", "Unsafe conversion input exceeds its byte bound.");
|
|
4600
4679
|
return policy;
|
|
4601
4680
|
};
|
|
@@ -6549,7 +6628,9 @@ Usage:
|
|
|
6549
6628
|
odla-ai discuss reply <topic> --body "..." [--markup "..."] [--mutation-id <id>]
|
|
6550
6629
|
odla-ai discuss resolve <topic> [--reopen] [--mutation-id <id>]
|
|
6551
6630
|
odla-ai discuss who --q <text> [--app <id>] [--kinds user,pm:task] [--json]
|
|
6552
|
-
odla-ai discuss watch [<topic>] [--by <authorId>] [--self <authorId>] [--interval <s>] [--timeout <s>] [--json]
|
|
6631
|
+
odla-ai discuss watch [<topic>] [--cursor <cursor>] [--by <authorId>] [--self <authorId>] [--interval <s>] [--timeout <s>] [--json|--jsonl]
|
|
6632
|
+
odla-ai agent jobs [--env dev] [--state pending|running|succeeded|dead_letter] [--limit 50] [--email <email>] [--json]
|
|
6633
|
+
odla-ai agent retry <job-id> [--env dev] [--email <email>] [--json]
|
|
6553
6634
|
odla-ai o11y status [--app <id>] [--env prod] [--minutes 60] [--json]
|
|
6554
6635
|
odla-ai whoami [--json]
|
|
6555
6636
|
odla-ai runbook ask "<question>" [--app <id>] [--all] [--json]
|
|
@@ -6597,6 +6678,8 @@ Usage:
|
|
|
6597
6678
|
odla-ai version
|
|
6598
6679
|
|
|
6599
6680
|
Commands:
|
|
6681
|
+
agent Inspect durable agent wakeups and explicitly requeue a
|
|
6682
|
+
dead-lettered job; JSON output is stable for remote operators.
|
|
6600
6683
|
runbook odla's operational procedures, stored in the database and read at
|
|
6601
6684
|
the moment they are followed. "ask" gives a written, cited answer;
|
|
6602
6685
|
"search" the passages behind it; "get" the whole document.
|
|
@@ -7450,61 +7533,214 @@ async function discussWho(ctx, parsed) {
|
|
|
7450
7533
|
});
|
|
7451
7534
|
}
|
|
7452
7535
|
|
|
7536
|
+
// src/discuss-watch-http.ts
|
|
7537
|
+
var WatchRequestError = class extends Error {
|
|
7538
|
+
constructor(message2, retryable, status, code) {
|
|
7539
|
+
super(message2);
|
|
7540
|
+
this.retryable = retryable;
|
|
7541
|
+
this.status = status;
|
|
7542
|
+
this.code = code;
|
|
7543
|
+
this.name = "WatchRequestError";
|
|
7544
|
+
}
|
|
7545
|
+
retryable;
|
|
7546
|
+
status;
|
|
7547
|
+
code;
|
|
7548
|
+
};
|
|
7549
|
+
var WatchCheckpointError = class extends Error {
|
|
7550
|
+
constructor(cursor, streamId) {
|
|
7551
|
+
super("discussion cursor requires a new checkpoint");
|
|
7552
|
+
this.cursor = cursor;
|
|
7553
|
+
this.streamId = streamId;
|
|
7554
|
+
this.name = "WatchCheckpointError";
|
|
7555
|
+
}
|
|
7556
|
+
cursor;
|
|
7557
|
+
streamId;
|
|
7558
|
+
code = "checkpoint_required";
|
|
7559
|
+
};
|
|
7560
|
+
var WatchRemoteError = class extends Error {
|
|
7561
|
+
constructor(cursor, cause) {
|
|
7562
|
+
super("discussion watch dependency remained unavailable", { cause });
|
|
7563
|
+
this.cursor = cursor;
|
|
7564
|
+
this.name = "WatchRemoteError";
|
|
7565
|
+
}
|
|
7566
|
+
cursor;
|
|
7567
|
+
code = "remote_unavailable";
|
|
7568
|
+
};
|
|
7569
|
+
var WatchTimeoutError = class extends Error {
|
|
7570
|
+
constructor(cursor) {
|
|
7571
|
+
super("nothing new before the timeout");
|
|
7572
|
+
this.cursor = cursor;
|
|
7573
|
+
this.name = "WatchTimeoutError";
|
|
7574
|
+
}
|
|
7575
|
+
cursor;
|
|
7576
|
+
code = "watch_timeout";
|
|
7577
|
+
};
|
|
7578
|
+
function requestPath(topicId, app, cursor) {
|
|
7579
|
+
const params = new URLSearchParams();
|
|
7580
|
+
if (topicId) params.set("topic", topicId);
|
|
7581
|
+
if (app) params.set("app", app);
|
|
7582
|
+
if (cursor) params.set("cursor", cursor);
|
|
7583
|
+
return `/watch?${params}`;
|
|
7584
|
+
}
|
|
7585
|
+
async function getWatchPage(ctx, path) {
|
|
7586
|
+
let res;
|
|
7587
|
+
try {
|
|
7588
|
+
res = await ctx.doFetch(`${ctx.platformUrl}/registry/discuss${path}`, {
|
|
7589
|
+
headers: { authorization: `Bearer ${ctx.token}` }
|
|
7590
|
+
});
|
|
7591
|
+
} catch (error) {
|
|
7592
|
+
throw new WatchRequestError(
|
|
7593
|
+
`discuss watch request failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
7594
|
+
true
|
|
7595
|
+
);
|
|
7596
|
+
}
|
|
7597
|
+
const data = await res.json().catch(() => ({}));
|
|
7598
|
+
if (res.status === 409 && data.code === "checkpoint_required") {
|
|
7599
|
+
throw new WatchCheckpointError(data.cursor, data.streamId);
|
|
7600
|
+
}
|
|
7601
|
+
if (!res.ok) {
|
|
7602
|
+
throw new WatchRequestError(
|
|
7603
|
+
`discuss watch failed: ${data.error ?? `registry returned ${res.status}`}`,
|
|
7604
|
+
res.status === 429 || res.status >= 500,
|
|
7605
|
+
res.status,
|
|
7606
|
+
data.code ?? (res.status === 401 || res.status === 403 ? "auth_failed" : void 0)
|
|
7607
|
+
);
|
|
7608
|
+
}
|
|
7609
|
+
return data;
|
|
7610
|
+
}
|
|
7611
|
+
|
|
7453
7612
|
// src/discuss-watch.ts
|
|
7454
7613
|
var DEFAULT_INTERVAL_MS = 15e3;
|
|
7455
|
-
var
|
|
7614
|
+
var MAX_CONSECUTIVE_FAILURES = 5;
|
|
7615
|
+
var MAX_BACKOFF_MS = 3e4;
|
|
7456
7616
|
function numberOpt2(parsed, flag, fallback) {
|
|
7457
7617
|
const raw = stringOpt(parsed.options[flag]);
|
|
7458
7618
|
const value = raw == null ? NaN : Number(raw);
|
|
7459
7619
|
return Number.isFinite(value) && value > 0 ? value : fallback;
|
|
7460
7620
|
}
|
|
7461
|
-
|
|
7462
|
-
|
|
7463
|
-
headers: { authorization: `Bearer ${ctx.token}` }
|
|
7464
|
-
});
|
|
7465
|
-
const data = await res.json().catch(() => ({}));
|
|
7466
|
-
if (!res.ok) throw new Error(`discuss watch failed: ${data.error ?? `registry returned ${res.status}`}`);
|
|
7467
|
-
return data;
|
|
7621
|
+
function jsonl(ctx, parsed, value) {
|
|
7622
|
+
if (parsed.options.jsonl === true) ctx.out.log(JSON.stringify({ v: 1, ...value }));
|
|
7468
7623
|
}
|
|
7469
7624
|
async function discussWatch(ctx, topicId, parsed) {
|
|
7625
|
+
if (ctx.json && parsed.options.jsonl === true) throw new Error("--json and --jsonl cannot be combined");
|
|
7470
7626
|
const sleep = ctx.sleep ?? ((ms) => new Promise((resolve11) => setTimeout(resolve11, ms)));
|
|
7471
7627
|
const now = ctx.now ?? Date.now;
|
|
7472
|
-
const intervalMs = numberOpt2(parsed, "interval", DEFAULT_INTERVAL_MS / 1e3) * 1e3;
|
|
7473
|
-
const
|
|
7628
|
+
const intervalMs = (numberOpt2(parsed, "interval", DEFAULT_INTERVAL_MS / 1e3) ?? DEFAULT_INTERVAL_MS / 1e3) * 1e3;
|
|
7629
|
+
const timeoutSeconds = numberOpt2(parsed, "timeout");
|
|
7630
|
+
const deadline = timeoutSeconds === void 0 ? void 0 : now() + timeoutSeconds * 1e3;
|
|
7474
7631
|
const by = stringOpt(parsed.options.by);
|
|
7475
7632
|
const self = stringOpt(parsed.options.self);
|
|
7476
|
-
const
|
|
7477
|
-
|
|
7478
|
-
let
|
|
7479
|
-
let
|
|
7480
|
-
if (topicId) {
|
|
7481
|
-
const first = await get(ctx, `/topics/${encodeURIComponent(topicId)}`);
|
|
7482
|
-
seen = new Set(first.posts.map((post) => post.id));
|
|
7483
|
-
}
|
|
7633
|
+
const app = stringOpt(parsed.options.app);
|
|
7634
|
+
let cursor = stringOpt(parsed.options.cursor);
|
|
7635
|
+
let firstSuccess = true;
|
|
7636
|
+
let consecutiveFailures = 0;
|
|
7484
7637
|
for (; ; ) {
|
|
7485
|
-
|
|
7486
|
-
|
|
7487
|
-
|
|
7488
|
-
|
|
7489
|
-
|
|
7490
|
-
|
|
7491
|
-
|
|
7492
|
-
|
|
7493
|
-
|
|
7494
|
-
|
|
7495
|
-
|
|
7496
|
-
|
|
7497
|
-
|
|
7498
|
-
|
|
7638
|
+
let page;
|
|
7639
|
+
try {
|
|
7640
|
+
page = await getWatchPage(ctx, requestPath(topicId, app, cursor));
|
|
7641
|
+
consecutiveFailures = 0;
|
|
7642
|
+
} catch (error) {
|
|
7643
|
+
if (!(error instanceof WatchRequestError) || !error.retryable) {
|
|
7644
|
+
if (error instanceof WatchCheckpointError) {
|
|
7645
|
+
jsonl(ctx, parsed, {
|
|
7646
|
+
type: "status",
|
|
7647
|
+
state: "checkpoint_required",
|
|
7648
|
+
retryable: false,
|
|
7649
|
+
...error.cursor ? { cursor: error.cursor } : {},
|
|
7650
|
+
...error.streamId ? { streamId: error.streamId } : {}
|
|
7651
|
+
});
|
|
7652
|
+
}
|
|
7653
|
+
throw error;
|
|
7654
|
+
}
|
|
7655
|
+
consecutiveFailures++;
|
|
7656
|
+
jsonl(ctx, parsed, {
|
|
7657
|
+
type: "status",
|
|
7658
|
+
state: "degraded",
|
|
7659
|
+
retryable: true,
|
|
7660
|
+
attempt: consecutiveFailures,
|
|
7661
|
+
...cursor ? { cursor } : {},
|
|
7662
|
+
...error.status ? { status: error.status } : {}
|
|
7663
|
+
});
|
|
7664
|
+
if (consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {
|
|
7665
|
+
throw new WatchRemoteError(cursor, error);
|
|
7666
|
+
}
|
|
7667
|
+
if (deadline !== void 0 && now() >= deadline) {
|
|
7668
|
+
const result = report2(ctx, parsed, { found: false, cursor: cursor ?? "" });
|
|
7669
|
+
throw new WatchTimeoutError(result.cursor);
|
|
7670
|
+
}
|
|
7671
|
+
const base = Math.min(intervalMs, 1e3);
|
|
7672
|
+
const backoff = Math.min(MAX_BACKOFF_MS, base * 2 ** (consecutiveFailures - 1));
|
|
7673
|
+
await sleep(deadline === void 0 ? backoff : Math.min(backoff, Math.max(0, deadline - now())));
|
|
7674
|
+
continue;
|
|
7675
|
+
}
|
|
7676
|
+
cursor = page.cursor;
|
|
7677
|
+
const baseline = firstSuccess && page.events.length === 0;
|
|
7678
|
+
if (baseline) {
|
|
7679
|
+
jsonl(ctx, parsed, {
|
|
7680
|
+
type: "checkpoint",
|
|
7681
|
+
streamId: page.streamId,
|
|
7682
|
+
cursor,
|
|
7683
|
+
serverTime: page.serverTime
|
|
7684
|
+
});
|
|
7685
|
+
}
|
|
7686
|
+
firstSuccess = false;
|
|
7687
|
+
const matching = page.events.filter((event) => {
|
|
7688
|
+
if (topicId && (event.type !== "message" || event.action !== "created")) return false;
|
|
7689
|
+
return (!by || event.actor.id === by) && (!self || event.actor.id !== self);
|
|
7690
|
+
});
|
|
7691
|
+
for (const event of matching) {
|
|
7692
|
+
jsonl(ctx, parsed, {
|
|
7693
|
+
type: "event",
|
|
7694
|
+
streamId: page.streamId,
|
|
7695
|
+
eventId: event.id,
|
|
7696
|
+
cursor: event.cursor,
|
|
7697
|
+
event
|
|
7698
|
+
});
|
|
7699
|
+
}
|
|
7700
|
+
if (matching.length > 0) {
|
|
7701
|
+
jsonl(ctx, parsed, {
|
|
7702
|
+
type: "checkpoint",
|
|
7703
|
+
streamId: page.streamId,
|
|
7704
|
+
cursor,
|
|
7705
|
+
serverTime: page.serverTime
|
|
7706
|
+
});
|
|
7707
|
+
const posts = topicId ? matching.filter((event) => event.type === "message").map((event) => event.payload) : void 0;
|
|
7708
|
+
const topics = topicId ? void 0 : matching.filter((event) => event.type === "activity").map((event) => event.payload);
|
|
7709
|
+
return report2(ctx, parsed, {
|
|
7710
|
+
found: true,
|
|
7711
|
+
cursor,
|
|
7712
|
+
events: matching,
|
|
7713
|
+
...posts && posts.length > 0 ? { posts } : {},
|
|
7714
|
+
...topics && topics.length > 0 ? { topics } : {}
|
|
7715
|
+
});
|
|
7499
7716
|
}
|
|
7500
|
-
if (
|
|
7501
|
-
|
|
7717
|
+
if (page.events.length > 0) {
|
|
7718
|
+
jsonl(ctx, parsed, {
|
|
7719
|
+
type: "checkpoint",
|
|
7720
|
+
streamId: page.streamId,
|
|
7721
|
+
cursor,
|
|
7722
|
+
serverTime: page.serverTime
|
|
7723
|
+
});
|
|
7724
|
+
} else if (!baseline) {
|
|
7725
|
+
jsonl(ctx, parsed, {
|
|
7726
|
+
type: "heartbeat",
|
|
7727
|
+
streamId: page.streamId,
|
|
7728
|
+
cursor,
|
|
7729
|
+
serverTime: page.serverTime
|
|
7730
|
+
});
|
|
7731
|
+
}
|
|
7732
|
+
if (page.hasMore) continue;
|
|
7733
|
+
if (deadline !== void 0 && now() >= deadline) {
|
|
7734
|
+
return report2(ctx, parsed, { found: false, cursor });
|
|
7735
|
+
}
|
|
7736
|
+
const wait2 = deadline === void 0 ? intervalMs : Math.min(intervalMs, Math.max(0, deadline - now()));
|
|
7737
|
+
await sleep(wait2);
|
|
7502
7738
|
}
|
|
7503
7739
|
}
|
|
7504
|
-
function report2(ctx, result) {
|
|
7740
|
+
function report2(ctx, parsed, result) {
|
|
7505
7741
|
if (ctx.json) {
|
|
7506
7742
|
ctx.out.log(JSON.stringify(result, null, 2));
|
|
7507
|
-
} else if (result.found) {
|
|
7743
|
+
} else if (parsed.options.jsonl !== true && result.found) {
|
|
7508
7744
|
for (const post of result.posts ?? []) {
|
|
7509
7745
|
const who = post.authorKind === "bot" ? `${post.authorId} (agent)` : post.authorId;
|
|
7510
7746
|
ctx.out.log(`\u2014 ${who}
|
|
@@ -7516,13 +7752,6 @@ ${post.body}`);
|
|
|
7516
7752
|
}
|
|
7517
7753
|
return result;
|
|
7518
7754
|
}
|
|
7519
|
-
var WatchTimeoutError = class extends Error {
|
|
7520
|
-
code = "watch_timeout";
|
|
7521
|
-
constructor() {
|
|
7522
|
-
super("nothing new before the timeout");
|
|
7523
|
-
this.name = "WatchTimeoutError";
|
|
7524
|
-
}
|
|
7525
|
-
};
|
|
7526
7755
|
|
|
7527
7756
|
// src/discuss-command.ts
|
|
7528
7757
|
var ALLOWED = [
|
|
@@ -7544,6 +7773,8 @@ var ALLOWED = [
|
|
|
7544
7773
|
"self",
|
|
7545
7774
|
"interval",
|
|
7546
7775
|
"timeout",
|
|
7776
|
+
"cursor",
|
|
7777
|
+
"jsonl",
|
|
7547
7778
|
"mutation-id"
|
|
7548
7779
|
];
|
|
7549
7780
|
function requireId(id, action) {
|
|
@@ -7591,7 +7822,7 @@ async function discussCommand(parsed, deps = {}) {
|
|
|
7591
7822
|
return discussWho(ctx, parsed);
|
|
7592
7823
|
case "watch": {
|
|
7593
7824
|
const result = await discussWatch(ctx, id, parsed);
|
|
7594
|
-
if (!result.found) throw new WatchTimeoutError();
|
|
7825
|
+
if (!result.found) throw new WatchTimeoutError(result.cursor);
|
|
7595
7826
|
return;
|
|
7596
7827
|
}
|
|
7597
7828
|
default:
|
|
@@ -8579,6 +8810,7 @@ var PM_ENTITIES = Object.fromEntries(
|
|
|
8579
8810
|
["goal", "conformance", "task", "kanban", "decision", "bug"].map((entity) => [entity, PM_ACTIONS])
|
|
8580
8811
|
);
|
|
8581
8812
|
var COMMAND_SURFACE = {
|
|
8813
|
+
agent: { jobs: {}, retry: {} },
|
|
8582
8814
|
admin: {
|
|
8583
8815
|
ai: {
|
|
8584
8816
|
show: {},
|
|
@@ -10374,7 +10606,12 @@ async function securityStatus(parsed, dependencies) {
|
|
|
10374
10606
|
// src/cli.ts
|
|
10375
10607
|
function exitCodeFor(err) {
|
|
10376
10608
|
const code = err?.code;
|
|
10377
|
-
|
|
10609
|
+
if (code === "handshake_pending" || code === "watch_timeout") return 75;
|
|
10610
|
+
if (code === "checkpoint_required") return 3;
|
|
10611
|
+
if (code === "remote_unavailable") return 6;
|
|
10612
|
+
if (code === "auth_failed") return 5;
|
|
10613
|
+
if (code === "invalid_cursor") return 2;
|
|
10614
|
+
return 1;
|
|
10378
10615
|
}
|
|
10379
10616
|
async function runCli(argv = process.argv.slice(2), dependencies = {}) {
|
|
10380
10617
|
const runtime = {
|
|
@@ -10414,6 +10651,10 @@ async function runCli(argv = process.argv.slice(2), dependencies = {}) {
|
|
|
10414
10651
|
await adminCommand(parsed);
|
|
10415
10652
|
return;
|
|
10416
10653
|
}
|
|
10654
|
+
if (command === "agent") {
|
|
10655
|
+
await agentCommand(parsed, runtime);
|
|
10656
|
+
return;
|
|
10657
|
+
}
|
|
10417
10658
|
if (command === "app") {
|
|
10418
10659
|
await appCommand(parsed, runtime);
|
|
10419
10660
|
return;
|