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