@odla-ai/cli 0.35.3 → 0.36.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/dist/bin.cjs +409 -231
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-PYR73XBD.js → chunk-4QJ5NS64.js} +293 -136
- package/dist/chunk-4QJ5NS64.js.map +1 -0
- package/dist/{cli-ZBNA7J4T.js → cli-BFGY7F6X.js} +2 -2
- package/dist/index.cjs +361 -204
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-PYR73XBD.js.map +0 -1
- /package/dist/{cli-ZBNA7J4T.js.map → cli-BFGY7F6X.js.map} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -97,12 +97,12 @@ var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${_
|
|
|
97
97
|
var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
|
|
98
98
|
|
|
99
99
|
// src/admin-ai.ts
|
|
100
|
-
var
|
|
100
|
+
var import_node_process8 = __toESM(require("process"), 1);
|
|
101
101
|
|
|
102
102
|
// src/token.ts
|
|
103
103
|
var import_db = require("@odla-ai/db");
|
|
104
104
|
var import_node_crypto = require("crypto");
|
|
105
|
-
var
|
|
105
|
+
var import_node_process5 = __toESM(require("process"), 1);
|
|
106
106
|
|
|
107
107
|
// src/handshake-approval.ts
|
|
108
108
|
var import_node_process2 = __toESM(require("process"), 1);
|
|
@@ -259,13 +259,50 @@ function explainRejectedCredential(error) {
|
|
|
259
259
|
].join("\n");
|
|
260
260
|
}
|
|
261
261
|
|
|
262
|
-
// src/
|
|
262
|
+
// src/device-session.ts
|
|
263
263
|
var import_node_fs3 = require("fs");
|
|
264
|
+
var import_node_os = require("os");
|
|
264
265
|
var import_node_path2 = require("path");
|
|
266
|
+
var import_node_process4 = __toESM(require("process"), 1);
|
|
267
|
+
function deviceCredentialPath(env = import_node_process4.default.env) {
|
|
268
|
+
return env.ODLA_DEVICE_CREDENTIAL ?? (0, import_node_path2.join)(env.HOME ?? (0, import_node_os.homedir)(), ".odla", "device.json");
|
|
269
|
+
}
|
|
270
|
+
function readDeviceCredential(platform, env = import_node_process4.default.env) {
|
|
271
|
+
const path = deviceCredentialPath(env);
|
|
272
|
+
if (!(0, import_node_fs3.existsSync)(path)) return null;
|
|
273
|
+
try {
|
|
274
|
+
const parsed = JSON.parse((0, import_node_fs3.readFileSync)(path, "utf8"));
|
|
275
|
+
if (typeof parsed.token !== "string" || !parsed.token.startsWith("odla_device_")) return null;
|
|
276
|
+
if (parsed.platform !== platform) return null;
|
|
277
|
+
return { ...parsed, token: parsed.token, platform: parsed.platform };
|
|
278
|
+
} catch {
|
|
279
|
+
return null;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
async function mintDeviceSession(platformUrl, credential2, doFetch) {
|
|
283
|
+
const response2 = await doFetch(`${platformUrl.replace(/\/$/, "")}/registry/devices/session`, {
|
|
284
|
+
method: "POST",
|
|
285
|
+
headers: { authorization: `Bearer ${credential2.token}`, "content-type": "application/json" },
|
|
286
|
+
body: "{}"
|
|
287
|
+
});
|
|
288
|
+
const body = await response2.json().catch(() => ({}));
|
|
289
|
+
if (!response2.ok || typeof body.token !== "string") {
|
|
290
|
+
const revocable = response2.status === 401 || response2.status === 403 || response2.status === 404;
|
|
291
|
+
const detail = body.error?.message ?? (response2.ok ? `registry returned ${response2.status} with no session token` : `registry returned ${response2.status}`);
|
|
292
|
+
throw new Error(
|
|
293
|
+
`device session failed: ${detail} (${response2.status})` + (revocable ? " \u2014 if this machine's enrollment was revoked or has expired, enroll it again in Studio" : "")
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
return { token: body.token, expiresAt: body.expiresAt ?? Date.now() };
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// src/local.ts
|
|
300
|
+
var import_node_fs4 = require("fs");
|
|
301
|
+
var import_node_path3 = require("path");
|
|
265
302
|
var GITIGNORE_LINES = [".odla/*.local.json", ".odla/dev-token.json", ".dev.vars"];
|
|
266
303
|
function readJsonFile(path) {
|
|
267
304
|
try {
|
|
268
|
-
return JSON.parse((0,
|
|
305
|
+
return JSON.parse((0, import_node_fs4.readFileSync)(path, "utf8"));
|
|
269
306
|
} catch {
|
|
270
307
|
return null;
|
|
271
308
|
}
|
|
@@ -275,10 +312,10 @@ function writePrivateJson(path, value2) {
|
|
|
275
312
|
`);
|
|
276
313
|
}
|
|
277
314
|
function readCredentials(path) {
|
|
278
|
-
if (!(0,
|
|
315
|
+
if (!(0, import_node_fs4.existsSync)(path)) return null;
|
|
279
316
|
let value2;
|
|
280
317
|
try {
|
|
281
|
-
value2 = JSON.parse((0,
|
|
318
|
+
value2 = JSON.parse((0, import_node_fs4.readFileSync)(path, "utf8"));
|
|
282
319
|
} catch {
|
|
283
320
|
throw new Error(`credentials file ${path} is not valid JSON; fix or remove it before provisioning`);
|
|
284
321
|
}
|
|
@@ -308,14 +345,14 @@ function mergeCredential(current, update) {
|
|
|
308
345
|
return next;
|
|
309
346
|
}
|
|
310
347
|
function ensureGitignore(rootDir, localPaths = []) {
|
|
311
|
-
const path = (0,
|
|
312
|
-
const existing = (0,
|
|
348
|
+
const path = (0, import_node_path3.resolve)(rootDir, ".gitignore");
|
|
349
|
+
const existing = (0, import_node_fs4.existsSync)(path) ? (0, import_node_fs4.readFileSync)(path, "utf8") : "";
|
|
313
350
|
const configured = localPaths.map((localPath) => gitignoreEntry(rootDir, localPath)).filter((line2) => !!line2);
|
|
314
351
|
const wanted = [.../* @__PURE__ */ new Set([...GITIGNORE_LINES, ...configured])];
|
|
315
352
|
const missing = wanted.filter((line2) => !existing.split(/\r?\n/).includes(line2));
|
|
316
353
|
if (missing.length === 0) return;
|
|
317
354
|
const prefix = existing && !existing.endsWith("\n") ? "\n" : "";
|
|
318
|
-
(0,
|
|
355
|
+
(0, import_node_fs4.writeFileSync)(path, `${existing}${prefix}${missing.join("\n")}
|
|
319
356
|
`);
|
|
320
357
|
}
|
|
321
358
|
function o11yDevVars(cfg) {
|
|
@@ -329,7 +366,7 @@ function o11yDevVars(cfg) {
|
|
|
329
366
|
function resolveWriteDevVarsTarget(cfg, requested) {
|
|
330
367
|
if (!requested) return null;
|
|
331
368
|
if (requested === true) return cfg.local.devVarsFile;
|
|
332
|
-
return (0,
|
|
369
|
+
return (0, import_node_path3.resolve)((0, import_node_path3.dirname)(cfg.configPath), requested);
|
|
333
370
|
}
|
|
334
371
|
function writeDevVars(path, credentials, env, o11y) {
|
|
335
372
|
const entry = credentials.envs[env];
|
|
@@ -343,7 +380,7 @@ function writeDevVars(path, credentials, env, o11y) {
|
|
|
343
380
|
if (o11y.version) lines.push(`ODLA_O11Y_VERSION="${o11y.version}"`);
|
|
344
381
|
if (entry.o11yToken) lines.push(`ODLA_O11Y_TOKEN="${entry.o11yToken}"`);
|
|
345
382
|
}
|
|
346
|
-
const existing = (0,
|
|
383
|
+
const existing = (0, import_node_fs4.existsSync)(path) ? (0, import_node_fs4.readFileSync)(path, "utf8") : "";
|
|
347
384
|
const retained = existing.split(/\r?\n/).filter((line2) => !isManagedDevVar(line2));
|
|
348
385
|
while (retained.at(-1) === "") retained.pop();
|
|
349
386
|
const prefix = retained.length ? `${retained.join("\n")}
|
|
@@ -369,19 +406,19 @@ function isManagedDevVar(line2) {
|
|
|
369
406
|
return !!match?.[1] && MANAGED_DEV_VARS.has(match[1]);
|
|
370
407
|
}
|
|
371
408
|
function writePrivateText(path, text3) {
|
|
372
|
-
(0,
|
|
409
|
+
(0, import_node_fs4.mkdirSync)((0, import_node_path3.dirname)(path), { recursive: true });
|
|
373
410
|
const temporary = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
374
|
-
(0,
|
|
375
|
-
(0,
|
|
376
|
-
(0,
|
|
411
|
+
(0, import_node_fs4.writeFileSync)(temporary, text3, { mode: 384 });
|
|
412
|
+
(0, import_node_fs4.chmodSync)(temporary, 384);
|
|
413
|
+
(0, import_node_fs4.renameSync)(temporary, path);
|
|
377
414
|
}
|
|
378
415
|
function gitignoreEntry(rootDir, path) {
|
|
379
|
-
const rel = (0,
|
|
380
|
-
if (!rel || rel === ".." || rel.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) || (0,
|
|
416
|
+
const rel = (0, import_node_path3.relative)((0, import_node_path3.resolve)(rootDir), (0, import_node_path3.resolve)(path));
|
|
417
|
+
if (!rel || rel === ".." || rel.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) || (0, import_node_path3.isAbsolute)(rel)) return null;
|
|
381
418
|
return rel.replaceAll("\\", "/");
|
|
382
419
|
}
|
|
383
420
|
function displayPath(path, rootDir = process.cwd()) {
|
|
384
|
-
const rel = (0,
|
|
421
|
+
const rel = (0, import_node_path3.relative)(rootDir, path);
|
|
385
422
|
return rel && !rel.startsWith("..") ? rel : path;
|
|
386
423
|
}
|
|
387
424
|
|
|
@@ -393,14 +430,20 @@ async function getDeveloperToken(cfg, options, doFetch, out, grantRequest = {})
|
|
|
393
430
|
const cached = readJsonFile(cfg.local.tokenFile);
|
|
394
431
|
if (!grantRequest.forceReview && !grantRequest.freshLogin) {
|
|
395
432
|
if (options.token) return options.token;
|
|
396
|
-
if (
|
|
397
|
-
const declared =
|
|
433
|
+
if (import_node_process5.default.env.ODLA_DEV_TOKEN) {
|
|
434
|
+
const declared = import_node_process5.default.env.ODLA_DEV_TOKEN_AUDIENCE;
|
|
398
435
|
if (declared) {
|
|
399
436
|
if (platformAudience(declared) !== audience) throw new Error("ODLA_DEV_TOKEN_AUDIENCE does not match the configured platform");
|
|
400
437
|
} else if (audience !== "https://odla.ai") {
|
|
401
438
|
throw new Error("ODLA_DEV_TOKEN_AUDIENCE is required for a non-default platform");
|
|
402
439
|
}
|
|
403
|
-
return
|
|
440
|
+
return import_node_process5.default.env.ODLA_DEV_TOKEN;
|
|
441
|
+
}
|
|
442
|
+
const device = readDeviceCredential(audience);
|
|
443
|
+
if (device) {
|
|
444
|
+
const session = await mintDeviceSession(cfg.platformUrl, device, doFetch);
|
|
445
|
+
out.error(`auth: session minted by this enrolled device (${displayPath(deviceCredentialPath(), cfg.rootDir)})`);
|
|
446
|
+
return session.token;
|
|
404
447
|
}
|
|
405
448
|
if (cached?.token && cached.platform === audience && (cached.expiresAt ?? 0) > Date.now() + 6e4 && cachedGrantCovers(cached, grantIntent)) {
|
|
406
449
|
out.error(`auth: using cached developer token (${displayPath(cfg.local.tokenFile, cfg.rootDir)})`);
|
|
@@ -505,7 +548,7 @@ function stillPending(pending, email) {
|
|
|
505
548
|
);
|
|
506
549
|
}
|
|
507
550
|
function handshakeEmail(value2, cached) {
|
|
508
|
-
const email = (value2 ??
|
|
551
|
+
const email = (value2 ?? import_node_process5.default.env.ODLA_USER_EMAIL ?? cached ?? "").trim().toLowerCase();
|
|
509
552
|
if (/@users\.noreply\.github\.com$/i.test(email)) {
|
|
510
553
|
throw new Error(
|
|
511
554
|
`"${email}" is a GitHub commit identity, not an odla account email; use --email <signed-in-odla-account> or ODLA_USER_EMAIL`
|
|
@@ -536,12 +579,12 @@ function platformAudience(value2) {
|
|
|
536
579
|
}
|
|
537
580
|
|
|
538
581
|
// src/secret-input.ts
|
|
539
|
-
var
|
|
582
|
+
var import_node_process6 = __toESM(require("process"), 1);
|
|
540
583
|
var MAX_BYTES = 64 * 1024;
|
|
541
584
|
async function secretInputValue(options, kind = "credential") {
|
|
542
585
|
if (options.fromEnv && options.stdin) throw new Error("choose exactly one of --from-env or --stdin");
|
|
543
586
|
let value2;
|
|
544
|
-
if (options.fromEnv) value2 =
|
|
587
|
+
if (options.fromEnv) value2 = import_node_process6.default.env[options.fromEnv];
|
|
545
588
|
else if (options.stdin) value2 = await (options.readStdin ?? (() => readSecretStream(kind)))();
|
|
546
589
|
else throw new Error(`${kind} input required: use --from-env <NAME> or --stdin; values are never accepted as arguments`);
|
|
547
590
|
value2 = value2?.replace(/[\r\n]+$/, "");
|
|
@@ -549,7 +592,7 @@ async function secretInputValue(options, kind = "credential") {
|
|
|
549
592
|
if (new TextEncoder().encode(value2).byteLength > MAX_BYTES) throw new Error(`${kind} exceeds 64 KiB`);
|
|
550
593
|
return value2;
|
|
551
594
|
}
|
|
552
|
-
async function readSecretStream(kind, stream =
|
|
595
|
+
async function readSecretStream(kind, stream = import_node_process6.default.stdin) {
|
|
553
596
|
let value2 = "";
|
|
554
597
|
for await (const chunk of stream) {
|
|
555
598
|
value2 += String(chunk);
|
|
@@ -559,9 +602,9 @@ async function readSecretStream(kind, stream = import_node_process5.default.stdi
|
|
|
559
602
|
}
|
|
560
603
|
|
|
561
604
|
// src/admin-ai-auth.ts
|
|
562
|
-
var
|
|
563
|
-
var
|
|
564
|
-
var
|
|
605
|
+
var import_node_fs5 = require("fs");
|
|
606
|
+
var import_node_path4 = require("path");
|
|
607
|
+
var import_node_process7 = __toESM(require("process"), 1);
|
|
565
608
|
var import_db2 = require("@odla-ai/db");
|
|
566
609
|
async function getScopedPlatformToken(options) {
|
|
567
610
|
return resolveAdminPlatformToken(options);
|
|
@@ -569,7 +612,7 @@ async function getScopedPlatformToken(options) {
|
|
|
569
612
|
async function resolveAdminPlatformToken(options) {
|
|
570
613
|
const audience = platformAudience(options.platform);
|
|
571
614
|
if (options.token) return options.token;
|
|
572
|
-
const fromEnv =
|
|
615
|
+
const fromEnv = import_node_process7.default.env.ODLA_ADMIN_TOKEN;
|
|
573
616
|
if (fromEnv) return audienceBoundEnvToken(fromEnv, audience);
|
|
574
617
|
return scopedToken(
|
|
575
618
|
audience,
|
|
@@ -581,7 +624,7 @@ async function resolveAdminPlatformToken(options) {
|
|
|
581
624
|
}
|
|
582
625
|
function audienceBoundEnvToken(token, platform) {
|
|
583
626
|
const audience = platformAudience(platform);
|
|
584
|
-
const declared =
|
|
627
|
+
const declared = import_node_process7.default.env.ODLA_ADMIN_TOKEN_AUDIENCE;
|
|
585
628
|
if (declared) {
|
|
586
629
|
if (platformAudience(declared) !== audience) throw new Error("ODLA_ADMIN_TOKEN_AUDIENCE does not match the configured platform");
|
|
587
630
|
} else if (audience !== "https://odla.ai") {
|
|
@@ -594,6 +637,7 @@ var SCOPE_PURPOSE = {
|
|
|
594
637
|
"app:config:read": "compare checked-in intent with an exact-id app Registry configuration",
|
|
595
638
|
"app:config:write": "apply or inspect one revision-bound configuration operation for an app you own",
|
|
596
639
|
"platform:runbook:write": "read and edit all of odla's operational runbooks, including admin-visible content",
|
|
640
|
+
"app:device:enroll": "enrol this machine so it can mint its own short-lived credentials without asking you again",
|
|
597
641
|
"platform:ai:policy:write": "change System AI model routing",
|
|
598
642
|
"platform:ai:policy:read": "read System AI model routing",
|
|
599
643
|
"platform:ai:credential:write": "replace a stored AI provider key",
|
|
@@ -606,8 +650,8 @@ var SCOPE_PURPOSE = {
|
|
|
606
650
|
};
|
|
607
651
|
async function scopedToken(platform, scope, options, doFetch, out) {
|
|
608
652
|
const audience = platformAudience(platform);
|
|
609
|
-
const rootDir = options.rootDir ??
|
|
610
|
-
const tokenFile = options.tokenFile ?? (0,
|
|
653
|
+
const rootDir = options.rootDir ?? import_node_process7.default.cwd();
|
|
654
|
+
const tokenFile = options.tokenFile ?? (0, import_node_path4.join)(rootDir, ".odla/admin-token.local.json");
|
|
611
655
|
const cache2 = options.cache === false ? null : readJsonFile(tokenFile);
|
|
612
656
|
const cached = cache2?.platform === audience ? cache2.tokens?.[scope] : void 0;
|
|
613
657
|
if (cached?.token && (cached.expiresAt ?? 0) > Date.now() + 6e4) {
|
|
@@ -634,7 +678,7 @@ async function scopedToken(platform, scope, options, doFetch, out) {
|
|
|
634
678
|
if (options.cache !== false) {
|
|
635
679
|
const tokens = cache2?.platform === audience ? { ...cache2.tokens ?? {} } : {};
|
|
636
680
|
tokens[scope] = { token, expiresAt };
|
|
637
|
-
if ((0,
|
|
681
|
+
if ((0, import_node_fs5.existsSync)((0, import_node_path4.join)(rootDir, ".git"))) ensureGitignore(rootDir, [tokenFile]);
|
|
638
682
|
writePrivateJson(tokenFile, { platform: audience, email, tokens });
|
|
639
683
|
out.error(`auth: cached ${scope} grant (${tokenFile}; mode 0600)`);
|
|
640
684
|
} else {
|
|
@@ -811,7 +855,7 @@ function isRecord2(value2) {
|
|
|
811
855
|
|
|
812
856
|
// src/admin-ai.ts
|
|
813
857
|
async function adminAi(options) {
|
|
814
|
-
const platform = platformAudience(options.platform ??
|
|
858
|
+
const platform = platformAudience(options.platform ?? import_node_process8.default.env.ODLA_PLATFORM ?? "https://odla.ai");
|
|
815
859
|
const doFetch = options.fetch ?? fetch;
|
|
816
860
|
const out = options.stdout ?? console;
|
|
817
861
|
const usageQuery = options.action === "usage" ? adminAiUsageQuery(options) : void 0;
|
|
@@ -1066,13 +1110,13 @@ function addOption(options, name, value2) {
|
|
|
1066
1110
|
}
|
|
1067
1111
|
|
|
1068
1112
|
// src/operator-context.ts
|
|
1069
|
-
var
|
|
1070
|
-
var
|
|
1071
|
-
var
|
|
1113
|
+
var import_node_fs8 = require("fs");
|
|
1114
|
+
var import_node_path7 = require("path");
|
|
1115
|
+
var import_node_process10 = __toESM(require("process"), 1);
|
|
1072
1116
|
|
|
1073
1117
|
// src/config.ts
|
|
1074
|
-
var
|
|
1075
|
-
var
|
|
1118
|
+
var import_node_fs6 = require("fs");
|
|
1119
|
+
var import_node_path5 = require("path");
|
|
1076
1120
|
var import_node_url = require("url");
|
|
1077
1121
|
var import_apps = require("@odla-ai/apps");
|
|
1078
1122
|
|
|
@@ -1477,12 +1521,12 @@ var DEFAULT_SERVICES = ["db", "ai"];
|
|
|
1477
1521
|
var configImportSerial = 0;
|
|
1478
1522
|
var GOOGLE_CALENDAR_EVENTS_SCOPE = "https://www.googleapis.com/auth/calendar.events";
|
|
1479
1523
|
async function loadProjectConfig(configPath = "odla.config.mjs", options = {}) {
|
|
1480
|
-
const resolved = (0,
|
|
1481
|
-
if (!(0,
|
|
1524
|
+
const resolved = (0, import_node_path5.resolve)(configPath);
|
|
1525
|
+
if (!(0, import_node_fs6.existsSync)(resolved)) {
|
|
1482
1526
|
throw new Error(`config not found: ${configPath}. Run "odla-ai init" first or pass --config.`);
|
|
1483
1527
|
}
|
|
1484
1528
|
const raw = await loadConfigModule(resolved);
|
|
1485
|
-
const rootDir = (0,
|
|
1529
|
+
const rootDir = (0, import_node_path5.dirname)(resolved);
|
|
1486
1530
|
validateRawConfig(raw, resolved);
|
|
1487
1531
|
const platformUrl = trimSlash(process.env.ODLA_PLATFORM_URL || raw.platformUrl || DEFAULT_PLATFORM);
|
|
1488
1532
|
const dbEndpoint = trimSlash(process.env.ODLA_DB_ENDPOINT || raw.dbEndpoint || platformUrl);
|
|
@@ -1492,9 +1536,9 @@ async function loadProjectConfig(configPath = "odla.config.mjs", options = {}) {
|
|
|
1492
1536
|
validateCalendarConfig(raw, unique3([...envs, ...options.additionalEnvs ?? []]), services, resolved);
|
|
1493
1537
|
validateMonitoringConfig(raw, unique3([...envs, ...options.additionalEnvs ?? []]), services, resolved);
|
|
1494
1538
|
const local = {
|
|
1495
|
-
tokenFile: (0,
|
|
1496
|
-
credentialsFile: (0,
|
|
1497
|
-
devVarsFile: (0,
|
|
1539
|
+
tokenFile: (0, import_node_path5.resolve)(rootDir, raw.local?.tokenFile ?? ".odla/dev-token.json"),
|
|
1540
|
+
credentialsFile: (0, import_node_path5.resolve)(rootDir, raw.local?.credentialsFile ?? ".odla/credentials.local.json"),
|
|
1541
|
+
devVarsFile: (0, import_node_path5.resolve)(rootDir, raw.local?.devVarsFile ?? ".dev.vars"),
|
|
1498
1542
|
gitignore: raw.local?.gitignore ?? true
|
|
1499
1543
|
};
|
|
1500
1544
|
return {
|
|
@@ -1511,9 +1555,9 @@ async function loadProjectConfig(configPath = "odla.config.mjs", options = {}) {
|
|
|
1511
1555
|
async function resolveDataExport(cfg, value2, names) {
|
|
1512
1556
|
if (value2 === void 0 || value2 === null || value2 === false) return void 0;
|
|
1513
1557
|
if (typeof value2 !== "string") return value2;
|
|
1514
|
-
const target = (0,
|
|
1558
|
+
const target = (0, import_node_path5.isAbsolute)(value2) ? value2 : (0, import_node_path5.resolve)(cfg.rootDir, value2);
|
|
1515
1559
|
if (target.endsWith(".json")) {
|
|
1516
|
-
return JSON.parse((0,
|
|
1560
|
+
return JSON.parse((0, import_node_fs6.readFileSync)(target, "utf8"));
|
|
1517
1561
|
}
|
|
1518
1562
|
const mod = await import((0, import_node_url.pathToFileURL)(target).href);
|
|
1519
1563
|
for (const name of names) {
|
|
@@ -1589,7 +1633,7 @@ function validId2(value2) {
|
|
|
1589
1633
|
return typeof value2 === "string" && /^[a-z0-9][a-z0-9-]*$/.test(value2);
|
|
1590
1634
|
}
|
|
1591
1635
|
async function loadConfigModule(path) {
|
|
1592
|
-
if (path.endsWith(".json")) return JSON.parse((0,
|
|
1636
|
+
if (path.endsWith(".json")) return JSON.parse((0, import_node_fs6.readFileSync)(path, "utf8"));
|
|
1593
1637
|
const nonce = `${Date.now()}-${configImportSerial++}`;
|
|
1594
1638
|
const mod = await import(`${(0, import_node_url.pathToFileURL)(path).href}?reload=${nonce}`);
|
|
1595
1639
|
const value2 = mod.default ?? mod.config;
|
|
@@ -1604,18 +1648,18 @@ function unique3(values) {
|
|
|
1604
1648
|
}
|
|
1605
1649
|
|
|
1606
1650
|
// src/operator-profiles.ts
|
|
1607
|
-
var
|
|
1608
|
-
var
|
|
1609
|
-
var
|
|
1610
|
-
var
|
|
1651
|
+
var import_node_fs7 = require("fs");
|
|
1652
|
+
var import_node_os2 = require("os");
|
|
1653
|
+
var import_node_path6 = require("path");
|
|
1654
|
+
var import_node_process9 = __toESM(require("process"), 1);
|
|
1611
1655
|
function operatorProfileFile() {
|
|
1612
|
-
return (0,
|
|
1613
|
-
clean(
|
|
1656
|
+
return (0, import_node_path6.resolve)(
|
|
1657
|
+
clean(import_node_process9.default.env.ODLA_CONTEXT_FILE) ?? (0, import_node_path6.join)((0, import_node_os2.homedir)(), ".odla", "contexts.json")
|
|
1614
1658
|
);
|
|
1615
1659
|
}
|
|
1616
1660
|
function resolveOperatorProfile(parsed) {
|
|
1617
1661
|
const fromFlag = clean(stringOpt(parsed.options.context));
|
|
1618
|
-
const fromEnvironment = clean(
|
|
1662
|
+
const fromEnvironment = clean(import_node_process9.default.env.ODLA_CONTEXT);
|
|
1619
1663
|
const name = fromFlag ?? fromEnvironment ?? null;
|
|
1620
1664
|
const file = operatorProfileFile();
|
|
1621
1665
|
if (!name) {
|
|
@@ -1655,10 +1699,10 @@ function removeOperatorProfile(name, file = operatorProfileFile()) {
|
|
|
1655
1699
|
return true;
|
|
1656
1700
|
}
|
|
1657
1701
|
function operatorCredentialFiles(selection) {
|
|
1658
|
-
const base = selection.name ? (0,
|
|
1702
|
+
const base = selection.name ? (0, import_node_path6.join)((0, import_node_path6.dirname)(selection.file), "profiles", selection.name) : (0, import_node_path6.join)((0, import_node_os2.homedir)(), ".odla");
|
|
1659
1703
|
return {
|
|
1660
|
-
developer: (0,
|
|
1661
|
-
scoped: (0,
|
|
1704
|
+
developer: (0, import_node_path6.join)(base, "dev-token.json"),
|
|
1705
|
+
scoped: (0, import_node_path6.join)(base, "admin-token.local.json")
|
|
1662
1706
|
};
|
|
1663
1707
|
}
|
|
1664
1708
|
function assertOperatorName(value2, label) {
|
|
@@ -1669,10 +1713,10 @@ function assertOperatorName(value2, label) {
|
|
|
1669
1713
|
}
|
|
1670
1714
|
}
|
|
1671
1715
|
function readOperatorProfiles(file) {
|
|
1672
|
-
if (!(0,
|
|
1716
|
+
if (!(0, import_node_fs7.existsSync)(file)) return emptyProfiles();
|
|
1673
1717
|
let raw;
|
|
1674
1718
|
try {
|
|
1675
|
-
raw = JSON.parse((0,
|
|
1719
|
+
raw = JSON.parse((0, import_node_fs7.readFileSync)(file, "utf8"));
|
|
1676
1720
|
} catch {
|
|
1677
1721
|
throw new Error(`operator context file ${file} is not valid JSON`);
|
|
1678
1722
|
}
|
|
@@ -1736,21 +1780,21 @@ var DEFAULT_PLATFORM2 = "https://odla.ai";
|
|
|
1736
1780
|
async function resolveOperatorContext(parsed, options = {}) {
|
|
1737
1781
|
const profile = resolveOperatorProfile(parsed);
|
|
1738
1782
|
const configArgument = stringOpt(parsed.options.config) ?? "odla.config.mjs";
|
|
1739
|
-
const configPath = (0,
|
|
1783
|
+
const configPath = (0, import_node_path7.resolve)(configArgument);
|
|
1740
1784
|
const explicitConfig = parsed.options.config !== void 0;
|
|
1741
|
-
const hasConfig = (0,
|
|
1785
|
+
const hasConfig = (0, import_node_fs8.existsSync)(configPath);
|
|
1742
1786
|
if (!hasConfig && (!options.allowMissingConfig || explicitConfig)) {
|
|
1743
1787
|
await loadProjectConfig(configArgument);
|
|
1744
1788
|
}
|
|
1745
1789
|
const loaded = hasConfig ? await loadProjectConfig(configArgument) : void 0;
|
|
1746
1790
|
const platformFlag = clean2(stringOpt(parsed.options.platform));
|
|
1747
|
-
const platformEnvironment = clean2(
|
|
1791
|
+
const platformEnvironment = clean2(import_node_process10.default.env.ODLA_PLATFORM_URL);
|
|
1748
1792
|
const platformValue = platformAudience(
|
|
1749
1793
|
platformFlag ?? platformEnvironment ?? profile.value?.platform ?? loaded?.platformUrl ?? DEFAULT_PLATFORM2
|
|
1750
1794
|
);
|
|
1751
1795
|
const platformSource = platformFlag ? "flag" : platformEnvironment ? "environment" : profile.value ? "profile" : loaded ? "config" : "default";
|
|
1752
1796
|
const appFlag = clean2(stringOpt(parsed.options.app));
|
|
1753
|
-
const appEnvironment = clean2(
|
|
1797
|
+
const appEnvironment = clean2(import_node_process10.default.env.ODLA_APP_ID);
|
|
1754
1798
|
const appValue = appFlag ?? appEnvironment ?? profile.value?.app ?? loaded?.app.id ?? null;
|
|
1755
1799
|
const appSource = appFlag ? "flag" : appEnvironment ? "environment" : profile.value?.app ? "profile" : loaded ? "config" : "unresolved";
|
|
1756
1800
|
if (appValue) assertOperatorName(appValue, "app");
|
|
@@ -1760,16 +1804,16 @@ async function resolveOperatorContext(parsed, options = {}) {
|
|
|
1760
1804
|
);
|
|
1761
1805
|
}
|
|
1762
1806
|
const envFlag = clean2(stringOpt(parsed.options.env));
|
|
1763
|
-
const envEnvironment = clean2(
|
|
1807
|
+
const envEnvironment = clean2(import_node_process10.default.env.ODLA_ENV);
|
|
1764
1808
|
const environmentValue = envFlag ?? envEnvironment ?? profile.value?.environment ?? options.defaultEnvironment ?? null;
|
|
1765
1809
|
const environmentSource = envFlag ? "flag" : envEnvironment ? "environment" : profile.value?.environment ? "profile" : options.defaultEnvironment ? "default" : "unresolved";
|
|
1766
1810
|
if (environmentValue) {
|
|
1767
1811
|
assertOperatorName(environmentValue, "environment");
|
|
1768
1812
|
}
|
|
1769
|
-
const rootDir = loaded?.rootDir ??
|
|
1813
|
+
const rootDir = loaded?.rootDir ?? import_node_process10.default.cwd();
|
|
1770
1814
|
const profileCredentials = operatorCredentialFiles(profile);
|
|
1771
|
-
const tokenFile = clean2(
|
|
1772
|
-
const scopedTokenFile = clean2(
|
|
1815
|
+
const tokenFile = clean2(import_node_process10.default.env.ODLA_DEV_TOKEN_FILE) ? (0, import_node_path7.resolve)(import_node_process10.default.env.ODLA_DEV_TOKEN_FILE) : profile.name ? profileCredentials.developer : loaded?.local.tokenFile ?? profileCredentials.developer;
|
|
1816
|
+
const scopedTokenFile = clean2(import_node_process10.default.env.ODLA_ADMIN_TOKEN_FILE) ? (0, import_node_path7.resolve)(import_node_process10.default.env.ODLA_ADMIN_TOKEN_FILE) : profile.name ? profileCredentials.scoped : loaded ? (0, import_node_path7.join)(loaded.rootDir, ".odla", "admin-token.local.json") : profileCredentials.scoped;
|
|
1773
1817
|
const cfg = loaded ? {
|
|
1774
1818
|
...loaded,
|
|
1775
1819
|
platformUrl: platformValue,
|
|
@@ -1791,8 +1835,8 @@ async function resolveOperatorContext(parsed, options = {}) {
|
|
|
1791
1835
|
services: [],
|
|
1792
1836
|
local: {
|
|
1793
1837
|
tokenFile,
|
|
1794
|
-
credentialsFile: (0,
|
|
1795
|
-
devVarsFile: (0,
|
|
1838
|
+
credentialsFile: (0, import_node_path7.join)(rootDir, ".odla", "credentials.local.json"),
|
|
1839
|
+
devVarsFile: (0, import_node_path7.join)(rootDir, ".dev.vars"),
|
|
1796
1840
|
gitignore: true
|
|
1797
1841
|
}
|
|
1798
1842
|
};
|
|
@@ -1889,7 +1933,7 @@ async function adminCommand(parsed, deps = {}) {
|
|
|
1889
1933
|
}
|
|
1890
1934
|
|
|
1891
1935
|
// src/auth-command.ts
|
|
1892
|
-
var
|
|
1936
|
+
var import_node_process11 = __toESM(require("process"), 1);
|
|
1893
1937
|
|
|
1894
1938
|
// src/whoami-command.ts
|
|
1895
1939
|
var text2 = (value2) => typeof value2 === "string" && value2.trim() ? value2.trim() : null;
|
|
@@ -2051,7 +2095,7 @@ async function authCommand(parsed, deps = {}) {
|
|
|
2051
2095
|
const { cfg } = context;
|
|
2052
2096
|
const out = deps.stdout ?? console;
|
|
2053
2097
|
const doFetch = deps.fetch ?? fetch;
|
|
2054
|
-
const email = stringOpt(parsed.options.email) ??
|
|
2098
|
+
const email = stringOpt(parsed.options.email) ?? import_node_process11.default.env.ODLA_USER_EMAIL?.trim();
|
|
2055
2099
|
if (!email) {
|
|
2056
2100
|
throw new Error(
|
|
2057
2101
|
"auth login requires --email <odla-account> or ODLA_USER_EMAIL; confirm the signed-in odla email instead of using git or GitHub identity"
|
|
@@ -2204,7 +2248,7 @@ async function appExport(options) {
|
|
|
2204
2248
|
}
|
|
2205
2249
|
|
|
2206
2250
|
// src/app-import.ts
|
|
2207
|
-
var
|
|
2251
|
+
var import_node_fs9 = require("fs");
|
|
2208
2252
|
var import_import = require("@odla-ai/db/import");
|
|
2209
2253
|
function chooseIdMode(options, rows) {
|
|
2210
2254
|
const chosen = [options.idField && "field", options.key && "key", options.generateIds && "generate"].filter(Boolean);
|
|
@@ -2222,7 +2266,7 @@ async function appImport(options) {
|
|
|
2222
2266
|
const out = options.stdout ?? console;
|
|
2223
2267
|
const say = options.json ? (line2) => out.error(line2) : (line2) => out.log(line2);
|
|
2224
2268
|
const { tenant } = resolveTenant(cfg, options.env);
|
|
2225
|
-
const text3 = options.file === "-" ? (options.readStdin ?? (() => (0,
|
|
2269
|
+
const text3 = options.file === "-" ? (options.readStdin ?? (() => (0, import_node_fs9.readFileSync)(0, "utf8")))() : (0, import_node_fs9.readFileSync)(options.file, "utf8");
|
|
2226
2270
|
const { format, sources } = (0, import_import.parseImport)(text3, options.ns);
|
|
2227
2271
|
if (format === "namespace-map" && options.ns) {
|
|
2228
2272
|
throw new Error("--ns cannot be combined with a {namespace: rows} file \u2014 the file already names each namespace");
|
|
@@ -2412,7 +2456,7 @@ async function appCommand(parsed, dependencies = {}) {
|
|
|
2412
2456
|
|
|
2413
2457
|
// src/brand-command.ts
|
|
2414
2458
|
var import_promises = require("fs/promises");
|
|
2415
|
-
var
|
|
2459
|
+
var import_node_path8 = require("path");
|
|
2416
2460
|
|
|
2417
2461
|
// src/brand-design-unpack.ts
|
|
2418
2462
|
var import_node_zlib = require("zlib");
|
|
@@ -2514,15 +2558,15 @@ function describeUnpack(result, outDir) {
|
|
|
2514
2558
|
// src/brand-command.ts
|
|
2515
2559
|
var USAGE = "usage: odla-ai brand design unpack <bundle.html|-> [--out <dir>] [--json]";
|
|
2516
2560
|
async function readBundle(source, deps) {
|
|
2517
|
-
if (source !== "-") return (0, import_promises.readFile)((0,
|
|
2561
|
+
if (source !== "-") return (0, import_promises.readFile)((0, import_node_path8.resolve)(source), "utf8");
|
|
2518
2562
|
const readStdin = deps.readStdin;
|
|
2519
2563
|
if (!readStdin) throw new Error("reading a bundle from stdin is not supported here");
|
|
2520
2564
|
return readStdin();
|
|
2521
2565
|
}
|
|
2522
2566
|
async function writeAll(result, outDir) {
|
|
2523
2567
|
for (const file of result.files) {
|
|
2524
|
-
const target = (0,
|
|
2525
|
-
await (0, import_promises.mkdir)((0,
|
|
2568
|
+
const target = (0, import_node_path8.resolve)(outDir, file.path);
|
|
2569
|
+
await (0, import_promises.mkdir)((0, import_node_path8.dirname)(target), { recursive: true });
|
|
2526
2570
|
await (0, import_promises.writeFile)(target, file.bytes);
|
|
2527
2571
|
}
|
|
2528
2572
|
}
|
|
@@ -2530,7 +2574,7 @@ async function designUnpack(parsed, deps) {
|
|
|
2530
2574
|
assertArgs(parsed, ["out", "json"], 4);
|
|
2531
2575
|
const source = parsed.positionals[3];
|
|
2532
2576
|
if (!source) throw new Error(USAGE);
|
|
2533
|
-
const outDir = (0,
|
|
2577
|
+
const outDir = (0, import_node_path8.resolve)(stringOpt(parsed.options.out) ?? "design");
|
|
2534
2578
|
const result = unpackDesign(await readBundle(source, deps));
|
|
2535
2579
|
await writeAll(result, outDir);
|
|
2536
2580
|
const out = deps.stdout ?? console;
|
|
@@ -3135,12 +3179,12 @@ async function safeText4(response2) {
|
|
|
3135
3179
|
|
|
3136
3180
|
// src/config-operation-command.ts
|
|
3137
3181
|
var import_apps6 = require("@odla-ai/apps");
|
|
3138
|
-
var
|
|
3182
|
+
var import_node_path9 = require("path");
|
|
3139
3183
|
|
|
3140
3184
|
// src/version.ts
|
|
3141
|
-
var
|
|
3185
|
+
var import_node_fs10 = require("fs");
|
|
3142
3186
|
function cliVersion() {
|
|
3143
|
-
const pkg = JSON.parse((0,
|
|
3187
|
+
const pkg = JSON.parse((0, import_node_fs10.readFileSync)(new URL("../package.json", importMetaUrl), "utf8"));
|
|
3144
3188
|
return pkg.version ?? "unknown";
|
|
3145
3189
|
}
|
|
3146
3190
|
|
|
@@ -3156,7 +3200,7 @@ var ConfigOperationCommandError = class extends Error {
|
|
|
3156
3200
|
|
|
3157
3201
|
// src/config-operation-validate.ts
|
|
3158
3202
|
var import_apps3 = require("@odla-ai/apps");
|
|
3159
|
-
var
|
|
3203
|
+
var import_node_fs11 = require("fs");
|
|
3160
3204
|
|
|
3161
3205
|
// src/config-reconcile-digest.ts
|
|
3162
3206
|
var import_node_crypto2 = require("crypto");
|
|
@@ -3192,7 +3236,7 @@ var SERVICE = /^[a-z][a-z0-9-]{0,39}$/;
|
|
|
3192
3236
|
function readPlan(path) {
|
|
3193
3237
|
let value2;
|
|
3194
3238
|
try {
|
|
3195
|
-
const raw = (0,
|
|
3239
|
+
const raw = (0, import_node_fs11.readFileSync)(path, "utf8");
|
|
3196
3240
|
if (Buffer.byteLength(raw) > 128 * 1024) throw new Error("plan exceeds 128 KiB");
|
|
3197
3241
|
value2 = JSON.parse(raw);
|
|
3198
3242
|
} catch (error) {
|
|
@@ -3565,7 +3609,7 @@ async function operationClient(cfg, options, purpose) {
|
|
|
3565
3609
|
platform: cfg.platformUrl,
|
|
3566
3610
|
scope: "app:config:write",
|
|
3567
3611
|
token: options.token,
|
|
3568
|
-
tokenFile: (0,
|
|
3612
|
+
tokenFile: (0, import_node_path9.join)(cfg.rootDir, ".odla", "admin-token.local.json"),
|
|
3569
3613
|
rootDir: cfg.rootDir,
|
|
3570
3614
|
email: options.email,
|
|
3571
3615
|
open: options.open,
|
|
@@ -3620,7 +3664,7 @@ function record4(value2) {
|
|
|
3620
3664
|
|
|
3621
3665
|
// src/config-reconcile-command.ts
|
|
3622
3666
|
var import_apps8 = require("@odla-ai/apps");
|
|
3623
|
-
var
|
|
3667
|
+
var import_node_path10 = require("path");
|
|
3624
3668
|
|
|
3625
3669
|
// src/config-reconcile.ts
|
|
3626
3670
|
var import_apps7 = require("@odla-ai/apps");
|
|
@@ -3916,7 +3960,7 @@ async function inspectConfig(options) {
|
|
|
3916
3960
|
platform: cfg.platformUrl,
|
|
3917
3961
|
scope: "app:config:read",
|
|
3918
3962
|
token: options.token,
|
|
3919
|
-
tokenFile: (0,
|
|
3963
|
+
tokenFile: (0, import_node_path10.join)(cfg.rootDir, ".odla", "admin-token.local.json"),
|
|
3920
3964
|
rootDir: cfg.rootDir,
|
|
3921
3965
|
email: options.email,
|
|
3922
3966
|
open: options.open,
|
|
@@ -4048,13 +4092,13 @@ function quoteArg2(value2) {
|
|
|
4048
4092
|
|
|
4049
4093
|
// src/doctor-checks.ts
|
|
4050
4094
|
var import_node_child_process3 = require("child_process");
|
|
4051
|
-
var
|
|
4052
|
-
var
|
|
4095
|
+
var import_node_fs13 = require("fs");
|
|
4096
|
+
var import_node_path12 = require("path");
|
|
4053
4097
|
|
|
4054
4098
|
// src/wrangler.ts
|
|
4055
4099
|
var import_node_child_process2 = require("child_process");
|
|
4056
|
-
var
|
|
4057
|
-
var
|
|
4100
|
+
var import_node_fs12 = require("fs");
|
|
4101
|
+
var import_node_path11 = require("path");
|
|
4058
4102
|
var defaultRunner = (cmd, args, opts) => new Promise((resolvePromise, reject) => {
|
|
4059
4103
|
const child = (0, import_node_child_process2.spawn)(cmd, args, { cwd: opts?.cwd, stdio: ["pipe", "pipe", "pipe"] });
|
|
4060
4104
|
let stdout = "";
|
|
@@ -4068,15 +4112,15 @@ var defaultRunner = (cmd, args, opts) => new Promise((resolvePromise, reject) =>
|
|
|
4068
4112
|
var WRANGLER_CONFIG_FILES = ["wrangler.jsonc", "wrangler.json", "wrangler.toml"];
|
|
4069
4113
|
function findWranglerConfig(rootDir) {
|
|
4070
4114
|
for (const name of WRANGLER_CONFIG_FILES) {
|
|
4071
|
-
const path = (0,
|
|
4072
|
-
if ((0,
|
|
4115
|
+
const path = (0, import_node_path11.join)(rootDir, name);
|
|
4116
|
+
if ((0, import_node_fs12.existsSync)(path)) return path;
|
|
4073
4117
|
}
|
|
4074
4118
|
return null;
|
|
4075
4119
|
}
|
|
4076
4120
|
function readWranglerConfig(path) {
|
|
4077
4121
|
if (path.endsWith(".toml")) return null;
|
|
4078
4122
|
try {
|
|
4079
|
-
return JSON.parse(stripJsonComments((0,
|
|
4123
|
+
return JSON.parse(stripJsonComments((0, import_node_fs12.readFileSync)(path, "utf8")));
|
|
4080
4124
|
} catch {
|
|
4081
4125
|
return null;
|
|
4082
4126
|
}
|
|
@@ -4226,10 +4270,10 @@ function wranglerWarnings(rootDir) {
|
|
|
4226
4270
|
for (const { label, block } of blocks) {
|
|
4227
4271
|
const assets = block.assets;
|
|
4228
4272
|
if (assets?.directory) {
|
|
4229
|
-
const dir = (0,
|
|
4230
|
-
if (dir === (0,
|
|
4273
|
+
const dir = (0, import_node_path12.resolve)(rootDir, assets.directory);
|
|
4274
|
+
if (dir === (0, import_node_path12.resolve)(rootDir)) {
|
|
4231
4275
|
warnings.push(`${label}assets.directory is the project root \u2014 point it at a dedicated build dir (wrangler dev fails with "spawn EBADF")`);
|
|
4232
|
-
} else if ((0,
|
|
4276
|
+
} else if ((0, import_node_fs13.existsSync)((0, import_node_path12.join)(dir, "node_modules"))) {
|
|
4233
4277
|
warnings.push(`${label}assets.directory contains node_modules \u2014 wrangler dev's watcher will exhaust file descriptors`);
|
|
4234
4278
|
}
|
|
4235
4279
|
}
|
|
@@ -4264,13 +4308,13 @@ function o11yProjectWarnings(rootDir) {
|
|
|
4264
4308
|
warnings.push("cannot verify o11y Worker instrumentation \u2014 add a parseable wrangler.jsonc/json config");
|
|
4265
4309
|
return warnings;
|
|
4266
4310
|
}
|
|
4267
|
-
const main = typeof config.main === "string" ? (0,
|
|
4268
|
-
if (!main || !(0,
|
|
4311
|
+
const main = typeof config.main === "string" ? (0, import_node_path12.resolve)(rootDir, config.main) : null;
|
|
4312
|
+
if (!main || !(0, import_node_fs13.existsSync)(main)) {
|
|
4269
4313
|
warnings.push("cannot verify o11y Worker instrumentation \u2014 wrangler main is missing or unreadable");
|
|
4270
4314
|
} else {
|
|
4271
4315
|
let source = "";
|
|
4272
4316
|
try {
|
|
4273
|
-
source = (0,
|
|
4317
|
+
source = (0, import_node_fs13.readFileSync)(main, "utf8");
|
|
4274
4318
|
} catch {
|
|
4275
4319
|
}
|
|
4276
4320
|
if (!/\bwithObservability\b/.test(source)) {
|
|
@@ -4294,7 +4338,7 @@ function calendarProjectWarnings(rootDir) {
|
|
|
4294
4338
|
}
|
|
4295
4339
|
function readPackageJson(rootDir) {
|
|
4296
4340
|
try {
|
|
4297
|
-
return JSON.parse((0,
|
|
4341
|
+
return JSON.parse((0, import_node_fs13.readFileSync)((0, import_node_path12.join)(rootDir, "package.json"), "utf8"));
|
|
4298
4342
|
} catch {
|
|
4299
4343
|
return null;
|
|
4300
4344
|
}
|
|
@@ -4588,14 +4632,14 @@ function harnessOption(value2, flag) {
|
|
|
4588
4632
|
}
|
|
4589
4633
|
|
|
4590
4634
|
// src/init.ts
|
|
4591
|
-
var
|
|
4592
|
-
var
|
|
4635
|
+
var import_node_fs14 = require("fs");
|
|
4636
|
+
var import_node_path13 = require("path");
|
|
4593
4637
|
var import_apps9 = require("@odla-ai/apps");
|
|
4594
4638
|
function initProject(options) {
|
|
4595
4639
|
const out = options.stdout ?? console;
|
|
4596
|
-
const rootDir = (0,
|
|
4597
|
-
const configPath = (0,
|
|
4598
|
-
if ((0,
|
|
4640
|
+
const rootDir = (0, import_node_path13.resolve)(options.rootDir ?? process.cwd());
|
|
4641
|
+
const configPath = (0, import_node_path13.resolve)(rootDir, options.configPath ?? "odla.config.mjs");
|
|
4642
|
+
if ((0, import_node_fs14.existsSync)(configPath) && !options.force) {
|
|
4599
4643
|
throw new Error(`${configPath} already exists. Pass --force to overwrite.`);
|
|
4600
4644
|
}
|
|
4601
4645
|
if (!/^[a-z0-9][a-z0-9-]*$/.test(options.appId)) {
|
|
@@ -4611,20 +4655,20 @@ function initProject(options) {
|
|
|
4611
4655
|
}
|
|
4612
4656
|
}
|
|
4613
4657
|
const aiProvider = options.aiProvider;
|
|
4614
|
-
(0,
|
|
4615
|
-
(0,
|
|
4616
|
-
(0,
|
|
4617
|
-
(0,
|
|
4618
|
-
writeIfMissing((0,
|
|
4619
|
-
writeIfMissing((0,
|
|
4658
|
+
(0, import_node_fs14.mkdirSync)((0, import_node_path13.dirname)(configPath), { recursive: true });
|
|
4659
|
+
(0, import_node_fs14.mkdirSync)((0, import_node_path13.resolve)(rootDir, "src/odla"), { recursive: true });
|
|
4660
|
+
(0, import_node_fs14.mkdirSync)((0, import_node_path13.resolve)(rootDir, ".odla"), { recursive: true });
|
|
4661
|
+
(0, import_node_fs14.writeFileSync)(configPath, configTemplate({ appId: options.appId, name: options.name, envs, services, aiProvider }));
|
|
4662
|
+
writeIfMissing((0, import_node_path13.resolve)(rootDir, "src/odla/schema.mjs"), schemaTemplate());
|
|
4663
|
+
writeIfMissing((0, import_node_path13.resolve)(rootDir, "src/odla/rules.mjs"), rulesTemplate());
|
|
4620
4664
|
ensureGitignore(rootDir);
|
|
4621
4665
|
out.log(`created ${relativeDisplay(configPath, rootDir)}`);
|
|
4622
4666
|
out.log("created src/odla/schema.mjs and src/odla/rules.mjs");
|
|
4623
4667
|
out.log("updated .gitignore for local odla credentials");
|
|
4624
4668
|
}
|
|
4625
4669
|
function writeIfMissing(path, text3) {
|
|
4626
|
-
if ((0,
|
|
4627
|
-
(0,
|
|
4670
|
+
if ((0, import_node_fs14.existsSync)(path)) return;
|
|
4671
|
+
(0, import_node_fs14.writeFileSync)(path, text3);
|
|
4628
4672
|
}
|
|
4629
4673
|
function configTemplate(input) {
|
|
4630
4674
|
const calendar = input.services.includes("calendar") ? ` calendar: {
|
|
@@ -4920,9 +4964,9 @@ function printReport(report5, out) {
|
|
|
4920
4964
|
}
|
|
4921
4965
|
|
|
4922
4966
|
// src/skill.ts
|
|
4923
|
-
var
|
|
4924
|
-
var
|
|
4925
|
-
var
|
|
4967
|
+
var import_node_fs15 = require("fs");
|
|
4968
|
+
var import_node_os3 = require("os");
|
|
4969
|
+
var import_node_path14 = require("path");
|
|
4926
4970
|
var import_node_url2 = require("url");
|
|
4927
4971
|
|
|
4928
4972
|
// src/skill-adapters.ts
|
|
@@ -5021,8 +5065,8 @@ function installSkill(options = {}) {
|
|
|
5021
5065
|
const files = listFiles(sourceDir);
|
|
5022
5066
|
if (files.length === 0) throw new Error(`no bundled skills found at ${sourceDir}`);
|
|
5023
5067
|
const harnesses = normalizeHarnesses(options.harnesses, options.global === true);
|
|
5024
|
-
const root = (0,
|
|
5025
|
-
const home = (0,
|
|
5068
|
+
const root = (0, import_node_path14.resolve)(options.dir ?? process.cwd());
|
|
5069
|
+
const home = (0, import_node_path14.resolve)(options.homeDir ?? (0, import_node_os3.homedir)());
|
|
5026
5070
|
const plans = /* @__PURE__ */ new Map();
|
|
5027
5071
|
const targets = /* @__PURE__ */ new Map();
|
|
5028
5072
|
const rememberTarget = (harness, target) => {
|
|
@@ -5036,48 +5080,48 @@ function installSkill(options = {}) {
|
|
|
5036
5080
|
plans.set(target, { target, content: content2, boundary, managedMerge });
|
|
5037
5081
|
};
|
|
5038
5082
|
const planSkillTree = (targetDir2, boundary = root) => {
|
|
5039
|
-
for (const rel of files) plan((0,
|
|
5083
|
+
for (const rel of files) plan((0, import_node_path14.join)(targetDir2, rel), (0, import_node_fs15.readFileSync)((0, import_node_path14.join)(sourceDir, rel), "utf8"), false, boundary);
|
|
5040
5084
|
};
|
|
5041
5085
|
let targetDir;
|
|
5042
5086
|
if (options.global) {
|
|
5043
|
-
const claudeRoot = (0,
|
|
5044
|
-
const codexRoot = (0,
|
|
5087
|
+
const claudeRoot = (0, import_node_path14.join)(home, ".claude", "skills");
|
|
5088
|
+
const codexRoot = (0, import_node_path14.resolve)(options.codexHomeDir ?? process.env.CODEX_HOME ?? (0, import_node_path14.join)(home, ".codex"), "skills");
|
|
5045
5089
|
targetDir = harnesses[0] === "codex" ? codexRoot : claudeRoot;
|
|
5046
5090
|
for (const harness of harnesses) {
|
|
5047
5091
|
const skillRoot = harness === "claude" ? claudeRoot : codexRoot;
|
|
5048
|
-
planSkillTree(skillRoot, harness === "claude" ? home : (0,
|
|
5092
|
+
planSkillTree(skillRoot, harness === "claude" ? home : (0, import_node_path14.dirname)((0, import_node_path14.dirname)(codexRoot)));
|
|
5049
5093
|
rememberTarget(harness, skillRoot);
|
|
5050
5094
|
}
|
|
5051
5095
|
} else {
|
|
5052
|
-
const sharedRoot = (0,
|
|
5096
|
+
const sharedRoot = (0, import_node_path14.join)(root, ".agents", "skills");
|
|
5053
5097
|
planSkillTree(sharedRoot);
|
|
5054
|
-
const claudeRoot = (0,
|
|
5098
|
+
const claudeRoot = (0, import_node_path14.join)(root, ".claude", "skills");
|
|
5055
5099
|
targetDir = harnesses.includes("claude") ? claudeRoot : sharedRoot;
|
|
5056
5100
|
for (const harness of harnesses) rememberTarget(harness, sharedRoot);
|
|
5057
5101
|
if (harnesses.includes("claude")) {
|
|
5058
5102
|
for (const skill of skillNames(files)) {
|
|
5059
|
-
const canonical2 = (0,
|
|
5060
|
-
plan((0,
|
|
5103
|
+
const canonical2 = (0, import_node_fs15.readFileSync)((0, import_node_path14.join)(sourceDir, skill, "SKILL.md"), "utf8");
|
|
5104
|
+
plan((0, import_node_path14.join)(claudeRoot, skill, "SKILL.md"), claudeAdapter(skill, canonical2));
|
|
5061
5105
|
}
|
|
5062
5106
|
rememberTarget("claude", claudeRoot);
|
|
5063
5107
|
}
|
|
5064
5108
|
if (harnesses.includes("cursor")) {
|
|
5065
|
-
const cursorRule = (0,
|
|
5109
|
+
const cursorRule = (0, import_node_path14.join)(root, ".cursor", "rules", "odla.mdc");
|
|
5066
5110
|
plan(cursorRule, CURSOR_RULE);
|
|
5067
5111
|
rememberTarget("cursor", cursorRule);
|
|
5068
5112
|
}
|
|
5069
5113
|
if (harnesses.includes("agents")) {
|
|
5070
|
-
const agentsFile = (0,
|
|
5114
|
+
const agentsFile = (0, import_node_path14.join)(root, "AGENTS.md");
|
|
5071
5115
|
plan(agentsFile, managedFileContent(agentsFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
|
|
5072
5116
|
rememberTarget("agents", agentsFile);
|
|
5073
5117
|
}
|
|
5074
5118
|
if (harnesses.includes("copilot")) {
|
|
5075
|
-
const copilotFile = (0,
|
|
5119
|
+
const copilotFile = (0, import_node_path14.join)(root, ".github", "copilot-instructions.md");
|
|
5076
5120
|
plan(copilotFile, managedFileContent(copilotFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
|
|
5077
5121
|
rememberTarget("copilot", copilotFile);
|
|
5078
5122
|
}
|
|
5079
5123
|
if (harnesses.includes("gemini")) {
|
|
5080
|
-
const geminiFile = (0,
|
|
5124
|
+
const geminiFile = (0, import_node_path14.join)(root, "GEMINI.md");
|
|
5081
5125
|
plan(geminiFile, managedFileContent(geminiFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
|
|
5082
5126
|
rememberTarget("gemini", geminiFile);
|
|
5083
5127
|
}
|
|
@@ -5091,11 +5135,11 @@ function installSkill(options = {}) {
|
|
|
5091
5135
|
conflicts.push(`${file.target} (redirected by symbolic link ${symlink})`);
|
|
5092
5136
|
continue;
|
|
5093
5137
|
}
|
|
5094
|
-
if (!(0,
|
|
5138
|
+
if (!(0, import_node_fs15.existsSync)(file.target)) {
|
|
5095
5139
|
writtenPaths.add(file.target);
|
|
5096
5140
|
continue;
|
|
5097
5141
|
}
|
|
5098
|
-
const current = (0,
|
|
5142
|
+
const current = (0, import_node_fs15.readFileSync)(file.target, "utf8");
|
|
5099
5143
|
if (current === file.content) {
|
|
5100
5144
|
unchangedPaths.add(file.target);
|
|
5101
5145
|
} else if (file.managedMerge || options.force) {
|
|
@@ -5112,9 +5156,9 @@ ${conflicts.map((f) => ` - ${f}`).join("\n")}`
|
|
|
5112
5156
|
);
|
|
5113
5157
|
}
|
|
5114
5158
|
for (const file of plans.values()) {
|
|
5115
|
-
if (!(0,
|
|
5116
|
-
(0,
|
|
5117
|
-
(0,
|
|
5159
|
+
if (!(0, import_node_fs15.existsSync)(file.target) || (0, import_node_fs15.readFileSync)(file.target, "utf8") !== file.content) {
|
|
5160
|
+
(0, import_node_fs15.mkdirSync)((0, import_node_path14.dirname)(file.target), { recursive: true });
|
|
5161
|
+
(0, import_node_fs15.writeFileSync)(file.target, file.content);
|
|
5118
5162
|
}
|
|
5119
5163
|
}
|
|
5120
5164
|
const skills = skillNames(files);
|
|
@@ -5133,7 +5177,7 @@ ${conflicts.map((f) => ` - ${f}`).join("\n")}`
|
|
|
5133
5177
|
};
|
|
5134
5178
|
}
|
|
5135
5179
|
function pathsUnder(root, paths) {
|
|
5136
|
-
return [...paths].map((path) => (0,
|
|
5180
|
+
return [...paths].map((path) => (0, import_node_path14.relative)(root, path)).filter((path) => path !== ".." && !path.startsWith(`..${import_node_path14.sep}`) && !(0, import_node_path14.isAbsolute)(path)).sort();
|
|
5137
5181
|
}
|
|
5138
5182
|
function normalizeHarnesses(values, global) {
|
|
5139
5183
|
const requested = values?.length ? values : ["claude"];
|
|
@@ -5155,9 +5199,9 @@ function normalizeHarnesses(values, global) {
|
|
|
5155
5199
|
function managedFileContent(path, block, force, boundary) {
|
|
5156
5200
|
const symlink = symlinkedComponent(boundary, path);
|
|
5157
5201
|
if (symlink) throw new Error(`refusing to manage ${path}: symbolic link component ${symlink}`);
|
|
5158
|
-
if (!(0,
|
|
5202
|
+
if (!(0, import_node_fs15.existsSync)(path)) return `${block}
|
|
5159
5203
|
`;
|
|
5160
|
-
const current = (0,
|
|
5204
|
+
const current = (0, import_node_fs15.readFileSync)(path, "utf8");
|
|
5161
5205
|
const start = "<!-- odla-ai agent setup:start -->";
|
|
5162
5206
|
const end = "<!-- odla-ai agent setup:end -->";
|
|
5163
5207
|
const startAt = current.indexOf(start);
|
|
@@ -5178,15 +5222,15 @@ function managedFileContent(path, block, force, boundary) {
|
|
|
5178
5222
|
return `${current.slice(0, startAt)}${block}${current.slice(afterEnd)}`;
|
|
5179
5223
|
}
|
|
5180
5224
|
function symlinkedComponent(boundary, target) {
|
|
5181
|
-
const rel = (0,
|
|
5182
|
-
if (rel === ".." || rel.startsWith(`..${
|
|
5225
|
+
const rel = (0, import_node_path14.relative)(boundary, target);
|
|
5226
|
+
if (rel === ".." || rel.startsWith(`..${import_node_path14.sep}`) || (0, import_node_path14.isAbsolute)(rel)) {
|
|
5183
5227
|
throw new Error(`agent setup target escapes its install root: ${target}`);
|
|
5184
5228
|
}
|
|
5185
5229
|
let current = boundary;
|
|
5186
|
-
for (const part of rel.split(
|
|
5187
|
-
current = (0,
|
|
5230
|
+
for (const part of rel.split(import_node_path14.sep).filter(Boolean)) {
|
|
5231
|
+
current = (0, import_node_path14.join)(current, part);
|
|
5188
5232
|
try {
|
|
5189
|
-
if ((0,
|
|
5233
|
+
if ((0, import_node_fs15.lstatSync)(current).isSymbolicLink()) return current;
|
|
5190
5234
|
} catch (error) {
|
|
5191
5235
|
if (error.code !== "ENOENT") throw error;
|
|
5192
5236
|
}
|
|
@@ -5197,13 +5241,13 @@ function skillNames(files) {
|
|
|
5197
5241
|
return [...new Set(files.filter((file) => /(^|[\\/])SKILL\.md$/.test(file)).map((file) => file.split(/[\\/]/)[0]))].sort();
|
|
5198
5242
|
}
|
|
5199
5243
|
function listFiles(dir) {
|
|
5200
|
-
if (!(0,
|
|
5244
|
+
if (!(0, import_node_fs15.existsSync)(dir)) return [];
|
|
5201
5245
|
const results = [];
|
|
5202
5246
|
const walk = (current) => {
|
|
5203
|
-
for (const entry of (0,
|
|
5204
|
-
const path = (0,
|
|
5247
|
+
for (const entry of (0, import_node_fs15.readdirSync)(current, { withFileTypes: true })) {
|
|
5248
|
+
const path = (0, import_node_path14.join)(current, entry.name);
|
|
5205
5249
|
if (entry.isDirectory()) walk(path);
|
|
5206
|
-
else results.push((0,
|
|
5250
|
+
else results.push((0, import_node_path14.relative)(dir, path));
|
|
5207
5251
|
}
|
|
5208
5252
|
};
|
|
5209
5253
|
walk(dir);
|
|
@@ -5558,9 +5602,9 @@ async function projectCommand(command, parsed, deps) {
|
|
|
5558
5602
|
}
|
|
5559
5603
|
|
|
5560
5604
|
// src/code-connect.ts
|
|
5561
|
-
var
|
|
5562
|
-
var
|
|
5563
|
-
var
|
|
5605
|
+
var import_node_fs16 = require("fs");
|
|
5606
|
+
var import_node_os4 = require("os");
|
|
5607
|
+
var import_node_path15 = require("path");
|
|
5564
5608
|
|
|
5565
5609
|
// ../harness/dist/chunk-3QP4VDQS.js
|
|
5566
5610
|
var HARNESS_PROTOCOL_VERSION = 1;
|
|
@@ -6669,8 +6713,8 @@ function rollup(graph, kind, options = {}) {
|
|
|
6669
6713
|
for (const node of nodesOfKind(graph, kind)) {
|
|
6670
6714
|
if (options.prefix && !node.name.startsWith(options.prefix)) continue;
|
|
6671
6715
|
const key = node.name.split(separator).slice(0, depth).join(separator);
|
|
6672
|
-
const
|
|
6673
|
-
if (
|
|
6716
|
+
const list3 = groups.get(key);
|
|
6717
|
+
if (list3) list3.push(node);
|
|
6674
6718
|
else groups.set(key, [node]);
|
|
6675
6719
|
}
|
|
6676
6720
|
return [...groups].map(([prefix, nodes]) => ({
|
|
@@ -6685,7 +6729,7 @@ function dirname8(path) {
|
|
|
6685
6729
|
const at = path.lastIndexOf("/");
|
|
6686
6730
|
return at <= 0 ? "." : path.slice(0, at);
|
|
6687
6731
|
}
|
|
6688
|
-
function
|
|
6732
|
+
function join12(base, specifier) {
|
|
6689
6733
|
const parts = [];
|
|
6690
6734
|
const segments = `${base === "." ? "" : `${base}/`}${specifier}`.split("/");
|
|
6691
6735
|
for (const segment of segments) {
|
|
@@ -6709,7 +6753,7 @@ var BARE_IMPORT = /^\s*import\s*["']([^"']+)["']/gm;
|
|
|
6709
6753
|
var isSourcePath = (path) => SOURCE.test(path);
|
|
6710
6754
|
function resolveImport(fromPath, specifier, known) {
|
|
6711
6755
|
if (!specifier.startsWith(".")) return null;
|
|
6712
|
-
const base =
|
|
6756
|
+
const base = join12(dirname8(fromPath), specifier);
|
|
6713
6757
|
const candidates = [
|
|
6714
6758
|
base,
|
|
6715
6759
|
base.replace(/\.js$/, ".ts"),
|
|
@@ -9607,8 +9651,8 @@ var CODE_BUILD_RECIPES = Object.freeze([{
|
|
|
9607
9651
|
// src/code-connect.ts
|
|
9608
9652
|
async function codeConnect(options) {
|
|
9609
9653
|
const cwd = options.cwd ?? process.cwd();
|
|
9610
|
-
const configPath = (0,
|
|
9611
|
-
const cfg = (0,
|
|
9654
|
+
const configPath = (0, import_node_path15.resolve)(cwd, options.configPath);
|
|
9655
|
+
const cfg = (0, import_node_fs16.existsSync)(configPath) ? await loadProjectConfig(configPath) : null;
|
|
9612
9656
|
const requestedAppId = options.appId?.trim();
|
|
9613
9657
|
if (requestedAppId && !/^[a-z0-9][a-z0-9-]{1,62}$/.test(requestedAppId)) {
|
|
9614
9658
|
throw new Error("--app-id must be a valid odla app id");
|
|
@@ -9637,7 +9681,7 @@ async function codeConnect(options) {
|
|
|
9637
9681
|
const doFetch = options.fetch ?? fetch;
|
|
9638
9682
|
const engine = await (options.selectEngine ?? selectContainerEngine)(options.engine ?? "auto");
|
|
9639
9683
|
const hostPlatform = process.platform === "darwin" ? "macos" : "linux";
|
|
9640
|
-
const hostName = (options.name ?? (0,
|
|
9684
|
+
const hostName = (options.name ?? (0, import_node_os4.hostname)()).trim();
|
|
9641
9685
|
if (!hostName || hostName.length > 120) throw new Error("--name must contain 1 to 120 characters");
|
|
9642
9686
|
const repository = await inferGitHubRepository(cwd, options.readGitOrigin);
|
|
9643
9687
|
const localSource = await (options.prepareLocalSource ?? prepareCodeLocalSource)(
|
|
@@ -9674,8 +9718,8 @@ async function codeConnect(options) {
|
|
|
9674
9718
|
platform: hostPlatform,
|
|
9675
9719
|
arch: process.arch,
|
|
9676
9720
|
engines: [engine],
|
|
9677
|
-
cpuCount: (0,
|
|
9678
|
-
memoryBytes: (0,
|
|
9721
|
+
cpuCount: (0, import_node_os4.cpus)().length,
|
|
9722
|
+
memoryBytes: (0, import_node_os4.totalmem)(),
|
|
9679
9723
|
source: descriptor2,
|
|
9680
9724
|
images: {
|
|
9681
9725
|
ready: true,
|
|
@@ -10042,13 +10086,13 @@ async function codeCommand(parsed, dependencies) {
|
|
|
10042
10086
|
}
|
|
10043
10087
|
|
|
10044
10088
|
// src/operator-credentials.ts
|
|
10045
|
-
var
|
|
10089
|
+
var import_node_process12 = __toESM(require("process"), 1);
|
|
10046
10090
|
function developerTokenStatus(context, parsed, now = Date.now()) {
|
|
10047
10091
|
const cached = readJsonFile(context.cfg.local.tokenFile);
|
|
10048
10092
|
const cacheStatus = !cached?.token ? "missing" : cached.platform !== context.platform.value ? "other-platform" : (cached.expiresAt ?? 0) <= now + 6e4 ? "expired" : "valid";
|
|
10049
10093
|
const source = clean3(
|
|
10050
10094
|
stringOpt(parsed.options.token)
|
|
10051
|
-
) ? "flag" : clean3(
|
|
10095
|
+
) ? "flag" : clean3(import_node_process12.default.env.ODLA_DEV_TOKEN) ? "environment" : cacheStatus === "valid" ? "cache" : "missing";
|
|
10052
10096
|
return {
|
|
10053
10097
|
source,
|
|
10054
10098
|
cacheFile: context.cfg.local.tokenFile,
|
|
@@ -10370,6 +10414,9 @@ Usage:
|
|
|
10370
10414
|
odla-ai security run [target] --ack-redacted-source [--env dev] [--profile odla] [--fail-on high]
|
|
10371
10415
|
odla-ai security run [target] --self --ack-redacted-source
|
|
10372
10416
|
odla-ai provision [--live] [--config odla.config.mjs] [--email <odla-account>] [--request-grant] [--no-open] [--wait <seconds>] [--dry-run] [--push-secrets] [--rotate-o11y-token] [--write-dev-vars[=path]] [--yes]
|
|
10417
|
+
odla-ai device enroll [--app <id>[,<id>...]] [--name <label>] [--capability <c>[,<c>...]] [--email <odla-account>] [--no-open] [--json]
|
|
10418
|
+
odla-ai device list [--email <odla-account>] [--json]
|
|
10419
|
+
odla-ai device revoke <device-id> [--email <odla-account>] [--json]
|
|
10373
10420
|
odla-ai credentials list [--config odla.config.mjs] [--env dev] [--all] [--json]
|
|
10374
10421
|
odla-ai credentials revoke <receipt-id> [--config odla.config.mjs] [--json]
|
|
10375
10422
|
odla-ai smoke [--config odla.config.mjs] [--env dev] [--runtime] [--email <odla-account>] [--no-open]
|
|
@@ -10475,6 +10522,10 @@ Commands:
|
|
|
10475
10522
|
stable status, incident, and report JSON to agents and CI.
|
|
10476
10523
|
platform Read canonical fleet health, releases, provider load/freshness,
|
|
10477
10524
|
explicit unknowns, and next actions through a read-only grant.
|
|
10525
|
+
device Enrol THIS machine once, then stop asking. A human approves the
|
|
10526
|
+
enrollment in the browser; from then on this terminal mints its
|
|
10527
|
+
own short-lived credentials for the named projects with nobody's
|
|
10528
|
+
attention, until the device expires or is revoked.
|
|
10478
10529
|
provision Register services, compose integrations, persist credentials, optionally push secrets.
|
|
10479
10530
|
"provision --live --yes" initializes only the live instance of
|
|
10480
10531
|
an existing sandbox app and enables every configured service;
|
|
@@ -11725,8 +11776,8 @@ async function pmWatch(ctx, parsed) {
|
|
|
11725
11776
|
}
|
|
11726
11777
|
|
|
11727
11778
|
// src/pm-project-context.ts
|
|
11728
|
-
var
|
|
11729
|
-
var pmProjectContextFile = (rootDir) => (0,
|
|
11779
|
+
var import_node_path16 = require("path");
|
|
11780
|
+
var pmProjectContextFile = (rootDir) => (0, import_node_path16.resolve)(rootDir, ".odla", "pm-project.local.json");
|
|
11730
11781
|
function readPmProjectContext(rootDir) {
|
|
11731
11782
|
const value2 = readJsonFile(pmProjectContextFile(rootDir));
|
|
11732
11783
|
return value2 && typeof value2.appId === "string" && typeof value2.projectId === "string" ? value2 : null;
|
|
@@ -12757,7 +12808,7 @@ function percent(value2) {
|
|
|
12757
12808
|
// src/provision.ts
|
|
12758
12809
|
var import_apps13 = require("@odla-ai/apps");
|
|
12759
12810
|
var import_ai5 = require("@odla-ai/ai");
|
|
12760
|
-
var
|
|
12811
|
+
var import_node_process13 = __toESM(require("process"), 1);
|
|
12761
12812
|
|
|
12762
12813
|
// src/integration-provision.ts
|
|
12763
12814
|
var import_db3 = require("@odla-ai/db");
|
|
@@ -13193,7 +13244,7 @@ async function provision(options) {
|
|
|
13193
13244
|
await provisionIntegrationSeeds(doFetch, cfg.dbEndpoint, tenantId, dbKey, database.integrations, env, out);
|
|
13194
13245
|
}
|
|
13195
13246
|
if (cfg.services.includes("ai") && cfg.ai?.provider && cfg.ai.keyEnv) {
|
|
13196
|
-
const key =
|
|
13247
|
+
const key = import_node_process13.default.env[cfg.ai.keyEnv];
|
|
13197
13248
|
if (key) {
|
|
13198
13249
|
const secretName = cfg.ai.secretName ?? defaultSecretName(cfg.ai.provider);
|
|
13199
13250
|
await (0, import_ai5.putSecret)({ endpoint: cfg.dbEndpoint, token, fetch: doFetch }, tenantId, secretName, key);
|
|
@@ -13234,8 +13285,8 @@ async function provision(options) {
|
|
|
13234
13285
|
}
|
|
13235
13286
|
|
|
13236
13287
|
// src/record.ts
|
|
13237
|
-
var
|
|
13238
|
-
var
|
|
13288
|
+
var import_node_fs17 = require("fs");
|
|
13289
|
+
var import_node_process14 = __toESM(require("process"), 1);
|
|
13239
13290
|
|
|
13240
13291
|
// src/surface.ts
|
|
13241
13292
|
var PM_ACTIONS = {
|
|
@@ -13305,6 +13356,7 @@ var COMMAND_SURFACE = {
|
|
|
13305
13356
|
config: { diff: {}, plan: {}, apply: {} },
|
|
13306
13357
|
context: { show: {}, list: {}, save: {}, remove: {} },
|
|
13307
13358
|
credentials: { list: {}, revoke: {} },
|
|
13359
|
+
device: { enroll: {}, list: {}, revoke: {} },
|
|
13308
13360
|
// `watch`, `read`, `reply`, and `resolve` take a topic id from there on.
|
|
13309
13361
|
discuss: {
|
|
13310
13362
|
groups: {},
|
|
@@ -13416,7 +13468,7 @@ function surfacePaths(node = COMMAND_SURFACE, prefix = []) {
|
|
|
13416
13468
|
|
|
13417
13469
|
// src/record.ts
|
|
13418
13470
|
function recordInvocation(parsed) {
|
|
13419
|
-
const file =
|
|
13471
|
+
const file = import_node_process14.default.env.ODLA_CLI_RECORD;
|
|
13420
13472
|
if (!file) return;
|
|
13421
13473
|
try {
|
|
13422
13474
|
const entry = {
|
|
@@ -13424,7 +13476,7 @@ function recordInvocation(parsed) {
|
|
|
13424
13476
|
options: Object.entries(parsed.options).map(([name, value2]) => value2 === false ? `no-${name}` : name).sort()
|
|
13425
13477
|
};
|
|
13426
13478
|
if (!entry.path.length) return;
|
|
13427
|
-
(0,
|
|
13479
|
+
(0, import_node_fs17.appendFileSync)(file, `${JSON.stringify(entry)}
|
|
13428
13480
|
`);
|
|
13429
13481
|
} catch {
|
|
13430
13482
|
}
|
|
@@ -13453,8 +13505,109 @@ function renderAdvisories(out, advisories, env = process.env) {
|
|
|
13453
13505
|
}
|
|
13454
13506
|
}
|
|
13455
13507
|
|
|
13508
|
+
// src/device-command.ts
|
|
13509
|
+
var import_node_fs18 = require("fs");
|
|
13510
|
+
var import_node_path17 = require("path");
|
|
13511
|
+
var import_node_process15 = __toESM(require("process"), 1);
|
|
13512
|
+
async function deviceCommand(parsed, deps) {
|
|
13513
|
+
const action2 = parsed.positionals[1] ?? "";
|
|
13514
|
+
const out = deps.stdout ?? console;
|
|
13515
|
+
const doFetch = deps.fetch ?? fetch;
|
|
13516
|
+
const cfg = await loadProjectConfig(stringOpt(parsed.options.config));
|
|
13517
|
+
const json = parsed.options.json === true;
|
|
13518
|
+
if (action2 === "enroll") return enroll(parsed, deps, cfg, doFetch, out, json);
|
|
13519
|
+
if (action2 === "list") return list2(parsed, deps, cfg, doFetch, out, json);
|
|
13520
|
+
if (action2 === "revoke") return revoke(parsed, deps, cfg, doFetch, out, json);
|
|
13521
|
+
throw new Error('odla-ai device expects "enroll", "list", or "revoke"');
|
|
13522
|
+
}
|
|
13523
|
+
async function enroll(parsed, deps, cfg, doFetch, out, json) {
|
|
13524
|
+
const name = stringOpt(parsed.options.name) ?? defaultDeviceName();
|
|
13525
|
+
const apps = (stringOpt(parsed.options.app) ?? cfg.app.id).split(",").map((id2) => id2.trim()).filter(Boolean);
|
|
13526
|
+
if (apps.length === 0) throw new Error("device enroll needs --app <id>[,<id>\u2026]");
|
|
13527
|
+
const token = await scopedToken2(parsed, deps, cfg, doFetch, out, `odla CLI (enroll ${name})`);
|
|
13528
|
+
const response2 = await doFetch(`${cfg.platformUrl}/registry/devices`, {
|
|
13529
|
+
method: "POST",
|
|
13530
|
+
headers: { authorization: `Bearer ${token}`, "content-type": "application/json" },
|
|
13531
|
+
body: JSON.stringify({
|
|
13532
|
+
name,
|
|
13533
|
+
platform: import_node_process15.default.platform,
|
|
13534
|
+
appIds: apps,
|
|
13535
|
+
...parsed.options.capability ? { capabilities: String(parsed.options.capability).split(",").map((c) => c.trim()).filter(Boolean) } : {}
|
|
13536
|
+
})
|
|
13537
|
+
});
|
|
13538
|
+
const body = await response2.json().catch(() => ({}));
|
|
13539
|
+
if (!response2.ok || !body.token || !body.device) {
|
|
13540
|
+
throw new Error(`device enroll failed: ${body.error?.message ?? `registry returned ${response2.status}`} (${response2.status})`);
|
|
13541
|
+
}
|
|
13542
|
+
const path = deviceCredentialPath();
|
|
13543
|
+
(0, import_node_fs18.mkdirSync)((0, import_node_path17.dirname)(path), { recursive: true });
|
|
13544
|
+
(0, import_node_fs18.writeFileSync)(path, JSON.stringify({
|
|
13545
|
+
token: body.token,
|
|
13546
|
+
platform: cfg.platformUrl.replace(/\/$/, ""),
|
|
13547
|
+
deviceId: body.device.deviceId,
|
|
13548
|
+
name
|
|
13549
|
+
}, null, 2));
|
|
13550
|
+
(0, import_node_fs18.chmodSync)(path, 384);
|
|
13551
|
+
out.error(`device: enrolled "${name}" for ${body.device.appIds.join(", ")}; credential written to ${path}`);
|
|
13552
|
+
out.error("device: this terminal will mint its own credentials from now on \u2014 no further approvals.");
|
|
13553
|
+
if (json) {
|
|
13554
|
+
out.log(JSON.stringify({ deviceId: body.device.deviceId, name, appIds: body.device.appIds, expiresAt: body.device.expiresAt }, null, 2));
|
|
13555
|
+
}
|
|
13556
|
+
}
|
|
13557
|
+
async function list2(parsed, deps, cfg, doFetch, out, json) {
|
|
13558
|
+
const token = await scopedToken2(parsed, deps, cfg, doFetch, out, "odla CLI (device list)");
|
|
13559
|
+
const response2 = await doFetch(`${cfg.platformUrl}/registry/devices`, {
|
|
13560
|
+
headers: { authorization: `Bearer ${token}` }
|
|
13561
|
+
});
|
|
13562
|
+
const body = await response2.json().catch(() => ({}));
|
|
13563
|
+
if (!response2.ok || !body.devices) {
|
|
13564
|
+
throw new Error(`device list failed: ${body.error?.message ?? `registry returned ${response2.status}`} (${response2.status})`);
|
|
13565
|
+
}
|
|
13566
|
+
if (json) return out.log(JSON.stringify(body.devices, null, 2));
|
|
13567
|
+
if (body.devices.length === 0) return out.log("no enrolled devices");
|
|
13568
|
+
for (const device of body.devices) {
|
|
13569
|
+
const state2 = device.revokedAt ? "revoked" : device.expiresAt <= Date.now() ? "expired" : "active";
|
|
13570
|
+
out.log(`${device.deviceId} ${state2.padEnd(7)} ${device.name} [${device.appIds.join(", ")}]`);
|
|
13571
|
+
}
|
|
13572
|
+
}
|
|
13573
|
+
async function revoke(parsed, deps, cfg, doFetch, out, json) {
|
|
13574
|
+
const deviceId = parsed.positionals[2];
|
|
13575
|
+
if (!deviceId) throw new Error("device revoke needs the device id from `odla-ai device list`");
|
|
13576
|
+
const token = await scopedToken2(parsed, deps, cfg, doFetch, out, "odla CLI (device revoke)");
|
|
13577
|
+
const response2 = await doFetch(`${cfg.platformUrl}/registry/devices/${encodeURIComponent(deviceId)}/revoke`, {
|
|
13578
|
+
method: "POST",
|
|
13579
|
+
headers: { authorization: `Bearer ${token}` }
|
|
13580
|
+
});
|
|
13581
|
+
if (!response2.ok) {
|
|
13582
|
+
const body = await response2.json().catch(() => ({}));
|
|
13583
|
+
throw new Error(`device revoke failed: ${body.error?.message ?? `registry returned ${response2.status}`} (${response2.status})`);
|
|
13584
|
+
}
|
|
13585
|
+
out.error(`device: revoked ${deviceId}; every credential it minted is revoked with it`);
|
|
13586
|
+
if (json) out.log(JSON.stringify({ deviceId, revoked: true }, null, 2));
|
|
13587
|
+
}
|
|
13588
|
+
async function scopedToken2(parsed, deps, cfg, doFetch, out, label) {
|
|
13589
|
+
const { credentials } = await resolveOperatorContext(parsed, { allowMissingConfig: true });
|
|
13590
|
+
const scopedTokenFile = credentials.scopedTokenFile;
|
|
13591
|
+
return getScopedPlatformToken({
|
|
13592
|
+
platform: cfg.platformUrl,
|
|
13593
|
+
scope: "app:device:enroll",
|
|
13594
|
+
email: stringOpt(parsed.options.email),
|
|
13595
|
+
label,
|
|
13596
|
+
fetch: doFetch,
|
|
13597
|
+
stdout: out,
|
|
13598
|
+
open: parsed.options.open === false ? false : parsed.options.open === true ? true : void 0,
|
|
13599
|
+
openApprovalUrl: deps.openUrl,
|
|
13600
|
+
rootDir: cfg.rootDir,
|
|
13601
|
+
tokenFile: scopedTokenFile,
|
|
13602
|
+
...stringOpt(parsed.options.token) ? { token: stringOpt(parsed.options.token) } : {}
|
|
13603
|
+
});
|
|
13604
|
+
}
|
|
13605
|
+
function defaultDeviceName() {
|
|
13606
|
+
return `${import_node_process15.default.env.HOSTNAME ?? import_node_process15.default.env.HOST ?? "machine"}-${import_node_process15.default.platform}`;
|
|
13607
|
+
}
|
|
13608
|
+
|
|
13456
13609
|
// src/runbook-actions.ts
|
|
13457
|
-
var
|
|
13610
|
+
var import_node_fs19 = require("fs");
|
|
13458
13611
|
|
|
13459
13612
|
// src/runbook-requires.ts
|
|
13460
13613
|
var SPEC = /^(@?[\w./-]+?)@(\d+\.\d+\.\d+(?:[\w.-]*)?)$/;
|
|
@@ -13550,7 +13703,7 @@ async function bySlug(ctx, slug) {
|
|
|
13550
13703
|
function readBody(file, inline) {
|
|
13551
13704
|
if (inline !== void 0) return inline;
|
|
13552
13705
|
if (file === void 0) throw new Error("supply the new text with --file <path>, --file - (stdin), or --body");
|
|
13553
|
-
return (0,
|
|
13706
|
+
return (0, import_node_fs19.readFileSync)(file === "-" ? 0 : file, "utf8");
|
|
13554
13707
|
}
|
|
13555
13708
|
var stamp = (ms) => ms ? new Date(ms).toISOString().slice(0, 16).replace("T", " ") : "";
|
|
13556
13709
|
async function runbookList(ctx, all, query) {
|
|
@@ -13642,8 +13795,8 @@ async function runbookRemove(ctx, slug) {
|
|
|
13642
13795
|
}
|
|
13643
13796
|
|
|
13644
13797
|
// src/runbook-import.ts
|
|
13645
|
-
var
|
|
13646
|
-
var
|
|
13798
|
+
var import_node_fs20 = require("fs");
|
|
13799
|
+
var import_node_path18 = require("path");
|
|
13647
13800
|
function parseRunbook(text3, slug) {
|
|
13648
13801
|
let rest = text3;
|
|
13649
13802
|
const meta = {};
|
|
@@ -13668,12 +13821,12 @@ function parseRunbook(text3, slug) {
|
|
|
13668
13821
|
};
|
|
13669
13822
|
}
|
|
13670
13823
|
function readRunbookDir(dir) {
|
|
13671
|
-
if (!(0,
|
|
13672
|
-
const files = (0,
|
|
13824
|
+
if (!(0, import_node_fs20.statSync)(dir, { throwIfNoEntry: false })?.isDirectory()) throw new Error(`not a directory: ${dir}`);
|
|
13825
|
+
const files = (0, import_node_fs20.readdirSync)(dir).filter((f) => f.endsWith(".md")).sort();
|
|
13673
13826
|
if (!files.length) throw new Error(`no .md files in ${dir}`);
|
|
13674
13827
|
return files.map((file) => {
|
|
13675
|
-
const slug = (0,
|
|
13676
|
-
const parsed = parseRunbook((0,
|
|
13828
|
+
const slug = (0, import_node_path18.basename)(file, ".md");
|
|
13829
|
+
const parsed = parseRunbook((0, import_node_fs20.readFileSync)((0, import_node_path18.join)(dir, file), "utf8"), slug);
|
|
13677
13830
|
return { file, slug, ...parsed, words: parsed.body.split(/\s+/).filter(Boolean).length };
|
|
13678
13831
|
});
|
|
13679
13832
|
}
|
|
@@ -13746,8 +13899,8 @@ async function upsert(ctx, r, visibility) {
|
|
|
13746
13899
|
|
|
13747
13900
|
// src/runbook-impact.ts
|
|
13748
13901
|
var import_node_child_process6 = require("child_process");
|
|
13749
|
-
var
|
|
13750
|
-
var
|
|
13902
|
+
var import_node_fs21 = require("fs");
|
|
13903
|
+
var import_node_path19 = require("path");
|
|
13751
13904
|
|
|
13752
13905
|
// src/runbook-impact-scan.ts
|
|
13753
13906
|
var DECL = /^[+-]\s*export\s+(?:declare\s+)?(?:default\s+)?(?:abstract\s+)?(?:async\s+)?(?:const|let|var|function|class|interface|type|enum)\s+([A-Za-z_$][\w$]*)/;
|
|
@@ -13916,10 +14069,10 @@ ${body.split("\n").map((line2) => `+${line2}`).join("\n")}
|
|
|
13916
14069
|
}
|
|
13917
14070
|
function manifestLabeller(root) {
|
|
13918
14071
|
return (workspace) => {
|
|
13919
|
-
const manifest = (0,
|
|
13920
|
-
if (!(0,
|
|
14072
|
+
const manifest = (0, import_node_path19.join)(root, workspace, "package.json");
|
|
14073
|
+
if (!(0, import_node_fs21.existsSync)(manifest)) return void 0;
|
|
13921
14074
|
try {
|
|
13922
|
-
const name = JSON.parse((0,
|
|
14075
|
+
const name = JSON.parse((0, import_node_fs21.readFileSync)(manifest, "utf8")).name;
|
|
13923
14076
|
return typeof name === "string" ? name : void 0;
|
|
13924
14077
|
} catch {
|
|
13925
14078
|
return void 0;
|
|
@@ -13986,7 +14139,7 @@ function report4(ctx, impacts) {
|
|
|
13986
14139
|
async function runbookImpact(ctx, options, deps = {}) {
|
|
13987
14140
|
const cwd = deps.cwd ?? process.cwd();
|
|
13988
14141
|
const runGit = deps.runGit ?? gitRunner(cwd);
|
|
13989
|
-
const read3 = deps.readRepoFile ?? ((path) => (0,
|
|
14142
|
+
const read3 = deps.readRepoFile ?? ((path) => (0, import_node_fs21.readFileSync)((0, import_node_path19.join)(cwd, path), "utf8"));
|
|
13990
14143
|
const surfaces = changedSurfaces(collectDiff(runGit, options.base, read3), manifestLabeller(cwd));
|
|
13991
14144
|
if (!surfaces.length) {
|
|
13992
14145
|
return ctx.out.log(
|
|
@@ -14119,12 +14272,12 @@ async function runbookComment(ctx, slug, body) {
|
|
|
14119
14272
|
|
|
14120
14273
|
// src/runbook-editor.ts
|
|
14121
14274
|
var import_node_child_process7 = require("child_process");
|
|
14122
|
-
var
|
|
14123
|
-
var
|
|
14124
|
-
var
|
|
14125
|
-
var
|
|
14275
|
+
var import_node_fs22 = require("fs");
|
|
14276
|
+
var import_node_os5 = require("os");
|
|
14277
|
+
var import_node_path20 = require("path");
|
|
14278
|
+
var import_node_process16 = __toESM(require("process"), 1);
|
|
14126
14279
|
var EDITOR_ENV = ["ODLA_EDITOR", "VISUAL", "EDITOR"];
|
|
14127
|
-
function resolveEditor(env =
|
|
14280
|
+
function resolveEditor(env = import_node_process16.default.env) {
|
|
14128
14281
|
for (const name of EDITOR_ENV) {
|
|
14129
14282
|
const value2 = env[name];
|
|
14130
14283
|
if (value2 && value2.trim()) return value2.trim();
|
|
@@ -14138,8 +14291,8 @@ function defaultRun(command, path) {
|
|
|
14138
14291
|
return result.status ?? 0;
|
|
14139
14292
|
}
|
|
14140
14293
|
function editText(initial, slug, deps = {}) {
|
|
14141
|
-
const env = deps.env ??
|
|
14142
|
-
const interactive = deps.interactive ?? (() => Boolean(
|
|
14294
|
+
const env = deps.env ?? import_node_process16.default.env;
|
|
14295
|
+
const interactive = deps.interactive ?? (() => Boolean(import_node_process16.default.stdin.isTTY));
|
|
14143
14296
|
const editor = resolveEditor(env);
|
|
14144
14297
|
if (!editor)
|
|
14145
14298
|
throw new Error(
|
|
@@ -14147,16 +14300,16 @@ function editText(initial, slug, deps = {}) {
|
|
|
14147
14300
|
);
|
|
14148
14301
|
if (!interactive())
|
|
14149
14302
|
throw new Error(`cannot open an editor without a terminal \u2014 pass --file <path> or --body "\u2026" instead`);
|
|
14150
|
-
const dir = (0,
|
|
14151
|
-
const file = (0,
|
|
14303
|
+
const dir = (0, import_node_fs22.mkdtempSync)((0, import_node_path20.join)((0, import_node_os5.tmpdir)(), "odla-runbook-"));
|
|
14304
|
+
const file = (0, import_node_path20.join)(dir, `${slug}.md`);
|
|
14152
14305
|
try {
|
|
14153
|
-
(0,
|
|
14306
|
+
(0, import_node_fs22.writeFileSync)(file, initial, { mode: 384 });
|
|
14154
14307
|
const code = defaultRunOrInjected(deps)(editor, file);
|
|
14155
14308
|
if (code !== 0) throw new Error(`editor "${editor}" exited with ${code}; nothing was written`);
|
|
14156
|
-
const edited = (0,
|
|
14309
|
+
const edited = (0, import_node_fs22.readFileSync)(file, "utf8");
|
|
14157
14310
|
return edited === initial ? null : edited;
|
|
14158
14311
|
} finally {
|
|
14159
|
-
(0,
|
|
14312
|
+
(0, import_node_fs22.rmSync)(dir, { recursive: true, force: true });
|
|
14160
14313
|
}
|
|
14161
14314
|
}
|
|
14162
14315
|
var defaultRunOrInjected = (deps) => deps.run ?? defaultRun;
|
|
@@ -14500,7 +14653,7 @@ function hostedSeverity(value2, flag) {
|
|
|
14500
14653
|
var import_security2 = require("@odla-ai/security");
|
|
14501
14654
|
|
|
14502
14655
|
// src/security.ts
|
|
14503
|
-
var
|
|
14656
|
+
var import_node_path21 = require("path");
|
|
14504
14657
|
var import_security = require("@odla-ai/security");
|
|
14505
14658
|
var import_node3 = require("@odla-ai/security/node");
|
|
14506
14659
|
async function runHostedSecurity(options) {
|
|
@@ -14512,9 +14665,9 @@ async function runHostedSecurity(options) {
|
|
|
14512
14665
|
const appId = selfAudit ? "odla-ai" : cfg.app.id;
|
|
14513
14666
|
const env = selfAudit ? "prod" : selectEnv(options.env, cfg.envs, cfg.configPath, cfg.rootDir);
|
|
14514
14667
|
const platform = options.platform ?? cfg?.platformUrl ?? "https://odla.ai";
|
|
14515
|
-
const target = (0,
|
|
14516
|
-
const output = (0,
|
|
14517
|
-
const outputRelative = (0,
|
|
14668
|
+
const target = (0, import_node_path21.resolve)(options.target ?? cfg?.rootDir ?? ".");
|
|
14669
|
+
const output = (0, import_node_path21.resolve)(options.out ?? (0, import_node_path21.resolve)(target, ".odla/security/hosted"));
|
|
14670
|
+
const outputRelative = (0, import_node_path21.relative)(target, output).split(import_node_path21.sep).join("/");
|
|
14518
14671
|
if (!outputRelative) throw new Error("Hosted security output cannot be the repository root");
|
|
14519
14672
|
const profile = profileFor(options.profile ?? "odla", options.maxHuntTasks ?? 12);
|
|
14520
14673
|
const tokenRequest = {
|
|
@@ -14526,7 +14679,7 @@ async function runHostedSecurity(options) {
|
|
|
14526
14679
|
};
|
|
14527
14680
|
const token = await injectedToken(options, tokenRequest);
|
|
14528
14681
|
const snapshot = await (0, import_node3.snapshotDirectory)(target, {
|
|
14529
|
-
exclude: !outputRelative.startsWith("../") && !(0,
|
|
14682
|
+
exclude: !outputRelative.startsWith("../") && !(0, import_node_path21.isAbsolute)(outputRelative) ? [outputRelative] : []
|
|
14530
14683
|
});
|
|
14531
14684
|
const hosted = await (0, import_security.createPlatformSecurityReasoners)({
|
|
14532
14685
|
platform,
|
|
@@ -14544,7 +14697,7 @@ async function runHostedSecurity(options) {
|
|
|
14544
14697
|
});
|
|
14545
14698
|
const harness = (0, import_security.createSecurityHarness)({
|
|
14546
14699
|
profile,
|
|
14547
|
-
store: new import_node3.FileRunStore((0,
|
|
14700
|
+
store: new import_node3.FileRunStore((0, import_node_path21.resolve)(output, "state")),
|
|
14548
14701
|
discoveryReasoner: hosted.discoveryReasoner,
|
|
14549
14702
|
validationReasoner: hosted.validationReasoner,
|
|
14550
14703
|
policy: {
|
|
@@ -14568,7 +14721,7 @@ async function runHostedSecurity(options) {
|
|
|
14568
14721
|
function selectEnv(requested, declared, configPath, rootDir) {
|
|
14569
14722
|
const env = requested ?? (declared.includes("dev") ? "dev" : declared[0]);
|
|
14570
14723
|
if (!env || !declared.includes(env)) {
|
|
14571
|
-
const shown = (0,
|
|
14724
|
+
const shown = (0, import_node_path21.relative)(rootDir, configPath) || configPath;
|
|
14572
14725
|
throw new Error(`env "${env ?? ""}" is not declared in ${shown}`);
|
|
14573
14726
|
}
|
|
14574
14727
|
return env;
|
|
@@ -14597,7 +14750,7 @@ function printSummary(out, appId, env, run, report5, output) {
|
|
|
14597
14750
|
out.log(` coverage: ${report5.coverageStatus} ${complete}/${report5.coverage.length} blocked=${report5.metrics.blockedCells} shallow=${report5.metrics.shallowCells} unscheduled=${report5.metrics.unscheduledCells} budget_exhausted=${report5.metrics.budgetExhaustedCells}`);
|
|
14598
14751
|
if (report5.callBudget) out.log(` calls: discovery=${formatBudget(report5.callBudget.discovery)} validation=${formatBudget(report5.callBudget.validation)}`);
|
|
14599
14752
|
out.log(` findings: confirmed=${report5.metrics.confirmed} needs_reproduction=${report5.metrics.needsReproduction} candidates=${report5.metrics.candidates}`);
|
|
14600
|
-
out.log(` report: ${(0,
|
|
14753
|
+
out.log(` report: ${(0, import_node_path21.resolve)(output, "REPORT.md")}`);
|
|
14601
14754
|
}
|
|
14602
14755
|
function formatBudget(usage) {
|
|
14603
14756
|
return usage ? `${usage.usedCalls}/${usage.maxCalls} skipped=${usage.skippedCalls}` : "caller-managed";
|
|
@@ -15094,6 +15247,10 @@ async function dispatchCli(argv, dependencies) {
|
|
|
15094
15247
|
await contextCommand(parsed, runtime);
|
|
15095
15248
|
return;
|
|
15096
15249
|
}
|
|
15250
|
+
if (command === "device") {
|
|
15251
|
+
await deviceCommand(parsed, runtime);
|
|
15252
|
+
return;
|
|
15253
|
+
}
|
|
15097
15254
|
if (command === "credentials") {
|
|
15098
15255
|
await credentialCommand(parsed, runtime);
|
|
15099
15256
|
return;
|