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