@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/bin.cjs
CHANGED
|
@@ -377,10 +377,54 @@ var init_cached_credential = __esm({
|
|
|
377
377
|
}
|
|
378
378
|
});
|
|
379
379
|
|
|
380
|
+
// src/device-session.ts
|
|
381
|
+
function deviceCredentialPath(env = import_node_process4.default.env) {
|
|
382
|
+
return env.ODLA_DEVICE_CREDENTIAL ?? (0, import_node_path3.join)(env.HOME ?? (0, import_node_os.homedir)(), ".odla", "device.json");
|
|
383
|
+
}
|
|
384
|
+
function readDeviceCredential(platform, env = import_node_process4.default.env) {
|
|
385
|
+
const path = deviceCredentialPath(env);
|
|
386
|
+
if (!(0, import_node_fs6.existsSync)(path)) return null;
|
|
387
|
+
try {
|
|
388
|
+
const parsed = JSON.parse((0, import_node_fs6.readFileSync)(path, "utf8"));
|
|
389
|
+
if (typeof parsed.token !== "string" || !parsed.token.startsWith("odla_device_")) return null;
|
|
390
|
+
if (parsed.platform !== platform) return null;
|
|
391
|
+
return { ...parsed, token: parsed.token, platform: parsed.platform };
|
|
392
|
+
} catch {
|
|
393
|
+
return null;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
async function mintDeviceSession(platformUrl, credential2, doFetch) {
|
|
397
|
+
const response2 = await doFetch(`${platformUrl.replace(/\/$/, "")}/registry/devices/session`, {
|
|
398
|
+
method: "POST",
|
|
399
|
+
headers: { authorization: `Bearer ${credential2.token}`, "content-type": "application/json" },
|
|
400
|
+
body: "{}"
|
|
401
|
+
});
|
|
402
|
+
const body = await response2.json().catch(() => ({}));
|
|
403
|
+
if (!response2.ok || typeof body.token !== "string") {
|
|
404
|
+
const revocable = response2.status === 401 || response2.status === 403 || response2.status === 404;
|
|
405
|
+
const detail = body.error?.message ?? (response2.ok ? `registry returned ${response2.status} with no session token` : `registry returned ${response2.status}`);
|
|
406
|
+
throw new Error(
|
|
407
|
+
`device session failed: ${detail} (${response2.status})` + (revocable ? " \u2014 if this machine's enrollment was revoked or has expired, enroll it again in Studio" : "")
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
return { token: body.token, expiresAt: body.expiresAt ?? Date.now() };
|
|
411
|
+
}
|
|
412
|
+
var import_node_fs6, import_node_os, import_node_path3, import_node_process4;
|
|
413
|
+
var init_device_session = __esm({
|
|
414
|
+
"src/device-session.ts"() {
|
|
415
|
+
"use strict";
|
|
416
|
+
init_cjs_shims();
|
|
417
|
+
import_node_fs6 = require("fs");
|
|
418
|
+
import_node_os = require("os");
|
|
419
|
+
import_node_path3 = require("path");
|
|
420
|
+
import_node_process4 = __toESM(require("process"), 1);
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
|
|
380
424
|
// src/local.ts
|
|
381
425
|
function readJsonFile(path) {
|
|
382
426
|
try {
|
|
383
|
-
return JSON.parse((0,
|
|
427
|
+
return JSON.parse((0, import_node_fs7.readFileSync)(path, "utf8"));
|
|
384
428
|
} catch {
|
|
385
429
|
return null;
|
|
386
430
|
}
|
|
@@ -390,10 +434,10 @@ function writePrivateJson(path, value2) {
|
|
|
390
434
|
`);
|
|
391
435
|
}
|
|
392
436
|
function readCredentials(path) {
|
|
393
|
-
if (!(0,
|
|
437
|
+
if (!(0, import_node_fs7.existsSync)(path)) return null;
|
|
394
438
|
let value2;
|
|
395
439
|
try {
|
|
396
|
-
value2 = JSON.parse((0,
|
|
440
|
+
value2 = JSON.parse((0, import_node_fs7.readFileSync)(path, "utf8"));
|
|
397
441
|
} catch {
|
|
398
442
|
throw new Error(`credentials file ${path} is not valid JSON; fix or remove it before provisioning`);
|
|
399
443
|
}
|
|
@@ -423,14 +467,14 @@ function mergeCredential(current, update) {
|
|
|
423
467
|
return next;
|
|
424
468
|
}
|
|
425
469
|
function ensureGitignore(rootDir, localPaths = []) {
|
|
426
|
-
const path = (0,
|
|
427
|
-
const existing = (0,
|
|
470
|
+
const path = (0, import_node_path4.resolve)(rootDir, ".gitignore");
|
|
471
|
+
const existing = (0, import_node_fs7.existsSync)(path) ? (0, import_node_fs7.readFileSync)(path, "utf8") : "";
|
|
428
472
|
const configured = localPaths.map((localPath) => gitignoreEntry(rootDir, localPath)).filter((line2) => !!line2);
|
|
429
473
|
const wanted = [.../* @__PURE__ */ new Set([...GITIGNORE_LINES, ...configured])];
|
|
430
474
|
const missing = wanted.filter((line2) => !existing.split(/\r?\n/).includes(line2));
|
|
431
475
|
if (missing.length === 0) return;
|
|
432
476
|
const prefix = existing && !existing.endsWith("\n") ? "\n" : "";
|
|
433
|
-
(0,
|
|
477
|
+
(0, import_node_fs7.writeFileSync)(path, `${existing}${prefix}${missing.join("\n")}
|
|
434
478
|
`);
|
|
435
479
|
}
|
|
436
480
|
function o11yDevVars(cfg) {
|
|
@@ -444,7 +488,7 @@ function o11yDevVars(cfg) {
|
|
|
444
488
|
function resolveWriteDevVarsTarget(cfg, requested) {
|
|
445
489
|
if (!requested) return null;
|
|
446
490
|
if (requested === true) return cfg.local.devVarsFile;
|
|
447
|
-
return (0,
|
|
491
|
+
return (0, import_node_path4.resolve)((0, import_node_path4.dirname)(cfg.configPath), requested);
|
|
448
492
|
}
|
|
449
493
|
function writeDevVars(path, credentials, env, o11y) {
|
|
450
494
|
const entry = credentials.envs[env];
|
|
@@ -458,7 +502,7 @@ function writeDevVars(path, credentials, env, o11y) {
|
|
|
458
502
|
if (o11y.version) lines.push(`ODLA_O11Y_VERSION="${o11y.version}"`);
|
|
459
503
|
if (entry.o11yToken) lines.push(`ODLA_O11Y_TOKEN="${entry.o11yToken}"`);
|
|
460
504
|
}
|
|
461
|
-
const existing = (0,
|
|
505
|
+
const existing = (0, import_node_fs7.existsSync)(path) ? (0, import_node_fs7.readFileSync)(path, "utf8") : "";
|
|
462
506
|
const retained = existing.split(/\r?\n/).filter((line2) => !isManagedDevVar(line2));
|
|
463
507
|
while (retained.at(-1) === "") retained.pop();
|
|
464
508
|
const prefix = retained.length ? `${retained.join("\n")}
|
|
@@ -472,28 +516,28 @@ function isManagedDevVar(line2) {
|
|
|
472
516
|
return !!match?.[1] && MANAGED_DEV_VARS.has(match[1]);
|
|
473
517
|
}
|
|
474
518
|
function writePrivateText(path, text3) {
|
|
475
|
-
(0,
|
|
519
|
+
(0, import_node_fs7.mkdirSync)((0, import_node_path4.dirname)(path), { recursive: true });
|
|
476
520
|
const temporary = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
477
|
-
(0,
|
|
478
|
-
(0,
|
|
479
|
-
(0,
|
|
521
|
+
(0, import_node_fs7.writeFileSync)(temporary, text3, { mode: 384 });
|
|
522
|
+
(0, import_node_fs7.chmodSync)(temporary, 384);
|
|
523
|
+
(0, import_node_fs7.renameSync)(temporary, path);
|
|
480
524
|
}
|
|
481
525
|
function gitignoreEntry(rootDir, path) {
|
|
482
|
-
const rel = (0,
|
|
483
|
-
if (!rel || rel === ".." || rel.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) || (0,
|
|
526
|
+
const rel = (0, import_node_path4.relative)((0, import_node_path4.resolve)(rootDir), (0, import_node_path4.resolve)(path));
|
|
527
|
+
if (!rel || rel === ".." || rel.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) || (0, import_node_path4.isAbsolute)(rel)) return null;
|
|
484
528
|
return rel.replaceAll("\\", "/");
|
|
485
529
|
}
|
|
486
530
|
function displayPath(path, rootDir = process.cwd()) {
|
|
487
|
-
const rel = (0,
|
|
531
|
+
const rel = (0, import_node_path4.relative)(rootDir, path);
|
|
488
532
|
return rel && !rel.startsWith("..") ? rel : path;
|
|
489
533
|
}
|
|
490
|
-
var
|
|
534
|
+
var import_node_fs7, import_node_path4, GITIGNORE_LINES, MANAGED_DEV_VARS;
|
|
491
535
|
var init_local = __esm({
|
|
492
536
|
"src/local.ts"() {
|
|
493
537
|
"use strict";
|
|
494
538
|
init_cjs_shims();
|
|
495
|
-
|
|
496
|
-
|
|
539
|
+
import_node_fs7 = require("fs");
|
|
540
|
+
import_node_path4 = require("path");
|
|
497
541
|
GITIGNORE_LINES = [".odla/*.local.json", ".odla/dev-token.json", ".dev.vars"];
|
|
498
542
|
MANAGED_DEV_VARS = /* @__PURE__ */ new Set([
|
|
499
543
|
"ODLA_PLATFORM",
|
|
@@ -518,14 +562,20 @@ async function getDeveloperToken(cfg, options, doFetch, out, grantRequest = {})
|
|
|
518
562
|
const cached = readJsonFile(cfg.local.tokenFile);
|
|
519
563
|
if (!grantRequest.forceReview && !grantRequest.freshLogin) {
|
|
520
564
|
if (options.token) return options.token;
|
|
521
|
-
if (
|
|
522
|
-
const declared =
|
|
565
|
+
if (import_node_process5.default.env.ODLA_DEV_TOKEN) {
|
|
566
|
+
const declared = import_node_process5.default.env.ODLA_DEV_TOKEN_AUDIENCE;
|
|
523
567
|
if (declared) {
|
|
524
568
|
if (platformAudience(declared) !== audience) throw new Error("ODLA_DEV_TOKEN_AUDIENCE does not match the configured platform");
|
|
525
569
|
} else if (audience !== "https://odla.ai") {
|
|
526
570
|
throw new Error("ODLA_DEV_TOKEN_AUDIENCE is required for a non-default platform");
|
|
527
571
|
}
|
|
528
|
-
return
|
|
572
|
+
return import_node_process5.default.env.ODLA_DEV_TOKEN;
|
|
573
|
+
}
|
|
574
|
+
const device = readDeviceCredential(audience);
|
|
575
|
+
if (device) {
|
|
576
|
+
const session = await mintDeviceSession(cfg.platformUrl, device, doFetch);
|
|
577
|
+
out.error(`auth: session minted by this enrolled device (${displayPath(deviceCredentialPath(), cfg.rootDir)})`);
|
|
578
|
+
return session.token;
|
|
529
579
|
}
|
|
530
580
|
if (cached?.token && cached.platform === audience && (cached.expiresAt ?? 0) > Date.now() + 6e4 && cachedGrantCovers(cached, grantIntent)) {
|
|
531
581
|
out.error(`auth: using cached developer token (${displayPath(cfg.local.tokenFile, cfg.rootDir)})`);
|
|
@@ -630,7 +680,7 @@ function stillPending(pending, email) {
|
|
|
630
680
|
);
|
|
631
681
|
}
|
|
632
682
|
function handshakeEmail(value2, cached) {
|
|
633
|
-
const email = (value2 ??
|
|
683
|
+
const email = (value2 ?? import_node_process5.default.env.ODLA_USER_EMAIL ?? cached ?? "").trim().toLowerCase();
|
|
634
684
|
if (/@users\.noreply\.github\.com$/i.test(email)) {
|
|
635
685
|
throw new Error(
|
|
636
686
|
`"${email}" is a GitHub commit identity, not an odla account email; use --email <signed-in-odla-account> or ODLA_USER_EMAIL`
|
|
@@ -659,17 +709,18 @@ function platformAudience(value2) {
|
|
|
659
709
|
}
|
|
660
710
|
return url.origin;
|
|
661
711
|
}
|
|
662
|
-
var import_db, import_node_crypto,
|
|
712
|
+
var import_db, import_node_crypto, import_node_process5;
|
|
663
713
|
var init_token = __esm({
|
|
664
714
|
"src/token.ts"() {
|
|
665
715
|
"use strict";
|
|
666
716
|
init_cjs_shims();
|
|
667
717
|
import_db = require("@odla-ai/db");
|
|
668
718
|
import_node_crypto = require("crypto");
|
|
669
|
-
|
|
719
|
+
import_node_process5 = __toESM(require("process"), 1);
|
|
670
720
|
init_handshake_approval();
|
|
671
721
|
init_handshake_state();
|
|
672
722
|
init_cached_credential();
|
|
723
|
+
init_device_session();
|
|
673
724
|
init_local();
|
|
674
725
|
}
|
|
675
726
|
});
|
|
@@ -678,7 +729,7 @@ var init_token = __esm({
|
|
|
678
729
|
async function secretInputValue(options, kind = "credential") {
|
|
679
730
|
if (options.fromEnv && options.stdin) throw new Error("choose exactly one of --from-env or --stdin");
|
|
680
731
|
let value2;
|
|
681
|
-
if (options.fromEnv) value2 =
|
|
732
|
+
if (options.fromEnv) value2 = import_node_process6.default.env[options.fromEnv];
|
|
682
733
|
else if (options.stdin) value2 = await (options.readStdin ?? (() => readSecretStream(kind)))();
|
|
683
734
|
else throw new Error(`${kind} input required: use --from-env <NAME> or --stdin; values are never accepted as arguments`);
|
|
684
735
|
value2 = value2?.replace(/[\r\n]+$/, "");
|
|
@@ -686,7 +737,7 @@ async function secretInputValue(options, kind = "credential") {
|
|
|
686
737
|
if (new TextEncoder().encode(value2).byteLength > MAX_BYTES) throw new Error(`${kind} exceeds 64 KiB`);
|
|
687
738
|
return value2;
|
|
688
739
|
}
|
|
689
|
-
async function readSecretStream(kind, stream =
|
|
740
|
+
async function readSecretStream(kind, stream = import_node_process6.default.stdin) {
|
|
690
741
|
let value2 = "";
|
|
691
742
|
for await (const chunk of stream) {
|
|
692
743
|
value2 += String(chunk);
|
|
@@ -694,12 +745,12 @@ async function readSecretStream(kind, stream = import_node_process5.default.stdi
|
|
|
694
745
|
}
|
|
695
746
|
return value2;
|
|
696
747
|
}
|
|
697
|
-
var
|
|
748
|
+
var import_node_process6, MAX_BYTES;
|
|
698
749
|
var init_secret_input = __esm({
|
|
699
750
|
"src/secret-input.ts"() {
|
|
700
751
|
"use strict";
|
|
701
752
|
init_cjs_shims();
|
|
702
|
-
|
|
753
|
+
import_node_process6 = __toESM(require("process"), 1);
|
|
703
754
|
MAX_BYTES = 64 * 1024;
|
|
704
755
|
}
|
|
705
756
|
});
|
|
@@ -711,7 +762,7 @@ async function getScopedPlatformToken(options) {
|
|
|
711
762
|
async function resolveAdminPlatformToken(options) {
|
|
712
763
|
const audience = platformAudience(options.platform);
|
|
713
764
|
if (options.token) return options.token;
|
|
714
|
-
const fromEnv =
|
|
765
|
+
const fromEnv = import_node_process7.default.env.ODLA_ADMIN_TOKEN;
|
|
715
766
|
if (fromEnv) return audienceBoundEnvToken(fromEnv, audience);
|
|
716
767
|
return scopedToken(
|
|
717
768
|
audience,
|
|
@@ -723,7 +774,7 @@ async function resolveAdminPlatformToken(options) {
|
|
|
723
774
|
}
|
|
724
775
|
function audienceBoundEnvToken(token, platform) {
|
|
725
776
|
const audience = platformAudience(platform);
|
|
726
|
-
const declared =
|
|
777
|
+
const declared = import_node_process7.default.env.ODLA_ADMIN_TOKEN_AUDIENCE;
|
|
727
778
|
if (declared) {
|
|
728
779
|
if (platformAudience(declared) !== audience) throw new Error("ODLA_ADMIN_TOKEN_AUDIENCE does not match the configured platform");
|
|
729
780
|
} else if (audience !== "https://odla.ai") {
|
|
@@ -733,8 +784,8 @@ function audienceBoundEnvToken(token, platform) {
|
|
|
733
784
|
}
|
|
734
785
|
async function scopedToken(platform, scope, options, doFetch, out) {
|
|
735
786
|
const audience = platformAudience(platform);
|
|
736
|
-
const rootDir = options.rootDir ??
|
|
737
|
-
const tokenFile = options.tokenFile ?? (0,
|
|
787
|
+
const rootDir = options.rootDir ?? import_node_process7.default.cwd();
|
|
788
|
+
const tokenFile = options.tokenFile ?? (0, import_node_path5.join)(rootDir, ".odla/admin-token.local.json");
|
|
738
789
|
const cache2 = options.cache === false ? null : readJsonFile(tokenFile);
|
|
739
790
|
const cached = cache2?.platform === audience ? cache2.tokens?.[scope] : void 0;
|
|
740
791
|
if (cached?.token && (cached.expiresAt ?? 0) > Date.now() + 6e4) {
|
|
@@ -761,7 +812,7 @@ async function scopedToken(platform, scope, options, doFetch, out) {
|
|
|
761
812
|
if (options.cache !== false) {
|
|
762
813
|
const tokens = cache2?.platform === audience ? { ...cache2.tokens ?? {} } : {};
|
|
763
814
|
tokens[scope] = { token, expiresAt };
|
|
764
|
-
if ((0,
|
|
815
|
+
if ((0, import_node_fs8.existsSync)((0, import_node_path5.join)(rootDir, ".git"))) ensureGitignore(rootDir, [tokenFile]);
|
|
765
816
|
writePrivateJson(tokenFile, { platform: audience, email, tokens });
|
|
766
817
|
out.error(`auth: cached ${scope} grant (${tokenFile}; mode 0600)`);
|
|
767
818
|
} else {
|
|
@@ -769,14 +820,14 @@ async function scopedToken(platform, scope, options, doFetch, out) {
|
|
|
769
820
|
}
|
|
770
821
|
return token;
|
|
771
822
|
}
|
|
772
|
-
var
|
|
823
|
+
var import_node_fs8, import_node_path5, import_node_process7, import_db2, SCOPE_PURPOSE;
|
|
773
824
|
var init_admin_ai_auth = __esm({
|
|
774
825
|
"src/admin-ai-auth.ts"() {
|
|
775
826
|
"use strict";
|
|
776
827
|
init_cjs_shims();
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
828
|
+
import_node_fs8 = require("fs");
|
|
829
|
+
import_node_path5 = require("path");
|
|
830
|
+
import_node_process7 = __toESM(require("process"), 1);
|
|
780
831
|
import_db2 = require("@odla-ai/db");
|
|
781
832
|
init_local();
|
|
782
833
|
init_handshake_approval();
|
|
@@ -786,6 +837,7 @@ var init_admin_ai_auth = __esm({
|
|
|
786
837
|
"app:config:read": "compare checked-in intent with an exact-id app Registry configuration",
|
|
787
838
|
"app:config:write": "apply or inspect one revision-bound configuration operation for an app you own",
|
|
788
839
|
"platform:runbook:write": "read and edit all of odla's operational runbooks, including admin-visible content",
|
|
840
|
+
"app:device:enroll": "enrol this machine so it can mint its own short-lived credentials without asking you again",
|
|
789
841
|
"platform:ai:policy:write": "change System AI model routing",
|
|
790
842
|
"platform:ai:policy:read": "read System AI model routing",
|
|
791
843
|
"platform:ai:credential:write": "replace a stored AI provider key",
|
|
@@ -994,7 +1046,7 @@ var init_admin_ai_usage = __esm({
|
|
|
994
1046
|
|
|
995
1047
|
// src/admin-ai.ts
|
|
996
1048
|
async function adminAi(options) {
|
|
997
|
-
const platform = platformAudience(options.platform ??
|
|
1049
|
+
const platform = platformAudience(options.platform ?? import_node_process8.default.env.ODLA_PLATFORM ?? "https://odla.ai");
|
|
998
1050
|
const doFetch = options.fetch ?? fetch;
|
|
999
1051
|
const out = options.stdout ?? console;
|
|
1000
1052
|
const usageQuery = options.action === "usage" ? adminAiUsageQuery(options) : void 0;
|
|
@@ -1172,12 +1224,12 @@ function apiError3(action2, status, body) {
|
|
|
1172
1224
|
function isRecord3(value2) {
|
|
1173
1225
|
return Boolean(value2) && typeof value2 === "object" && !Array.isArray(value2);
|
|
1174
1226
|
}
|
|
1175
|
-
var
|
|
1227
|
+
var import_node_process8;
|
|
1176
1228
|
var init_admin_ai = __esm({
|
|
1177
1229
|
"src/admin-ai.ts"() {
|
|
1178
1230
|
"use strict";
|
|
1179
1231
|
init_cjs_shims();
|
|
1180
|
-
|
|
1232
|
+
import_node_process8 = __toESM(require("process"), 1);
|
|
1181
1233
|
init_token();
|
|
1182
1234
|
init_secret_input();
|
|
1183
1235
|
init_admin_ai_auth();
|
|
@@ -1691,12 +1743,12 @@ var init_monitoring_validation = __esm({
|
|
|
1691
1743
|
|
|
1692
1744
|
// src/config.ts
|
|
1693
1745
|
async function loadProjectConfig(configPath = "odla.config.mjs", options = {}) {
|
|
1694
|
-
const resolved = (0,
|
|
1695
|
-
if (!(0,
|
|
1746
|
+
const resolved = (0, import_node_path6.resolve)(configPath);
|
|
1747
|
+
if (!(0, import_node_fs9.existsSync)(resolved)) {
|
|
1696
1748
|
throw new Error(`config not found: ${configPath}. Run "odla-ai init" first or pass --config.`);
|
|
1697
1749
|
}
|
|
1698
1750
|
const raw = await loadConfigModule(resolved);
|
|
1699
|
-
const rootDir = (0,
|
|
1751
|
+
const rootDir = (0, import_node_path6.dirname)(resolved);
|
|
1700
1752
|
validateRawConfig(raw, resolved);
|
|
1701
1753
|
const platformUrl = trimSlash(process.env.ODLA_PLATFORM_URL || raw.platformUrl || DEFAULT_PLATFORM);
|
|
1702
1754
|
const dbEndpoint = trimSlash(process.env.ODLA_DB_ENDPOINT || raw.dbEndpoint || platformUrl);
|
|
@@ -1706,9 +1758,9 @@ async function loadProjectConfig(configPath = "odla.config.mjs", options = {}) {
|
|
|
1706
1758
|
validateCalendarConfig(raw, unique3([...envs, ...options.additionalEnvs ?? []]), services, resolved);
|
|
1707
1759
|
validateMonitoringConfig(raw, unique3([...envs, ...options.additionalEnvs ?? []]), services, resolved);
|
|
1708
1760
|
const local = {
|
|
1709
|
-
tokenFile: (0,
|
|
1710
|
-
credentialsFile: (0,
|
|
1711
|
-
devVarsFile: (0,
|
|
1761
|
+
tokenFile: (0, import_node_path6.resolve)(rootDir, raw.local?.tokenFile ?? ".odla/dev-token.json"),
|
|
1762
|
+
credentialsFile: (0, import_node_path6.resolve)(rootDir, raw.local?.credentialsFile ?? ".odla/credentials.local.json"),
|
|
1763
|
+
devVarsFile: (0, import_node_path6.resolve)(rootDir, raw.local?.devVarsFile ?? ".dev.vars"),
|
|
1712
1764
|
gitignore: raw.local?.gitignore ?? true
|
|
1713
1765
|
};
|
|
1714
1766
|
return {
|
|
@@ -1725,9 +1777,9 @@ async function loadProjectConfig(configPath = "odla.config.mjs", options = {}) {
|
|
|
1725
1777
|
async function resolveDataExport(cfg, value2, names) {
|
|
1726
1778
|
if (value2 === void 0 || value2 === null || value2 === false) return void 0;
|
|
1727
1779
|
if (typeof value2 !== "string") return value2;
|
|
1728
|
-
const target = (0,
|
|
1780
|
+
const target = (0, import_node_path6.isAbsolute)(value2) ? value2 : (0, import_node_path6.resolve)(cfg.rootDir, value2);
|
|
1729
1781
|
if (target.endsWith(".json")) {
|
|
1730
|
-
return JSON.parse((0,
|
|
1782
|
+
return JSON.parse((0, import_node_fs9.readFileSync)(target, "utf8"));
|
|
1731
1783
|
}
|
|
1732
1784
|
const mod = await import((0, import_node_url.pathToFileURL)(target).href);
|
|
1733
1785
|
for (const name of names) {
|
|
@@ -1803,7 +1855,7 @@ function validId2(value2) {
|
|
|
1803
1855
|
return typeof value2 === "string" && /^[a-z0-9][a-z0-9-]*$/.test(value2);
|
|
1804
1856
|
}
|
|
1805
1857
|
async function loadConfigModule(path) {
|
|
1806
|
-
if (path.endsWith(".json")) return JSON.parse((0,
|
|
1858
|
+
if (path.endsWith(".json")) return JSON.parse((0, import_node_fs9.readFileSync)(path, "utf8"));
|
|
1807
1859
|
const nonce = `${Date.now()}-${configImportSerial++}`;
|
|
1808
1860
|
const mod = await import(`${(0, import_node_url.pathToFileURL)(path).href}?reload=${nonce}`);
|
|
1809
1861
|
const value2 = mod.default ?? mod.config;
|
|
@@ -1816,13 +1868,13 @@ function trimSlash(value2) {
|
|
|
1816
1868
|
function unique3(values) {
|
|
1817
1869
|
return [...new Set(values.filter(Boolean))];
|
|
1818
1870
|
}
|
|
1819
|
-
var
|
|
1871
|
+
var import_node_fs9, import_node_path6, import_node_url, import_apps, DEFAULT_PLATFORM, DEFAULT_ENVS, DEFAULT_SERVICES, configImportSerial, GOOGLE_CALENDAR_EVENTS_SCOPE;
|
|
1820
1872
|
var init_config = __esm({
|
|
1821
1873
|
"src/config.ts"() {
|
|
1822
1874
|
"use strict";
|
|
1823
1875
|
init_cjs_shims();
|
|
1824
|
-
|
|
1825
|
-
|
|
1876
|
+
import_node_fs9 = require("fs");
|
|
1877
|
+
import_node_path6 = require("path");
|
|
1826
1878
|
import_node_url = require("url");
|
|
1827
1879
|
import_apps = require("@odla-ai/apps");
|
|
1828
1880
|
init_ai_config_validation();
|
|
@@ -1840,13 +1892,13 @@ var init_config = __esm({
|
|
|
1840
1892
|
|
|
1841
1893
|
// src/operator-profiles.ts
|
|
1842
1894
|
function operatorProfileFile() {
|
|
1843
|
-
return (0,
|
|
1844
|
-
clean(
|
|
1895
|
+
return (0, import_node_path7.resolve)(
|
|
1896
|
+
clean(import_node_process9.default.env.ODLA_CONTEXT_FILE) ?? (0, import_node_path7.join)((0, import_node_os2.homedir)(), ".odla", "contexts.json")
|
|
1845
1897
|
);
|
|
1846
1898
|
}
|
|
1847
1899
|
function resolveOperatorProfile(parsed) {
|
|
1848
1900
|
const fromFlag = clean(stringOpt(parsed.options.context));
|
|
1849
|
-
const fromEnvironment = clean(
|
|
1901
|
+
const fromEnvironment = clean(import_node_process9.default.env.ODLA_CONTEXT);
|
|
1850
1902
|
const name = fromFlag ?? fromEnvironment ?? null;
|
|
1851
1903
|
const file = operatorProfileFile();
|
|
1852
1904
|
if (!name) {
|
|
@@ -1886,10 +1938,10 @@ function removeOperatorProfile(name, file = operatorProfileFile()) {
|
|
|
1886
1938
|
return true;
|
|
1887
1939
|
}
|
|
1888
1940
|
function operatorCredentialFiles(selection) {
|
|
1889
|
-
const base = selection.name ? (0,
|
|
1941
|
+
const base = selection.name ? (0, import_node_path7.join)((0, import_node_path7.dirname)(selection.file), "profiles", selection.name) : (0, import_node_path7.join)((0, import_node_os2.homedir)(), ".odla");
|
|
1890
1942
|
return {
|
|
1891
|
-
developer: (0,
|
|
1892
|
-
scoped: (0,
|
|
1943
|
+
developer: (0, import_node_path7.join)(base, "dev-token.json"),
|
|
1944
|
+
scoped: (0, import_node_path7.join)(base, "admin-token.local.json")
|
|
1893
1945
|
};
|
|
1894
1946
|
}
|
|
1895
1947
|
function assertOperatorName(value2, label) {
|
|
@@ -1900,10 +1952,10 @@ function assertOperatorName(value2, label) {
|
|
|
1900
1952
|
}
|
|
1901
1953
|
}
|
|
1902
1954
|
function readOperatorProfiles(file) {
|
|
1903
|
-
if (!(0,
|
|
1955
|
+
if (!(0, import_node_fs10.existsSync)(file)) return emptyProfiles();
|
|
1904
1956
|
let raw;
|
|
1905
1957
|
try {
|
|
1906
|
-
raw = JSON.parse((0,
|
|
1958
|
+
raw = JSON.parse((0, import_node_fs10.readFileSync)(file, "utf8"));
|
|
1907
1959
|
} catch {
|
|
1908
1960
|
throw new Error(`operator context file ${file} is not valid JSON`);
|
|
1909
1961
|
}
|
|
@@ -1961,15 +2013,15 @@ function clean(value2) {
|
|
|
1961
2013
|
const normalized = value2?.trim();
|
|
1962
2014
|
return normalized || void 0;
|
|
1963
2015
|
}
|
|
1964
|
-
var
|
|
2016
|
+
var import_node_fs10, import_node_os2, import_node_path7, import_node_process9;
|
|
1965
2017
|
var init_operator_profiles = __esm({
|
|
1966
2018
|
"src/operator-profiles.ts"() {
|
|
1967
2019
|
"use strict";
|
|
1968
2020
|
init_cjs_shims();
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
2021
|
+
import_node_fs10 = require("fs");
|
|
2022
|
+
import_node_os2 = require("os");
|
|
2023
|
+
import_node_path7 = require("path");
|
|
2024
|
+
import_node_process9 = __toESM(require("process"), 1);
|
|
1973
2025
|
init_argv();
|
|
1974
2026
|
init_local();
|
|
1975
2027
|
init_token();
|
|
@@ -1980,21 +2032,21 @@ var init_operator_profiles = __esm({
|
|
|
1980
2032
|
async function resolveOperatorContext(parsed, options = {}) {
|
|
1981
2033
|
const profile = resolveOperatorProfile(parsed);
|
|
1982
2034
|
const configArgument = stringOpt(parsed.options.config) ?? "odla.config.mjs";
|
|
1983
|
-
const configPath = (0,
|
|
2035
|
+
const configPath = (0, import_node_path8.resolve)(configArgument);
|
|
1984
2036
|
const explicitConfig = parsed.options.config !== void 0;
|
|
1985
|
-
const hasConfig = (0,
|
|
2037
|
+
const hasConfig = (0, import_node_fs11.existsSync)(configPath);
|
|
1986
2038
|
if (!hasConfig && (!options.allowMissingConfig || explicitConfig)) {
|
|
1987
2039
|
await loadProjectConfig(configArgument);
|
|
1988
2040
|
}
|
|
1989
2041
|
const loaded = hasConfig ? await loadProjectConfig(configArgument) : void 0;
|
|
1990
2042
|
const platformFlag = clean2(stringOpt(parsed.options.platform));
|
|
1991
|
-
const platformEnvironment = clean2(
|
|
2043
|
+
const platformEnvironment = clean2(import_node_process10.default.env.ODLA_PLATFORM_URL);
|
|
1992
2044
|
const platformValue = platformAudience(
|
|
1993
2045
|
platformFlag ?? platformEnvironment ?? profile.value?.platform ?? loaded?.platformUrl ?? DEFAULT_PLATFORM2
|
|
1994
2046
|
);
|
|
1995
2047
|
const platformSource = platformFlag ? "flag" : platformEnvironment ? "environment" : profile.value ? "profile" : loaded ? "config" : "default";
|
|
1996
2048
|
const appFlag = clean2(stringOpt(parsed.options.app));
|
|
1997
|
-
const appEnvironment = clean2(
|
|
2049
|
+
const appEnvironment = clean2(import_node_process10.default.env.ODLA_APP_ID);
|
|
1998
2050
|
const appValue = appFlag ?? appEnvironment ?? profile.value?.app ?? loaded?.app.id ?? null;
|
|
1999
2051
|
const appSource = appFlag ? "flag" : appEnvironment ? "environment" : profile.value?.app ? "profile" : loaded ? "config" : "unresolved";
|
|
2000
2052
|
if (appValue) assertOperatorName(appValue, "app");
|
|
@@ -2004,16 +2056,16 @@ async function resolveOperatorContext(parsed, options = {}) {
|
|
|
2004
2056
|
);
|
|
2005
2057
|
}
|
|
2006
2058
|
const envFlag = clean2(stringOpt(parsed.options.env));
|
|
2007
|
-
const envEnvironment = clean2(
|
|
2059
|
+
const envEnvironment = clean2(import_node_process10.default.env.ODLA_ENV);
|
|
2008
2060
|
const environmentValue = envFlag ?? envEnvironment ?? profile.value?.environment ?? options.defaultEnvironment ?? null;
|
|
2009
2061
|
const environmentSource = envFlag ? "flag" : envEnvironment ? "environment" : profile.value?.environment ? "profile" : options.defaultEnvironment ? "default" : "unresolved";
|
|
2010
2062
|
if (environmentValue) {
|
|
2011
2063
|
assertOperatorName(environmentValue, "environment");
|
|
2012
2064
|
}
|
|
2013
|
-
const rootDir = loaded?.rootDir ??
|
|
2065
|
+
const rootDir = loaded?.rootDir ?? import_node_process10.default.cwd();
|
|
2014
2066
|
const profileCredentials = operatorCredentialFiles(profile);
|
|
2015
|
-
const tokenFile = clean2(
|
|
2016
|
-
const scopedTokenFile = clean2(
|
|
2067
|
+
const tokenFile = clean2(import_node_process10.default.env.ODLA_DEV_TOKEN_FILE) ? (0, import_node_path8.resolve)(import_node_process10.default.env.ODLA_DEV_TOKEN_FILE) : profile.name ? profileCredentials.developer : loaded?.local.tokenFile ?? profileCredentials.developer;
|
|
2068
|
+
const scopedTokenFile = clean2(import_node_process10.default.env.ODLA_ADMIN_TOKEN_FILE) ? (0, import_node_path8.resolve)(import_node_process10.default.env.ODLA_ADMIN_TOKEN_FILE) : profile.name ? profileCredentials.scoped : loaded ? (0, import_node_path8.join)(loaded.rootDir, ".odla", "admin-token.local.json") : profileCredentials.scoped;
|
|
2017
2069
|
const cfg = loaded ? {
|
|
2018
2070
|
...loaded,
|
|
2019
2071
|
platformUrl: platformValue,
|
|
@@ -2035,8 +2087,8 @@ async function resolveOperatorContext(parsed, options = {}) {
|
|
|
2035
2087
|
services: [],
|
|
2036
2088
|
local: {
|
|
2037
2089
|
tokenFile,
|
|
2038
|
-
credentialsFile: (0,
|
|
2039
|
-
devVarsFile: (0,
|
|
2090
|
+
credentialsFile: (0, import_node_path8.join)(rootDir, ".odla", "credentials.local.json"),
|
|
2091
|
+
devVarsFile: (0, import_node_path8.join)(rootDir, ".dev.vars"),
|
|
2040
2092
|
gitignore: true
|
|
2041
2093
|
}
|
|
2042
2094
|
};
|
|
@@ -2068,14 +2120,14 @@ function clean2(value2) {
|
|
|
2068
2120
|
const normalized = value2?.trim();
|
|
2069
2121
|
return normalized || void 0;
|
|
2070
2122
|
}
|
|
2071
|
-
var
|
|
2123
|
+
var import_node_fs11, import_node_path8, import_node_process10, DEFAULT_PLATFORM2;
|
|
2072
2124
|
var init_operator_context = __esm({
|
|
2073
2125
|
"src/operator-context.ts"() {
|
|
2074
2126
|
"use strict";
|
|
2075
2127
|
init_cjs_shims();
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2128
|
+
import_node_fs11 = require("fs");
|
|
2129
|
+
import_node_path8 = require("path");
|
|
2130
|
+
import_node_process10 = __toESM(require("process"), 1);
|
|
2079
2131
|
init_argv();
|
|
2080
2132
|
init_config();
|
|
2081
2133
|
init_operator_profiles();
|
|
@@ -2327,7 +2379,7 @@ async function authCommand(parsed, deps = {}) {
|
|
|
2327
2379
|
const { cfg } = context;
|
|
2328
2380
|
const out = deps.stdout ?? console;
|
|
2329
2381
|
const doFetch = deps.fetch ?? fetch;
|
|
2330
|
-
const email = stringOpt(parsed.options.email) ??
|
|
2382
|
+
const email = stringOpt(parsed.options.email) ?? import_node_process11.default.env.ODLA_USER_EMAIL?.trim();
|
|
2331
2383
|
if (!email) {
|
|
2332
2384
|
throw new Error(
|
|
2333
2385
|
"auth login requires --email <odla-account> or ODLA_USER_EMAIL; confirm the signed-in odla email instead of using git or GitHub identity"
|
|
@@ -2361,12 +2413,12 @@ async function authCommand(parsed, deps = {}) {
|
|
|
2361
2413
|
out.log(`Authorized ${identity.displayName}${handle} for ${cfg.app.id}.`);
|
|
2362
2414
|
out.log(`odla account: ${identity.email ?? "not returned"}`);
|
|
2363
2415
|
}
|
|
2364
|
-
var
|
|
2416
|
+
var import_node_process11;
|
|
2365
2417
|
var init_auth_command = __esm({
|
|
2366
2418
|
"src/auth-command.ts"() {
|
|
2367
2419
|
"use strict";
|
|
2368
2420
|
init_cjs_shims();
|
|
2369
|
-
|
|
2421
|
+
import_node_process11 = __toESM(require("process"), 1);
|
|
2370
2422
|
init_argv();
|
|
2371
2423
|
init_operator_context();
|
|
2372
2424
|
init_token();
|
|
@@ -2539,7 +2591,7 @@ async function appImport(options) {
|
|
|
2539
2591
|
const out = options.stdout ?? console;
|
|
2540
2592
|
const say = options.json ? (line2) => out.error(line2) : (line2) => out.log(line2);
|
|
2541
2593
|
const { tenant } = resolveTenant(cfg, options.env);
|
|
2542
|
-
const text3 = options.file === "-" ? (options.readStdin ?? (() => (0,
|
|
2594
|
+
const text3 = options.file === "-" ? (options.readStdin ?? (() => (0, import_node_fs12.readFileSync)(0, "utf8")))() : (0, import_node_fs12.readFileSync)(options.file, "utf8");
|
|
2543
2595
|
const { format, sources } = (0, import_import.parseImport)(text3, options.ns);
|
|
2544
2596
|
if (format === "namespace-map" && options.ns) {
|
|
2545
2597
|
throw new Error("--ns cannot be combined with a {namespace: rows} file \u2014 the file already names each namespace");
|
|
@@ -2567,12 +2619,12 @@ ${detail}${more}`);
|
|
|
2567
2619
|
}
|
|
2568
2620
|
return requireStudioHuman(options.configPath, "database import", "database", options.env);
|
|
2569
2621
|
}
|
|
2570
|
-
var
|
|
2622
|
+
var import_node_fs12, import_import;
|
|
2571
2623
|
var init_app_import = __esm({
|
|
2572
2624
|
"src/app-import.ts"() {
|
|
2573
2625
|
"use strict";
|
|
2574
2626
|
init_cjs_shims();
|
|
2575
|
-
|
|
2627
|
+
import_node_fs12 = require("fs");
|
|
2576
2628
|
import_import = require("@odla-ai/db/import");
|
|
2577
2629
|
init_config();
|
|
2578
2630
|
init_human_session();
|
|
@@ -2883,15 +2935,15 @@ var init_brand_design_unpack = __esm({
|
|
|
2883
2935
|
|
|
2884
2936
|
// src/brand-command.ts
|
|
2885
2937
|
async function readBundle(source, deps) {
|
|
2886
|
-
if (source !== "-") return (0, import_promises.readFile)((0,
|
|
2938
|
+
if (source !== "-") return (0, import_promises.readFile)((0, import_node_path9.resolve)(source), "utf8");
|
|
2887
2939
|
const readStdin = deps.readStdin;
|
|
2888
2940
|
if (!readStdin) throw new Error("reading a bundle from stdin is not supported here");
|
|
2889
2941
|
return readStdin();
|
|
2890
2942
|
}
|
|
2891
2943
|
async function writeAll(result, outDir) {
|
|
2892
2944
|
for (const file of result.files) {
|
|
2893
|
-
const target = (0,
|
|
2894
|
-
await (0, import_promises.mkdir)((0,
|
|
2945
|
+
const target = (0, import_node_path9.resolve)(outDir, file.path);
|
|
2946
|
+
await (0, import_promises.mkdir)((0, import_node_path9.dirname)(target), { recursive: true });
|
|
2895
2947
|
await (0, import_promises.writeFile)(target, file.bytes);
|
|
2896
2948
|
}
|
|
2897
2949
|
}
|
|
@@ -2899,7 +2951,7 @@ async function designUnpack(parsed, deps) {
|
|
|
2899
2951
|
assertArgs(parsed, ["out", "json"], 4);
|
|
2900
2952
|
const source = parsed.positionals[3];
|
|
2901
2953
|
if (!source) throw new Error(USAGE);
|
|
2902
|
-
const outDir = (0,
|
|
2954
|
+
const outDir = (0, import_node_path9.resolve)(stringOpt(parsed.options.out) ?? "design");
|
|
2903
2955
|
const result = unpackDesign(await readBundle(source, deps));
|
|
2904
2956
|
await writeAll(result, outDir);
|
|
2905
2957
|
const out = deps.stdout ?? console;
|
|
@@ -2925,13 +2977,13 @@ async function brandCommand(parsed, deps) {
|
|
|
2925
2977
|
}
|
|
2926
2978
|
throw new Error(USAGE);
|
|
2927
2979
|
}
|
|
2928
|
-
var import_promises,
|
|
2980
|
+
var import_promises, import_node_path9, USAGE;
|
|
2929
2981
|
var init_brand_command = __esm({
|
|
2930
2982
|
"src/brand-command.ts"() {
|
|
2931
2983
|
"use strict";
|
|
2932
2984
|
init_cjs_shims();
|
|
2933
2985
|
import_promises = require("fs/promises");
|
|
2934
|
-
|
|
2986
|
+
import_node_path9 = require("path");
|
|
2935
2987
|
init_argv();
|
|
2936
2988
|
init_brand_design_unpack();
|
|
2937
2989
|
USAGE = "usage: odla-ai brand design unpack <bundle.html|-> [--out <dir>] [--json]";
|
|
@@ -3569,7 +3621,7 @@ var init_config_reconcile_digest = __esm({
|
|
|
3569
3621
|
function readPlan(path) {
|
|
3570
3622
|
let value2;
|
|
3571
3623
|
try {
|
|
3572
|
-
const raw = (0,
|
|
3624
|
+
const raw = (0, import_node_fs13.readFileSync)(path, "utf8");
|
|
3573
3625
|
if (Buffer.byteLength(raw) > 128 * 1024) throw new Error("plan exceeds 128 KiB");
|
|
3574
3626
|
value2 = JSON.parse(raw);
|
|
3575
3627
|
} catch (error) {
|
|
@@ -3685,13 +3737,13 @@ function invalidPlan(message2) {
|
|
|
3685
3737
|
function record3(value2) {
|
|
3686
3738
|
return !!value2 && typeof value2 === "object" && !Array.isArray(value2);
|
|
3687
3739
|
}
|
|
3688
|
-
var import_apps3,
|
|
3740
|
+
var import_apps3, import_node_fs13, DIGEST, REVISION, OPERATION_ID, ACTION_ID, ENV, SERVICE;
|
|
3689
3741
|
var init_config_operation_validate = __esm({
|
|
3690
3742
|
"src/config-operation-validate.ts"() {
|
|
3691
3743
|
"use strict";
|
|
3692
3744
|
init_cjs_shims();
|
|
3693
3745
|
import_apps3 = require("@odla-ai/apps");
|
|
3694
|
-
|
|
3746
|
+
import_node_fs13 = require("fs");
|
|
3695
3747
|
init_config_operation_error();
|
|
3696
3748
|
init_config_reconcile_digest();
|
|
3697
3749
|
DIGEST = /^sha256:[0-9a-f]{64}$/;
|
|
@@ -3979,7 +4031,7 @@ async function operationClient(cfg, options, purpose) {
|
|
|
3979
4031
|
platform: cfg.platformUrl,
|
|
3980
4032
|
scope: "app:config:write",
|
|
3981
4033
|
token: options.token,
|
|
3982
|
-
tokenFile: (0,
|
|
4034
|
+
tokenFile: (0, import_node_path10.join)(cfg.rootDir, ".odla", "admin-token.local.json"),
|
|
3983
4035
|
rootDir: cfg.rootDir,
|
|
3984
4036
|
email: options.email,
|
|
3985
4037
|
open: options.open,
|
|
@@ -4031,13 +4083,13 @@ function normalizeRequestError(error) {
|
|
|
4031
4083
|
function record4(value2) {
|
|
4032
4084
|
return !!value2 && typeof value2 === "object" && !Array.isArray(value2);
|
|
4033
4085
|
}
|
|
4034
|
-
var import_apps6,
|
|
4086
|
+
var import_apps6, import_node_path10, IDEMPOTENCY_KEY, DEFAULT_WAIT_SECONDS, DEFAULT_INTERVAL_SECONDS;
|
|
4035
4087
|
var init_config_operation_command = __esm({
|
|
4036
4088
|
"src/config-operation-command.ts"() {
|
|
4037
4089
|
"use strict";
|
|
4038
4090
|
init_cjs_shims();
|
|
4039
4091
|
import_apps6 = require("@odla-ai/apps");
|
|
4040
|
-
|
|
4092
|
+
import_node_path10 = require("path");
|
|
4041
4093
|
init_admin_ai_auth();
|
|
4042
4094
|
init_version();
|
|
4043
4095
|
init_config();
|
|
@@ -4360,7 +4412,7 @@ async function inspectConfig(options) {
|
|
|
4360
4412
|
platform: cfg.platformUrl,
|
|
4361
4413
|
scope: "app:config:read",
|
|
4362
4414
|
token: options.token,
|
|
4363
|
-
tokenFile: (0,
|
|
4415
|
+
tokenFile: (0, import_node_path11.join)(cfg.rootDir, ".odla", "admin-token.local.json"),
|
|
4364
4416
|
rootDir: cfg.rootDir,
|
|
4365
4417
|
email: options.email,
|
|
4366
4418
|
open: options.open,
|
|
@@ -4489,13 +4541,13 @@ function studioSettingsUrl(reconciliation) {
|
|
|
4489
4541
|
function quoteArg2(value2) {
|
|
4490
4542
|
return `'${value2.replace(/'/g, `'\\''`)}'`;
|
|
4491
4543
|
}
|
|
4492
|
-
var import_apps8,
|
|
4544
|
+
var import_apps8, import_node_path11;
|
|
4493
4545
|
var init_config_reconcile_command = __esm({
|
|
4494
4546
|
"src/config-reconcile-command.ts"() {
|
|
4495
4547
|
"use strict";
|
|
4496
4548
|
init_cjs_shims();
|
|
4497
4549
|
import_apps8 = require("@odla-ai/apps");
|
|
4498
|
-
|
|
4550
|
+
import_node_path11 = require("path");
|
|
4499
4551
|
init_admin_ai_auth();
|
|
4500
4552
|
init_config();
|
|
4501
4553
|
init_config_reconcile_digest();
|
|
@@ -4508,15 +4560,15 @@ var init_config_reconcile_command = __esm({
|
|
|
4508
4560
|
// src/wrangler.ts
|
|
4509
4561
|
function findWranglerConfig(rootDir) {
|
|
4510
4562
|
for (const name of WRANGLER_CONFIG_FILES) {
|
|
4511
|
-
const path = (0,
|
|
4512
|
-
if ((0,
|
|
4563
|
+
const path = (0, import_node_path12.join)(rootDir, name);
|
|
4564
|
+
if ((0, import_node_fs14.existsSync)(path)) return path;
|
|
4513
4565
|
}
|
|
4514
4566
|
return null;
|
|
4515
4567
|
}
|
|
4516
4568
|
function readWranglerConfig(path) {
|
|
4517
4569
|
if (path.endsWith(".toml")) return null;
|
|
4518
4570
|
try {
|
|
4519
|
-
return JSON.parse(stripJsonComments((0,
|
|
4571
|
+
return JSON.parse(stripJsonComments((0, import_node_fs14.readFileSync)(path, "utf8")));
|
|
4520
4572
|
} catch {
|
|
4521
4573
|
return null;
|
|
4522
4574
|
}
|
|
@@ -4620,14 +4672,14 @@ function wranglerBulkSecrets(run, opts) {
|
|
|
4620
4672
|
];
|
|
4621
4673
|
return run("npx", args, { input: JSON.stringify(opts.secrets), cwd: opts.cwd });
|
|
4622
4674
|
}
|
|
4623
|
-
var import_node_child_process2,
|
|
4675
|
+
var import_node_child_process2, import_node_fs14, import_node_path12, defaultRunner, WRANGLER_CONFIG_FILES;
|
|
4624
4676
|
var init_wrangler = __esm({
|
|
4625
4677
|
"src/wrangler.ts"() {
|
|
4626
4678
|
"use strict";
|
|
4627
4679
|
init_cjs_shims();
|
|
4628
4680
|
import_node_child_process2 = require("child_process");
|
|
4629
|
-
|
|
4630
|
-
|
|
4681
|
+
import_node_fs14 = require("fs");
|
|
4682
|
+
import_node_path12 = require("path");
|
|
4631
4683
|
defaultRunner = (cmd, args, opts) => new Promise((resolvePromise, reject) => {
|
|
4632
4684
|
const child = (0, import_node_child_process2.spawn)(cmd, args, { cwd: opts?.cwd, stdio: ["pipe", "pipe", "pipe"] });
|
|
4633
4685
|
let stdout = "";
|
|
@@ -4686,10 +4738,10 @@ function wranglerWarnings(rootDir) {
|
|
|
4686
4738
|
for (const { label, block } of blocks) {
|
|
4687
4739
|
const assets = block.assets;
|
|
4688
4740
|
if (assets?.directory) {
|
|
4689
|
-
const dir = (0,
|
|
4690
|
-
if (dir === (0,
|
|
4741
|
+
const dir = (0, import_node_path13.resolve)(rootDir, assets.directory);
|
|
4742
|
+
if (dir === (0, import_node_path13.resolve)(rootDir)) {
|
|
4691
4743
|
warnings.push(`${label}assets.directory is the project root \u2014 point it at a dedicated build dir (wrangler dev fails with "spawn EBADF")`);
|
|
4692
|
-
} else if ((0,
|
|
4744
|
+
} else if ((0, import_node_fs15.existsSync)((0, import_node_path13.join)(dir, "node_modules"))) {
|
|
4693
4745
|
warnings.push(`${label}assets.directory contains node_modules \u2014 wrangler dev's watcher will exhaust file descriptors`);
|
|
4694
4746
|
}
|
|
4695
4747
|
}
|
|
@@ -4724,13 +4776,13 @@ function o11yProjectWarnings(rootDir) {
|
|
|
4724
4776
|
warnings.push("cannot verify o11y Worker instrumentation \u2014 add a parseable wrangler.jsonc/json config");
|
|
4725
4777
|
return warnings;
|
|
4726
4778
|
}
|
|
4727
|
-
const main = typeof config.main === "string" ? (0,
|
|
4728
|
-
if (!main || !(0,
|
|
4779
|
+
const main = typeof config.main === "string" ? (0, import_node_path13.resolve)(rootDir, config.main) : null;
|
|
4780
|
+
if (!main || !(0, import_node_fs15.existsSync)(main)) {
|
|
4729
4781
|
warnings.push("cannot verify o11y Worker instrumentation \u2014 wrangler main is missing or unreadable");
|
|
4730
4782
|
} else {
|
|
4731
4783
|
let source = "";
|
|
4732
4784
|
try {
|
|
4733
|
-
source = (0,
|
|
4785
|
+
source = (0, import_node_fs15.readFileSync)(main, "utf8");
|
|
4734
4786
|
} catch {
|
|
4735
4787
|
}
|
|
4736
4788
|
if (!/\bwithObservability\b/.test(source)) {
|
|
@@ -4754,19 +4806,19 @@ function calendarProjectWarnings(rootDir) {
|
|
|
4754
4806
|
}
|
|
4755
4807
|
function readPackageJson(rootDir) {
|
|
4756
4808
|
try {
|
|
4757
|
-
return JSON.parse((0,
|
|
4809
|
+
return JSON.parse((0, import_node_fs15.readFileSync)((0, import_node_path13.join)(rootDir, "package.json"), "utf8"));
|
|
4758
4810
|
} catch {
|
|
4759
4811
|
return null;
|
|
4760
4812
|
}
|
|
4761
4813
|
}
|
|
4762
|
-
var import_node_child_process3,
|
|
4814
|
+
var import_node_child_process3, import_node_fs15, import_node_path13, defaultExec;
|
|
4763
4815
|
var init_doctor_checks = __esm({
|
|
4764
4816
|
"src/doctor-checks.ts"() {
|
|
4765
4817
|
"use strict";
|
|
4766
4818
|
init_cjs_shims();
|
|
4767
4819
|
import_node_child_process3 = require("child_process");
|
|
4768
|
-
|
|
4769
|
-
|
|
4820
|
+
import_node_fs15 = require("fs");
|
|
4821
|
+
import_node_path13 = require("path");
|
|
4770
4822
|
init_redact();
|
|
4771
4823
|
init_local();
|
|
4772
4824
|
init_wrangler();
|
|
@@ -5103,9 +5155,9 @@ var init_harness_options = __esm({
|
|
|
5103
5155
|
// src/init.ts
|
|
5104
5156
|
function initProject(options) {
|
|
5105
5157
|
const out = options.stdout ?? console;
|
|
5106
|
-
const rootDir = (0,
|
|
5107
|
-
const configPath = (0,
|
|
5108
|
-
if ((0,
|
|
5158
|
+
const rootDir = (0, import_node_path14.resolve)(options.rootDir ?? process.cwd());
|
|
5159
|
+
const configPath = (0, import_node_path14.resolve)(rootDir, options.configPath ?? "odla.config.mjs");
|
|
5160
|
+
if ((0, import_node_fs16.existsSync)(configPath) && !options.force) {
|
|
5109
5161
|
throw new Error(`${configPath} already exists. Pass --force to overwrite.`);
|
|
5110
5162
|
}
|
|
5111
5163
|
if (!/^[a-z0-9][a-z0-9-]*$/.test(options.appId)) {
|
|
@@ -5121,20 +5173,20 @@ function initProject(options) {
|
|
|
5121
5173
|
}
|
|
5122
5174
|
}
|
|
5123
5175
|
const aiProvider = options.aiProvider;
|
|
5124
|
-
(0,
|
|
5125
|
-
(0,
|
|
5126
|
-
(0,
|
|
5127
|
-
(0,
|
|
5128
|
-
writeIfMissing((0,
|
|
5129
|
-
writeIfMissing((0,
|
|
5176
|
+
(0, import_node_fs16.mkdirSync)((0, import_node_path14.dirname)(configPath), { recursive: true });
|
|
5177
|
+
(0, import_node_fs16.mkdirSync)((0, import_node_path14.resolve)(rootDir, "src/odla"), { recursive: true });
|
|
5178
|
+
(0, import_node_fs16.mkdirSync)((0, import_node_path14.resolve)(rootDir, ".odla"), { recursive: true });
|
|
5179
|
+
(0, import_node_fs16.writeFileSync)(configPath, configTemplate({ appId: options.appId, name: options.name, envs, services, aiProvider }));
|
|
5180
|
+
writeIfMissing((0, import_node_path14.resolve)(rootDir, "src/odla/schema.mjs"), schemaTemplate());
|
|
5181
|
+
writeIfMissing((0, import_node_path14.resolve)(rootDir, "src/odla/rules.mjs"), rulesTemplate());
|
|
5130
5182
|
ensureGitignore(rootDir);
|
|
5131
5183
|
out.log(`created ${relativeDisplay(configPath, rootDir)}`);
|
|
5132
5184
|
out.log("created src/odla/schema.mjs and src/odla/rules.mjs");
|
|
5133
5185
|
out.log("updated .gitignore for local odla credentials");
|
|
5134
5186
|
}
|
|
5135
5187
|
function writeIfMissing(path, text3) {
|
|
5136
|
-
if ((0,
|
|
5137
|
-
(0,
|
|
5188
|
+
if ((0, import_node_fs16.existsSync)(path)) return;
|
|
5189
|
+
(0, import_node_fs16.writeFileSync)(path, text3);
|
|
5138
5190
|
}
|
|
5139
5191
|
function configTemplate(input) {
|
|
5140
5192
|
const calendar = input.services.includes("calendar") ? ` calendar: {
|
|
@@ -5237,13 +5289,13 @@ function defaultKeyEnv(provider) {
|
|
|
5237
5289
|
function relativeDisplay(path, rootDir) {
|
|
5238
5290
|
return path.startsWith(rootDir) ? path.slice(rootDir.length + 1) : path;
|
|
5239
5291
|
}
|
|
5240
|
-
var
|
|
5292
|
+
var import_node_fs16, import_node_path14, import_apps9;
|
|
5241
5293
|
var init_init = __esm({
|
|
5242
5294
|
"src/init.ts"() {
|
|
5243
5295
|
"use strict";
|
|
5244
5296
|
init_cjs_shims();
|
|
5245
|
-
|
|
5246
|
-
|
|
5297
|
+
import_node_fs16 = require("fs");
|
|
5298
|
+
import_node_path14 = require("path");
|
|
5247
5299
|
import_apps9 = require("@odla-ai/apps");
|
|
5248
5300
|
init_local();
|
|
5249
5301
|
}
|
|
@@ -5576,8 +5628,8 @@ function installSkill(options = {}) {
|
|
|
5576
5628
|
const files = listFiles(sourceDir);
|
|
5577
5629
|
if (files.length === 0) throw new Error(`no bundled skills found at ${sourceDir}`);
|
|
5578
5630
|
const harnesses = normalizeHarnesses(options.harnesses, options.global === true);
|
|
5579
|
-
const root = (0,
|
|
5580
|
-
const home = (0,
|
|
5631
|
+
const root = (0, import_node_path15.resolve)(options.dir ?? process.cwd());
|
|
5632
|
+
const home = (0, import_node_path15.resolve)(options.homeDir ?? (0, import_node_os3.homedir)());
|
|
5581
5633
|
const plans = /* @__PURE__ */ new Map();
|
|
5582
5634
|
const targets = /* @__PURE__ */ new Map();
|
|
5583
5635
|
const rememberTarget = (harness, target) => {
|
|
@@ -5591,48 +5643,48 @@ function installSkill(options = {}) {
|
|
|
5591
5643
|
plans.set(target, { target, content: content2, boundary, managedMerge });
|
|
5592
5644
|
};
|
|
5593
5645
|
const planSkillTree = (targetDir2, boundary = root) => {
|
|
5594
|
-
for (const rel of files) plan((0,
|
|
5646
|
+
for (const rel of files) plan((0, import_node_path15.join)(targetDir2, rel), (0, import_node_fs17.readFileSync)((0, import_node_path15.join)(sourceDir, rel), "utf8"), false, boundary);
|
|
5595
5647
|
};
|
|
5596
5648
|
let targetDir;
|
|
5597
5649
|
if (options.global) {
|
|
5598
|
-
const claudeRoot = (0,
|
|
5599
|
-
const codexRoot = (0,
|
|
5650
|
+
const claudeRoot = (0, import_node_path15.join)(home, ".claude", "skills");
|
|
5651
|
+
const codexRoot = (0, import_node_path15.resolve)(options.codexHomeDir ?? process.env.CODEX_HOME ?? (0, import_node_path15.join)(home, ".codex"), "skills");
|
|
5600
5652
|
targetDir = harnesses[0] === "codex" ? codexRoot : claudeRoot;
|
|
5601
5653
|
for (const harness of harnesses) {
|
|
5602
5654
|
const skillRoot = harness === "claude" ? claudeRoot : codexRoot;
|
|
5603
|
-
planSkillTree(skillRoot, harness === "claude" ? home : (0,
|
|
5655
|
+
planSkillTree(skillRoot, harness === "claude" ? home : (0, import_node_path15.dirname)((0, import_node_path15.dirname)(codexRoot)));
|
|
5604
5656
|
rememberTarget(harness, skillRoot);
|
|
5605
5657
|
}
|
|
5606
5658
|
} else {
|
|
5607
|
-
const sharedRoot = (0,
|
|
5659
|
+
const sharedRoot = (0, import_node_path15.join)(root, ".agents", "skills");
|
|
5608
5660
|
planSkillTree(sharedRoot);
|
|
5609
|
-
const claudeRoot = (0,
|
|
5661
|
+
const claudeRoot = (0, import_node_path15.join)(root, ".claude", "skills");
|
|
5610
5662
|
targetDir = harnesses.includes("claude") ? claudeRoot : sharedRoot;
|
|
5611
5663
|
for (const harness of harnesses) rememberTarget(harness, sharedRoot);
|
|
5612
5664
|
if (harnesses.includes("claude")) {
|
|
5613
5665
|
for (const skill of skillNames(files)) {
|
|
5614
|
-
const canonical2 = (0,
|
|
5615
|
-
plan((0,
|
|
5666
|
+
const canonical2 = (0, import_node_fs17.readFileSync)((0, import_node_path15.join)(sourceDir, skill, "SKILL.md"), "utf8");
|
|
5667
|
+
plan((0, import_node_path15.join)(claudeRoot, skill, "SKILL.md"), claudeAdapter(skill, canonical2));
|
|
5616
5668
|
}
|
|
5617
5669
|
rememberTarget("claude", claudeRoot);
|
|
5618
5670
|
}
|
|
5619
5671
|
if (harnesses.includes("cursor")) {
|
|
5620
|
-
const cursorRule = (0,
|
|
5672
|
+
const cursorRule = (0, import_node_path15.join)(root, ".cursor", "rules", "odla.mdc");
|
|
5621
5673
|
plan(cursorRule, CURSOR_RULE);
|
|
5622
5674
|
rememberTarget("cursor", cursorRule);
|
|
5623
5675
|
}
|
|
5624
5676
|
if (harnesses.includes("agents")) {
|
|
5625
|
-
const agentsFile = (0,
|
|
5677
|
+
const agentsFile = (0, import_node_path15.join)(root, "AGENTS.md");
|
|
5626
5678
|
plan(agentsFile, managedFileContent(agentsFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
|
|
5627
5679
|
rememberTarget("agents", agentsFile);
|
|
5628
5680
|
}
|
|
5629
5681
|
if (harnesses.includes("copilot")) {
|
|
5630
|
-
const copilotFile = (0,
|
|
5682
|
+
const copilotFile = (0, import_node_path15.join)(root, ".github", "copilot-instructions.md");
|
|
5631
5683
|
plan(copilotFile, managedFileContent(copilotFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
|
|
5632
5684
|
rememberTarget("copilot", copilotFile);
|
|
5633
5685
|
}
|
|
5634
5686
|
if (harnesses.includes("gemini")) {
|
|
5635
|
-
const geminiFile = (0,
|
|
5687
|
+
const geminiFile = (0, import_node_path15.join)(root, "GEMINI.md");
|
|
5636
5688
|
plan(geminiFile, managedFileContent(geminiFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
|
|
5637
5689
|
rememberTarget("gemini", geminiFile);
|
|
5638
5690
|
}
|
|
@@ -5646,11 +5698,11 @@ function installSkill(options = {}) {
|
|
|
5646
5698
|
conflicts.push(`${file.target} (redirected by symbolic link ${symlink})`);
|
|
5647
5699
|
continue;
|
|
5648
5700
|
}
|
|
5649
|
-
if (!(0,
|
|
5701
|
+
if (!(0, import_node_fs17.existsSync)(file.target)) {
|
|
5650
5702
|
writtenPaths.add(file.target);
|
|
5651
5703
|
continue;
|
|
5652
5704
|
}
|
|
5653
|
-
const current = (0,
|
|
5705
|
+
const current = (0, import_node_fs17.readFileSync)(file.target, "utf8");
|
|
5654
5706
|
if (current === file.content) {
|
|
5655
5707
|
unchangedPaths.add(file.target);
|
|
5656
5708
|
} else if (file.managedMerge || options.force) {
|
|
@@ -5667,9 +5719,9 @@ ${conflicts.map((f) => ` - ${f}`).join("\n")}`
|
|
|
5667
5719
|
);
|
|
5668
5720
|
}
|
|
5669
5721
|
for (const file of plans.values()) {
|
|
5670
|
-
if (!(0,
|
|
5671
|
-
(0,
|
|
5672
|
-
(0,
|
|
5722
|
+
if (!(0, import_node_fs17.existsSync)(file.target) || (0, import_node_fs17.readFileSync)(file.target, "utf8") !== file.content) {
|
|
5723
|
+
(0, import_node_fs17.mkdirSync)((0, import_node_path15.dirname)(file.target), { recursive: true });
|
|
5724
|
+
(0, import_node_fs17.writeFileSync)(file.target, file.content);
|
|
5673
5725
|
}
|
|
5674
5726
|
}
|
|
5675
5727
|
const skills = skillNames(files);
|
|
@@ -5688,7 +5740,7 @@ ${conflicts.map((f) => ` - ${f}`).join("\n")}`
|
|
|
5688
5740
|
};
|
|
5689
5741
|
}
|
|
5690
5742
|
function pathsUnder(root, paths) {
|
|
5691
|
-
return [...paths].map((path) => (0,
|
|
5743
|
+
return [...paths].map((path) => (0, import_node_path15.relative)(root, path)).filter((path) => path !== ".." && !path.startsWith(`..${import_node_path15.sep}`) && !(0, import_node_path15.isAbsolute)(path)).sort();
|
|
5692
5744
|
}
|
|
5693
5745
|
function normalizeHarnesses(values, global) {
|
|
5694
5746
|
const requested = values?.length ? values : ["claude"];
|
|
@@ -5710,9 +5762,9 @@ function normalizeHarnesses(values, global) {
|
|
|
5710
5762
|
function managedFileContent(path, block, force, boundary) {
|
|
5711
5763
|
const symlink = symlinkedComponent(boundary, path);
|
|
5712
5764
|
if (symlink) throw new Error(`refusing to manage ${path}: symbolic link component ${symlink}`);
|
|
5713
|
-
if (!(0,
|
|
5765
|
+
if (!(0, import_node_fs17.existsSync)(path)) return `${block}
|
|
5714
5766
|
`;
|
|
5715
|
-
const current = (0,
|
|
5767
|
+
const current = (0, import_node_fs17.readFileSync)(path, "utf8");
|
|
5716
5768
|
const start = "<!-- odla-ai agent setup:start -->";
|
|
5717
5769
|
const end = "<!-- odla-ai agent setup:end -->";
|
|
5718
5770
|
const startAt = current.indexOf(start);
|
|
@@ -5733,15 +5785,15 @@ function managedFileContent(path, block, force, boundary) {
|
|
|
5733
5785
|
return `${current.slice(0, startAt)}${block}${current.slice(afterEnd)}`;
|
|
5734
5786
|
}
|
|
5735
5787
|
function symlinkedComponent(boundary, target) {
|
|
5736
|
-
const rel = (0,
|
|
5737
|
-
if (rel === ".." || rel.startsWith(`..${
|
|
5788
|
+
const rel = (0, import_node_path15.relative)(boundary, target);
|
|
5789
|
+
if (rel === ".." || rel.startsWith(`..${import_node_path15.sep}`) || (0, import_node_path15.isAbsolute)(rel)) {
|
|
5738
5790
|
throw new Error(`agent setup target escapes its install root: ${target}`);
|
|
5739
5791
|
}
|
|
5740
5792
|
let current = boundary;
|
|
5741
|
-
for (const part of rel.split(
|
|
5742
|
-
current = (0,
|
|
5793
|
+
for (const part of rel.split(import_node_path15.sep).filter(Boolean)) {
|
|
5794
|
+
current = (0, import_node_path15.join)(current, part);
|
|
5743
5795
|
try {
|
|
5744
|
-
if ((0,
|
|
5796
|
+
if ((0, import_node_fs17.lstatSync)(current).isSymbolicLink()) return current;
|
|
5745
5797
|
} catch (error) {
|
|
5746
5798
|
if (error.code !== "ENOENT") throw error;
|
|
5747
5799
|
}
|
|
@@ -5752,26 +5804,26 @@ function skillNames(files) {
|
|
|
5752
5804
|
return [...new Set(files.filter((file) => /(^|[\\/])SKILL\.md$/.test(file)).map((file) => file.split(/[\\/]/)[0]))].sort();
|
|
5753
5805
|
}
|
|
5754
5806
|
function listFiles(dir) {
|
|
5755
|
-
if (!(0,
|
|
5807
|
+
if (!(0, import_node_fs17.existsSync)(dir)) return [];
|
|
5756
5808
|
const results = [];
|
|
5757
5809
|
const walk = (current) => {
|
|
5758
|
-
for (const entry of (0,
|
|
5759
|
-
const path = (0,
|
|
5810
|
+
for (const entry of (0, import_node_fs17.readdirSync)(current, { withFileTypes: true })) {
|
|
5811
|
+
const path = (0, import_node_path15.join)(current, entry.name);
|
|
5760
5812
|
if (entry.isDirectory()) walk(path);
|
|
5761
|
-
else results.push((0,
|
|
5813
|
+
else results.push((0, import_node_path15.relative)(dir, path));
|
|
5762
5814
|
}
|
|
5763
5815
|
};
|
|
5764
5816
|
walk(dir);
|
|
5765
5817
|
return results.sort();
|
|
5766
5818
|
}
|
|
5767
|
-
var
|
|
5819
|
+
var import_node_fs17, import_node_os3, import_node_path15, import_node_url2, AGENT_HARNESSES;
|
|
5768
5820
|
var init_skill = __esm({
|
|
5769
5821
|
"src/skill.ts"() {
|
|
5770
5822
|
"use strict";
|
|
5771
5823
|
init_cjs_shims();
|
|
5772
|
-
|
|
5773
|
-
|
|
5774
|
-
|
|
5824
|
+
import_node_fs17 = require("fs");
|
|
5825
|
+
import_node_os3 = require("os");
|
|
5826
|
+
import_node_path15 = require("path");
|
|
5775
5827
|
import_node_url2 = require("url");
|
|
5776
5828
|
init_skill_adapters();
|
|
5777
5829
|
AGENT_HARNESSES = ["claude", "codex", "cursor", "copilot", "gemini", "agents"];
|
|
@@ -7322,8 +7374,8 @@ function rollup(graph, kind, options = {}) {
|
|
|
7322
7374
|
for (const node of nodesOfKind(graph, kind)) {
|
|
7323
7375
|
if (options.prefix && !node.name.startsWith(options.prefix)) continue;
|
|
7324
7376
|
const key = node.name.split(separator).slice(0, depth).join(separator);
|
|
7325
|
-
const
|
|
7326
|
-
if (
|
|
7377
|
+
const list3 = groups.get(key);
|
|
7378
|
+
if (list3) list3.push(node);
|
|
7327
7379
|
else groups.set(key, [node]);
|
|
7328
7380
|
}
|
|
7329
7381
|
return [...groups].map(([prefix, nodes]) => ({
|
|
@@ -7348,7 +7400,7 @@ function dirname9(path) {
|
|
|
7348
7400
|
const at = path.lastIndexOf("/");
|
|
7349
7401
|
return at <= 0 ? "." : path.slice(0, at);
|
|
7350
7402
|
}
|
|
7351
|
-
function
|
|
7403
|
+
function join13(base, specifier) {
|
|
7352
7404
|
const parts = [];
|
|
7353
7405
|
const segments = `${base === "." ? "" : `${base}/`}${specifier}`.split("/");
|
|
7354
7406
|
for (const segment of segments) {
|
|
@@ -7360,7 +7412,7 @@ function join12(base, specifier) {
|
|
|
7360
7412
|
}
|
|
7361
7413
|
function resolveImport(fromPath, specifier, known) {
|
|
7362
7414
|
if (!specifier.startsWith(".")) return null;
|
|
7363
|
-
const base =
|
|
7415
|
+
const base = join13(dirname9(fromPath), specifier);
|
|
7364
7416
|
const candidates = [
|
|
7365
7417
|
base,
|
|
7366
7418
|
base.replace(/\.js$/, ".ts"),
|
|
@@ -10286,8 +10338,8 @@ var init_code_runtime_config = __esm({
|
|
|
10286
10338
|
// src/code-connect.ts
|
|
10287
10339
|
async function codeConnect(options) {
|
|
10288
10340
|
const cwd = options.cwd ?? process.cwd();
|
|
10289
|
-
const configPath = (0,
|
|
10290
|
-
const cfg = (0,
|
|
10341
|
+
const configPath = (0, import_node_path16.resolve)(cwd, options.configPath);
|
|
10342
|
+
const cfg = (0, import_node_fs18.existsSync)(configPath) ? await loadProjectConfig(configPath) : null;
|
|
10291
10343
|
const requestedAppId = options.appId?.trim();
|
|
10292
10344
|
if (requestedAppId && !/^[a-z0-9][a-z0-9-]{1,62}$/.test(requestedAppId)) {
|
|
10293
10345
|
throw new Error("--app-id must be a valid odla app id");
|
|
@@ -10316,7 +10368,7 @@ async function codeConnect(options) {
|
|
|
10316
10368
|
const doFetch = options.fetch ?? fetch;
|
|
10317
10369
|
const engine = await (options.selectEngine ?? selectContainerEngine)(options.engine ?? "auto");
|
|
10318
10370
|
const hostPlatform = process.platform === "darwin" ? "macos" : "linux";
|
|
10319
|
-
const hostName = (options.name ?? (0,
|
|
10371
|
+
const hostName = (options.name ?? (0, import_node_os4.hostname)()).trim();
|
|
10320
10372
|
if (!hostName || hostName.length > 120) throw new Error("--name must contain 1 to 120 characters");
|
|
10321
10373
|
const repository = await inferGitHubRepository(cwd, options.readGitOrigin);
|
|
10322
10374
|
const localSource = await (options.prepareLocalSource ?? prepareCodeLocalSource)(
|
|
@@ -10353,8 +10405,8 @@ async function codeConnect(options) {
|
|
|
10353
10405
|
platform: hostPlatform,
|
|
10354
10406
|
arch: process.arch,
|
|
10355
10407
|
engines: [engine],
|
|
10356
|
-
cpuCount: (0,
|
|
10357
|
-
memoryBytes: (0,
|
|
10408
|
+
cpuCount: (0, import_node_os4.cpus)().length,
|
|
10409
|
+
memoryBytes: (0, import_node_os4.totalmem)(),
|
|
10358
10410
|
source: descriptor2,
|
|
10359
10411
|
images: {
|
|
10360
10412
|
ready: true,
|
|
@@ -10451,14 +10503,14 @@ function apiFailure(action2, status, value2) {
|
|
|
10451
10503
|
function record6(value2) {
|
|
10452
10504
|
return value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
|
|
10453
10505
|
}
|
|
10454
|
-
var
|
|
10506
|
+
var import_node_fs18, import_node_os4, import_node_path16;
|
|
10455
10507
|
var init_code_connect = __esm({
|
|
10456
10508
|
"src/code-connect.ts"() {
|
|
10457
10509
|
"use strict";
|
|
10458
10510
|
init_cjs_shims();
|
|
10459
|
-
|
|
10460
|
-
|
|
10461
|
-
|
|
10511
|
+
import_node_fs18 = require("fs");
|
|
10512
|
+
import_node_os4 = require("os");
|
|
10513
|
+
import_node_path16 = require("path");
|
|
10462
10514
|
init_node();
|
|
10463
10515
|
init_admin_ai_auth();
|
|
10464
10516
|
init_config();
|
|
@@ -10775,7 +10827,7 @@ function developerTokenStatus(context, parsed, now = Date.now()) {
|
|
|
10775
10827
|
const cacheStatus = !cached?.token ? "missing" : cached.platform !== context.platform.value ? "other-platform" : (cached.expiresAt ?? 0) <= now + 6e4 ? "expired" : "valid";
|
|
10776
10828
|
const source = clean3(
|
|
10777
10829
|
stringOpt(parsed.options.token)
|
|
10778
|
-
) ? "flag" : clean3(
|
|
10830
|
+
) ? "flag" : clean3(import_node_process12.default.env.ODLA_DEV_TOKEN) ? "environment" : cacheStatus === "valid" ? "cache" : "missing";
|
|
10779
10831
|
return {
|
|
10780
10832
|
source,
|
|
10781
10833
|
cacheFile: context.cfg.local.tokenFile,
|
|
@@ -10786,12 +10838,12 @@ function clean3(value2) {
|
|
|
10786
10838
|
const normalized = value2?.trim();
|
|
10787
10839
|
return normalized || void 0;
|
|
10788
10840
|
}
|
|
10789
|
-
var
|
|
10841
|
+
var import_node_process12;
|
|
10790
10842
|
var init_operator_credentials = __esm({
|
|
10791
10843
|
"src/operator-credentials.ts"() {
|
|
10792
10844
|
"use strict";
|
|
10793
10845
|
init_cjs_shims();
|
|
10794
|
-
|
|
10846
|
+
import_node_process12 = __toESM(require("process"), 1);
|
|
10795
10847
|
init_argv();
|
|
10796
10848
|
init_local();
|
|
10797
10849
|
}
|
|
@@ -11132,6 +11184,9 @@ Usage:
|
|
|
11132
11184
|
odla-ai security run [target] --ack-redacted-source [--env dev] [--profile odla] [--fail-on high]
|
|
11133
11185
|
odla-ai security run [target] --self --ack-redacted-source
|
|
11134
11186
|
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]
|
|
11187
|
+
odla-ai device enroll [--app <id>[,<id>...]] [--name <label>] [--capability <c>[,<c>...]] [--email <odla-account>] [--no-open] [--json]
|
|
11188
|
+
odla-ai device list [--email <odla-account>] [--json]
|
|
11189
|
+
odla-ai device revoke <device-id> [--email <odla-account>] [--json]
|
|
11135
11190
|
odla-ai credentials list [--config odla.config.mjs] [--env dev] [--all] [--json]
|
|
11136
11191
|
odla-ai credentials revoke <receipt-id> [--config odla.config.mjs] [--json]
|
|
11137
11192
|
odla-ai smoke [--config odla.config.mjs] [--env dev] [--runtime] [--email <odla-account>] [--no-open]
|
|
@@ -11239,6 +11294,10 @@ Commands:
|
|
|
11239
11294
|
stable status, incident, and report JSON to agents and CI.
|
|
11240
11295
|
platform Read canonical fleet health, releases, provider load/freshness,
|
|
11241
11296
|
explicit unknowns, and next actions through a read-only grant.
|
|
11297
|
+
device Enrol THIS machine once, then stop asking. A human approves the
|
|
11298
|
+
enrollment in the browser; from then on this terminal mints its
|
|
11299
|
+
own short-lived credentials for the named projects with nobody's
|
|
11300
|
+
attention, until the device expires or is revoked.
|
|
11242
11301
|
provision Register services, compose integrations, persist credentials, optionally push secrets.
|
|
11243
11302
|
"provision --live --yes" initializes only the live instance of
|
|
11244
11303
|
an existing sandbox app and enables every configured service;
|
|
@@ -12613,14 +12672,14 @@ function readPmProjectContext(rootDir) {
|
|
|
12613
12672
|
function writePmProjectContext(rootDir, value2) {
|
|
12614
12673
|
writePrivateJson(pmProjectContextFile(rootDir), { ...value2, selectedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
12615
12674
|
}
|
|
12616
|
-
var
|
|
12675
|
+
var import_node_path17, pmProjectContextFile;
|
|
12617
12676
|
var init_pm_project_context = __esm({
|
|
12618
12677
|
"src/pm-project-context.ts"() {
|
|
12619
12678
|
"use strict";
|
|
12620
12679
|
init_cjs_shims();
|
|
12621
|
-
|
|
12680
|
+
import_node_path17 = require("path");
|
|
12622
12681
|
init_local();
|
|
12623
|
-
pmProjectContextFile = (rootDir) => (0,
|
|
12682
|
+
pmProjectContextFile = (rootDir) => (0, import_node_path17.resolve)(rootDir, ".odla", "pm-project.local.json");
|
|
12624
12683
|
}
|
|
12625
12684
|
});
|
|
12626
12685
|
|
|
@@ -14192,7 +14251,7 @@ async function provision(options) {
|
|
|
14192
14251
|
await provisionIntegrationSeeds(doFetch, cfg.dbEndpoint, tenantId, dbKey, database.integrations, env, out);
|
|
14193
14252
|
}
|
|
14194
14253
|
if (cfg.services.includes("ai") && cfg.ai?.provider && cfg.ai.keyEnv) {
|
|
14195
|
-
const key =
|
|
14254
|
+
const key = import_node_process13.default.env[cfg.ai.keyEnv];
|
|
14196
14255
|
if (key) {
|
|
14197
14256
|
const secretName = cfg.ai.secretName ?? defaultSecretName(cfg.ai.provider);
|
|
14198
14257
|
await (0, import_ai5.putSecret)({ endpoint: cfg.dbEndpoint, token, fetch: doFetch }, tenantId, secretName, key);
|
|
@@ -14231,14 +14290,14 @@ async function provision(options) {
|
|
|
14231
14290
|
}
|
|
14232
14291
|
}
|
|
14233
14292
|
}
|
|
14234
|
-
var import_apps13, import_ai5,
|
|
14293
|
+
var import_apps13, import_ai5, import_node_process13;
|
|
14235
14294
|
var init_provision = __esm({
|
|
14236
14295
|
"src/provision.ts"() {
|
|
14237
14296
|
"use strict";
|
|
14238
14297
|
init_cjs_shims();
|
|
14239
14298
|
import_apps13 = require("@odla-ai/apps");
|
|
14240
14299
|
import_ai5 = require("@odla-ai/ai");
|
|
14241
|
-
|
|
14300
|
+
import_node_process13 = __toESM(require("process"), 1);
|
|
14242
14301
|
init_config();
|
|
14243
14302
|
init_calendar();
|
|
14244
14303
|
init_calendar_errors();
|
|
@@ -14364,6 +14423,7 @@ var init_surface = __esm({
|
|
|
14364
14423
|
config: { diff: {}, plan: {}, apply: {} },
|
|
14365
14424
|
context: { show: {}, list: {}, save: {}, remove: {} },
|
|
14366
14425
|
credentials: { list: {}, revoke: {} },
|
|
14426
|
+
device: { enroll: {}, list: {}, revoke: {} },
|
|
14367
14427
|
// `watch`, `read`, `reply`, and `resolve` take a topic id from there on.
|
|
14368
14428
|
discuss: {
|
|
14369
14429
|
groups: {},
|
|
@@ -14432,7 +14492,7 @@ var init_surface = __esm({
|
|
|
14432
14492
|
|
|
14433
14493
|
// src/record.ts
|
|
14434
14494
|
function recordInvocation(parsed) {
|
|
14435
|
-
const file =
|
|
14495
|
+
const file = import_node_process14.default.env.ODLA_CLI_RECORD;
|
|
14436
14496
|
if (!file) return;
|
|
14437
14497
|
try {
|
|
14438
14498
|
const entry = {
|
|
@@ -14440,18 +14500,18 @@ function recordInvocation(parsed) {
|
|
|
14440
14500
|
options: Object.entries(parsed.options).map(([name, value2]) => value2 === false ? `no-${name}` : name).sort()
|
|
14441
14501
|
};
|
|
14442
14502
|
if (!entry.path.length) return;
|
|
14443
|
-
(0,
|
|
14503
|
+
(0, import_node_fs19.appendFileSync)(file, `${JSON.stringify(entry)}
|
|
14444
14504
|
`);
|
|
14445
14505
|
} catch {
|
|
14446
14506
|
}
|
|
14447
14507
|
}
|
|
14448
|
-
var
|
|
14508
|
+
var import_node_fs19, import_node_process14;
|
|
14449
14509
|
var init_record = __esm({
|
|
14450
14510
|
"src/record.ts"() {
|
|
14451
14511
|
"use strict";
|
|
14452
14512
|
init_cjs_shims();
|
|
14453
|
-
|
|
14454
|
-
|
|
14513
|
+
import_node_fs19 = require("fs");
|
|
14514
|
+
import_node_process14 = __toESM(require("process"), 1);
|
|
14455
14515
|
init_surface();
|
|
14456
14516
|
}
|
|
14457
14517
|
});
|
|
@@ -14486,6 +14546,119 @@ var init_advisory_output = __esm({
|
|
|
14486
14546
|
}
|
|
14487
14547
|
});
|
|
14488
14548
|
|
|
14549
|
+
// src/device-command.ts
|
|
14550
|
+
async function deviceCommand(parsed, deps) {
|
|
14551
|
+
const action2 = parsed.positionals[1] ?? "";
|
|
14552
|
+
const out = deps.stdout ?? console;
|
|
14553
|
+
const doFetch = deps.fetch ?? fetch;
|
|
14554
|
+
const cfg = await loadProjectConfig(stringOpt(parsed.options.config));
|
|
14555
|
+
const json = parsed.options.json === true;
|
|
14556
|
+
if (action2 === "enroll") return enroll(parsed, deps, cfg, doFetch, out, json);
|
|
14557
|
+
if (action2 === "list") return list2(parsed, deps, cfg, doFetch, out, json);
|
|
14558
|
+
if (action2 === "revoke") return revoke(parsed, deps, cfg, doFetch, out, json);
|
|
14559
|
+
throw new Error('odla-ai device expects "enroll", "list", or "revoke"');
|
|
14560
|
+
}
|
|
14561
|
+
async function enroll(parsed, deps, cfg, doFetch, out, json) {
|
|
14562
|
+
const name = stringOpt(parsed.options.name) ?? defaultDeviceName();
|
|
14563
|
+
const apps = (stringOpt(parsed.options.app) ?? cfg.app.id).split(",").map((id2) => id2.trim()).filter(Boolean);
|
|
14564
|
+
if (apps.length === 0) throw new Error("device enroll needs --app <id>[,<id>\u2026]");
|
|
14565
|
+
const token = await scopedToken2(parsed, deps, cfg, doFetch, out, `odla CLI (enroll ${name})`);
|
|
14566
|
+
const response2 = await doFetch(`${cfg.platformUrl}/registry/devices`, {
|
|
14567
|
+
method: "POST",
|
|
14568
|
+
headers: { authorization: `Bearer ${token}`, "content-type": "application/json" },
|
|
14569
|
+
body: JSON.stringify({
|
|
14570
|
+
name,
|
|
14571
|
+
platform: import_node_process15.default.platform,
|
|
14572
|
+
appIds: apps,
|
|
14573
|
+
...parsed.options.capability ? { capabilities: String(parsed.options.capability).split(",").map((c) => c.trim()).filter(Boolean) } : {}
|
|
14574
|
+
})
|
|
14575
|
+
});
|
|
14576
|
+
const body = await response2.json().catch(() => ({}));
|
|
14577
|
+
if (!response2.ok || !body.token || !body.device) {
|
|
14578
|
+
throw new Error(`device enroll failed: ${body.error?.message ?? `registry returned ${response2.status}`} (${response2.status})`);
|
|
14579
|
+
}
|
|
14580
|
+
const path = deviceCredentialPath();
|
|
14581
|
+
(0, import_node_fs20.mkdirSync)((0, import_node_path18.dirname)(path), { recursive: true });
|
|
14582
|
+
(0, import_node_fs20.writeFileSync)(path, JSON.stringify({
|
|
14583
|
+
token: body.token,
|
|
14584
|
+
platform: cfg.platformUrl.replace(/\/$/, ""),
|
|
14585
|
+
deviceId: body.device.deviceId,
|
|
14586
|
+
name
|
|
14587
|
+
}, null, 2));
|
|
14588
|
+
(0, import_node_fs20.chmodSync)(path, 384);
|
|
14589
|
+
out.error(`device: enrolled "${name}" for ${body.device.appIds.join(", ")}; credential written to ${path}`);
|
|
14590
|
+
out.error("device: this terminal will mint its own credentials from now on \u2014 no further approvals.");
|
|
14591
|
+
if (json) {
|
|
14592
|
+
out.log(JSON.stringify({ deviceId: body.device.deviceId, name, appIds: body.device.appIds, expiresAt: body.device.expiresAt }, null, 2));
|
|
14593
|
+
}
|
|
14594
|
+
}
|
|
14595
|
+
async function list2(parsed, deps, cfg, doFetch, out, json) {
|
|
14596
|
+
const token = await scopedToken2(parsed, deps, cfg, doFetch, out, "odla CLI (device list)");
|
|
14597
|
+
const response2 = await doFetch(`${cfg.platformUrl}/registry/devices`, {
|
|
14598
|
+
headers: { authorization: `Bearer ${token}` }
|
|
14599
|
+
});
|
|
14600
|
+
const body = await response2.json().catch(() => ({}));
|
|
14601
|
+
if (!response2.ok || !body.devices) {
|
|
14602
|
+
throw new Error(`device list failed: ${body.error?.message ?? `registry returned ${response2.status}`} (${response2.status})`);
|
|
14603
|
+
}
|
|
14604
|
+
if (json) return out.log(JSON.stringify(body.devices, null, 2));
|
|
14605
|
+
if (body.devices.length === 0) return out.log("no enrolled devices");
|
|
14606
|
+
for (const device of body.devices) {
|
|
14607
|
+
const state2 = device.revokedAt ? "revoked" : device.expiresAt <= Date.now() ? "expired" : "active";
|
|
14608
|
+
out.log(`${device.deviceId} ${state2.padEnd(7)} ${device.name} [${device.appIds.join(", ")}]`);
|
|
14609
|
+
}
|
|
14610
|
+
}
|
|
14611
|
+
async function revoke(parsed, deps, cfg, doFetch, out, json) {
|
|
14612
|
+
const deviceId = parsed.positionals[2];
|
|
14613
|
+
if (!deviceId) throw new Error("device revoke needs the device id from `odla-ai device list`");
|
|
14614
|
+
const token = await scopedToken2(parsed, deps, cfg, doFetch, out, "odla CLI (device revoke)");
|
|
14615
|
+
const response2 = await doFetch(`${cfg.platformUrl}/registry/devices/${encodeURIComponent(deviceId)}/revoke`, {
|
|
14616
|
+
method: "POST",
|
|
14617
|
+
headers: { authorization: `Bearer ${token}` }
|
|
14618
|
+
});
|
|
14619
|
+
if (!response2.ok) {
|
|
14620
|
+
const body = await response2.json().catch(() => ({}));
|
|
14621
|
+
throw new Error(`device revoke failed: ${body.error?.message ?? `registry returned ${response2.status}`} (${response2.status})`);
|
|
14622
|
+
}
|
|
14623
|
+
out.error(`device: revoked ${deviceId}; every credential it minted is revoked with it`);
|
|
14624
|
+
if (json) out.log(JSON.stringify({ deviceId, revoked: true }, null, 2));
|
|
14625
|
+
}
|
|
14626
|
+
async function scopedToken2(parsed, deps, cfg, doFetch, out, label) {
|
|
14627
|
+
const { credentials } = await resolveOperatorContext(parsed, { allowMissingConfig: true });
|
|
14628
|
+
const scopedTokenFile = credentials.scopedTokenFile;
|
|
14629
|
+
return getScopedPlatformToken({
|
|
14630
|
+
platform: cfg.platformUrl,
|
|
14631
|
+
scope: "app:device:enroll",
|
|
14632
|
+
email: stringOpt(parsed.options.email),
|
|
14633
|
+
label,
|
|
14634
|
+
fetch: doFetch,
|
|
14635
|
+
stdout: out,
|
|
14636
|
+
open: parsed.options.open === false ? false : parsed.options.open === true ? true : void 0,
|
|
14637
|
+
openApprovalUrl: deps.openUrl,
|
|
14638
|
+
rootDir: cfg.rootDir,
|
|
14639
|
+
tokenFile: scopedTokenFile,
|
|
14640
|
+
...stringOpt(parsed.options.token) ? { token: stringOpt(parsed.options.token) } : {}
|
|
14641
|
+
});
|
|
14642
|
+
}
|
|
14643
|
+
function defaultDeviceName() {
|
|
14644
|
+
return `${import_node_process15.default.env.HOSTNAME ?? import_node_process15.default.env.HOST ?? "machine"}-${import_node_process15.default.platform}`;
|
|
14645
|
+
}
|
|
14646
|
+
var import_node_fs20, import_node_path18, import_node_process15;
|
|
14647
|
+
var init_device_command = __esm({
|
|
14648
|
+
"src/device-command.ts"() {
|
|
14649
|
+
"use strict";
|
|
14650
|
+
init_cjs_shims();
|
|
14651
|
+
import_node_fs20 = require("fs");
|
|
14652
|
+
import_node_path18 = require("path");
|
|
14653
|
+
import_node_process15 = __toESM(require("process"), 1);
|
|
14654
|
+
init_argv();
|
|
14655
|
+
init_admin_ai_auth();
|
|
14656
|
+
init_device_session();
|
|
14657
|
+
init_config();
|
|
14658
|
+
init_operator_context();
|
|
14659
|
+
}
|
|
14660
|
+
});
|
|
14661
|
+
|
|
14489
14662
|
// src/runbook-actions.ts
|
|
14490
14663
|
async function call(ctx, method, path, body) {
|
|
14491
14664
|
const res = await ctx.doFetch(`${ctx.platformUrl.replace(/\/$/, "")}/registry/pm${path}`, {
|
|
@@ -14532,7 +14705,7 @@ async function bySlug(ctx, slug) {
|
|
|
14532
14705
|
function readBody(file, inline) {
|
|
14533
14706
|
if (inline !== void 0) return inline;
|
|
14534
14707
|
if (file === void 0) throw new Error("supply the new text with --file <path>, --file - (stdin), or --body");
|
|
14535
|
-
return (0,
|
|
14708
|
+
return (0, import_node_fs21.readFileSync)(file === "-" ? 0 : file, "utf8");
|
|
14536
14709
|
}
|
|
14537
14710
|
async function runbookList(ctx, all, query) {
|
|
14538
14711
|
const params = new URLSearchParams();
|
|
@@ -14621,12 +14794,12 @@ async function runbookRemove(ctx, slug) {
|
|
|
14621
14794
|
await call(ctx, "DELETE", `/runbook/${encodeURIComponent(runbook.id)}`);
|
|
14622
14795
|
ctx.out.log(`removed ${slug}`);
|
|
14623
14796
|
}
|
|
14624
|
-
var
|
|
14797
|
+
var import_node_fs21, PLATFORM_SCOPE, stamp;
|
|
14625
14798
|
var init_runbook_actions = __esm({
|
|
14626
14799
|
"src/runbook-actions.ts"() {
|
|
14627
14800
|
"use strict";
|
|
14628
14801
|
init_cjs_shims();
|
|
14629
|
-
|
|
14802
|
+
import_node_fs21 = require("fs");
|
|
14630
14803
|
init_version();
|
|
14631
14804
|
init_runbook_requires();
|
|
14632
14805
|
PLATFORM_SCOPE = "$platform";
|
|
@@ -14659,12 +14832,12 @@ function parseRunbook(text3, slug) {
|
|
|
14659
14832
|
};
|
|
14660
14833
|
}
|
|
14661
14834
|
function readRunbookDir(dir) {
|
|
14662
|
-
if (!(0,
|
|
14663
|
-
const files = (0,
|
|
14835
|
+
if (!(0, import_node_fs22.statSync)(dir, { throwIfNoEntry: false })?.isDirectory()) throw new Error(`not a directory: ${dir}`);
|
|
14836
|
+
const files = (0, import_node_fs22.readdirSync)(dir).filter((f) => f.endsWith(".md")).sort();
|
|
14664
14837
|
if (!files.length) throw new Error(`no .md files in ${dir}`);
|
|
14665
14838
|
return files.map((file) => {
|
|
14666
|
-
const slug = (0,
|
|
14667
|
-
const parsed = parseRunbook((0,
|
|
14839
|
+
const slug = (0, import_node_path19.basename)(file, ".md");
|
|
14840
|
+
const parsed = parseRunbook((0, import_node_fs22.readFileSync)((0, import_node_path19.join)(dir, file), "utf8"), slug);
|
|
14668
14841
|
return { file, slug, ...parsed, words: parsed.body.split(/\s+/).filter(Boolean).length };
|
|
14669
14842
|
});
|
|
14670
14843
|
}
|
|
@@ -14734,13 +14907,13 @@ async function upsert(ctx, r, visibility) {
|
|
|
14734
14907
|
);
|
|
14735
14908
|
return "updated";
|
|
14736
14909
|
}
|
|
14737
|
-
var
|
|
14910
|
+
var import_node_fs22, import_node_path19;
|
|
14738
14911
|
var init_runbook_import = __esm({
|
|
14739
14912
|
"src/runbook-import.ts"() {
|
|
14740
14913
|
"use strict";
|
|
14741
14914
|
init_cjs_shims();
|
|
14742
|
-
|
|
14743
|
-
|
|
14915
|
+
import_node_fs22 = require("fs");
|
|
14916
|
+
import_node_path19 = require("path");
|
|
14744
14917
|
init_runbook_actions();
|
|
14745
14918
|
}
|
|
14746
14919
|
});
|
|
@@ -14918,10 +15091,10 @@ ${body.split("\n").map((line2) => `+${line2}`).join("\n")}
|
|
|
14918
15091
|
}
|
|
14919
15092
|
function manifestLabeller(root) {
|
|
14920
15093
|
return (workspace) => {
|
|
14921
|
-
const manifest = (0,
|
|
14922
|
-
if (!(0,
|
|
15094
|
+
const manifest = (0, import_node_path20.join)(root, workspace, "package.json");
|
|
15095
|
+
if (!(0, import_node_fs23.existsSync)(manifest)) return void 0;
|
|
14923
15096
|
try {
|
|
14924
|
-
const name = JSON.parse((0,
|
|
15097
|
+
const name = JSON.parse((0, import_node_fs23.readFileSync)(manifest, "utf8")).name;
|
|
14925
15098
|
return typeof name === "string" ? name : void 0;
|
|
14926
15099
|
} catch {
|
|
14927
15100
|
return void 0;
|
|
@@ -14987,7 +15160,7 @@ function report4(ctx, impacts) {
|
|
|
14987
15160
|
async function runbookImpact(ctx, options, deps = {}) {
|
|
14988
15161
|
const cwd = deps.cwd ?? process.cwd();
|
|
14989
15162
|
const runGit = deps.runGit ?? gitRunner(cwd);
|
|
14990
|
-
const read3 = deps.readRepoFile ?? ((path) => (0,
|
|
15163
|
+
const read3 = deps.readRepoFile ?? ((path) => (0, import_node_fs23.readFileSync)((0, import_node_path20.join)(cwd, path), "utf8"));
|
|
14991
15164
|
const surfaces = changedSurfaces(collectDiff(runGit, options.base, read3), manifestLabeller(cwd));
|
|
14992
15165
|
if (!surfaces.length) {
|
|
14993
15166
|
return ctx.out.log(
|
|
@@ -14998,14 +15171,14 @@ async function runbookImpact(ctx, options, deps = {}) {
|
|
|
14998
15171
|
if (ctx.json) return ctx.out.log(JSON.stringify({ base: options.base, impacts }, null, 2));
|
|
14999
15172
|
report4(ctx, impacts);
|
|
15000
15173
|
}
|
|
15001
|
-
var import_node_child_process6,
|
|
15174
|
+
var import_node_child_process6, import_node_fs23, import_node_path20, SOURCE3, editHint;
|
|
15002
15175
|
var init_runbook_impact = __esm({
|
|
15003
15176
|
"src/runbook-impact.ts"() {
|
|
15004
15177
|
"use strict";
|
|
15005
15178
|
init_cjs_shims();
|
|
15006
15179
|
import_node_child_process6 = require("child_process");
|
|
15007
|
-
|
|
15008
|
-
|
|
15180
|
+
import_node_fs23 = require("fs");
|
|
15181
|
+
import_node_path20 = require("path");
|
|
15009
15182
|
init_runbook_impact_scan();
|
|
15010
15183
|
init_runbook_actions();
|
|
15011
15184
|
SOURCE3 = /\.(ts|tsx|js|jsx|mts|cts)$/;
|
|
@@ -15151,7 +15324,7 @@ var init_runbook_search_command = __esm({
|
|
|
15151
15324
|
});
|
|
15152
15325
|
|
|
15153
15326
|
// src/runbook-editor.ts
|
|
15154
|
-
function resolveEditor(env =
|
|
15327
|
+
function resolveEditor(env = import_node_process16.default.env) {
|
|
15155
15328
|
for (const name of EDITOR_ENV) {
|
|
15156
15329
|
const value2 = env[name];
|
|
15157
15330
|
if (value2 && value2.trim()) return value2.trim();
|
|
@@ -15165,8 +15338,8 @@ function defaultRun(command, path) {
|
|
|
15165
15338
|
return result.status ?? 0;
|
|
15166
15339
|
}
|
|
15167
15340
|
function editText(initial, slug, deps = {}) {
|
|
15168
|
-
const env = deps.env ??
|
|
15169
|
-
const interactive = deps.interactive ?? (() => Boolean(
|
|
15341
|
+
const env = deps.env ?? import_node_process16.default.env;
|
|
15342
|
+
const interactive = deps.interactive ?? (() => Boolean(import_node_process16.default.stdin.isTTY));
|
|
15170
15343
|
const editor = resolveEditor(env);
|
|
15171
15344
|
if (!editor)
|
|
15172
15345
|
throw new Error(
|
|
@@ -15174,28 +15347,28 @@ function editText(initial, slug, deps = {}) {
|
|
|
15174
15347
|
);
|
|
15175
15348
|
if (!interactive())
|
|
15176
15349
|
throw new Error(`cannot open an editor without a terminal \u2014 pass --file <path> or --body "\u2026" instead`);
|
|
15177
|
-
const dir = (0,
|
|
15178
|
-
const file = (0,
|
|
15350
|
+
const dir = (0, import_node_fs24.mkdtempSync)((0, import_node_path21.join)((0, import_node_os5.tmpdir)(), "odla-runbook-"));
|
|
15351
|
+
const file = (0, import_node_path21.join)(dir, `${slug}.md`);
|
|
15179
15352
|
try {
|
|
15180
|
-
(0,
|
|
15353
|
+
(0, import_node_fs24.writeFileSync)(file, initial, { mode: 384 });
|
|
15181
15354
|
const code = defaultRunOrInjected(deps)(editor, file);
|
|
15182
15355
|
if (code !== 0) throw new Error(`editor "${editor}" exited with ${code}; nothing was written`);
|
|
15183
|
-
const edited = (0,
|
|
15356
|
+
const edited = (0, import_node_fs24.readFileSync)(file, "utf8");
|
|
15184
15357
|
return edited === initial ? null : edited;
|
|
15185
15358
|
} finally {
|
|
15186
|
-
(0,
|
|
15359
|
+
(0, import_node_fs24.rmSync)(dir, { recursive: true, force: true });
|
|
15187
15360
|
}
|
|
15188
15361
|
}
|
|
15189
|
-
var import_node_child_process7,
|
|
15362
|
+
var import_node_child_process7, import_node_fs24, import_node_os5, import_node_path21, import_node_process16, EDITOR_ENV, defaultRunOrInjected;
|
|
15190
15363
|
var init_runbook_editor = __esm({
|
|
15191
15364
|
"src/runbook-editor.ts"() {
|
|
15192
15365
|
"use strict";
|
|
15193
15366
|
init_cjs_shims();
|
|
15194
15367
|
import_node_child_process7 = require("child_process");
|
|
15195
|
-
|
|
15196
|
-
|
|
15197
|
-
|
|
15198
|
-
|
|
15368
|
+
import_node_fs24 = require("fs");
|
|
15369
|
+
import_node_os5 = require("os");
|
|
15370
|
+
import_node_path21 = require("path");
|
|
15371
|
+
import_node_process16 = __toESM(require("process"), 1);
|
|
15199
15372
|
EDITOR_ENV = ["ODLA_EDITOR", "VISUAL", "EDITOR"];
|
|
15200
15373
|
defaultRunOrInjected = (deps) => deps.run ?? defaultRun;
|
|
15201
15374
|
}
|
|
@@ -15590,9 +15763,9 @@ async function runHostedSecurity(options) {
|
|
|
15590
15763
|
const appId = selfAudit ? "odla-ai" : cfg.app.id;
|
|
15591
15764
|
const env = selfAudit ? "prod" : selectEnv(options.env, cfg.envs, cfg.configPath, cfg.rootDir);
|
|
15592
15765
|
const platform = options.platform ?? cfg?.platformUrl ?? "https://odla.ai";
|
|
15593
|
-
const target = (0,
|
|
15594
|
-
const output = (0,
|
|
15595
|
-
const outputRelative = (0,
|
|
15766
|
+
const target = (0, import_node_path22.resolve)(options.target ?? cfg?.rootDir ?? ".");
|
|
15767
|
+
const output = (0, import_node_path22.resolve)(options.out ?? (0, import_node_path22.resolve)(target, ".odla/security/hosted"));
|
|
15768
|
+
const outputRelative = (0, import_node_path22.relative)(target, output).split(import_node_path22.sep).join("/");
|
|
15596
15769
|
if (!outputRelative) throw new Error("Hosted security output cannot be the repository root");
|
|
15597
15770
|
const profile = profileFor(options.profile ?? "odla", options.maxHuntTasks ?? 12);
|
|
15598
15771
|
const tokenRequest = {
|
|
@@ -15604,7 +15777,7 @@ async function runHostedSecurity(options) {
|
|
|
15604
15777
|
};
|
|
15605
15778
|
const token = await injectedToken(options, tokenRequest);
|
|
15606
15779
|
const snapshot = await (0, import_node3.snapshotDirectory)(target, {
|
|
15607
|
-
exclude: !outputRelative.startsWith("../") && !(0,
|
|
15780
|
+
exclude: !outputRelative.startsWith("../") && !(0, import_node_path22.isAbsolute)(outputRelative) ? [outputRelative] : []
|
|
15608
15781
|
});
|
|
15609
15782
|
const hosted = await (0, import_security.createPlatformSecurityReasoners)({
|
|
15610
15783
|
platform,
|
|
@@ -15622,7 +15795,7 @@ async function runHostedSecurity(options) {
|
|
|
15622
15795
|
});
|
|
15623
15796
|
const harness = (0, import_security.createSecurityHarness)({
|
|
15624
15797
|
profile,
|
|
15625
|
-
store: new import_node3.FileRunStore((0,
|
|
15798
|
+
store: new import_node3.FileRunStore((0, import_node_path22.resolve)(output, "state")),
|
|
15626
15799
|
discoveryReasoner: hosted.discoveryReasoner,
|
|
15627
15800
|
validationReasoner: hosted.validationReasoner,
|
|
15628
15801
|
policy: {
|
|
@@ -15646,7 +15819,7 @@ async function runHostedSecurity(options) {
|
|
|
15646
15819
|
function selectEnv(requested, declared, configPath, rootDir) {
|
|
15647
15820
|
const env = requested ?? (declared.includes("dev") ? "dev" : declared[0]);
|
|
15648
15821
|
if (!env || !declared.includes(env)) {
|
|
15649
|
-
const shown = (0,
|
|
15822
|
+
const shown = (0, import_node_path22.relative)(rootDir, configPath) || configPath;
|
|
15650
15823
|
throw new Error(`env "${env ?? ""}" is not declared in ${shown}`);
|
|
15651
15824
|
}
|
|
15652
15825
|
return env;
|
|
@@ -15675,17 +15848,17 @@ function printSummary(out, appId, env, run, report5, output) {
|
|
|
15675
15848
|
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}`);
|
|
15676
15849
|
if (report5.callBudget) out.log(` calls: discovery=${formatBudget(report5.callBudget.discovery)} validation=${formatBudget(report5.callBudget.validation)}`);
|
|
15677
15850
|
out.log(` findings: confirmed=${report5.metrics.confirmed} needs_reproduction=${report5.metrics.needsReproduction} candidates=${report5.metrics.candidates}`);
|
|
15678
|
-
out.log(` report: ${(0,
|
|
15851
|
+
out.log(` report: ${(0, import_node_path22.resolve)(output, "REPORT.md")}`);
|
|
15679
15852
|
}
|
|
15680
15853
|
function formatBudget(usage) {
|
|
15681
15854
|
return usage ? `${usage.usedCalls}/${usage.maxCalls} skipped=${usage.skippedCalls}` : "caller-managed";
|
|
15682
15855
|
}
|
|
15683
|
-
var
|
|
15856
|
+
var import_node_path22, import_security, import_node3;
|
|
15684
15857
|
var init_security = __esm({
|
|
15685
15858
|
"src/security.ts"() {
|
|
15686
15859
|
"use strict";
|
|
15687
15860
|
init_cjs_shims();
|
|
15688
|
-
|
|
15861
|
+
import_node_path22 = require("path");
|
|
15689
15862
|
import_security = require("@odla-ai/security");
|
|
15690
15863
|
import_node3 = require("@odla-ai/security/node");
|
|
15691
15864
|
init_config();
|
|
@@ -16219,6 +16392,10 @@ async function dispatchCli(argv2, dependencies) {
|
|
|
16219
16392
|
await contextCommand(parsed, runtime);
|
|
16220
16393
|
return;
|
|
16221
16394
|
}
|
|
16395
|
+
if (command === "device") {
|
|
16396
|
+
await deviceCommand(parsed, runtime);
|
|
16397
|
+
return;
|
|
16398
|
+
}
|
|
16222
16399
|
if (command === "credentials") {
|
|
16223
16400
|
await credentialCommand(parsed, runtime);
|
|
16224
16401
|
return;
|
|
@@ -16381,6 +16558,7 @@ var init_cli = __esm({
|
|
|
16381
16558
|
init_record();
|
|
16382
16559
|
init_advisory_output();
|
|
16383
16560
|
init_cached_credential();
|
|
16561
|
+
init_device_command();
|
|
16384
16562
|
init_redact();
|
|
16385
16563
|
init_runbook_command();
|
|
16386
16564
|
init_security_command();
|