@odla-ai/cli 0.25.9 → 0.25.11
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 -2
- package/dist/bin.cjs +82 -24
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-HXUVCUEM.js → chunk-FYSV7POZ.js} +83 -25
- package/dist/chunk-FYSV7POZ.js.map +1 -0
- package/dist/index.cjs +82 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-HXUVCUEM.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -6623,7 +6623,7 @@ Usage:
|
|
|
6623
6623
|
odla-ai pm handoff --app <id> [--json]
|
|
6624
6624
|
odla-ai discuss groups [--json]
|
|
6625
6625
|
odla-ai discuss list [--app <id>] [--q <text>] [--state open|resolved|all] [--json]
|
|
6626
|
-
odla-ai discuss read <topic> [--json]
|
|
6626
|
+
odla-ai discuss read <topic> [--limit <n> --offset <n>] [--json]
|
|
6627
6627
|
odla-ai discuss post --app <id> --subject "..." --body "..." [--markup "... @[Label](kind/id)"] [--mutation-id <id>]
|
|
6628
6628
|
odla-ai discuss reply <topic> --body "..." [--markup "..."] [--mutation-id <id>]
|
|
6629
6629
|
odla-ai discuss resolve <topic> [--reopen] [--mutation-id <id>]
|
|
@@ -7469,22 +7469,59 @@ async function discussList(ctx, parsed) {
|
|
|
7469
7469
|
}
|
|
7470
7470
|
});
|
|
7471
7471
|
}
|
|
7472
|
-
async function discussRead(ctx, id) {
|
|
7473
|
-
const
|
|
7474
|
-
|
|
7475
|
-
|
|
7476
|
-
|
|
7477
|
-
|
|
7478
|
-
|
|
7479
|
-
|
|
7480
|
-
|
|
7481
|
-
|
|
7482
|
-
|
|
7483
|
-
|
|
7484
|
-
|
|
7485
|
-
|
|
7472
|
+
async function discussRead(ctx, id, parsed) {
|
|
7473
|
+
const requestedLimit = stringOpt(parsed.options.limit);
|
|
7474
|
+
const requestedOffset = stringOpt(parsed.options.offset);
|
|
7475
|
+
if (requestedLimit !== void 0 || requestedOffset !== void 0) {
|
|
7476
|
+
const query = new URLSearchParams({
|
|
7477
|
+
limit: requestedLimit ?? "200",
|
|
7478
|
+
offset: requestedOffset ?? "0"
|
|
7479
|
+
});
|
|
7480
|
+
const page = await request(ctx, "GET", `/topics/${encodeURIComponent(id)}?${query}`);
|
|
7481
|
+
emit(ctx, page, () => renderRead(ctx, page.topic, page.posts));
|
|
7482
|
+
return;
|
|
7483
|
+
}
|
|
7484
|
+
for (let scan = 0; scan < 3; scan++) {
|
|
7485
|
+
const posts = /* @__PURE__ */ new Map();
|
|
7486
|
+
let topic = null;
|
|
7487
|
+
let offset = 0;
|
|
7488
|
+
for (; ; ) {
|
|
7489
|
+
const page = await request(
|
|
7490
|
+
ctx,
|
|
7491
|
+
"GET",
|
|
7492
|
+
`/topics/${encodeURIComponent(id)}?limit=200&offset=${offset}`
|
|
7493
|
+
);
|
|
7494
|
+
topic = page.topic;
|
|
7495
|
+
for (const post of page.posts) posts.set(post.id, post);
|
|
7496
|
+
if (posts.size > 1e4) throw new Error("discuss read failed: conversation exceeds 10000 posts");
|
|
7497
|
+
if (!page.page?.hasMore) break;
|
|
7498
|
+
if (page.page.nextOffset === null || page.page.nextOffset <= offset) {
|
|
7499
|
+
throw new Error("discuss read failed: registry returned a non-advancing post page");
|
|
7500
|
+
}
|
|
7501
|
+
offset = page.page.nextOffset;
|
|
7486
7502
|
}
|
|
7487
|
-
|
|
7503
|
+
const ordered = [...posts.values()].sort(
|
|
7504
|
+
(a, b) => a.createdAt - b.createdAt || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)
|
|
7505
|
+
);
|
|
7506
|
+
const expected = Number.isInteger(topic.replyCount) ? topic.replyCount + 1 : ordered.length;
|
|
7507
|
+
if (ordered.length === expected) {
|
|
7508
|
+
const result = { topic, posts: ordered };
|
|
7509
|
+
emit(ctx, result, () => renderRead(ctx, result.topic, result.posts));
|
|
7510
|
+
return;
|
|
7511
|
+
}
|
|
7512
|
+
if (offset === 0) throw new Error("discuss read failed: registry did not provide forward post pages");
|
|
7513
|
+
}
|
|
7514
|
+
throw new Error("discuss read failed: conversation changed during every complete-read attempt");
|
|
7515
|
+
}
|
|
7516
|
+
function renderRead(ctx, topic, posts) {
|
|
7517
|
+
ctx.out.log(`${topic.subject} [${state(topic)}] ${topic.appId ?? ""}`);
|
|
7518
|
+
for (const post of posts) {
|
|
7519
|
+
const who = post.authorKind === "bot" ? `${post.authorId} (agent)` : post.authorId;
|
|
7520
|
+
ctx.out.log(`
|
|
7521
|
+
\u2014 ${who}`);
|
|
7522
|
+
ctx.out.log(bodyWithRefs(post));
|
|
7523
|
+
for (const file of post.attachments ?? []) ctx.out.log(` [attachment] ${file.name} (${file.size} bytes)`);
|
|
7524
|
+
}
|
|
7488
7525
|
}
|
|
7489
7526
|
async function discussPost(ctx, parsed) {
|
|
7490
7527
|
const appId = stringOpt(parsed.options.app);
|
|
@@ -7811,7 +7848,7 @@ async function discussCommand(parsed, deps = {}) {
|
|
|
7811
7848
|
case "topics":
|
|
7812
7849
|
return discussList(ctx, parsed);
|
|
7813
7850
|
case "read":
|
|
7814
|
-
return discussRead(ctx, requireId(id, "read"));
|
|
7851
|
+
return discussRead(ctx, requireId(id, "read"), parsed);
|
|
7815
7852
|
case "post":
|
|
7816
7853
|
return discussPost(ctx, parsed);
|
|
7817
7854
|
case "reply":
|
|
@@ -8138,6 +8175,7 @@ function statusVerdict(reads) {
|
|
|
8138
8175
|
sourceHttp(reasons, "canary", reads.canary);
|
|
8139
8176
|
sourceHttp(reasons, "collector", reads.collector);
|
|
8140
8177
|
sourceHttp(reasons, "provider", reads.provider);
|
|
8178
|
+
sourceHttp(reasons, "provider", reads.providerHistory, "provider_history_");
|
|
8141
8179
|
const liveStatus = String(reads.liveSync.body.status ?? "");
|
|
8142
8180
|
if (liveStatus === "partial") {
|
|
8143
8181
|
reasons.push({
|
|
@@ -8209,6 +8247,14 @@ function statusVerdict(reads) {
|
|
|
8209
8247
|
severity: "degraded"
|
|
8210
8248
|
});
|
|
8211
8249
|
}
|
|
8250
|
+
const providerHistoryStatus = String(reads.providerHistory.body.status ?? "");
|
|
8251
|
+
if (providerHistoryStatus && providerHistoryStatus !== "observed" && providerHistoryStatus !== "no_data") {
|
|
8252
|
+
reasons.push({
|
|
8253
|
+
source: "provider",
|
|
8254
|
+
code: `provider_history_${providerHistoryStatus}`,
|
|
8255
|
+
severity: "degraded"
|
|
8256
|
+
});
|
|
8257
|
+
}
|
|
8212
8258
|
return {
|
|
8213
8259
|
status: reasons.some((reason) => reason.severity === "unhealthy") ? "unhealthy" : reasons.length > 0 ? "degraded" : "healthy",
|
|
8214
8260
|
reasons
|
|
@@ -8228,11 +8274,11 @@ function collectorReason(read3, fallback) {
|
|
|
8228
8274
|
);
|
|
8229
8275
|
return typeof first?.code === "string" && first.code ? first.code : fallback;
|
|
8230
8276
|
}
|
|
8231
|
-
function sourceHttp(reasons, source, read3) {
|
|
8277
|
+
function sourceHttp(reasons, source, read3, prefix = "") {
|
|
8232
8278
|
if (read3.httpStatus >= 200 && read3.httpStatus < 300) return;
|
|
8233
8279
|
reasons.push({
|
|
8234
8280
|
source,
|
|
8235
|
-
code:
|
|
8281
|
+
code: `${prefix}http_${read3.httpStatus}`,
|
|
8236
8282
|
severity: read3.httpStatus >= 500 && read3.httpStatus !== 501 ? "unhealthy" : "degraded"
|
|
8237
8283
|
});
|
|
8238
8284
|
}
|
|
@@ -8274,7 +8320,7 @@ async function o11yCommand(parsed, deps = {}) {
|
|
|
8274
8320
|
const base = `${cfg.platformUrl}/o11y/${encodeURIComponent(appId)}`;
|
|
8275
8321
|
const query = new URLSearchParams({ env, minutes: String(minutes) });
|
|
8276
8322
|
const headers = { authorization: `Bearer ${token}` };
|
|
8277
|
-
const [application, liveSync, canary, collector, provider] = await Promise.all([
|
|
8323
|
+
const [application, liveSync, canary, collector, provider, providerHistory] = await Promise.all([
|
|
8278
8324
|
read2(`${base}/metrics/red?${query}`, headers, doFetch),
|
|
8279
8325
|
read2(
|
|
8280
8326
|
`${base}/live-sync/health?${query}`,
|
|
@@ -8287,17 +8333,19 @@ async function o11yCommand(parsed, deps = {}) {
|
|
|
8287
8333
|
doFetch
|
|
8288
8334
|
),
|
|
8289
8335
|
read2(`${base}/self-health?${query}`, headers, doFetch),
|
|
8290
|
-
read2(`${base}/provider/cloudflare?${query}`, headers, doFetch)
|
|
8336
|
+
read2(`${base}/provider/cloudflare?${query}`, headers, doFetch),
|
|
8337
|
+
read2(`${base}/provider/cloudflare/history?${query}`, headers, doFetch)
|
|
8291
8338
|
]);
|
|
8292
8339
|
const verdict = statusVerdict({
|
|
8293
8340
|
application,
|
|
8294
8341
|
liveSync,
|
|
8295
8342
|
canary,
|
|
8296
8343
|
collector,
|
|
8297
|
-
provider
|
|
8344
|
+
provider,
|
|
8345
|
+
providerHistory
|
|
8298
8346
|
});
|
|
8299
8347
|
const status = {
|
|
8300
|
-
schemaVersion:
|
|
8348
|
+
schemaVersion: 5,
|
|
8301
8349
|
scope: { appId, env, minutes },
|
|
8302
8350
|
observedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8303
8351
|
verdict,
|
|
@@ -8305,7 +8353,8 @@ async function o11yCommand(parsed, deps = {}) {
|
|
|
8305
8353
|
liveSync,
|
|
8306
8354
|
canary,
|
|
8307
8355
|
collector,
|
|
8308
|
-
provider
|
|
8356
|
+
provider,
|
|
8357
|
+
providerHistory
|
|
8309
8358
|
};
|
|
8310
8359
|
if (parsed.options.json === true) {
|
|
8311
8360
|
out.log(JSON.stringify(status, null, 2));
|
|
@@ -8365,6 +8414,11 @@ function printStatus2(status, out) {
|
|
|
8365
8414
|
out.log(
|
|
8366
8415
|
`cloudflare ${status.provider.httpStatus} ${String(status.provider.body.status ?? status.provider.body.error ?? "unavailable")} ${numeric3(providerMetrics.requests)} invocations ${numeric3(providerMetrics.errors)} runtime errors`
|
|
8367
8416
|
);
|
|
8417
|
+
const providerPoints = Array.isArray(status.providerHistory.body.points) ? status.providerHistory.body.points.length : 0;
|
|
8418
|
+
const providerFreshness = record6(status.providerHistory.body.freshness) ? status.providerHistory.body.freshness : {};
|
|
8419
|
+
out.log(
|
|
8420
|
+
`cloudflare-history ${status.providerHistory.httpStatus} ${String(status.providerHistory.body.status ?? status.providerHistory.body.error ?? "unavailable")} ${providerPoints} snapshots ${optionalAge(providerFreshness.ageMs)} old`
|
|
8421
|
+
);
|
|
8368
8422
|
out.log(
|
|
8369
8423
|
`verdict ${status.verdict.status} ${status.verdict.reasons.map((reason) => `${reason.source}:${reason.code}`).join(", ") || "all required signals healthy"}`
|
|
8370
8424
|
);
|
|
@@ -8383,6 +8437,10 @@ function numeric3(value) {
|
|
|
8383
8437
|
function optionalNumeric(value) {
|
|
8384
8438
|
return typeof value === "number" && Number.isFinite(value) ? `${value} ms` : "\u2014";
|
|
8385
8439
|
}
|
|
8440
|
+
function optionalAge(value) {
|
|
8441
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return "unknown";
|
|
8442
|
+
return `${Math.max(0, Math.round(value / 1e3))}s`;
|
|
8443
|
+
}
|
|
8386
8444
|
|
|
8387
8445
|
// src/provision.ts
|
|
8388
8446
|
var import_apps5 = require("@odla-ai/apps");
|