@odla-ai/cli 0.20.2 → 0.22.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/dist/bin.cjs +181 -150
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-XBPDHNWO.js → chunk-DINDRUDA.js} +55 -25
- package/dist/chunk-DINDRUDA.js.map +1 -0
- package/dist/index.cjs +192 -159
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -3
- package/dist/index.d.ts +11 -3
- package/dist/index.js +3 -1
- package/package.json +1 -1
- package/dist/chunk-XBPDHNWO.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -61,6 +61,7 @@ __export(index_exports, {
|
|
|
61
61
|
inferGitHubRepository: () => inferGitHubRepository,
|
|
62
62
|
initProject: () => initProject,
|
|
63
63
|
installSkill: () => installSkill,
|
|
64
|
+
invocationPath: () => invocationPath,
|
|
64
65
|
isTerminalHostedSecurityStatus: () => isTerminalHostedSecurityStatus,
|
|
65
66
|
listGitHubSecuritySources: () => listGitHubSecuritySources,
|
|
66
67
|
listHostedSecurityJobs: () => listHostedSecurityJobs,
|
|
@@ -116,7 +117,7 @@ function approvalLines(prompt) {
|
|
|
116
117
|
return lines;
|
|
117
118
|
}
|
|
118
119
|
function printApproval(out, prompt) {
|
|
119
|
-
for (const line of approvalLines(prompt)) out.
|
|
120
|
+
for (const line of approvalLines(prompt)) out.error(line);
|
|
120
121
|
}
|
|
121
122
|
function reminderLines(prompt) {
|
|
122
123
|
return [
|
|
@@ -336,7 +337,7 @@ async function getDeveloperToken(cfg, options, doFetch, out) {
|
|
|
336
337
|
}
|
|
337
338
|
const cached = readJsonFile(cfg.local.tokenFile);
|
|
338
339
|
if (cached?.token && cached.platform === audience && (cached.expiresAt ?? 0) > Date.now() + 6e4) {
|
|
339
|
-
out.
|
|
340
|
+
out.error(`auth: using cached developer token (${displayPath(cfg.local.tokenFile, cfg.rootDir)})`);
|
|
340
341
|
return cached.token;
|
|
341
342
|
}
|
|
342
343
|
const ctx = {
|
|
@@ -353,16 +354,16 @@ async function getDeveloperToken(cfg, options, doFetch, out) {
|
|
|
353
354
|
const { token, expiresAt } = await resumePendingHandshake(ctx, waitMs) ?? await freshHandshake(ctx, waitMs);
|
|
354
355
|
clearPendingHandshake(ctx.pendingFile);
|
|
355
356
|
writePrivateJson(cfg.local.tokenFile, { platform: audience, email: ctx.email, token, expiresAt });
|
|
356
|
-
out.
|
|
357
|
+
out.error(`auth: developer token cached (${displayPath(cfg.local.tokenFile, cfg.rootDir)})`);
|
|
357
358
|
return token;
|
|
358
359
|
}
|
|
359
360
|
async function resumePendingHandshake(ctx, waitMs) {
|
|
360
361
|
const pending = readPendingHandshake(ctx.pendingFile, ctx.audience, ctx.email);
|
|
361
362
|
if (!pending) return null;
|
|
362
|
-
ctx.out.
|
|
363
|
-
ctx.out.
|
|
363
|
+
ctx.out.error("");
|
|
364
|
+
ctx.out.error(`auth: resuming pending handshake \u2014 ${approvalHint(pending)}`);
|
|
364
365
|
await launchApproval(ctx, pending.approvalUrl);
|
|
365
|
-
ctx.out.
|
|
366
|
+
ctx.out.error("");
|
|
366
367
|
const stopReminder = approvalReminder(ctx.out, pending);
|
|
367
368
|
try {
|
|
368
369
|
return await (0, import_db.collectToken)({
|
|
@@ -378,7 +379,7 @@ async function resumePendingHandshake(ctx, waitMs) {
|
|
|
378
379
|
if (code === "handshake_pending") throw stillPending(pending, ctx.email);
|
|
379
380
|
if (code === "handshake_expired" || code === "handshake_timeout") {
|
|
380
381
|
clearPendingHandshake(ctx.pendingFile);
|
|
381
|
-
ctx.out.
|
|
382
|
+
ctx.out.error("auth: pending handshake lapsed unapproved; starting a fresh one");
|
|
382
383
|
return null;
|
|
383
384
|
}
|
|
384
385
|
if (code === "handshake_denied") clearPendingHandshake(ctx.pendingFile);
|
|
@@ -436,12 +437,12 @@ async function launchApproval(ctx, approvalUrl) {
|
|
|
436
437
|
if (ctx.browser.open) {
|
|
437
438
|
try {
|
|
438
439
|
await (ctx.options.openApprovalUrl ?? openUrl)(approvalUrl);
|
|
439
|
-
ctx.out.
|
|
440
|
+
ctx.out.error(`auth: asked the OS to open a browser${ctx.browser.mode === "auto" ? " (auto)" : ""} \u2014 if no tab appeared, use the URL above`);
|
|
440
441
|
} catch (err) {
|
|
441
|
-
ctx.out.
|
|
442
|
+
ctx.out.error(`auth: could not open browser (${err instanceof Error ? err.message : String(err)})`);
|
|
442
443
|
}
|
|
443
444
|
} else if (ctx.browser.reason) {
|
|
444
|
-
ctx.out.
|
|
445
|
+
ctx.out.error(`auth: browser launch skipped (${ctx.browser.reason}) \u2014 show the human the URL above`);
|
|
445
446
|
}
|
|
446
447
|
}
|
|
447
448
|
function stillPending(pending, email) {
|
|
@@ -566,7 +567,7 @@ async function scopedToken(platform, scope, options, doFetch, out) {
|
|
|
566
567
|
const cache = options.cache === false ? null : readJsonFile(tokenFile);
|
|
567
568
|
const cached = cache?.platform === audience ? cache.tokens?.[scope] : void 0;
|
|
568
569
|
if (cached?.token && (cached.expiresAt ?? 0) > Date.now() + 6e4) {
|
|
569
|
-
out.
|
|
570
|
+
out.error(`auth: using cached ${scope} grant (${tokenFile})`);
|
|
570
571
|
return cached.token;
|
|
571
572
|
}
|
|
572
573
|
const email = handshakeEmail(options.email, cache?.platform === audience ? cache.email : void 0);
|
|
@@ -597,9 +598,9 @@ async function scopedToken(platform, scope, options, doFetch, out) {
|
|
|
597
598
|
tokens[scope] = { token, expiresAt };
|
|
598
599
|
if ((0, import_node_fs3.existsSync)((0, import_node_path3.join)(rootDir, ".git"))) ensureGitignore(rootDir, [tokenFile]);
|
|
599
600
|
writePrivateJson(tokenFile, { platform: audience, email, tokens });
|
|
600
|
-
out.
|
|
601
|
+
out.error(`auth: cached ${scope} grant (${tokenFile}; mode 0600)`);
|
|
601
602
|
} else {
|
|
602
|
-
out.
|
|
603
|
+
out.error(`auth: ${scope} grant is in memory only; its credential record remains in odla-ai/db`);
|
|
603
604
|
}
|
|
604
605
|
return token;
|
|
605
606
|
}
|
|
@@ -7858,13 +7859,163 @@ ${env}: credentials are already saved; retry "odla-ai secrets push --env ${env}$
|
|
|
7858
7859
|
}
|
|
7859
7860
|
}
|
|
7860
7861
|
|
|
7862
|
+
// src/record.ts
|
|
7863
|
+
var import_node_fs13 = require("fs");
|
|
7864
|
+
var import_node_process8 = __toESM(require("process"), 1);
|
|
7865
|
+
|
|
7866
|
+
// src/surface.ts
|
|
7867
|
+
var PM_ACTIONS = {
|
|
7868
|
+
list: {},
|
|
7869
|
+
add: {},
|
|
7870
|
+
create: {},
|
|
7871
|
+
get: {},
|
|
7872
|
+
set: {},
|
|
7873
|
+
update: {},
|
|
7874
|
+
status: {},
|
|
7875
|
+
move: {},
|
|
7876
|
+
done: {},
|
|
7877
|
+
comment: {},
|
|
7878
|
+
comments: {},
|
|
7879
|
+
rm: {},
|
|
7880
|
+
delete: {}
|
|
7881
|
+
};
|
|
7882
|
+
var PM_ENTITIES = Object.fromEntries(
|
|
7883
|
+
["goal", "conformance", "task", "kanban", "decision", "bug"].map((entity) => [entity, PM_ACTIONS])
|
|
7884
|
+
);
|
|
7885
|
+
var COMMAND_SURFACE = {
|
|
7886
|
+
admin: {
|
|
7887
|
+
ai: {
|
|
7888
|
+
show: {},
|
|
7889
|
+
set: {},
|
|
7890
|
+
credentials: {},
|
|
7891
|
+
models: {},
|
|
7892
|
+
usage: {},
|
|
7893
|
+
audit: {},
|
|
7894
|
+
credential: { set: {} }
|
|
7895
|
+
}
|
|
7896
|
+
},
|
|
7897
|
+
app: {
|
|
7898
|
+
archive: {},
|
|
7899
|
+
restore: {},
|
|
7900
|
+
export: {},
|
|
7901
|
+
import: {},
|
|
7902
|
+
rename: {},
|
|
7903
|
+
"refresh-sandbox": {},
|
|
7904
|
+
"go-live": {},
|
|
7905
|
+
promote: {},
|
|
7906
|
+
owners: { list: {}, add: {}, remove: {} }
|
|
7907
|
+
},
|
|
7908
|
+
calendar: { status: {}, calendars: {}, connect: {}, disconnect: {} },
|
|
7909
|
+
capabilities: {},
|
|
7910
|
+
code: { connect: {} },
|
|
7911
|
+
doctor: {},
|
|
7912
|
+
help: {},
|
|
7913
|
+
init: {},
|
|
7914
|
+
pm: PM_ENTITIES,
|
|
7915
|
+
provision: {},
|
|
7916
|
+
runbook: {
|
|
7917
|
+
ask: {},
|
|
7918
|
+
search: {},
|
|
7919
|
+
impact: {},
|
|
7920
|
+
list: {},
|
|
7921
|
+
get: {},
|
|
7922
|
+
cat: {},
|
|
7923
|
+
new: {},
|
|
7924
|
+
edit: {},
|
|
7925
|
+
comment: {},
|
|
7926
|
+
import: {},
|
|
7927
|
+
visibility: {},
|
|
7928
|
+
publish: {},
|
|
7929
|
+
archive: {},
|
|
7930
|
+
history: {},
|
|
7931
|
+
revert: {},
|
|
7932
|
+
rm: {},
|
|
7933
|
+
lint: {}
|
|
7934
|
+
},
|
|
7935
|
+
secrets: { push: {}, set: {}, "set-clerk-key": {} },
|
|
7936
|
+
security: {
|
|
7937
|
+
plan: {},
|
|
7938
|
+
sources: {},
|
|
7939
|
+
run: {},
|
|
7940
|
+
status: {},
|
|
7941
|
+
report: {},
|
|
7942
|
+
github: { connect: {}, disconnect: {} }
|
|
7943
|
+
},
|
|
7944
|
+
setup: {},
|
|
7945
|
+
skill: { install: {} },
|
|
7946
|
+
smoke: {},
|
|
7947
|
+
version: {},
|
|
7948
|
+
whoami: {}
|
|
7949
|
+
};
|
|
7950
|
+
function acceptedAfter(path) {
|
|
7951
|
+
let node = COMMAND_SURFACE;
|
|
7952
|
+
for (const word of path) {
|
|
7953
|
+
node = node?.[word];
|
|
7954
|
+
if (!node) return [];
|
|
7955
|
+
}
|
|
7956
|
+
return Object.keys(node).sort();
|
|
7957
|
+
}
|
|
7958
|
+
function validateInvocation(words2) {
|
|
7959
|
+
let node = COMMAND_SURFACE;
|
|
7960
|
+
const walked = [];
|
|
7961
|
+
for (const word of words2) {
|
|
7962
|
+
if (Object.keys(node).length === 0) return null;
|
|
7963
|
+
const next = node[word];
|
|
7964
|
+
if (!next) return { validPrefix: walked.join(" "), word, accepted: Object.keys(node).sort() };
|
|
7965
|
+
walked.push(word);
|
|
7966
|
+
node = next;
|
|
7967
|
+
}
|
|
7968
|
+
return null;
|
|
7969
|
+
}
|
|
7970
|
+
function describeProblem(problem) {
|
|
7971
|
+
const where = problem.validPrefix ? `after "${problem.validPrefix}"` : "as a command";
|
|
7972
|
+
return `"${problem.word}" is not accepted ${where} \u2014 try: ${problem.accepted.join(", ")}`;
|
|
7973
|
+
}
|
|
7974
|
+
function invocationPath(words2) {
|
|
7975
|
+
let node = COMMAND_SURFACE;
|
|
7976
|
+
const path = [];
|
|
7977
|
+
for (const word of words2) {
|
|
7978
|
+
const next = node[word];
|
|
7979
|
+
if (!next) break;
|
|
7980
|
+
path.push(word);
|
|
7981
|
+
node = next;
|
|
7982
|
+
if (Object.keys(node).length === 0) break;
|
|
7983
|
+
}
|
|
7984
|
+
return path;
|
|
7985
|
+
}
|
|
7986
|
+
function surfacePaths(node = COMMAND_SURFACE, prefix = []) {
|
|
7987
|
+
const paths = [];
|
|
7988
|
+
for (const [word, child] of Object.entries(node)) {
|
|
7989
|
+
const path = [...prefix, word];
|
|
7990
|
+
paths.push(path);
|
|
7991
|
+
paths.push(...surfacePaths(child, path));
|
|
7992
|
+
}
|
|
7993
|
+
return paths;
|
|
7994
|
+
}
|
|
7995
|
+
|
|
7996
|
+
// src/record.ts
|
|
7997
|
+
function recordInvocation(parsed) {
|
|
7998
|
+
const file = import_node_process8.default.env.ODLA_CLI_RECORD;
|
|
7999
|
+
if (!file) return;
|
|
8000
|
+
try {
|
|
8001
|
+
const entry = {
|
|
8002
|
+
path: invocationPath(parsed.positionals),
|
|
8003
|
+
options: Object.keys(parsed.options).sort()
|
|
8004
|
+
};
|
|
8005
|
+
if (!entry.path.length) return;
|
|
8006
|
+
(0, import_node_fs13.appendFileSync)(file, `${JSON.stringify(entry)}
|
|
8007
|
+
`);
|
|
8008
|
+
} catch {
|
|
8009
|
+
}
|
|
8010
|
+
}
|
|
8011
|
+
|
|
7861
8012
|
// src/runbook-command.ts
|
|
7862
|
-
var
|
|
8013
|
+
var import_node_fs18 = require("fs");
|
|
7863
8014
|
var import_node_path14 = require("path");
|
|
7864
|
-
var
|
|
8015
|
+
var import_node_process10 = __toESM(require("process"), 1);
|
|
7865
8016
|
|
|
7866
8017
|
// src/runbook-actions.ts
|
|
7867
|
-
var
|
|
8018
|
+
var import_node_fs14 = require("fs");
|
|
7868
8019
|
|
|
7869
8020
|
// src/runbook-requires.ts
|
|
7870
8021
|
var SPEC = /^(@?[\w./-]+?)@(\d+\.\d+\.\d+(?:[\w.-]*)?)$/;
|
|
@@ -7949,7 +8100,7 @@ async function bySlug(ctx, slug) {
|
|
|
7949
8100
|
function readBody(file, inline) {
|
|
7950
8101
|
if (inline !== void 0) return inline;
|
|
7951
8102
|
if (file === void 0) throw new Error("supply the new text with --file <path>, --file - (stdin), or --body");
|
|
7952
|
-
return (0,
|
|
8103
|
+
return (0, import_node_fs14.readFileSync)(file === "-" ? 0 : file, "utf8");
|
|
7953
8104
|
}
|
|
7954
8105
|
var stamp = (ms) => ms ? new Date(ms).toISOString().slice(0, 16).replace("T", " ") : "";
|
|
7955
8106
|
async function runbookList(ctx, all, query) {
|
|
@@ -8036,7 +8187,7 @@ async function runbookRemove(ctx, slug) {
|
|
|
8036
8187
|
}
|
|
8037
8188
|
|
|
8038
8189
|
// src/runbook-import.ts
|
|
8039
|
-
var
|
|
8190
|
+
var import_node_fs15 = require("fs");
|
|
8040
8191
|
var import_node_path11 = require("path");
|
|
8041
8192
|
function parseRunbook(text, slug) {
|
|
8042
8193
|
let rest = text;
|
|
@@ -8062,12 +8213,12 @@ function parseRunbook(text, slug) {
|
|
|
8062
8213
|
};
|
|
8063
8214
|
}
|
|
8064
8215
|
function readRunbookDir(dir) {
|
|
8065
|
-
if (!(0,
|
|
8066
|
-
const files = (0,
|
|
8216
|
+
if (!(0, import_node_fs15.statSync)(dir, { throwIfNoEntry: false })?.isDirectory()) throw new Error(`not a directory: ${dir}`);
|
|
8217
|
+
const files = (0, import_node_fs15.readdirSync)(dir).filter((f) => f.endsWith(".md")).sort();
|
|
8067
8218
|
if (!files.length) throw new Error(`no .md files in ${dir}`);
|
|
8068
8219
|
return files.map((file) => {
|
|
8069
8220
|
const slug = (0, import_node_path11.basename)(file, ".md");
|
|
8070
|
-
const parsed = parseRunbook((0,
|
|
8221
|
+
const parsed = parseRunbook((0, import_node_fs15.readFileSync)((0, import_node_path11.join)(dir, file), "utf8"), slug);
|
|
8071
8222
|
return { file, slug, ...parsed, words: parsed.body.split(/\s+/).filter(Boolean).length };
|
|
8072
8223
|
});
|
|
8073
8224
|
}
|
|
@@ -8139,7 +8290,7 @@ async function upsert(ctx, r, visibility) {
|
|
|
8139
8290
|
|
|
8140
8291
|
// src/runbook-impact.ts
|
|
8141
8292
|
var import_node_child_process7 = require("child_process");
|
|
8142
|
-
var
|
|
8293
|
+
var import_node_fs16 = require("fs");
|
|
8143
8294
|
var import_node_path12 = require("path");
|
|
8144
8295
|
|
|
8145
8296
|
// src/runbook-impact-scan.ts
|
|
@@ -8310,9 +8461,9 @@ ${body.split("\n").map((line) => `+${line}`).join("\n")}
|
|
|
8310
8461
|
function manifestLabeller(root) {
|
|
8311
8462
|
return (workspace) => {
|
|
8312
8463
|
const manifest = (0, import_node_path12.join)(root, workspace, "package.json");
|
|
8313
|
-
if (!(0,
|
|
8464
|
+
if (!(0, import_node_fs16.existsSync)(manifest)) return void 0;
|
|
8314
8465
|
try {
|
|
8315
|
-
const name = JSON.parse((0,
|
|
8466
|
+
const name = JSON.parse((0, import_node_fs16.readFileSync)(manifest, "utf8")).name;
|
|
8316
8467
|
return typeof name === "string" ? name : void 0;
|
|
8317
8468
|
} catch {
|
|
8318
8469
|
return void 0;
|
|
@@ -8379,7 +8530,7 @@ function report2(ctx, impacts) {
|
|
|
8379
8530
|
async function runbookImpact(ctx, options, deps = {}) {
|
|
8380
8531
|
const cwd = deps.cwd ?? process.cwd();
|
|
8381
8532
|
const runGit = deps.runGit ?? gitRunner(cwd);
|
|
8382
|
-
const read2 = deps.readRepoFile ?? ((path) => (0,
|
|
8533
|
+
const read2 = deps.readRepoFile ?? ((path) => (0, import_node_fs16.readFileSync)((0, import_node_path12.join)(cwd, path), "utf8"));
|
|
8383
8534
|
const surfaces = changedSurfaces(collectDiff(runGit, options.base, read2), manifestLabeller(cwd));
|
|
8384
8535
|
if (!surfaces.length) {
|
|
8385
8536
|
return ctx.out.log(
|
|
@@ -8391,124 +8542,6 @@ async function runbookImpact(ctx, options, deps = {}) {
|
|
|
8391
8542
|
report2(ctx, impacts);
|
|
8392
8543
|
}
|
|
8393
8544
|
|
|
8394
|
-
// src/surface.ts
|
|
8395
|
-
var PM_ACTIONS = {
|
|
8396
|
-
list: {},
|
|
8397
|
-
add: {},
|
|
8398
|
-
create: {},
|
|
8399
|
-
get: {},
|
|
8400
|
-
set: {},
|
|
8401
|
-
update: {},
|
|
8402
|
-
status: {},
|
|
8403
|
-
move: {},
|
|
8404
|
-
done: {},
|
|
8405
|
-
comment: {},
|
|
8406
|
-
comments: {},
|
|
8407
|
-
rm: {},
|
|
8408
|
-
delete: {}
|
|
8409
|
-
};
|
|
8410
|
-
var PM_ENTITIES = Object.fromEntries(
|
|
8411
|
-
["goal", "conformance", "task", "kanban", "decision", "bug"].map((entity) => [entity, PM_ACTIONS])
|
|
8412
|
-
);
|
|
8413
|
-
var COMMAND_SURFACE = {
|
|
8414
|
-
admin: {
|
|
8415
|
-
ai: {
|
|
8416
|
-
show: {},
|
|
8417
|
-
set: {},
|
|
8418
|
-
credentials: {},
|
|
8419
|
-
models: {},
|
|
8420
|
-
usage: {},
|
|
8421
|
-
audit: {},
|
|
8422
|
-
credential: { set: {} }
|
|
8423
|
-
}
|
|
8424
|
-
},
|
|
8425
|
-
app: {
|
|
8426
|
-
archive: {},
|
|
8427
|
-
restore: {},
|
|
8428
|
-
export: {},
|
|
8429
|
-
import: {},
|
|
8430
|
-
rename: {},
|
|
8431
|
-
"refresh-sandbox": {},
|
|
8432
|
-
"go-live": {},
|
|
8433
|
-
promote: {},
|
|
8434
|
-
owners: { list: {}, add: {}, remove: {} }
|
|
8435
|
-
},
|
|
8436
|
-
calendar: { status: {}, calendars: {}, connect: {}, disconnect: {} },
|
|
8437
|
-
capabilities: {},
|
|
8438
|
-
code: { connect: {} },
|
|
8439
|
-
doctor: {},
|
|
8440
|
-
help: {},
|
|
8441
|
-
init: {},
|
|
8442
|
-
pm: PM_ENTITIES,
|
|
8443
|
-
provision: {},
|
|
8444
|
-
runbook: {
|
|
8445
|
-
ask: {},
|
|
8446
|
-
search: {},
|
|
8447
|
-
impact: {},
|
|
8448
|
-
list: {},
|
|
8449
|
-
get: {},
|
|
8450
|
-
cat: {},
|
|
8451
|
-
new: {},
|
|
8452
|
-
edit: {},
|
|
8453
|
-
comment: {},
|
|
8454
|
-
import: {},
|
|
8455
|
-
visibility: {},
|
|
8456
|
-
publish: {},
|
|
8457
|
-
archive: {},
|
|
8458
|
-
history: {},
|
|
8459
|
-
revert: {},
|
|
8460
|
-
rm: {},
|
|
8461
|
-
lint: {}
|
|
8462
|
-
},
|
|
8463
|
-
secrets: { push: {}, set: {}, "set-clerk-key": {} },
|
|
8464
|
-
security: {
|
|
8465
|
-
plan: {},
|
|
8466
|
-
sources: {},
|
|
8467
|
-
run: {},
|
|
8468
|
-
status: {},
|
|
8469
|
-
report: {},
|
|
8470
|
-
github: { connect: {}, disconnect: {} }
|
|
8471
|
-
},
|
|
8472
|
-
setup: {},
|
|
8473
|
-
skill: { install: {} },
|
|
8474
|
-
smoke: {},
|
|
8475
|
-
version: {},
|
|
8476
|
-
whoami: {}
|
|
8477
|
-
};
|
|
8478
|
-
function acceptedAfter(path) {
|
|
8479
|
-
let node = COMMAND_SURFACE;
|
|
8480
|
-
for (const word of path) {
|
|
8481
|
-
node = node?.[word];
|
|
8482
|
-
if (!node) return [];
|
|
8483
|
-
}
|
|
8484
|
-
return Object.keys(node).sort();
|
|
8485
|
-
}
|
|
8486
|
-
function validateInvocation(words2) {
|
|
8487
|
-
let node = COMMAND_SURFACE;
|
|
8488
|
-
const walked = [];
|
|
8489
|
-
for (const word of words2) {
|
|
8490
|
-
if (Object.keys(node).length === 0) return null;
|
|
8491
|
-
const next = node[word];
|
|
8492
|
-
if (!next) return { validPrefix: walked.join(" "), word, accepted: Object.keys(node).sort() };
|
|
8493
|
-
walked.push(word);
|
|
8494
|
-
node = next;
|
|
8495
|
-
}
|
|
8496
|
-
return null;
|
|
8497
|
-
}
|
|
8498
|
-
function describeProblem(problem) {
|
|
8499
|
-
const where = problem.validPrefix ? `after "${problem.validPrefix}"` : "as a command";
|
|
8500
|
-
return `"${problem.word}" is not accepted ${where} \u2014 try: ${problem.accepted.join(", ")}`;
|
|
8501
|
-
}
|
|
8502
|
-
function surfacePaths(node = COMMAND_SURFACE, prefix = []) {
|
|
8503
|
-
const paths = [];
|
|
8504
|
-
for (const [word, child] of Object.entries(node)) {
|
|
8505
|
-
const path = [...prefix, word];
|
|
8506
|
-
paths.push(path);
|
|
8507
|
-
paths.push(...surfacePaths(child, path));
|
|
8508
|
-
}
|
|
8509
|
-
return paths;
|
|
8510
|
-
}
|
|
8511
|
-
|
|
8512
8545
|
// src/runbook-lint.ts
|
|
8513
8546
|
function invocationsIn(body) {
|
|
8514
8547
|
const re = /(`|npx[^\S\n]+)?(?:@odla-ai\/cli(?:@[\w.-]+)?|odla-ai)((?:[^\S\n]+[a-z][\w-]*)+)/g;
|
|
@@ -8630,12 +8663,12 @@ async function runbookComment(ctx, slug, body) {
|
|
|
8630
8663
|
|
|
8631
8664
|
// src/runbook-editor.ts
|
|
8632
8665
|
var import_node_child_process8 = require("child_process");
|
|
8633
|
-
var
|
|
8666
|
+
var import_node_fs17 = require("fs");
|
|
8634
8667
|
var import_node_os4 = require("os");
|
|
8635
8668
|
var import_node_path13 = require("path");
|
|
8636
|
-
var
|
|
8669
|
+
var import_node_process9 = __toESM(require("process"), 1);
|
|
8637
8670
|
var EDITOR_ENV = ["ODLA_EDITOR", "VISUAL", "EDITOR"];
|
|
8638
|
-
function resolveEditor(env =
|
|
8671
|
+
function resolveEditor(env = import_node_process9.default.env) {
|
|
8639
8672
|
for (const name of EDITOR_ENV) {
|
|
8640
8673
|
const value = env[name];
|
|
8641
8674
|
if (value && value.trim()) return value.trim();
|
|
@@ -8649,8 +8682,8 @@ function defaultRun(command, path) {
|
|
|
8649
8682
|
return result.status ?? 0;
|
|
8650
8683
|
}
|
|
8651
8684
|
function editText(initial, slug, deps = {}) {
|
|
8652
|
-
const env = deps.env ??
|
|
8653
|
-
const interactive = deps.interactive ?? (() => Boolean(
|
|
8685
|
+
const env = deps.env ?? import_node_process9.default.env;
|
|
8686
|
+
const interactive = deps.interactive ?? (() => Boolean(import_node_process9.default.stdin.isTTY));
|
|
8654
8687
|
const editor = resolveEditor(env);
|
|
8655
8688
|
if (!editor)
|
|
8656
8689
|
throw new Error(
|
|
@@ -8658,16 +8691,16 @@ function editText(initial, slug, deps = {}) {
|
|
|
8658
8691
|
);
|
|
8659
8692
|
if (!interactive())
|
|
8660
8693
|
throw new Error(`cannot open an editor without a terminal \u2014 pass --file <path> or --body "\u2026" instead`);
|
|
8661
|
-
const dir = (0,
|
|
8694
|
+
const dir = (0, import_node_fs17.mkdtempSync)((0, import_node_path13.join)((0, import_node_os4.tmpdir)(), "odla-runbook-"));
|
|
8662
8695
|
const file = (0, import_node_path13.join)(dir, `${slug}.md`);
|
|
8663
8696
|
try {
|
|
8664
|
-
(0,
|
|
8697
|
+
(0, import_node_fs17.writeFileSync)(file, initial, { mode: 384 });
|
|
8665
8698
|
const code = defaultRunOrInjected(deps)(editor, file);
|
|
8666
8699
|
if (code !== 0) throw new Error(`editor "${editor}" exited with ${code}; nothing was written`);
|
|
8667
|
-
const edited = (0,
|
|
8700
|
+
const edited = (0, import_node_fs17.readFileSync)(file, "utf8");
|
|
8668
8701
|
return edited === initial ? null : edited;
|
|
8669
8702
|
} finally {
|
|
8670
|
-
(0,
|
|
8703
|
+
(0, import_node_fs17.rmSync)(dir, { recursive: true, force: true });
|
|
8671
8704
|
}
|
|
8672
8705
|
}
|
|
8673
8706
|
var defaultRunOrInjected = (deps) => deps.run ?? defaultRun;
|
|
@@ -8767,11 +8800,11 @@ var WRITES = /* @__PURE__ */ new Set(["new", "edit", "publish", "archive", "visi
|
|
|
8767
8800
|
var CONFIG_FREE = /* @__PURE__ */ new Set(["list", "search", "ask", "get", "cat", "history", "impact", "lint"]);
|
|
8768
8801
|
async function loadOrDefaultConfig(configPath, action, appId) {
|
|
8769
8802
|
const projectScoped = appId !== void 0;
|
|
8770
|
-
if (!projectScoped && CONFIG_FREE.has(action) && !(0,
|
|
8771
|
-
const rootDir =
|
|
8803
|
+
if (!projectScoped && CONFIG_FREE.has(action) && !(0, import_node_fs18.existsSync)((0, import_node_path14.resolve)(configPath))) {
|
|
8804
|
+
const rootDir = import_node_process10.default.cwd();
|
|
8772
8805
|
return {
|
|
8773
8806
|
configPath,
|
|
8774
|
-
platformUrl:
|
|
8807
|
+
platformUrl: import_node_process10.default.env.ODLA_PLATFORM_URL ?? "https://odla.ai",
|
|
8775
8808
|
rootDir,
|
|
8776
8809
|
app: { id: "", name: "" },
|
|
8777
8810
|
local: { tokenFile: (0, import_node_path14.join)(rootDir, ".odla/dev-token.json") }
|
|
@@ -8779,13 +8812,11 @@ async function loadOrDefaultConfig(configPath, action, appId) {
|
|
|
8779
8812
|
}
|
|
8780
8813
|
return loadProjectConfig(configPath);
|
|
8781
8814
|
}
|
|
8782
|
-
var PURE_STDOUT = /* @__PURE__ */ new Set(["get", "cat"]);
|
|
8783
8815
|
async function buildContext2(parsed, deps, action) {
|
|
8784
8816
|
const configPath = stringOpt(parsed.options.config) ?? "odla.config.mjs";
|
|
8785
8817
|
const cfg = await loadOrDefaultConfig(configPath, action, stringOpt(parsed.options.app));
|
|
8786
8818
|
const doFetch = deps.fetch ?? fetch;
|
|
8787
8819
|
const out = deps.stdout ?? console;
|
|
8788
|
-
const authOut = PURE_STDOUT.has(action) ? { log: (line) => out.error(line), error: (line) => out.error(line) } : out;
|
|
8789
8820
|
const appId = stringOpt(parsed.options.app) ?? PLATFORM_SCOPE;
|
|
8790
8821
|
const dryRun = parsed.options["dry-run"] === true;
|
|
8791
8822
|
if (action === "import" && dryRun) {
|
|
@@ -8805,7 +8836,7 @@ async function buildContext2(parsed, deps, action) {
|
|
|
8805
8836
|
email: stringOpt(parsed.options.email),
|
|
8806
8837
|
label: `odla CLI (runbook ${action})`,
|
|
8807
8838
|
fetch: doFetch,
|
|
8808
|
-
stdout:
|
|
8839
|
+
stdout: out,
|
|
8809
8840
|
openApprovalUrl: deps.openUrl,
|
|
8810
8841
|
// Anchor the grant cache to this project, not the shell's cwd.
|
|
8811
8842
|
rootDir: cfg.rootDir
|
|
@@ -8822,7 +8853,7 @@ async function buildContext2(parsed, deps, action) {
|
|
|
8822
8853
|
open: void 0
|
|
8823
8854
|
},
|
|
8824
8855
|
doFetch,
|
|
8825
|
-
|
|
8856
|
+
out
|
|
8826
8857
|
);
|
|
8827
8858
|
return {
|
|
8828
8859
|
platformUrl: cfg.platformUrl,
|
|
@@ -9634,6 +9665,7 @@ function exitCodeFor(err) {
|
|
|
9634
9665
|
}
|
|
9635
9666
|
async function runCli(argv = process.argv.slice(2), dependencies = {}) {
|
|
9636
9667
|
const parsed = parseArgv(argv);
|
|
9668
|
+
recordInvocation(parsed);
|
|
9637
9669
|
const command = parsed.positionals[0] ?? "help";
|
|
9638
9670
|
if (command === "version" || command === "-v" || parsed.options.version === true) {
|
|
9639
9671
|
assertArgs(parsed, ["version"], 1);
|
|
@@ -9776,6 +9808,7 @@ async function calendarCommand(parsed, dependencies) {
|
|
|
9776
9808
|
inferGitHubRepository,
|
|
9777
9809
|
initProject,
|
|
9778
9810
|
installSkill,
|
|
9811
|
+
invocationPath,
|
|
9779
9812
|
isTerminalHostedSecurityStatus,
|
|
9780
9813
|
listGitHubSecuritySources,
|
|
9781
9814
|
listHostedSecurityJobs,
|