@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/bin.cjs
CHANGED
|
@@ -57,7 +57,7 @@ function approvalLines(prompt) {
|
|
|
57
57
|
return lines;
|
|
58
58
|
}
|
|
59
59
|
function printApproval(out, prompt) {
|
|
60
|
-
for (const line of approvalLines(prompt)) out.
|
|
60
|
+
for (const line of approvalLines(prompt)) out.error(line);
|
|
61
61
|
}
|
|
62
62
|
function reminderLines(prompt) {
|
|
63
63
|
return [
|
|
@@ -277,7 +277,7 @@ async function getDeveloperToken(cfg, options, doFetch, out) {
|
|
|
277
277
|
}
|
|
278
278
|
const cached = readJsonFile(cfg.local.tokenFile);
|
|
279
279
|
if (cached?.token && cached.platform === audience && (cached.expiresAt ?? 0) > Date.now() + 6e4) {
|
|
280
|
-
out.
|
|
280
|
+
out.error(`auth: using cached developer token (${displayPath(cfg.local.tokenFile, cfg.rootDir)})`);
|
|
281
281
|
return cached.token;
|
|
282
282
|
}
|
|
283
283
|
const ctx = {
|
|
@@ -294,16 +294,16 @@ async function getDeveloperToken(cfg, options, doFetch, out) {
|
|
|
294
294
|
const { token, expiresAt } = await resumePendingHandshake(ctx, waitMs) ?? await freshHandshake(ctx, waitMs);
|
|
295
295
|
clearPendingHandshake(ctx.pendingFile);
|
|
296
296
|
writePrivateJson(cfg.local.tokenFile, { platform: audience, email: ctx.email, token, expiresAt });
|
|
297
|
-
out.
|
|
297
|
+
out.error(`auth: developer token cached (${displayPath(cfg.local.tokenFile, cfg.rootDir)})`);
|
|
298
298
|
return token;
|
|
299
299
|
}
|
|
300
300
|
async function resumePendingHandshake(ctx, waitMs) {
|
|
301
301
|
const pending = readPendingHandshake(ctx.pendingFile, ctx.audience, ctx.email);
|
|
302
302
|
if (!pending) return null;
|
|
303
|
-
ctx.out.
|
|
304
|
-
ctx.out.
|
|
303
|
+
ctx.out.error("");
|
|
304
|
+
ctx.out.error(`auth: resuming pending handshake \u2014 ${approvalHint(pending)}`);
|
|
305
305
|
await launchApproval(ctx, pending.approvalUrl);
|
|
306
|
-
ctx.out.
|
|
306
|
+
ctx.out.error("");
|
|
307
307
|
const stopReminder = approvalReminder(ctx.out, pending);
|
|
308
308
|
try {
|
|
309
309
|
return await (0, import_db.collectToken)({
|
|
@@ -319,7 +319,7 @@ async function resumePendingHandshake(ctx, waitMs) {
|
|
|
319
319
|
if (code === "handshake_pending") throw stillPending(pending, ctx.email);
|
|
320
320
|
if (code === "handshake_expired" || code === "handshake_timeout") {
|
|
321
321
|
clearPendingHandshake(ctx.pendingFile);
|
|
322
|
-
ctx.out.
|
|
322
|
+
ctx.out.error("auth: pending handshake lapsed unapproved; starting a fresh one");
|
|
323
323
|
return null;
|
|
324
324
|
}
|
|
325
325
|
if (code === "handshake_denied") clearPendingHandshake(ctx.pendingFile);
|
|
@@ -377,12 +377,12 @@ async function launchApproval(ctx, approvalUrl) {
|
|
|
377
377
|
if (ctx.browser.open) {
|
|
378
378
|
try {
|
|
379
379
|
await (ctx.options.openApprovalUrl ?? openUrl)(approvalUrl);
|
|
380
|
-
ctx.out.
|
|
380
|
+
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`);
|
|
381
381
|
} catch (err) {
|
|
382
|
-
ctx.out.
|
|
382
|
+
ctx.out.error(`auth: could not open browser (${err instanceof Error ? err.message : String(err)})`);
|
|
383
383
|
}
|
|
384
384
|
} else if (ctx.browser.reason) {
|
|
385
|
-
ctx.out.
|
|
385
|
+
ctx.out.error(`auth: browser launch skipped (${ctx.browser.reason}) \u2014 show the human the URL above`);
|
|
386
386
|
}
|
|
387
387
|
}
|
|
388
388
|
function stillPending(pending, email) {
|
|
@@ -507,7 +507,7 @@ async function scopedToken(platform, scope, options, doFetch, out) {
|
|
|
507
507
|
const cache = options.cache === false ? null : readJsonFile(tokenFile);
|
|
508
508
|
const cached = cache?.platform === audience ? cache.tokens?.[scope] : void 0;
|
|
509
509
|
if (cached?.token && (cached.expiresAt ?? 0) > Date.now() + 6e4) {
|
|
510
|
-
out.
|
|
510
|
+
out.error(`auth: using cached ${scope} grant (${tokenFile})`);
|
|
511
511
|
return cached.token;
|
|
512
512
|
}
|
|
513
513
|
const email = handshakeEmail(options.email, cache?.platform === audience ? cache.email : void 0);
|
|
@@ -538,9 +538,9 @@ async function scopedToken(platform, scope, options, doFetch, out) {
|
|
|
538
538
|
tokens[scope] = { token, expiresAt };
|
|
539
539
|
if ((0, import_node_fs3.existsSync)((0, import_node_path3.join)(rootDir, ".git"))) ensureGitignore(rootDir, [tokenFile]);
|
|
540
540
|
writePrivateJson(tokenFile, { platform: audience, email, tokens });
|
|
541
|
-
out.
|
|
541
|
+
out.error(`auth: cached ${scope} grant (${tokenFile}; mode 0600)`);
|
|
542
542
|
} else {
|
|
543
|
-
out.
|
|
543
|
+
out.error(`auth: ${scope} grant is in memory only; its credential record remains in odla-ai/db`);
|
|
544
544
|
}
|
|
545
545
|
return token;
|
|
546
546
|
}
|
|
@@ -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") }
|
|
@@ -8711,13 +8743,11 @@ async function loadOrDefaultConfig(configPath, action, appId) {
|
|
|
8711
8743
|
}
|
|
8712
8744
|
return loadProjectConfig(configPath);
|
|
8713
8745
|
}
|
|
8714
|
-
var PURE_STDOUT = /* @__PURE__ */ new Set(["get", "cat"]);
|
|
8715
8746
|
async function buildContext2(parsed, deps, action) {
|
|
8716
8747
|
const configPath = stringOpt(parsed.options.config) ?? "odla.config.mjs";
|
|
8717
8748
|
const cfg = await loadOrDefaultConfig(configPath, action, stringOpt(parsed.options.app));
|
|
8718
8749
|
const doFetch = deps.fetch ?? fetch;
|
|
8719
8750
|
const out = deps.stdout ?? console;
|
|
8720
|
-
const authOut = PURE_STDOUT.has(action) ? { log: (line) => out.error(line), error: (line) => out.error(line) } : out;
|
|
8721
8751
|
const appId = stringOpt(parsed.options.app) ?? PLATFORM_SCOPE;
|
|
8722
8752
|
const dryRun = parsed.options["dry-run"] === true;
|
|
8723
8753
|
if (action === "import" && dryRun) {
|
|
@@ -8737,7 +8767,7 @@ async function buildContext2(parsed, deps, action) {
|
|
|
8737
8767
|
email: stringOpt(parsed.options.email),
|
|
8738
8768
|
label: `odla CLI (runbook ${action})`,
|
|
8739
8769
|
fetch: doFetch,
|
|
8740
|
-
stdout:
|
|
8770
|
+
stdout: out,
|
|
8741
8771
|
openApprovalUrl: deps.openUrl,
|
|
8742
8772
|
// Anchor the grant cache to this project, not the shell's cwd.
|
|
8743
8773
|
rootDir: cfg.rootDir
|
|
@@ -8754,7 +8784,7 @@ async function buildContext2(parsed, deps, action) {
|
|
|
8754
8784
|
open: void 0
|
|
8755
8785
|
},
|
|
8756
8786
|
doFetch,
|
|
8757
|
-
|
|
8787
|
+
out
|
|
8758
8788
|
);
|
|
8759
8789
|
return {
|
|
8760
8790
|
platformUrl: cfg.platformUrl,
|
|
@@ -9555,6 +9585,7 @@ function exitCodeFor(err) {
|
|
|
9555
9585
|
}
|
|
9556
9586
|
async function runCli(argv = process.argv.slice(2), dependencies = {}) {
|
|
9557
9587
|
const parsed = parseArgv(argv);
|
|
9588
|
+
recordInvocation(parsed);
|
|
9558
9589
|
const command = parsed.positionals[0] ?? "help";
|
|
9559
9590
|
if (command === "version" || command === "-v" || parsed.options.version === true) {
|
|
9560
9591
|
assertArgs(parsed, ["version"], 1);
|