@odla-ai/cli 0.25.8 → 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 +13 -0
- package/dist/bin.cjs +201 -48
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-PM5FBL3S.js → chunk-HXUVCUEM.js} +202 -49
- package/dist/chunk-HXUVCUEM.js.map +1 -0
- package/dist/index.cjs +201 -48
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-PM5FBL3S.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -4668,13 +4668,13 @@ async function createConversionRegistry(config) {
|
|
|
4668
4668
|
policies.set(policy.conversionId, Object.freeze(policy));
|
|
4669
4669
|
}
|
|
4670
4670
|
const outputCounts = /* @__PURE__ */ new Map();
|
|
4671
|
-
const
|
|
4671
|
+
const get = (id, kind) => {
|
|
4672
4672
|
const policy = policies.get(id);
|
|
4673
4673
|
if (!policy || policy.output.kind !== kind) throw new CamelError("conversion_rejected", "Conversion policy is missing or has the wrong output kind.");
|
|
4674
4674
|
return policy;
|
|
4675
4675
|
};
|
|
4676
4676
|
const checked = (source, id, kind) => {
|
|
4677
|
-
const policy =
|
|
4677
|
+
const policy = get(id, kind);
|
|
4678
4678
|
if (utf8Length(source.value) > policy.maximumSourceBytes) throw new CamelError("limit_exceeded", "Unsafe conversion input exceeds its byte bound.");
|
|
4679
4679
|
return policy;
|
|
4680
4680
|
};
|
|
@@ -6628,7 +6628,7 @@ Usage:
|
|
|
6628
6628
|
odla-ai discuss reply <topic> --body "..." [--markup "..."] [--mutation-id <id>]
|
|
6629
6629
|
odla-ai discuss resolve <topic> [--reopen] [--mutation-id <id>]
|
|
6630
6630
|
odla-ai discuss who --q <text> [--app <id>] [--kinds user,pm:task] [--json]
|
|
6631
|
-
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
6632
|
odla-ai agent jobs [--env dev] [--state pending|running|succeeded|dead_letter] [--limit 50] [--email <email>] [--json]
|
|
6633
6633
|
odla-ai agent retry <job-id> [--env dev] [--email <email>] [--json]
|
|
6634
6634
|
odla-ai o11y status [--app <id>] [--env prod] [--minutes 60] [--json]
|
|
@@ -7533,61 +7533,214 @@ async function discussWho(ctx, parsed) {
|
|
|
7533
7533
|
});
|
|
7534
7534
|
}
|
|
7535
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
|
+
|
|
7536
7612
|
// src/discuss-watch.ts
|
|
7537
7613
|
var DEFAULT_INTERVAL_MS = 15e3;
|
|
7538
|
-
var
|
|
7614
|
+
var MAX_CONSECUTIVE_FAILURES = 5;
|
|
7615
|
+
var MAX_BACKOFF_MS = 3e4;
|
|
7539
7616
|
function numberOpt2(parsed, flag, fallback) {
|
|
7540
7617
|
const raw = stringOpt(parsed.options[flag]);
|
|
7541
7618
|
const value = raw == null ? NaN : Number(raw);
|
|
7542
7619
|
return Number.isFinite(value) && value > 0 ? value : fallback;
|
|
7543
7620
|
}
|
|
7544
|
-
|
|
7545
|
-
|
|
7546
|
-
headers: { authorization: `Bearer ${ctx.token}` }
|
|
7547
|
-
});
|
|
7548
|
-
const data = await res.json().catch(() => ({}));
|
|
7549
|
-
if (!res.ok) throw new Error(`discuss watch failed: ${data.error ?? `registry returned ${res.status}`}`);
|
|
7550
|
-
return data;
|
|
7621
|
+
function jsonl(ctx, parsed, value) {
|
|
7622
|
+
if (parsed.options.jsonl === true) ctx.out.log(JSON.stringify({ v: 1, ...value }));
|
|
7551
7623
|
}
|
|
7552
7624
|
async function discussWatch(ctx, topicId, parsed) {
|
|
7625
|
+
if (ctx.json && parsed.options.jsonl === true) throw new Error("--json and --jsonl cannot be combined");
|
|
7553
7626
|
const sleep = ctx.sleep ?? ((ms) => new Promise((resolve11) => setTimeout(resolve11, ms)));
|
|
7554
7627
|
const now = ctx.now ?? Date.now;
|
|
7555
|
-
const intervalMs = numberOpt2(parsed, "interval", DEFAULT_INTERVAL_MS / 1e3) * 1e3;
|
|
7556
|
-
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;
|
|
7557
7631
|
const by = stringOpt(parsed.options.by);
|
|
7558
7632
|
const self = stringOpt(parsed.options.self);
|
|
7559
|
-
const
|
|
7560
|
-
|
|
7561
|
-
let
|
|
7562
|
-
let
|
|
7563
|
-
if (topicId) {
|
|
7564
|
-
const first = await get(ctx, `/topics/${encodeURIComponent(topicId)}`);
|
|
7565
|
-
seen = new Set(first.posts.map((post) => post.id));
|
|
7566
|
-
}
|
|
7633
|
+
const app = stringOpt(parsed.options.app);
|
|
7634
|
+
let cursor = stringOpt(parsed.options.cursor);
|
|
7635
|
+
let firstSuccess = true;
|
|
7636
|
+
let consecutiveFailures = 0;
|
|
7567
7637
|
for (; ; ) {
|
|
7568
|
-
|
|
7569
|
-
|
|
7570
|
-
|
|
7571
|
-
|
|
7572
|
-
|
|
7573
|
-
|
|
7574
|
-
|
|
7575
|
-
|
|
7576
|
-
|
|
7577
|
-
|
|
7578
|
-
|
|
7579
|
-
|
|
7580
|
-
|
|
7581
|
-
|
|
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
|
+
});
|
|
7582
7685
|
}
|
|
7583
|
-
|
|
7584
|
-
|
|
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
|
+
});
|
|
7716
|
+
}
|
|
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);
|
|
7585
7738
|
}
|
|
7586
7739
|
}
|
|
7587
|
-
function report2(ctx, result) {
|
|
7740
|
+
function report2(ctx, parsed, result) {
|
|
7588
7741
|
if (ctx.json) {
|
|
7589
7742
|
ctx.out.log(JSON.stringify(result, null, 2));
|
|
7590
|
-
} else if (result.found) {
|
|
7743
|
+
} else if (parsed.options.jsonl !== true && result.found) {
|
|
7591
7744
|
for (const post of result.posts ?? []) {
|
|
7592
7745
|
const who = post.authorKind === "bot" ? `${post.authorId} (agent)` : post.authorId;
|
|
7593
7746
|
ctx.out.log(`\u2014 ${who}
|
|
@@ -7599,13 +7752,6 @@ ${post.body}`);
|
|
|
7599
7752
|
}
|
|
7600
7753
|
return result;
|
|
7601
7754
|
}
|
|
7602
|
-
var WatchTimeoutError = class extends Error {
|
|
7603
|
-
code = "watch_timeout";
|
|
7604
|
-
constructor() {
|
|
7605
|
-
super("nothing new before the timeout");
|
|
7606
|
-
this.name = "WatchTimeoutError";
|
|
7607
|
-
}
|
|
7608
|
-
};
|
|
7609
7755
|
|
|
7610
7756
|
// src/discuss-command.ts
|
|
7611
7757
|
var ALLOWED = [
|
|
@@ -7627,6 +7773,8 @@ var ALLOWED = [
|
|
|
7627
7773
|
"self",
|
|
7628
7774
|
"interval",
|
|
7629
7775
|
"timeout",
|
|
7776
|
+
"cursor",
|
|
7777
|
+
"jsonl",
|
|
7630
7778
|
"mutation-id"
|
|
7631
7779
|
];
|
|
7632
7780
|
function requireId(id, action) {
|
|
@@ -7674,7 +7822,7 @@ async function discussCommand(parsed, deps = {}) {
|
|
|
7674
7822
|
return discussWho(ctx, parsed);
|
|
7675
7823
|
case "watch": {
|
|
7676
7824
|
const result = await discussWatch(ctx, id, parsed);
|
|
7677
|
-
if (!result.found) throw new WatchTimeoutError();
|
|
7825
|
+
if (!result.found) throw new WatchTimeoutError(result.cursor);
|
|
7678
7826
|
return;
|
|
7679
7827
|
}
|
|
7680
7828
|
default:
|
|
@@ -10458,7 +10606,12 @@ async function securityStatus(parsed, dependencies) {
|
|
|
10458
10606
|
// src/cli.ts
|
|
10459
10607
|
function exitCodeFor(err) {
|
|
10460
10608
|
const code = err?.code;
|
|
10461
|
-
|
|
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;
|
|
10462
10615
|
}
|
|
10463
10616
|
async function runCli(argv = process.argv.slice(2), dependencies = {}) {
|
|
10464
10617
|
const runtime = {
|