@odla-ai/cli 0.18.0 → 0.20.0
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 +30 -14
- package/dist/bin.cjs +243 -14
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-TOSMNGGQ.js → chunk-I4XURPUK.js} +258 -15
- package/dist/chunk-I4XURPUK.js.map +1 -0
- package/dist/index.cjs +264 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +41 -1
- package/dist/index.d.ts +41 -1
- package/dist/index.js +13 -3
- package/package.json +1 -1
- package/skills/odla/SKILL.md +14 -0
- package/dist/chunk-TOSMNGGQ.js.map +0 -1
package/README.md
CHANGED
|
@@ -66,6 +66,23 @@ Platform runbook edits need admin or the `platform:runbook:write` capability,
|
|
|
66
66
|
which the CLI requests for you; a plain handshake token is never admin by
|
|
67
67
|
design, and `odla-ai whoami` reports what this terminal actually holds.
|
|
68
68
|
|
|
69
|
+
**Is the corpus itself still true?**
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
npx odla-ai runbook lint
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Because a runbook is edited without a release, its text can name a command that
|
|
76
|
+
only a newer CLI has — and no gate in any repository can see that, since the
|
|
77
|
+
text is a row in a database rather than a file. `lint` closes that: it holds
|
|
78
|
+
every `odla-ai …` command the runbooks name against this CLI's actual command
|
|
79
|
+
surface, and against the minimum versions each runbook declares in `requires`.
|
|
80
|
+
|
|
81
|
+
A runbook may record the versions its steps assume, as `name@version` specs read
|
|
82
|
+
as minimums (`@odla-ai/cli@0.18.0`). When yours is older, `runbook get` says so
|
|
83
|
+
on **stderr** — never stdout, so piping the procedure into an agent still yields
|
|
84
|
+
the procedure and nothing else.
|
|
85
|
+
|
|
69
86
|
## Install
|
|
70
87
|
|
|
71
88
|
No install needed — the package ships a single `odla-ai` binary, so npx can run
|
|
@@ -109,6 +126,7 @@ npx odla-ai runbook search "restore a tenant" --limit 5
|
|
|
109
126
|
npx odla-ai runbook get release
|
|
110
127
|
npx odla-ai runbook list
|
|
111
128
|
npx odla-ai runbook impact --base origin/main
|
|
129
|
+
npx odla-ai runbook lint
|
|
112
130
|
npx odla-ai runbook edit db --note "what changed"
|
|
113
131
|
npx odla-ai runbook comment release --body "step 4 no longer applies"
|
|
114
132
|
npx odla-ai whoami
|
|
@@ -118,7 +136,6 @@ npx odla-ai doctor
|
|
|
118
136
|
npx odla-ai calendar status --env dev
|
|
119
137
|
npx odla-ai calendar calendars --env dev
|
|
120
138
|
npx odla-ai calendar connect --env dev
|
|
121
|
-
npx odla-ai calendar resync --env dev
|
|
122
139
|
npx odla-ai calendar disconnect --env dev --yes
|
|
123
140
|
npx odla-ai capabilities --json
|
|
124
141
|
npx odla-ai code connect --platform https://odla.ai --app-id my-app --env dev --email owner@example.com
|
|
@@ -318,19 +335,18 @@ agent automation. Once connected, `calendar calendars` lists ids visible to the
|
|
|
318
335
|
Google identity so the checked-in selection can be refined. `calendar connect`
|
|
319
336
|
applies the checked-in booking-page setting and starts consent when the
|
|
320
337
|
connection is absent, failed, disconnected, or degraded (so an agent can
|
|
321
|
-
repair revoked credentials); a healthy connection is reused. A
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
Google authorization code, client secret, access token, or refresh token.
|
|
338
|
+
repair revoked credentials); a healthy connection is reused. A connection that
|
|
339
|
+
already holds provider credentials is reused without requesting Google consent
|
|
340
|
+
again, and an in-flight authorization is awaited rather than duplicated.
|
|
341
|
+
|
|
342
|
+
Nothing syncs: Google Calendar is the source of truth and odla stores no events
|
|
343
|
+
and no attendees, so there is no resync to ask for. `calendar disconnect --yes`
|
|
344
|
+
deletes this app/environment connection's encrypted platform token. It does not
|
|
345
|
+
revoke the shared user-to-Google-OAuth-project grant, because that could
|
|
346
|
+
invalidate other odla connections for the same user. A future explicitly global
|
|
347
|
+
revoke command would need cross-connection accounting and warning. A direct
|
|
348
|
+
production connect requires `--yes`. None of these commands accepts a Google
|
|
349
|
+
authorization code, client secret, access token, or refresh token.
|
|
334
350
|
|
|
335
351
|
The SDK uses the existing server-side db values (`ODLA_ENDPOINT`,
|
|
336
352
|
`ODLA_TENANT`, `ODLA_API_KEY`), so calendar adds no Worker secret. Never expose
|
package/dist/bin.cjs
CHANGED
|
@@ -6435,11 +6435,12 @@ Usage:
|
|
|
6435
6435
|
odla-ai runbook ask "<question>" [--app <id>] [--all] [--json]
|
|
6436
6436
|
odla-ai runbook search "<question>" [--app <id>] [--all] [--limit <n>] [--json]
|
|
6437
6437
|
odla-ai runbook impact [--base origin/main] [--app <id>] [--all] [--limit <n>] [--json]
|
|
6438
|
+
odla-ai runbook lint [--app <id>] [--all] [--json]
|
|
6438
6439
|
odla-ai runbook list [--app <id>] [--all] [--q <text>] [--json]
|
|
6439
6440
|
odla-ai runbook comment <slug> --body "..." [--app <id>]
|
|
6440
6441
|
odla-ai runbook get <slug> [--app <id>]
|
|
6441
|
-
odla-ai runbook new <slug> --title <t> --file <path|-> [--summary <s>] [--app <id>]
|
|
6442
|
-
odla-ai runbook edit <slug> [--file <path|->|--body "..."] [--note <why>] [--app <id>]
|
|
6442
|
+
odla-ai runbook new <slug> --title <t> --file <path|-> [--summary <s>] [--requires <specs>] [--app <id>]
|
|
6443
|
+
odla-ai runbook edit <slug> [--file <path|->|--body "..."] [--note <why>] [--requires <specs>] [--app <id>]
|
|
6443
6444
|
odla-ai runbook import <dir> [--visibility operator|admin] [--dry-run] [--app <id>]
|
|
6444
6445
|
odla-ai runbook publish <slug> [--app <id>]
|
|
6445
6446
|
odla-ai runbook visibility <slug> <operator|admin> [--app <id>]
|
|
@@ -6481,7 +6482,10 @@ Commands:
|
|
|
6481
6482
|
"search" the passages behind it; "get" the whole document.
|
|
6482
6483
|
"impact" diffs your working tree against a base ref and names the
|
|
6483
6484
|
runbooks that describe what you changed \u2014 run it after touching a
|
|
6484
|
-
package's exported API or JSDoc, or a Studio surface.
|
|
6485
|
+
package's exported API or JSDoc, or a Studio surface. "lint"
|
|
6486
|
+
checks the corpus the other way: every odla-ai command the
|
|
6487
|
+
runbooks name is held against this CLI's real command surface,
|
|
6488
|
+
and against the minimum versions each runbook declares. A wrong step
|
|
6485
6489
|
is fixed with "edit", which takes effect immediately: a runbook is
|
|
6486
6490
|
a row, not a release. Runbooks describe PROCEDURE; what an export
|
|
6487
6491
|
does and what it guarantees is JSDoc, rendered per package at
|
|
@@ -7802,6 +7806,55 @@ var import_node_process9 = __toESM(require("process"), 1);
|
|
|
7802
7806
|
|
|
7803
7807
|
// src/runbook-actions.ts
|
|
7804
7808
|
var import_node_fs13 = require("fs");
|
|
7809
|
+
|
|
7810
|
+
// src/runbook-requires.ts
|
|
7811
|
+
var SPEC = /^(@?[\w./-]+?)@(\d+\.\d+\.\d+(?:[\w.-]*)?)$/;
|
|
7812
|
+
function parseRequires(value) {
|
|
7813
|
+
if (!value) return [];
|
|
7814
|
+
const out = [];
|
|
7815
|
+
for (const token of value.split(/[\s,]+/).filter(Boolean)) {
|
|
7816
|
+
const match = SPEC.exec(token);
|
|
7817
|
+
if (match?.[1] && match[2]) out.push({ name: match[1], min: match[2] });
|
|
7818
|
+
}
|
|
7819
|
+
return out;
|
|
7820
|
+
}
|
|
7821
|
+
function compareVersions(a, b) {
|
|
7822
|
+
const parts = (v) => {
|
|
7823
|
+
const [core = "", pre = ""] = v.split("-", 2);
|
|
7824
|
+
return { nums: core.split(".").map((n) => Number.parseInt(n, 10) || 0), pre };
|
|
7825
|
+
};
|
|
7826
|
+
const left = parts(a);
|
|
7827
|
+
const right = parts(b);
|
|
7828
|
+
for (let i = 0; i < Math.max(left.nums.length, right.nums.length); i++) {
|
|
7829
|
+
const diff = (left.nums[i] ?? 0) - (right.nums[i] ?? 0);
|
|
7830
|
+
if (diff) return diff < 0 ? -1 : 1;
|
|
7831
|
+
}
|
|
7832
|
+
if (left.pre === right.pre) return 0;
|
|
7833
|
+
if (!left.pre) return 1;
|
|
7834
|
+
if (!right.pre) return -1;
|
|
7835
|
+
return left.pre < right.pre ? -1 : 1;
|
|
7836
|
+
}
|
|
7837
|
+
function satisfies(installed, min) {
|
|
7838
|
+
return compareVersions(installed, min) >= 0;
|
|
7839
|
+
}
|
|
7840
|
+
function unmetRequirements(requires, installedVersions) {
|
|
7841
|
+
const unmet = [];
|
|
7842
|
+
for (const requirement of parseRequires(requires)) {
|
|
7843
|
+
const installed = installedVersions[requirement.name];
|
|
7844
|
+
if (installed === void 0) {
|
|
7845
|
+
unmet.push({ ...requirement, installed: null });
|
|
7846
|
+
} else if (!satisfies(installed, requirement.min)) {
|
|
7847
|
+
unmet.push({ ...requirement, installed });
|
|
7848
|
+
}
|
|
7849
|
+
}
|
|
7850
|
+
return unmet;
|
|
7851
|
+
}
|
|
7852
|
+
function describeUnmet(slug, unmet) {
|
|
7853
|
+
const detail = unmet.map((u) => `${u.name}@${u.min}${u.installed ? ` (you have ${u.installed})` : " (not installed here)"}`).join(", ");
|
|
7854
|
+
return `note: runbook "${slug}" expects ${detail} \u2014 some steps may name commands your version does not have.`;
|
|
7855
|
+
}
|
|
7856
|
+
|
|
7857
|
+
// src/runbook-actions.ts
|
|
7805
7858
|
var PLATFORM_SCOPE = "$platform";
|
|
7806
7859
|
async function call(ctx, method, path, body) {
|
|
7807
7860
|
const res = await ctx.doFetch(`${ctx.platformUrl.replace(/\/$/, "")}/registry/pm${path}`, {
|
|
@@ -7856,19 +7909,23 @@ async function runbookList(ctx, all, query) {
|
|
|
7856
7909
|
async function runbookGet(ctx, slug) {
|
|
7857
7910
|
const runbook = await bySlug(ctx, slug);
|
|
7858
7911
|
if (ctx.json) return ctx.out.log(JSON.stringify(runbook, null, 2));
|
|
7912
|
+
const unmet = unmetRequirements(runbook.requires, { "@odla-ai/cli": cliVersion() });
|
|
7913
|
+
if (unmet.length) ctx.out.error(describeUnmet(slug, unmet));
|
|
7859
7914
|
ctx.out.log(runbook.body);
|
|
7860
7915
|
}
|
|
7861
|
-
async function runbookNew(ctx, slug, title, body, summary) {
|
|
7916
|
+
async function runbookNew(ctx, slug, title, body, summary, requires) {
|
|
7862
7917
|
const created = await call(ctx, "POST", "/runbook", {
|
|
7863
7918
|
appId: ctx.appId,
|
|
7864
|
-
input: { slug, title, body, ...summary ? { summary } : {} }
|
|
7919
|
+
input: { slug, title, body, ...summary ? { summary } : {}, ...requires ? { requires } : {} }
|
|
7865
7920
|
});
|
|
7866
7921
|
ctx.out.log(ctx.json ? JSON.stringify(created, null, 2) : `created ${slug} (${created.id})`);
|
|
7867
7922
|
}
|
|
7868
|
-
async function runbookEdit(ctx, slug, body, note) {
|
|
7923
|
+
async function runbookEdit(ctx, slug, body, note, requires) {
|
|
7869
7924
|
const runbook = await bySlug(ctx, slug);
|
|
7870
7925
|
const result = await call(ctx, "PATCH", `/runbook/${encodeURIComponent(runbook.id)}`, {
|
|
7871
|
-
|
|
7926
|
+
// An empty --requires clears the declaration; omitting the flag leaves
|
|
7927
|
+
// whatever is there, so an ordinary body edit never drops it.
|
|
7928
|
+
patch: { body, ...note ? { note } : {}, ...requires === void 0 ? {} : { requires: requires || null } }
|
|
7872
7929
|
});
|
|
7873
7930
|
if (ctx.json) return ctx.out.log(JSON.stringify(result, null, 2));
|
|
7874
7931
|
ctx.out.log(`${slug} \u2192 v${result.record?.version ?? runbook.version + 1}`);
|
|
@@ -8275,6 +8332,170 @@ async function runbookImpact(ctx, options, deps = {}) {
|
|
|
8275
8332
|
report2(ctx, impacts);
|
|
8276
8333
|
}
|
|
8277
8334
|
|
|
8335
|
+
// src/surface.ts
|
|
8336
|
+
var PM_ACTIONS = {
|
|
8337
|
+
list: {},
|
|
8338
|
+
add: {},
|
|
8339
|
+
create: {},
|
|
8340
|
+
get: {},
|
|
8341
|
+
set: {},
|
|
8342
|
+
update: {},
|
|
8343
|
+
status: {},
|
|
8344
|
+
move: {},
|
|
8345
|
+
done: {},
|
|
8346
|
+
comment: {},
|
|
8347
|
+
comments: {},
|
|
8348
|
+
rm: {},
|
|
8349
|
+
delete: {}
|
|
8350
|
+
};
|
|
8351
|
+
var PM_ENTITIES = Object.fromEntries(
|
|
8352
|
+
["goal", "conformance", "task", "kanban", "decision", "bug"].map((entity) => [entity, PM_ACTIONS])
|
|
8353
|
+
);
|
|
8354
|
+
var COMMAND_SURFACE = {
|
|
8355
|
+
admin: {
|
|
8356
|
+
ai: {
|
|
8357
|
+
show: {},
|
|
8358
|
+
set: {},
|
|
8359
|
+
credentials: {},
|
|
8360
|
+
models: {},
|
|
8361
|
+
usage: {},
|
|
8362
|
+
audit: {},
|
|
8363
|
+
credential: { set: {} }
|
|
8364
|
+
}
|
|
8365
|
+
},
|
|
8366
|
+
app: {
|
|
8367
|
+
archive: {},
|
|
8368
|
+
restore: {},
|
|
8369
|
+
export: {},
|
|
8370
|
+
import: {},
|
|
8371
|
+
rename: {},
|
|
8372
|
+
"refresh-sandbox": {},
|
|
8373
|
+
"go-live": {},
|
|
8374
|
+
promote: {},
|
|
8375
|
+
owners: { list: {}, add: {}, remove: {} }
|
|
8376
|
+
},
|
|
8377
|
+
calendar: { status: {}, calendars: {}, connect: {}, disconnect: {} },
|
|
8378
|
+
capabilities: {},
|
|
8379
|
+
code: { connect: {} },
|
|
8380
|
+
doctor: {},
|
|
8381
|
+
help: {},
|
|
8382
|
+
init: {},
|
|
8383
|
+
pm: PM_ENTITIES,
|
|
8384
|
+
provision: {},
|
|
8385
|
+
runbook: {
|
|
8386
|
+
ask: {},
|
|
8387
|
+
search: {},
|
|
8388
|
+
impact: {},
|
|
8389
|
+
list: {},
|
|
8390
|
+
get: {},
|
|
8391
|
+
cat: {},
|
|
8392
|
+
new: {},
|
|
8393
|
+
edit: {},
|
|
8394
|
+
comment: {},
|
|
8395
|
+
import: {},
|
|
8396
|
+
visibility: {},
|
|
8397
|
+
publish: {},
|
|
8398
|
+
archive: {},
|
|
8399
|
+
history: {},
|
|
8400
|
+
revert: {},
|
|
8401
|
+
rm: {},
|
|
8402
|
+
lint: {}
|
|
8403
|
+
},
|
|
8404
|
+
secrets: { push: {}, set: {}, "set-clerk-key": {} },
|
|
8405
|
+
security: {
|
|
8406
|
+
plan: {},
|
|
8407
|
+
sources: {},
|
|
8408
|
+
run: {},
|
|
8409
|
+
status: {},
|
|
8410
|
+
report: {},
|
|
8411
|
+
github: { connect: {}, disconnect: {} }
|
|
8412
|
+
},
|
|
8413
|
+
setup: {},
|
|
8414
|
+
skill: { install: {} },
|
|
8415
|
+
smoke: {},
|
|
8416
|
+
version: {},
|
|
8417
|
+
whoami: {}
|
|
8418
|
+
};
|
|
8419
|
+
function acceptedAfter(path) {
|
|
8420
|
+
let node = COMMAND_SURFACE;
|
|
8421
|
+
for (const word of path) {
|
|
8422
|
+
node = node?.[word];
|
|
8423
|
+
if (!node) return [];
|
|
8424
|
+
}
|
|
8425
|
+
return Object.keys(node).sort();
|
|
8426
|
+
}
|
|
8427
|
+
function validateInvocation(words2) {
|
|
8428
|
+
let node = COMMAND_SURFACE;
|
|
8429
|
+
const walked = [];
|
|
8430
|
+
for (const word of words2) {
|
|
8431
|
+
if (Object.keys(node).length === 0) return null;
|
|
8432
|
+
const next = node[word];
|
|
8433
|
+
if (!next) return { validPrefix: walked.join(" "), word, accepted: Object.keys(node).sort() };
|
|
8434
|
+
walked.push(word);
|
|
8435
|
+
node = next;
|
|
8436
|
+
}
|
|
8437
|
+
return null;
|
|
8438
|
+
}
|
|
8439
|
+
function describeProblem(problem) {
|
|
8440
|
+
const where = problem.validPrefix ? `after "${problem.validPrefix}"` : "as a command";
|
|
8441
|
+
return `"${problem.word}" is not accepted ${where} \u2014 try: ${problem.accepted.join(", ")}`;
|
|
8442
|
+
}
|
|
8443
|
+
|
|
8444
|
+
// src/runbook-lint.ts
|
|
8445
|
+
function invocationsIn(body) {
|
|
8446
|
+
const re = /(`|npx[^\S\n]+)?(?:@odla-ai\/cli(?:@[\w.-]+)?|odla-ai)((?:[^\S\n]+[a-z][\w-]*)+)/g;
|
|
8447
|
+
const found = [];
|
|
8448
|
+
for (const match of body.matchAll(re)) {
|
|
8449
|
+
if (!match[1]) continue;
|
|
8450
|
+
const words2 = match[2].trim().split(/[^\S\n]+/);
|
|
8451
|
+
if (words2.length) found.push(words2);
|
|
8452
|
+
}
|
|
8453
|
+
return found;
|
|
8454
|
+
}
|
|
8455
|
+
function lintRunbook(runbook, installed) {
|
|
8456
|
+
const findings = [];
|
|
8457
|
+
const at = { slug: runbook.slug, appId: runbook.appId };
|
|
8458
|
+
const declared = parseRequires(runbook.requires);
|
|
8459
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8460
|
+
for (const words2 of invocationsIn(runbook.body)) {
|
|
8461
|
+
const problem = validateInvocation(words2);
|
|
8462
|
+
if (!problem) continue;
|
|
8463
|
+
const key = `${problem.validPrefix}|${problem.word}`;
|
|
8464
|
+
if (seen.has(key)) continue;
|
|
8465
|
+
seen.add(key);
|
|
8466
|
+
findings.push({
|
|
8467
|
+
...at,
|
|
8468
|
+
kind: declared.length ? "command" : "undeclared",
|
|
8469
|
+
detail: declared.length ? `\`odla-ai ${words2.join(" ")}\`: ${describeProblem(problem)}` : `\`odla-ai ${words2.join(" ")}\`: ${describeProblem(problem)}. If a newer package added it, declare that with requires.`
|
|
8470
|
+
});
|
|
8471
|
+
}
|
|
8472
|
+
const unmet = unmetRequirements(runbook.requires, installed);
|
|
8473
|
+
if (unmet.length) findings.push({ ...at, kind: "requires", detail: describeUnmet(runbook.slug, unmet) });
|
|
8474
|
+
return findings;
|
|
8475
|
+
}
|
|
8476
|
+
async function runbookLint(ctx, all) {
|
|
8477
|
+
const params = new URLSearchParams();
|
|
8478
|
+
if (!all) params.set("app", ctx.appId);
|
|
8479
|
+
const page = await call(ctx, "GET", `/runbook${params.size ? `?${params}` : ""}`);
|
|
8480
|
+
const installed = { "@odla-ai/cli": cliVersion() };
|
|
8481
|
+
const findings = page.records.flatMap((runbook) => lintRunbook(runbook, installed));
|
|
8482
|
+
if (ctx.json) return ctx.out.log(JSON.stringify({ checked: page.records.length, findings }, null, 2));
|
|
8483
|
+
if (!page.records.length) return ctx.out.log("(no runbooks in scope)");
|
|
8484
|
+
if (!findings.length) {
|
|
8485
|
+
return ctx.out.log(
|
|
8486
|
+
`${page.records.length} runbook${page.records.length === 1 ? "" : "s"} checked; every command they name is real for @odla-ai/cli ${cliVersion()}.`
|
|
8487
|
+
);
|
|
8488
|
+
}
|
|
8489
|
+
ctx.out.log(`${findings.length} finding${findings.length === 1 ? "" : "s"} across ${page.records.length} runbooks:`);
|
|
8490
|
+
for (const finding of findings) {
|
|
8491
|
+
const scope = finding.appId === PLATFORM_SCOPE ? "" : ` --app ${finding.appId}`;
|
|
8492
|
+
ctx.out.log("");
|
|
8493
|
+
ctx.out.log(`${finding.slug} [${finding.kind}]`);
|
|
8494
|
+
ctx.out.log(` ${finding.detail}`);
|
|
8495
|
+
ctx.out.log(` odla-ai runbook get ${finding.slug}${scope}`);
|
|
8496
|
+
}
|
|
8497
|
+
}
|
|
8498
|
+
|
|
8278
8499
|
// src/runbook-search-command.ts
|
|
8279
8500
|
async function runbookSearch(ctx, query, all, limit) {
|
|
8280
8501
|
const params = new URLSearchParams({ q: query });
|
|
@@ -8467,14 +8688,15 @@ var ALLOWED2 = [
|
|
|
8467
8688
|
"visibility",
|
|
8468
8689
|
"dry-run",
|
|
8469
8690
|
"limit",
|
|
8470
|
-
"base"
|
|
8691
|
+
"base",
|
|
8692
|
+
"requires"
|
|
8471
8693
|
];
|
|
8472
8694
|
function requireSlug(slug, action) {
|
|
8473
8695
|
if (!slug) throw new Error(`"runbook ${action}" needs a slug, e.g. "odla-ai runbook ${action} release"`);
|
|
8474
8696
|
return slug;
|
|
8475
8697
|
}
|
|
8476
8698
|
var WRITES = /* @__PURE__ */ new Set(["new", "edit", "publish", "archive", "visibility", "revert", "rm", "import"]);
|
|
8477
|
-
var CONFIG_FREE = /* @__PURE__ */ new Set(["list", "search", "ask", "get", "cat", "history", "impact"]);
|
|
8699
|
+
var CONFIG_FREE = /* @__PURE__ */ new Set(["list", "search", "ask", "get", "cat", "history", "impact", "lint"]);
|
|
8478
8700
|
async function loadOrDefaultConfig(configPath, action, appId) {
|
|
8479
8701
|
const projectScoped = appId !== void 0;
|
|
8480
8702
|
if (!projectScoped && CONFIG_FREE.has(action) && !(0, import_node_fs17.existsSync)((0, import_node_path14.resolve)(configPath))) {
|
|
@@ -8556,6 +8778,8 @@ async function runbookCommand(parsed, deps = {}) {
|
|
|
8556
8778
|
if (!question) throw new Error('"runbook ask" needs a question, e.g. odla-ai runbook ask "how do I roll back a publish?"');
|
|
8557
8779
|
return runbookAsk(ctx, question, parsed.options.all === true);
|
|
8558
8780
|
}
|
|
8781
|
+
case "lint":
|
|
8782
|
+
return runbookLint(ctx, parsed.options.all === true);
|
|
8559
8783
|
case "impact":
|
|
8560
8784
|
return runbookImpact(
|
|
8561
8785
|
ctx,
|
|
@@ -8586,7 +8810,8 @@ async function runbookCommand(parsed, deps = {}) {
|
|
|
8586
8810
|
name,
|
|
8587
8811
|
stringOpt(parsed.options.title) ?? name,
|
|
8588
8812
|
readBody(stringOpt(parsed.options.file), stringOpt(parsed.options.body)),
|
|
8589
|
-
stringOpt(parsed.options.summary)
|
|
8813
|
+
stringOpt(parsed.options.summary),
|
|
8814
|
+
stringOpt(parsed.options.requires)
|
|
8590
8815
|
);
|
|
8591
8816
|
}
|
|
8592
8817
|
case "edit": {
|
|
@@ -8595,7 +8820,13 @@ async function runbookCommand(parsed, deps = {}) {
|
|
|
8595
8820
|
const inline = stringOpt(parsed.options.body);
|
|
8596
8821
|
const body = file === void 0 && inline === void 0 ? await editRunbook(ctx, name) : readBody(file, inline);
|
|
8597
8822
|
if (body === null) return ctx.out.log(`${name} unchanged; nothing written`);
|
|
8598
|
-
return runbookEdit(
|
|
8823
|
+
return runbookEdit(
|
|
8824
|
+
ctx,
|
|
8825
|
+
name,
|
|
8826
|
+
body,
|
|
8827
|
+
stringOpt(parsed.options.note),
|
|
8828
|
+
parsed.options.requires === void 0 ? void 0 : stringOpt(parsed.options.requires) ?? ""
|
|
8829
|
+
);
|
|
8599
8830
|
}
|
|
8600
8831
|
case "import": {
|
|
8601
8832
|
const dir = parsed.positionals[2];
|
|
@@ -8624,9 +8855,7 @@ async function runbookCommand(parsed, deps = {}) {
|
|
|
8624
8855
|
case "rm":
|
|
8625
8856
|
return runbookRemove(ctx, requireSlug(slug, "rm"));
|
|
8626
8857
|
default:
|
|
8627
|
-
throw new Error(
|
|
8628
|
-
`unknown runbook action "${action}". Try ask, search, impact, list, get, new, edit, comment, import, visibility, publish, archive, history, revert, rm.`
|
|
8629
|
-
);
|
|
8858
|
+
throw new Error(`unknown runbook action "${action}". Try ${acceptedAfter(["runbook"]).join(", ")}.`);
|
|
8630
8859
|
}
|
|
8631
8860
|
}
|
|
8632
8861
|
|