@odla-ai/cli 0.21.0 → 0.22.1
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 +33 -11
- package/dist/bin.cjs +166 -133
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-CZSQ443A.js → chunk-DINDRUDA.js} +40 -8
- package/dist/chunk-DINDRUDA.js.map +1 -0
- package/dist/index.cjs +177 -142
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +3 -1
- package/package.json +1 -1
- package/skills/odla/SKILL.md +8 -2
- package/skills/odla/references/build.md +6 -5
- package/skills/odla/references/pm.md +18 -4
- package/skills/odla/references/sdks.md +5 -5
- package/skills/odla-migrate/SKILL.md +10 -4
- package/skills/odla-migrate/references/phase-0-preflight.md +7 -4
- package/skills/odla-migrate/references/phase-1-static.md +3 -1
- package/skills/odla-migrate/references/phase-2-chapter.md +12 -0
- package/skills/odla-migrate/references/phase-2-db.md +11 -3
- package/skills/odla-migrate/references/phase-2b-calendar.md +6 -5
- package/skills/odla-migrate/references/phase-3-auth.md +15 -1
- package/skills/odla-migrate/references/phase-4-ai.md +2 -1
- package/skills/odla-migrate/references/phase-5-cutover.md +11 -9
- package/skills/odla-migrate/references/project-state.md +80 -0
- package/dist/chunk-CZSQ443A.js.map +0 -1
package/README.md
CHANGED
|
@@ -37,6 +37,26 @@ npx @odla-ai/cli runbook list # what exists
|
|
|
37
37
|
Reading works for any signed-in developer and needs no `odla.config.mjs` — the
|
|
38
38
|
advice is most useful before there is a project.
|
|
39
39
|
|
|
40
|
+
Runbooks tell an agent how to operate; PM tells every agent what this project is
|
|
41
|
+
trying to prove, what is active, what was decided, and what is broken. Once the
|
|
42
|
+
app is registered, start each session by reading its doing tasks, open bugs and
|
|
43
|
+
goals, and recent decisions:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npx odla-ai pm task list --app <appId> --column doing
|
|
47
|
+
npx odla-ai pm bug list --app <appId> --status open
|
|
48
|
+
npx odla-ai pm goal list --app <appId> --status open
|
|
49
|
+
npx odla-ai pm decision list --app <appId> --limit 20
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Read the active task, linked goal, and comments before changing code. Without
|
|
53
|
+
`--app`, list commands span every project the account co-owns, which is the
|
|
54
|
+
right first view when coordinating several efforts; scope every write to the
|
|
55
|
+
exact `appId` from that project's config. PM requires a registered app. Before
|
|
56
|
+
the first provision, use a focused commit/checkpoint, then initialize PM and
|
|
57
|
+
backfill that evidence immediately after registration. Never use a parallel
|
|
58
|
+
status diary or put a secret in PM.
|
|
59
|
+
|
|
40
60
|
**Runbooks are procedure; JSDoc is API.** What a function takes, returns, and
|
|
41
61
|
guarantees is documented on the export itself, ships in the installed
|
|
42
62
|
`.d.ts`, and renders per package at <https://odla.ai/docs>. A runbook will tell
|
|
@@ -93,11 +113,14 @@ npx @odla-ai/cli init --app-id my-app --name "My App"
|
|
|
93
113
|
```
|
|
94
114
|
|
|
95
115
|
For a project you keep working in, install it as a dev dependency so the
|
|
96
|
-
shorter `npx odla-ai` form works
|
|
116
|
+
shorter `npx odla-ai` form works. ODLA is under active development: use a normal
|
|
117
|
+
dependency declaration, commit the lockfile, and let `npm ci` reproduce the
|
|
118
|
+
resolved graph:
|
|
97
119
|
|
|
98
120
|
```bash
|
|
99
|
-
npm view @odla-ai/cli
|
|
100
|
-
npm i -D
|
|
121
|
+
npm view @odla-ai/cli version
|
|
122
|
+
npm i -D @odla-ai/cli
|
|
123
|
+
npm ls @odla-ai/cli
|
|
101
124
|
```
|
|
102
125
|
|
|
103
126
|
## Prerequisites
|
|
@@ -360,8 +383,8 @@ rolling-window/age-out, quota-backoff, retention/purge, or provider-derived
|
|
|
360
383
|
mutation-id control. Public health remains aggregate rather than per calendar.
|
|
361
384
|
|
|
362
385
|
The bundled build/migration skills add a passive pre-ship gate with
|
|
363
|
-
`@odla-ai/security`:
|
|
364
|
-
`npm i -D
|
|
386
|
+
`@odla-ai/security`: install it with a normal development range, commit the
|
|
387
|
+
lockfile, then run `npm i -D @odla-ai/security`, followed by
|
|
365
388
|
`npx odla-security scan . --profile odla --out .odla/security/pre-ship --fail-on high --fail-on-candidates critical`.
|
|
366
389
|
The passive scan remains a separate binary: it makes no model calls and does
|
|
367
390
|
not execute target code. After explicit redacted-source approval,
|
|
@@ -385,12 +408,11 @@ a conservative `Retry-After`. A provider-side `429` uses the sanitized
|
|
|
385
408
|
carries a bounded `Retry-After` only when the upstream supplied one. The CLI
|
|
386
409
|
does not echo arbitrary response text or silently change the admin-selected
|
|
387
410
|
model.
|
|
388
|
-
|
|
389
|
-
`npm
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
should use the published `odla-security` command above.
|
|
411
|
+
Confirm `npm view @odla-ai/security version` succeeds and record the installed
|
|
412
|
+
version from `npm ls @odla-ai/security` with the scan evidence. A registry
|
|
413
|
+
failure blocks the preflight; it is not a clean scan. odla's own engineering
|
|
414
|
+
environment uses an equivalent internal gate; customer projects should use the
|
|
415
|
+
published `odla-security` command above.
|
|
394
416
|
|
|
395
417
|
For a server-side, commit-pinned review, connect one repository through the
|
|
396
418
|
source-read-only odla GitHub App. The CLI infers `owner/name` from a safe GitHub HTTPS
|
package/dist/bin.cjs
CHANGED
|
@@ -7799,13 +7799,154 @@ ${env}: credentials are already saved; retry "odla-ai secrets push --env ${env}$
|
|
|
7799
7799
|
}
|
|
7800
7800
|
}
|
|
7801
7801
|
|
|
7802
|
+
// src/record.ts
|
|
7803
|
+
var import_node_fs13 = require("fs");
|
|
7804
|
+
var import_node_process8 = __toESM(require("process"), 1);
|
|
7805
|
+
|
|
7806
|
+
// src/surface.ts
|
|
7807
|
+
var PM_ACTIONS = {
|
|
7808
|
+
list: {},
|
|
7809
|
+
add: {},
|
|
7810
|
+
create: {},
|
|
7811
|
+
get: {},
|
|
7812
|
+
set: {},
|
|
7813
|
+
update: {},
|
|
7814
|
+
status: {},
|
|
7815
|
+
move: {},
|
|
7816
|
+
done: {},
|
|
7817
|
+
comment: {},
|
|
7818
|
+
comments: {},
|
|
7819
|
+
rm: {},
|
|
7820
|
+
delete: {}
|
|
7821
|
+
};
|
|
7822
|
+
var PM_ENTITIES = Object.fromEntries(
|
|
7823
|
+
["goal", "conformance", "task", "kanban", "decision", "bug"].map((entity) => [entity, PM_ACTIONS])
|
|
7824
|
+
);
|
|
7825
|
+
var COMMAND_SURFACE = {
|
|
7826
|
+
admin: {
|
|
7827
|
+
ai: {
|
|
7828
|
+
show: {},
|
|
7829
|
+
set: {},
|
|
7830
|
+
credentials: {},
|
|
7831
|
+
models: {},
|
|
7832
|
+
usage: {},
|
|
7833
|
+
audit: {},
|
|
7834
|
+
credential: { set: {} }
|
|
7835
|
+
}
|
|
7836
|
+
},
|
|
7837
|
+
app: {
|
|
7838
|
+
archive: {},
|
|
7839
|
+
restore: {},
|
|
7840
|
+
export: {},
|
|
7841
|
+
import: {},
|
|
7842
|
+
rename: {},
|
|
7843
|
+
"refresh-sandbox": {},
|
|
7844
|
+
"go-live": {},
|
|
7845
|
+
promote: {},
|
|
7846
|
+
owners: { list: {}, add: {}, remove: {} }
|
|
7847
|
+
},
|
|
7848
|
+
calendar: { status: {}, calendars: {}, connect: {}, disconnect: {} },
|
|
7849
|
+
capabilities: {},
|
|
7850
|
+
code: { connect: {} },
|
|
7851
|
+
doctor: {},
|
|
7852
|
+
help: {},
|
|
7853
|
+
init: {},
|
|
7854
|
+
pm: PM_ENTITIES,
|
|
7855
|
+
provision: {},
|
|
7856
|
+
runbook: {
|
|
7857
|
+
ask: {},
|
|
7858
|
+
search: {},
|
|
7859
|
+
impact: {},
|
|
7860
|
+
list: {},
|
|
7861
|
+
get: {},
|
|
7862
|
+
cat: {},
|
|
7863
|
+
new: {},
|
|
7864
|
+
edit: {},
|
|
7865
|
+
comment: {},
|
|
7866
|
+
import: {},
|
|
7867
|
+
visibility: {},
|
|
7868
|
+
publish: {},
|
|
7869
|
+
archive: {},
|
|
7870
|
+
history: {},
|
|
7871
|
+
revert: {},
|
|
7872
|
+
rm: {},
|
|
7873
|
+
lint: {}
|
|
7874
|
+
},
|
|
7875
|
+
secrets: { push: {}, set: {}, "set-clerk-key": {} },
|
|
7876
|
+
security: {
|
|
7877
|
+
plan: {},
|
|
7878
|
+
sources: {},
|
|
7879
|
+
run: {},
|
|
7880
|
+
status: {},
|
|
7881
|
+
report: {},
|
|
7882
|
+
github: { connect: {}, disconnect: {} }
|
|
7883
|
+
},
|
|
7884
|
+
setup: {},
|
|
7885
|
+
skill: { install: {} },
|
|
7886
|
+
smoke: {},
|
|
7887
|
+
version: {},
|
|
7888
|
+
whoami: {}
|
|
7889
|
+
};
|
|
7890
|
+
function acceptedAfter(path) {
|
|
7891
|
+
let node = COMMAND_SURFACE;
|
|
7892
|
+
for (const word of path) {
|
|
7893
|
+
node = node?.[word];
|
|
7894
|
+
if (!node) return [];
|
|
7895
|
+
}
|
|
7896
|
+
return Object.keys(node).sort();
|
|
7897
|
+
}
|
|
7898
|
+
function validateInvocation(words2) {
|
|
7899
|
+
let node = COMMAND_SURFACE;
|
|
7900
|
+
const walked = [];
|
|
7901
|
+
for (const word of words2) {
|
|
7902
|
+
if (Object.keys(node).length === 0) return null;
|
|
7903
|
+
const next = node[word];
|
|
7904
|
+
if (!next) return { validPrefix: walked.join(" "), word, accepted: Object.keys(node).sort() };
|
|
7905
|
+
walked.push(word);
|
|
7906
|
+
node = next;
|
|
7907
|
+
}
|
|
7908
|
+
return null;
|
|
7909
|
+
}
|
|
7910
|
+
function describeProblem(problem) {
|
|
7911
|
+
const where = problem.validPrefix ? `after "${problem.validPrefix}"` : "as a command";
|
|
7912
|
+
return `"${problem.word}" is not accepted ${where} \u2014 try: ${problem.accepted.join(", ")}`;
|
|
7913
|
+
}
|
|
7914
|
+
function invocationPath(words2) {
|
|
7915
|
+
let node = COMMAND_SURFACE;
|
|
7916
|
+
const path = [];
|
|
7917
|
+
for (const word of words2) {
|
|
7918
|
+
const next = node[word];
|
|
7919
|
+
if (!next) break;
|
|
7920
|
+
path.push(word);
|
|
7921
|
+
node = next;
|
|
7922
|
+
if (Object.keys(node).length === 0) break;
|
|
7923
|
+
}
|
|
7924
|
+
return path;
|
|
7925
|
+
}
|
|
7926
|
+
|
|
7927
|
+
// src/record.ts
|
|
7928
|
+
function recordInvocation(parsed) {
|
|
7929
|
+
const file = import_node_process8.default.env.ODLA_CLI_RECORD;
|
|
7930
|
+
if (!file) return;
|
|
7931
|
+
try {
|
|
7932
|
+
const entry = {
|
|
7933
|
+
path: invocationPath(parsed.positionals),
|
|
7934
|
+
options: Object.keys(parsed.options).sort()
|
|
7935
|
+
};
|
|
7936
|
+
if (!entry.path.length) return;
|
|
7937
|
+
(0, import_node_fs13.appendFileSync)(file, `${JSON.stringify(entry)}
|
|
7938
|
+
`);
|
|
7939
|
+
} catch {
|
|
7940
|
+
}
|
|
7941
|
+
}
|
|
7942
|
+
|
|
7802
7943
|
// src/runbook-command.ts
|
|
7803
|
-
var
|
|
7944
|
+
var import_node_fs18 = require("fs");
|
|
7804
7945
|
var import_node_path14 = require("path");
|
|
7805
|
-
var
|
|
7946
|
+
var import_node_process10 = __toESM(require("process"), 1);
|
|
7806
7947
|
|
|
7807
7948
|
// src/runbook-actions.ts
|
|
7808
|
-
var
|
|
7949
|
+
var import_node_fs14 = require("fs");
|
|
7809
7950
|
|
|
7810
7951
|
// src/runbook-requires.ts
|
|
7811
7952
|
var SPEC = /^(@?[\w./-]+?)@(\d+\.\d+\.\d+(?:[\w.-]*)?)$/;
|
|
@@ -7890,7 +8031,7 @@ async function bySlug(ctx, slug) {
|
|
|
7890
8031
|
function readBody(file, inline) {
|
|
7891
8032
|
if (inline !== void 0) return inline;
|
|
7892
8033
|
if (file === void 0) throw new Error("supply the new text with --file <path>, --file - (stdin), or --body");
|
|
7893
|
-
return (0,
|
|
8034
|
+
return (0, import_node_fs14.readFileSync)(file === "-" ? 0 : file, "utf8");
|
|
7894
8035
|
}
|
|
7895
8036
|
var stamp = (ms) => ms ? new Date(ms).toISOString().slice(0, 16).replace("T", " ") : "";
|
|
7896
8037
|
async function runbookList(ctx, all, query) {
|
|
@@ -7977,7 +8118,7 @@ async function runbookRemove(ctx, slug) {
|
|
|
7977
8118
|
}
|
|
7978
8119
|
|
|
7979
8120
|
// src/runbook-import.ts
|
|
7980
|
-
var
|
|
8121
|
+
var import_node_fs15 = require("fs");
|
|
7981
8122
|
var import_node_path11 = require("path");
|
|
7982
8123
|
function parseRunbook(text, slug) {
|
|
7983
8124
|
let rest = text;
|
|
@@ -8003,12 +8144,12 @@ function parseRunbook(text, slug) {
|
|
|
8003
8144
|
};
|
|
8004
8145
|
}
|
|
8005
8146
|
function readRunbookDir(dir) {
|
|
8006
|
-
if (!(0,
|
|
8007
|
-
const files = (0,
|
|
8147
|
+
if (!(0, import_node_fs15.statSync)(dir, { throwIfNoEntry: false })?.isDirectory()) throw new Error(`not a directory: ${dir}`);
|
|
8148
|
+
const files = (0, import_node_fs15.readdirSync)(dir).filter((f) => f.endsWith(".md")).sort();
|
|
8008
8149
|
if (!files.length) throw new Error(`no .md files in ${dir}`);
|
|
8009
8150
|
return files.map((file) => {
|
|
8010
8151
|
const slug = (0, import_node_path11.basename)(file, ".md");
|
|
8011
|
-
const parsed = parseRunbook((0,
|
|
8152
|
+
const parsed = parseRunbook((0, import_node_fs15.readFileSync)((0, import_node_path11.join)(dir, file), "utf8"), slug);
|
|
8012
8153
|
return { file, slug, ...parsed, words: parsed.body.split(/\s+/).filter(Boolean).length };
|
|
8013
8154
|
});
|
|
8014
8155
|
}
|
|
@@ -8080,7 +8221,7 @@ async function upsert(ctx, r, visibility) {
|
|
|
8080
8221
|
|
|
8081
8222
|
// src/runbook-impact.ts
|
|
8082
8223
|
var import_node_child_process7 = require("child_process");
|
|
8083
|
-
var
|
|
8224
|
+
var import_node_fs16 = require("fs");
|
|
8084
8225
|
var import_node_path12 = require("path");
|
|
8085
8226
|
|
|
8086
8227
|
// src/runbook-impact-scan.ts
|
|
@@ -8251,9 +8392,9 @@ ${body.split("\n").map((line) => `+${line}`).join("\n")}
|
|
|
8251
8392
|
function manifestLabeller(root) {
|
|
8252
8393
|
return (workspace) => {
|
|
8253
8394
|
const manifest = (0, import_node_path12.join)(root, workspace, "package.json");
|
|
8254
|
-
if (!(0,
|
|
8395
|
+
if (!(0, import_node_fs16.existsSync)(manifest)) return void 0;
|
|
8255
8396
|
try {
|
|
8256
|
-
const name = JSON.parse((0,
|
|
8397
|
+
const name = JSON.parse((0, import_node_fs16.readFileSync)(manifest, "utf8")).name;
|
|
8257
8398
|
return typeof name === "string" ? name : void 0;
|
|
8258
8399
|
} catch {
|
|
8259
8400
|
return void 0;
|
|
@@ -8320,7 +8461,7 @@ function report2(ctx, impacts) {
|
|
|
8320
8461
|
async function runbookImpact(ctx, options, deps = {}) {
|
|
8321
8462
|
const cwd = deps.cwd ?? process.cwd();
|
|
8322
8463
|
const runGit = deps.runGit ?? gitRunner(cwd);
|
|
8323
|
-
const read2 = deps.readRepoFile ?? ((path) => (0,
|
|
8464
|
+
const read2 = deps.readRepoFile ?? ((path) => (0, import_node_fs16.readFileSync)((0, import_node_path12.join)(cwd, path), "utf8"));
|
|
8324
8465
|
const surfaces = changedSurfaces(collectDiff(runGit, options.base, read2), manifestLabeller(cwd));
|
|
8325
8466
|
if (!surfaces.length) {
|
|
8326
8467
|
return ctx.out.log(
|
|
@@ -8332,115 +8473,6 @@ async function runbookImpact(ctx, options, deps = {}) {
|
|
|
8332
8473
|
report2(ctx, impacts);
|
|
8333
8474
|
}
|
|
8334
8475
|
|
|
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
8476
|
// src/runbook-lint.ts
|
|
8445
8477
|
function invocationsIn(body) {
|
|
8446
8478
|
const re = /(`|npx[^\S\n]+)?(?:@odla-ai\/cli(?:@[\w.-]+)?|odla-ai)((?:[^\S\n]+[a-z][\w-]*)+)/g;
|
|
@@ -8562,12 +8594,12 @@ async function runbookComment(ctx, slug, body) {
|
|
|
8562
8594
|
|
|
8563
8595
|
// src/runbook-editor.ts
|
|
8564
8596
|
var import_node_child_process8 = require("child_process");
|
|
8565
|
-
var
|
|
8597
|
+
var import_node_fs17 = require("fs");
|
|
8566
8598
|
var import_node_os4 = require("os");
|
|
8567
8599
|
var import_node_path13 = require("path");
|
|
8568
|
-
var
|
|
8600
|
+
var import_node_process9 = __toESM(require("process"), 1);
|
|
8569
8601
|
var EDITOR_ENV = ["ODLA_EDITOR", "VISUAL", "EDITOR"];
|
|
8570
|
-
function resolveEditor(env =
|
|
8602
|
+
function resolveEditor(env = import_node_process9.default.env) {
|
|
8571
8603
|
for (const name of EDITOR_ENV) {
|
|
8572
8604
|
const value = env[name];
|
|
8573
8605
|
if (value && value.trim()) return value.trim();
|
|
@@ -8581,8 +8613,8 @@ function defaultRun(command, path) {
|
|
|
8581
8613
|
return result.status ?? 0;
|
|
8582
8614
|
}
|
|
8583
8615
|
function editText(initial, slug, deps = {}) {
|
|
8584
|
-
const env = deps.env ??
|
|
8585
|
-
const interactive = deps.interactive ?? (() => Boolean(
|
|
8616
|
+
const env = deps.env ?? import_node_process9.default.env;
|
|
8617
|
+
const interactive = deps.interactive ?? (() => Boolean(import_node_process9.default.stdin.isTTY));
|
|
8586
8618
|
const editor = resolveEditor(env);
|
|
8587
8619
|
if (!editor)
|
|
8588
8620
|
throw new Error(
|
|
@@ -8590,16 +8622,16 @@ function editText(initial, slug, deps = {}) {
|
|
|
8590
8622
|
);
|
|
8591
8623
|
if (!interactive())
|
|
8592
8624
|
throw new Error(`cannot open an editor without a terminal \u2014 pass --file <path> or --body "\u2026" instead`);
|
|
8593
|
-
const dir = (0,
|
|
8625
|
+
const dir = (0, import_node_fs17.mkdtempSync)((0, import_node_path13.join)((0, import_node_os4.tmpdir)(), "odla-runbook-"));
|
|
8594
8626
|
const file = (0, import_node_path13.join)(dir, `${slug}.md`);
|
|
8595
8627
|
try {
|
|
8596
|
-
(0,
|
|
8628
|
+
(0, import_node_fs17.writeFileSync)(file, initial, { mode: 384 });
|
|
8597
8629
|
const code = defaultRunOrInjected(deps)(editor, file);
|
|
8598
8630
|
if (code !== 0) throw new Error(`editor "${editor}" exited with ${code}; nothing was written`);
|
|
8599
|
-
const edited = (0,
|
|
8631
|
+
const edited = (0, import_node_fs17.readFileSync)(file, "utf8");
|
|
8600
8632
|
return edited === initial ? null : edited;
|
|
8601
8633
|
} finally {
|
|
8602
|
-
(0,
|
|
8634
|
+
(0, import_node_fs17.rmSync)(dir, { recursive: true, force: true });
|
|
8603
8635
|
}
|
|
8604
8636
|
}
|
|
8605
8637
|
var defaultRunOrInjected = (deps) => deps.run ?? defaultRun;
|
|
@@ -8699,11 +8731,11 @@ var WRITES = /* @__PURE__ */ new Set(["new", "edit", "publish", "archive", "visi
|
|
|
8699
8731
|
var CONFIG_FREE = /* @__PURE__ */ new Set(["list", "search", "ask", "get", "cat", "history", "impact", "lint"]);
|
|
8700
8732
|
async function loadOrDefaultConfig(configPath, action, appId) {
|
|
8701
8733
|
const projectScoped = appId !== void 0;
|
|
8702
|
-
if (!projectScoped && CONFIG_FREE.has(action) && !(0,
|
|
8703
|
-
const rootDir =
|
|
8734
|
+
if (!projectScoped && CONFIG_FREE.has(action) && !(0, import_node_fs18.existsSync)((0, import_node_path14.resolve)(configPath))) {
|
|
8735
|
+
const rootDir = import_node_process10.default.cwd();
|
|
8704
8736
|
return {
|
|
8705
8737
|
configPath,
|
|
8706
|
-
platformUrl:
|
|
8738
|
+
platformUrl: import_node_process10.default.env.ODLA_PLATFORM_URL ?? "https://odla.ai",
|
|
8707
8739
|
rootDir,
|
|
8708
8740
|
app: { id: "", name: "" },
|
|
8709
8741
|
local: { tokenFile: (0, import_node_path14.join)(rootDir, ".odla/dev-token.json") }
|
|
@@ -9553,6 +9585,7 @@ function exitCodeFor(err) {
|
|
|
9553
9585
|
}
|
|
9554
9586
|
async function runCli(argv = process.argv.slice(2), dependencies = {}) {
|
|
9555
9587
|
const parsed = parseArgv(argv);
|
|
9588
|
+
recordInvocation(parsed);
|
|
9556
9589
|
const command = parsed.positionals[0] ?? "help";
|
|
9557
9590
|
if (command === "version" || command === "-v" || parsed.options.version === true) {
|
|
9558
9591
|
assertArgs(parsed, ["version"], 1);
|