@odla-ai/cli 0.35.1 → 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 +489 -231
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-SRL2TN24.js → chunk-DBZQIMES.js} +357 -137
- package/dist/chunk-DBZQIMES.js.map +1 -0
- package/dist/{cli-HS35QLXR.js → cli-BN6WLH5O.js} +2 -2
- package/dist/index.cjs +424 -204
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-SRL2TN24.js.map +0 -1
- /package/dist/{cli-HS35QLXR.js.map → cli-BN6WLH5O.js.map} +0 -0
package/dist/bin.cjs
CHANGED
|
@@ -347,10 +347,83 @@ var init_handshake_state = __esm({
|
|
|
347
347
|
}
|
|
348
348
|
});
|
|
349
349
|
|
|
350
|
+
// src/cached-credential.ts
|
|
351
|
+
function noteCachedCredential(tokenFile) {
|
|
352
|
+
noted = tokenFile;
|
|
353
|
+
}
|
|
354
|
+
function isCredentialRejection(error) {
|
|
355
|
+
const message2 = error instanceof Error ? error.message : String(error ?? "");
|
|
356
|
+
return /\((401|403)\)\s*$/.test(message2.trim());
|
|
357
|
+
}
|
|
358
|
+
function explainRejectedCredential(error) {
|
|
359
|
+
const tokenFile = noted;
|
|
360
|
+
if (!tokenFile || !isCredentialRejection(error)) return null;
|
|
361
|
+
noted = null;
|
|
362
|
+
(0, import_node_fs5.rmSync)(tokenFile, { force: true });
|
|
363
|
+
return [
|
|
364
|
+
"auth: the cached credential was rejected by odla, so it was revoked before its cached expiry.",
|
|
365
|
+
" The usual cause is a newer sign-in for this project: collecting a handshake retires the",
|
|
366
|
+
" principal's other credentials, so a second terminal or worktree supersedes this one.",
|
|
367
|
+
` Discarded ${tokenFile}; re-run this command to request a fresh approval.`
|
|
368
|
+
].join("\n");
|
|
369
|
+
}
|
|
370
|
+
var import_node_fs5, noted;
|
|
371
|
+
var init_cached_credential = __esm({
|
|
372
|
+
"src/cached-credential.ts"() {
|
|
373
|
+
"use strict";
|
|
374
|
+
init_cjs_shims();
|
|
375
|
+
import_node_fs5 = require("fs");
|
|
376
|
+
noted = null;
|
|
377
|
+
}
|
|
378
|
+
});
|
|
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
|
+
|
|
350
423
|
// src/local.ts
|
|
351
424
|
function readJsonFile(path) {
|
|
352
425
|
try {
|
|
353
|
-
return JSON.parse((0,
|
|
426
|
+
return JSON.parse((0, import_node_fs7.readFileSync)(path, "utf8"));
|
|
354
427
|
} catch {
|
|
355
428
|
return null;
|
|
356
429
|
}
|
|
@@ -360,10 +433,10 @@ function writePrivateJson(path, value2) {
|
|
|
360
433
|
`);
|
|
361
434
|
}
|
|
362
435
|
function readCredentials(path) {
|
|
363
|
-
if (!(0,
|
|
436
|
+
if (!(0, import_node_fs7.existsSync)(path)) return null;
|
|
364
437
|
let value2;
|
|
365
438
|
try {
|
|
366
|
-
value2 = JSON.parse((0,
|
|
439
|
+
value2 = JSON.parse((0, import_node_fs7.readFileSync)(path, "utf8"));
|
|
367
440
|
} catch {
|
|
368
441
|
throw new Error(`credentials file ${path} is not valid JSON; fix or remove it before provisioning`);
|
|
369
442
|
}
|
|
@@ -393,14 +466,14 @@ function mergeCredential(current, update) {
|
|
|
393
466
|
return next;
|
|
394
467
|
}
|
|
395
468
|
function ensureGitignore(rootDir, localPaths = []) {
|
|
396
|
-
const path = (0,
|
|
397
|
-
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") : "";
|
|
398
471
|
const configured = localPaths.map((localPath) => gitignoreEntry(rootDir, localPath)).filter((line2) => !!line2);
|
|
399
472
|
const wanted = [.../* @__PURE__ */ new Set([...GITIGNORE_LINES, ...configured])];
|
|
400
473
|
const missing = wanted.filter((line2) => !existing.split(/\r?\n/).includes(line2));
|
|
401
474
|
if (missing.length === 0) return;
|
|
402
475
|
const prefix = existing && !existing.endsWith("\n") ? "\n" : "";
|
|
403
|
-
(0,
|
|
476
|
+
(0, import_node_fs7.writeFileSync)(path, `${existing}${prefix}${missing.join("\n")}
|
|
404
477
|
`);
|
|
405
478
|
}
|
|
406
479
|
function o11yDevVars(cfg) {
|
|
@@ -414,7 +487,7 @@ function o11yDevVars(cfg) {
|
|
|
414
487
|
function resolveWriteDevVarsTarget(cfg, requested) {
|
|
415
488
|
if (!requested) return null;
|
|
416
489
|
if (requested === true) return cfg.local.devVarsFile;
|
|
417
|
-
return (0,
|
|
490
|
+
return (0, import_node_path4.resolve)((0, import_node_path4.dirname)(cfg.configPath), requested);
|
|
418
491
|
}
|
|
419
492
|
function writeDevVars(path, credentials, env, o11y) {
|
|
420
493
|
const entry = credentials.envs[env];
|
|
@@ -428,7 +501,7 @@ function writeDevVars(path, credentials, env, o11y) {
|
|
|
428
501
|
if (o11y.version) lines.push(`ODLA_O11Y_VERSION="${o11y.version}"`);
|
|
429
502
|
if (entry.o11yToken) lines.push(`ODLA_O11Y_TOKEN="${entry.o11yToken}"`);
|
|
430
503
|
}
|
|
431
|
-
const existing = (0,
|
|
504
|
+
const existing = (0, import_node_fs7.existsSync)(path) ? (0, import_node_fs7.readFileSync)(path, "utf8") : "";
|
|
432
505
|
const retained = existing.split(/\r?\n/).filter((line2) => !isManagedDevVar(line2));
|
|
433
506
|
while (retained.at(-1) === "") retained.pop();
|
|
434
507
|
const prefix = retained.length ? `${retained.join("\n")}
|
|
@@ -442,28 +515,28 @@ function isManagedDevVar(line2) {
|
|
|
442
515
|
return !!match?.[1] && MANAGED_DEV_VARS.has(match[1]);
|
|
443
516
|
}
|
|
444
517
|
function writePrivateText(path, text3) {
|
|
445
|
-
(0,
|
|
518
|
+
(0, import_node_fs7.mkdirSync)((0, import_node_path4.dirname)(path), { recursive: true });
|
|
446
519
|
const temporary = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
447
|
-
(0,
|
|
448
|
-
(0,
|
|
449
|
-
(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);
|
|
450
523
|
}
|
|
451
524
|
function gitignoreEntry(rootDir, path) {
|
|
452
|
-
const rel = (0,
|
|
453
|
-
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;
|
|
454
527
|
return rel.replaceAll("\\", "/");
|
|
455
528
|
}
|
|
456
529
|
function displayPath(path, rootDir = process.cwd()) {
|
|
457
|
-
const rel = (0,
|
|
530
|
+
const rel = (0, import_node_path4.relative)(rootDir, path);
|
|
458
531
|
return rel && !rel.startsWith("..") ? rel : path;
|
|
459
532
|
}
|
|
460
|
-
var
|
|
533
|
+
var import_node_fs7, import_node_path4, GITIGNORE_LINES, MANAGED_DEV_VARS;
|
|
461
534
|
var init_local = __esm({
|
|
462
535
|
"src/local.ts"() {
|
|
463
536
|
"use strict";
|
|
464
537
|
init_cjs_shims();
|
|
465
|
-
|
|
466
|
-
|
|
538
|
+
import_node_fs7 = require("fs");
|
|
539
|
+
import_node_path4 = require("path");
|
|
467
540
|
GITIGNORE_LINES = [".odla/*.local.json", ".odla/dev-token.json", ".dev.vars"];
|
|
468
541
|
MANAGED_DEV_VARS = /* @__PURE__ */ new Set([
|
|
469
542
|
"ODLA_PLATFORM",
|
|
@@ -488,17 +561,24 @@ async function getDeveloperToken(cfg, options, doFetch, out, grantRequest = {})
|
|
|
488
561
|
const cached = readJsonFile(cfg.local.tokenFile);
|
|
489
562
|
if (!grantRequest.forceReview && !grantRequest.freshLogin) {
|
|
490
563
|
if (options.token) return options.token;
|
|
491
|
-
if (
|
|
492
|
-
const declared =
|
|
564
|
+
if (import_node_process5.default.env.ODLA_DEV_TOKEN) {
|
|
565
|
+
const declared = import_node_process5.default.env.ODLA_DEV_TOKEN_AUDIENCE;
|
|
493
566
|
if (declared) {
|
|
494
567
|
if (platformAudience(declared) !== audience) throw new Error("ODLA_DEV_TOKEN_AUDIENCE does not match the configured platform");
|
|
495
568
|
} else if (audience !== "https://odla.ai") {
|
|
496
569
|
throw new Error("ODLA_DEV_TOKEN_AUDIENCE is required for a non-default platform");
|
|
497
570
|
}
|
|
498
|
-
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;
|
|
499
578
|
}
|
|
500
579
|
if (cached?.token && cached.platform === audience && (cached.expiresAt ?? 0) > Date.now() + 6e4 && cachedGrantCovers(cached, grantIntent)) {
|
|
501
580
|
out.error(`auth: using cached developer token (${displayPath(cfg.local.tokenFile, cfg.rootDir)})`);
|
|
581
|
+
noteCachedCredential(cfg.local.tokenFile);
|
|
502
582
|
return cached.token;
|
|
503
583
|
}
|
|
504
584
|
} else {
|
|
@@ -599,7 +679,7 @@ function stillPending(pending, email) {
|
|
|
599
679
|
);
|
|
600
680
|
}
|
|
601
681
|
function handshakeEmail(value2, cached) {
|
|
602
|
-
const email = (value2 ??
|
|
682
|
+
const email = (value2 ?? import_node_process5.default.env.ODLA_USER_EMAIL ?? cached ?? "").trim().toLowerCase();
|
|
603
683
|
if (/@users\.noreply\.github\.com$/i.test(email)) {
|
|
604
684
|
throw new Error(
|
|
605
685
|
`"${email}" is a GitHub commit identity, not an odla account email; use --email <signed-in-odla-account> or ODLA_USER_EMAIL`
|
|
@@ -628,16 +708,18 @@ function platformAudience(value2) {
|
|
|
628
708
|
}
|
|
629
709
|
return url.origin;
|
|
630
710
|
}
|
|
631
|
-
var import_db, import_node_crypto,
|
|
711
|
+
var import_db, import_node_crypto, import_node_process5;
|
|
632
712
|
var init_token = __esm({
|
|
633
713
|
"src/token.ts"() {
|
|
634
714
|
"use strict";
|
|
635
715
|
init_cjs_shims();
|
|
636
716
|
import_db = require("@odla-ai/db");
|
|
637
717
|
import_node_crypto = require("crypto");
|
|
638
|
-
|
|
718
|
+
import_node_process5 = __toESM(require("process"), 1);
|
|
639
719
|
init_handshake_approval();
|
|
640
720
|
init_handshake_state();
|
|
721
|
+
init_cached_credential();
|
|
722
|
+
init_device_session();
|
|
641
723
|
init_local();
|
|
642
724
|
}
|
|
643
725
|
});
|
|
@@ -646,7 +728,7 @@ var init_token = __esm({
|
|
|
646
728
|
async function secretInputValue(options, kind = "credential") {
|
|
647
729
|
if (options.fromEnv && options.stdin) throw new Error("choose exactly one of --from-env or --stdin");
|
|
648
730
|
let value2;
|
|
649
|
-
if (options.fromEnv) value2 =
|
|
731
|
+
if (options.fromEnv) value2 = import_node_process6.default.env[options.fromEnv];
|
|
650
732
|
else if (options.stdin) value2 = await (options.readStdin ?? (() => readSecretStream(kind)))();
|
|
651
733
|
else throw new Error(`${kind} input required: use --from-env <NAME> or --stdin; values are never accepted as arguments`);
|
|
652
734
|
value2 = value2?.replace(/[\r\n]+$/, "");
|
|
@@ -654,7 +736,7 @@ async function secretInputValue(options, kind = "credential") {
|
|
|
654
736
|
if (new TextEncoder().encode(value2).byteLength > MAX_BYTES) throw new Error(`${kind} exceeds 64 KiB`);
|
|
655
737
|
return value2;
|
|
656
738
|
}
|
|
657
|
-
async function readSecretStream(kind, stream =
|
|
739
|
+
async function readSecretStream(kind, stream = import_node_process6.default.stdin) {
|
|
658
740
|
let value2 = "";
|
|
659
741
|
for await (const chunk of stream) {
|
|
660
742
|
value2 += String(chunk);
|
|
@@ -662,12 +744,12 @@ async function readSecretStream(kind, stream = import_node_process5.default.stdi
|
|
|
662
744
|
}
|
|
663
745
|
return value2;
|
|
664
746
|
}
|
|
665
|
-
var
|
|
747
|
+
var import_node_process6, MAX_BYTES;
|
|
666
748
|
var init_secret_input = __esm({
|
|
667
749
|
"src/secret-input.ts"() {
|
|
668
750
|
"use strict";
|
|
669
751
|
init_cjs_shims();
|
|
670
|
-
|
|
752
|
+
import_node_process6 = __toESM(require("process"), 1);
|
|
671
753
|
MAX_BYTES = 64 * 1024;
|
|
672
754
|
}
|
|
673
755
|
});
|
|
@@ -679,7 +761,7 @@ async function getScopedPlatformToken(options) {
|
|
|
679
761
|
async function resolveAdminPlatformToken(options) {
|
|
680
762
|
const audience = platformAudience(options.platform);
|
|
681
763
|
if (options.token) return options.token;
|
|
682
|
-
const fromEnv =
|
|
764
|
+
const fromEnv = import_node_process7.default.env.ODLA_ADMIN_TOKEN;
|
|
683
765
|
if (fromEnv) return audienceBoundEnvToken(fromEnv, audience);
|
|
684
766
|
return scopedToken(
|
|
685
767
|
audience,
|
|
@@ -691,7 +773,7 @@ async function resolveAdminPlatformToken(options) {
|
|
|
691
773
|
}
|
|
692
774
|
function audienceBoundEnvToken(token, platform) {
|
|
693
775
|
const audience = platformAudience(platform);
|
|
694
|
-
const declared =
|
|
776
|
+
const declared = import_node_process7.default.env.ODLA_ADMIN_TOKEN_AUDIENCE;
|
|
695
777
|
if (declared) {
|
|
696
778
|
if (platformAudience(declared) !== audience) throw new Error("ODLA_ADMIN_TOKEN_AUDIENCE does not match the configured platform");
|
|
697
779
|
} else if (audience !== "https://odla.ai") {
|
|
@@ -701,8 +783,8 @@ function audienceBoundEnvToken(token, platform) {
|
|
|
701
783
|
}
|
|
702
784
|
async function scopedToken(platform, scope, options, doFetch, out) {
|
|
703
785
|
const audience = platformAudience(platform);
|
|
704
|
-
const rootDir = options.rootDir ??
|
|
705
|
-
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");
|
|
706
788
|
const cache2 = options.cache === false ? null : readJsonFile(tokenFile);
|
|
707
789
|
const cached = cache2?.platform === audience ? cache2.tokens?.[scope] : void 0;
|
|
708
790
|
if (cached?.token && (cached.expiresAt ?? 0) > Date.now() + 6e4) {
|
|
@@ -729,7 +811,7 @@ async function scopedToken(platform, scope, options, doFetch, out) {
|
|
|
729
811
|
if (options.cache !== false) {
|
|
730
812
|
const tokens = cache2?.platform === audience ? { ...cache2.tokens ?? {} } : {};
|
|
731
813
|
tokens[scope] = { token, expiresAt };
|
|
732
|
-
if ((0,
|
|
814
|
+
if ((0, import_node_fs8.existsSync)((0, import_node_path5.join)(rootDir, ".git"))) ensureGitignore(rootDir, [tokenFile]);
|
|
733
815
|
writePrivateJson(tokenFile, { platform: audience, email, tokens });
|
|
734
816
|
out.error(`auth: cached ${scope} grant (${tokenFile}; mode 0600)`);
|
|
735
817
|
} else {
|
|
@@ -737,14 +819,14 @@ async function scopedToken(platform, scope, options, doFetch, out) {
|
|
|
737
819
|
}
|
|
738
820
|
return token;
|
|
739
821
|
}
|
|
740
|
-
var
|
|
822
|
+
var import_node_fs8, import_node_path5, import_node_process7, import_db2, SCOPE_PURPOSE;
|
|
741
823
|
var init_admin_ai_auth = __esm({
|
|
742
824
|
"src/admin-ai-auth.ts"() {
|
|
743
825
|
"use strict";
|
|
744
826
|
init_cjs_shims();
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
827
|
+
import_node_fs8 = require("fs");
|
|
828
|
+
import_node_path5 = require("path");
|
|
829
|
+
import_node_process7 = __toESM(require("process"), 1);
|
|
748
830
|
import_db2 = require("@odla-ai/db");
|
|
749
831
|
init_local();
|
|
750
832
|
init_handshake_approval();
|
|
@@ -754,6 +836,7 @@ var init_admin_ai_auth = __esm({
|
|
|
754
836
|
"app:config:read": "compare checked-in intent with an exact-id app Registry configuration",
|
|
755
837
|
"app:config:write": "apply or inspect one revision-bound configuration operation for an app you own",
|
|
756
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",
|
|
757
840
|
"platform:ai:policy:write": "change System AI model routing",
|
|
758
841
|
"platform:ai:policy:read": "read System AI model routing",
|
|
759
842
|
"platform:ai:credential:write": "replace a stored AI provider key",
|
|
@@ -962,7 +1045,7 @@ var init_admin_ai_usage = __esm({
|
|
|
962
1045
|
|
|
963
1046
|
// src/admin-ai.ts
|
|
964
1047
|
async function adminAi(options) {
|
|
965
|
-
const platform = platformAudience(options.platform ??
|
|
1048
|
+
const platform = platformAudience(options.platform ?? import_node_process8.default.env.ODLA_PLATFORM ?? "https://odla.ai");
|
|
966
1049
|
const doFetch = options.fetch ?? fetch;
|
|
967
1050
|
const out = options.stdout ?? console;
|
|
968
1051
|
const usageQuery = options.action === "usage" ? adminAiUsageQuery(options) : void 0;
|
|
@@ -1140,12 +1223,12 @@ function apiError3(action2, status, body) {
|
|
|
1140
1223
|
function isRecord3(value2) {
|
|
1141
1224
|
return Boolean(value2) && typeof value2 === "object" && !Array.isArray(value2);
|
|
1142
1225
|
}
|
|
1143
|
-
var
|
|
1226
|
+
var import_node_process8;
|
|
1144
1227
|
var init_admin_ai = __esm({
|
|
1145
1228
|
"src/admin-ai.ts"() {
|
|
1146
1229
|
"use strict";
|
|
1147
1230
|
init_cjs_shims();
|
|
1148
|
-
|
|
1231
|
+
import_node_process8 = __toESM(require("process"), 1);
|
|
1149
1232
|
init_token();
|
|
1150
1233
|
init_secret_input();
|
|
1151
1234
|
init_admin_ai_auth();
|
|
@@ -1659,12 +1742,12 @@ var init_monitoring_validation = __esm({
|
|
|
1659
1742
|
|
|
1660
1743
|
// src/config.ts
|
|
1661
1744
|
async function loadProjectConfig(configPath = "odla.config.mjs", options = {}) {
|
|
1662
|
-
const resolved = (0,
|
|
1663
|
-
if (!(0,
|
|
1745
|
+
const resolved = (0, import_node_path6.resolve)(configPath);
|
|
1746
|
+
if (!(0, import_node_fs9.existsSync)(resolved)) {
|
|
1664
1747
|
throw new Error(`config not found: ${configPath}. Run "odla-ai init" first or pass --config.`);
|
|
1665
1748
|
}
|
|
1666
1749
|
const raw = await loadConfigModule(resolved);
|
|
1667
|
-
const rootDir = (0,
|
|
1750
|
+
const rootDir = (0, import_node_path6.dirname)(resolved);
|
|
1668
1751
|
validateRawConfig(raw, resolved);
|
|
1669
1752
|
const platformUrl = trimSlash(process.env.ODLA_PLATFORM_URL || raw.platformUrl || DEFAULT_PLATFORM);
|
|
1670
1753
|
const dbEndpoint = trimSlash(process.env.ODLA_DB_ENDPOINT || raw.dbEndpoint || platformUrl);
|
|
@@ -1674,9 +1757,9 @@ async function loadProjectConfig(configPath = "odla.config.mjs", options = {}) {
|
|
|
1674
1757
|
validateCalendarConfig(raw, unique3([...envs, ...options.additionalEnvs ?? []]), services, resolved);
|
|
1675
1758
|
validateMonitoringConfig(raw, unique3([...envs, ...options.additionalEnvs ?? []]), services, resolved);
|
|
1676
1759
|
const local = {
|
|
1677
|
-
tokenFile: (0,
|
|
1678
|
-
credentialsFile: (0,
|
|
1679
|
-
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"),
|
|
1680
1763
|
gitignore: raw.local?.gitignore ?? true
|
|
1681
1764
|
};
|
|
1682
1765
|
return {
|
|
@@ -1693,9 +1776,9 @@ async function loadProjectConfig(configPath = "odla.config.mjs", options = {}) {
|
|
|
1693
1776
|
async function resolveDataExport(cfg, value2, names) {
|
|
1694
1777
|
if (value2 === void 0 || value2 === null || value2 === false) return void 0;
|
|
1695
1778
|
if (typeof value2 !== "string") return value2;
|
|
1696
|
-
const target = (0,
|
|
1779
|
+
const target = (0, import_node_path6.isAbsolute)(value2) ? value2 : (0, import_node_path6.resolve)(cfg.rootDir, value2);
|
|
1697
1780
|
if (target.endsWith(".json")) {
|
|
1698
|
-
return JSON.parse((0,
|
|
1781
|
+
return JSON.parse((0, import_node_fs9.readFileSync)(target, "utf8"));
|
|
1699
1782
|
}
|
|
1700
1783
|
const mod = await import((0, import_node_url.pathToFileURL)(target).href);
|
|
1701
1784
|
for (const name of names) {
|
|
@@ -1771,7 +1854,7 @@ function validId2(value2) {
|
|
|
1771
1854
|
return typeof value2 === "string" && /^[a-z0-9][a-z0-9-]*$/.test(value2);
|
|
1772
1855
|
}
|
|
1773
1856
|
async function loadConfigModule(path) {
|
|
1774
|
-
if (path.endsWith(".json")) return JSON.parse((0,
|
|
1857
|
+
if (path.endsWith(".json")) return JSON.parse((0, import_node_fs9.readFileSync)(path, "utf8"));
|
|
1775
1858
|
const nonce = `${Date.now()}-${configImportSerial++}`;
|
|
1776
1859
|
const mod = await import(`${(0, import_node_url.pathToFileURL)(path).href}?reload=${nonce}`);
|
|
1777
1860
|
const value2 = mod.default ?? mod.config;
|
|
@@ -1784,13 +1867,13 @@ function trimSlash(value2) {
|
|
|
1784
1867
|
function unique3(values) {
|
|
1785
1868
|
return [...new Set(values.filter(Boolean))];
|
|
1786
1869
|
}
|
|
1787
|
-
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;
|
|
1788
1871
|
var init_config = __esm({
|
|
1789
1872
|
"src/config.ts"() {
|
|
1790
1873
|
"use strict";
|
|
1791
1874
|
init_cjs_shims();
|
|
1792
|
-
|
|
1793
|
-
|
|
1875
|
+
import_node_fs9 = require("fs");
|
|
1876
|
+
import_node_path6 = require("path");
|
|
1794
1877
|
import_node_url = require("url");
|
|
1795
1878
|
import_apps = require("@odla-ai/apps");
|
|
1796
1879
|
init_ai_config_validation();
|
|
@@ -1808,13 +1891,13 @@ var init_config = __esm({
|
|
|
1808
1891
|
|
|
1809
1892
|
// src/operator-profiles.ts
|
|
1810
1893
|
function operatorProfileFile() {
|
|
1811
|
-
return (0,
|
|
1812
|
-
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")
|
|
1813
1896
|
);
|
|
1814
1897
|
}
|
|
1815
1898
|
function resolveOperatorProfile(parsed) {
|
|
1816
1899
|
const fromFlag = clean(stringOpt(parsed.options.context));
|
|
1817
|
-
const fromEnvironment = clean(
|
|
1900
|
+
const fromEnvironment = clean(import_node_process9.default.env.ODLA_CONTEXT);
|
|
1818
1901
|
const name = fromFlag ?? fromEnvironment ?? null;
|
|
1819
1902
|
const file = operatorProfileFile();
|
|
1820
1903
|
if (!name) {
|
|
@@ -1854,10 +1937,10 @@ function removeOperatorProfile(name, file = operatorProfileFile()) {
|
|
|
1854
1937
|
return true;
|
|
1855
1938
|
}
|
|
1856
1939
|
function operatorCredentialFiles(selection) {
|
|
1857
|
-
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");
|
|
1858
1941
|
return {
|
|
1859
|
-
developer: (0,
|
|
1860
|
-
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")
|
|
1861
1944
|
};
|
|
1862
1945
|
}
|
|
1863
1946
|
function assertOperatorName(value2, label) {
|
|
@@ -1868,10 +1951,10 @@ function assertOperatorName(value2, label) {
|
|
|
1868
1951
|
}
|
|
1869
1952
|
}
|
|
1870
1953
|
function readOperatorProfiles(file) {
|
|
1871
|
-
if (!(0,
|
|
1954
|
+
if (!(0, import_node_fs10.existsSync)(file)) return emptyProfiles();
|
|
1872
1955
|
let raw;
|
|
1873
1956
|
try {
|
|
1874
|
-
raw = JSON.parse((0,
|
|
1957
|
+
raw = JSON.parse((0, import_node_fs10.readFileSync)(file, "utf8"));
|
|
1875
1958
|
} catch {
|
|
1876
1959
|
throw new Error(`operator context file ${file} is not valid JSON`);
|
|
1877
1960
|
}
|
|
@@ -1929,15 +2012,15 @@ function clean(value2) {
|
|
|
1929
2012
|
const normalized = value2?.trim();
|
|
1930
2013
|
return normalized || void 0;
|
|
1931
2014
|
}
|
|
1932
|
-
var
|
|
2015
|
+
var import_node_fs10, import_node_os2, import_node_path7, import_node_process9;
|
|
1933
2016
|
var init_operator_profiles = __esm({
|
|
1934
2017
|
"src/operator-profiles.ts"() {
|
|
1935
2018
|
"use strict";
|
|
1936
2019
|
init_cjs_shims();
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
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);
|
|
1941
2024
|
init_argv();
|
|
1942
2025
|
init_local();
|
|
1943
2026
|
init_token();
|
|
@@ -1948,21 +2031,21 @@ var init_operator_profiles = __esm({
|
|
|
1948
2031
|
async function resolveOperatorContext(parsed, options = {}) {
|
|
1949
2032
|
const profile = resolveOperatorProfile(parsed);
|
|
1950
2033
|
const configArgument = stringOpt(parsed.options.config) ?? "odla.config.mjs";
|
|
1951
|
-
const configPath = (0,
|
|
2034
|
+
const configPath = (0, import_node_path8.resolve)(configArgument);
|
|
1952
2035
|
const explicitConfig = parsed.options.config !== void 0;
|
|
1953
|
-
const hasConfig = (0,
|
|
2036
|
+
const hasConfig = (0, import_node_fs11.existsSync)(configPath);
|
|
1954
2037
|
if (!hasConfig && (!options.allowMissingConfig || explicitConfig)) {
|
|
1955
2038
|
await loadProjectConfig(configArgument);
|
|
1956
2039
|
}
|
|
1957
2040
|
const loaded = hasConfig ? await loadProjectConfig(configArgument) : void 0;
|
|
1958
2041
|
const platformFlag = clean2(stringOpt(parsed.options.platform));
|
|
1959
|
-
const platformEnvironment = clean2(
|
|
2042
|
+
const platformEnvironment = clean2(import_node_process10.default.env.ODLA_PLATFORM_URL);
|
|
1960
2043
|
const platformValue = platformAudience(
|
|
1961
2044
|
platformFlag ?? platformEnvironment ?? profile.value?.platform ?? loaded?.platformUrl ?? DEFAULT_PLATFORM2
|
|
1962
2045
|
);
|
|
1963
2046
|
const platformSource = platformFlag ? "flag" : platformEnvironment ? "environment" : profile.value ? "profile" : loaded ? "config" : "default";
|
|
1964
2047
|
const appFlag = clean2(stringOpt(parsed.options.app));
|
|
1965
|
-
const appEnvironment = clean2(
|
|
2048
|
+
const appEnvironment = clean2(import_node_process10.default.env.ODLA_APP_ID);
|
|
1966
2049
|
const appValue = appFlag ?? appEnvironment ?? profile.value?.app ?? loaded?.app.id ?? null;
|
|
1967
2050
|
const appSource = appFlag ? "flag" : appEnvironment ? "environment" : profile.value?.app ? "profile" : loaded ? "config" : "unresolved";
|
|
1968
2051
|
if (appValue) assertOperatorName(appValue, "app");
|
|
@@ -1972,16 +2055,16 @@ async function resolveOperatorContext(parsed, options = {}) {
|
|
|
1972
2055
|
);
|
|
1973
2056
|
}
|
|
1974
2057
|
const envFlag = clean2(stringOpt(parsed.options.env));
|
|
1975
|
-
const envEnvironment = clean2(
|
|
2058
|
+
const envEnvironment = clean2(import_node_process10.default.env.ODLA_ENV);
|
|
1976
2059
|
const environmentValue = envFlag ?? envEnvironment ?? profile.value?.environment ?? options.defaultEnvironment ?? null;
|
|
1977
2060
|
const environmentSource = envFlag ? "flag" : envEnvironment ? "environment" : profile.value?.environment ? "profile" : options.defaultEnvironment ? "default" : "unresolved";
|
|
1978
2061
|
if (environmentValue) {
|
|
1979
2062
|
assertOperatorName(environmentValue, "environment");
|
|
1980
2063
|
}
|
|
1981
|
-
const rootDir = loaded?.rootDir ??
|
|
2064
|
+
const rootDir = loaded?.rootDir ?? import_node_process10.default.cwd();
|
|
1982
2065
|
const profileCredentials = operatorCredentialFiles(profile);
|
|
1983
|
-
const tokenFile = clean2(
|
|
1984
|
-
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;
|
|
1985
2068
|
const cfg = loaded ? {
|
|
1986
2069
|
...loaded,
|
|
1987
2070
|
platformUrl: platformValue,
|
|
@@ -2003,8 +2086,8 @@ async function resolveOperatorContext(parsed, options = {}) {
|
|
|
2003
2086
|
services: [],
|
|
2004
2087
|
local: {
|
|
2005
2088
|
tokenFile,
|
|
2006
|
-
credentialsFile: (0,
|
|
2007
|
-
devVarsFile: (0,
|
|
2089
|
+
credentialsFile: (0, import_node_path8.join)(rootDir, ".odla", "credentials.local.json"),
|
|
2090
|
+
devVarsFile: (0, import_node_path8.join)(rootDir, ".dev.vars"),
|
|
2008
2091
|
gitignore: true
|
|
2009
2092
|
}
|
|
2010
2093
|
};
|
|
@@ -2036,14 +2119,14 @@ function clean2(value2) {
|
|
|
2036
2119
|
const normalized = value2?.trim();
|
|
2037
2120
|
return normalized || void 0;
|
|
2038
2121
|
}
|
|
2039
|
-
var
|
|
2122
|
+
var import_node_fs11, import_node_path8, import_node_process10, DEFAULT_PLATFORM2;
|
|
2040
2123
|
var init_operator_context = __esm({
|
|
2041
2124
|
"src/operator-context.ts"() {
|
|
2042
2125
|
"use strict";
|
|
2043
2126
|
init_cjs_shims();
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2127
|
+
import_node_fs11 = require("fs");
|
|
2128
|
+
import_node_path8 = require("path");
|
|
2129
|
+
import_node_process10 = __toESM(require("process"), 1);
|
|
2047
2130
|
init_argv();
|
|
2048
2131
|
init_config();
|
|
2049
2132
|
init_operator_profiles();
|
|
@@ -2295,7 +2378,7 @@ async function authCommand(parsed, deps = {}) {
|
|
|
2295
2378
|
const { cfg } = context;
|
|
2296
2379
|
const out = deps.stdout ?? console;
|
|
2297
2380
|
const doFetch = deps.fetch ?? fetch;
|
|
2298
|
-
const email = stringOpt(parsed.options.email) ??
|
|
2381
|
+
const email = stringOpt(parsed.options.email) ?? import_node_process11.default.env.ODLA_USER_EMAIL?.trim();
|
|
2299
2382
|
if (!email) {
|
|
2300
2383
|
throw new Error(
|
|
2301
2384
|
"auth login requires --email <odla-account> or ODLA_USER_EMAIL; confirm the signed-in odla email instead of using git or GitHub identity"
|
|
@@ -2329,12 +2412,12 @@ async function authCommand(parsed, deps = {}) {
|
|
|
2329
2412
|
out.log(`Authorized ${identity.displayName}${handle} for ${cfg.app.id}.`);
|
|
2330
2413
|
out.log(`odla account: ${identity.email ?? "not returned"}`);
|
|
2331
2414
|
}
|
|
2332
|
-
var
|
|
2415
|
+
var import_node_process11;
|
|
2333
2416
|
var init_auth_command = __esm({
|
|
2334
2417
|
"src/auth-command.ts"() {
|
|
2335
2418
|
"use strict";
|
|
2336
2419
|
init_cjs_shims();
|
|
2337
|
-
|
|
2420
|
+
import_node_process11 = __toESM(require("process"), 1);
|
|
2338
2421
|
init_argv();
|
|
2339
2422
|
init_operator_context();
|
|
2340
2423
|
init_token();
|
|
@@ -2507,7 +2590,7 @@ async function appImport(options) {
|
|
|
2507
2590
|
const out = options.stdout ?? console;
|
|
2508
2591
|
const say = options.json ? (line2) => out.error(line2) : (line2) => out.log(line2);
|
|
2509
2592
|
const { tenant } = resolveTenant(cfg, options.env);
|
|
2510
|
-
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");
|
|
2511
2594
|
const { format, sources } = (0, import_import.parseImport)(text3, options.ns);
|
|
2512
2595
|
if (format === "namespace-map" && options.ns) {
|
|
2513
2596
|
throw new Error("--ns cannot be combined with a {namespace: rows} file \u2014 the file already names each namespace");
|
|
@@ -2535,12 +2618,12 @@ ${detail}${more}`);
|
|
|
2535
2618
|
}
|
|
2536
2619
|
return requireStudioHuman(options.configPath, "database import", "database", options.env);
|
|
2537
2620
|
}
|
|
2538
|
-
var
|
|
2621
|
+
var import_node_fs12, import_import;
|
|
2539
2622
|
var init_app_import = __esm({
|
|
2540
2623
|
"src/app-import.ts"() {
|
|
2541
2624
|
"use strict";
|
|
2542
2625
|
init_cjs_shims();
|
|
2543
|
-
|
|
2626
|
+
import_node_fs12 = require("fs");
|
|
2544
2627
|
import_import = require("@odla-ai/db/import");
|
|
2545
2628
|
init_config();
|
|
2546
2629
|
init_human_session();
|
|
@@ -2851,15 +2934,15 @@ var init_brand_design_unpack = __esm({
|
|
|
2851
2934
|
|
|
2852
2935
|
// src/brand-command.ts
|
|
2853
2936
|
async function readBundle(source, deps) {
|
|
2854
|
-
if (source !== "-") return (0, import_promises.readFile)((0,
|
|
2937
|
+
if (source !== "-") return (0, import_promises.readFile)((0, import_node_path9.resolve)(source), "utf8");
|
|
2855
2938
|
const readStdin = deps.readStdin;
|
|
2856
2939
|
if (!readStdin) throw new Error("reading a bundle from stdin is not supported here");
|
|
2857
2940
|
return readStdin();
|
|
2858
2941
|
}
|
|
2859
2942
|
async function writeAll(result, outDir) {
|
|
2860
2943
|
for (const file of result.files) {
|
|
2861
|
-
const target = (0,
|
|
2862
|
-
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 });
|
|
2863
2946
|
await (0, import_promises.writeFile)(target, file.bytes);
|
|
2864
2947
|
}
|
|
2865
2948
|
}
|
|
@@ -2867,7 +2950,7 @@ async function designUnpack(parsed, deps) {
|
|
|
2867
2950
|
assertArgs(parsed, ["out", "json"], 4);
|
|
2868
2951
|
const source = parsed.positionals[3];
|
|
2869
2952
|
if (!source) throw new Error(USAGE);
|
|
2870
|
-
const outDir = (0,
|
|
2953
|
+
const outDir = (0, import_node_path9.resolve)(stringOpt(parsed.options.out) ?? "design");
|
|
2871
2954
|
const result = unpackDesign(await readBundle(source, deps));
|
|
2872
2955
|
await writeAll(result, outDir);
|
|
2873
2956
|
const out = deps.stdout ?? console;
|
|
@@ -2893,13 +2976,13 @@ async function brandCommand(parsed, deps) {
|
|
|
2893
2976
|
}
|
|
2894
2977
|
throw new Error(USAGE);
|
|
2895
2978
|
}
|
|
2896
|
-
var import_promises,
|
|
2979
|
+
var import_promises, import_node_path9, USAGE;
|
|
2897
2980
|
var init_brand_command = __esm({
|
|
2898
2981
|
"src/brand-command.ts"() {
|
|
2899
2982
|
"use strict";
|
|
2900
2983
|
init_cjs_shims();
|
|
2901
2984
|
import_promises = require("fs/promises");
|
|
2902
|
-
|
|
2985
|
+
import_node_path9 = require("path");
|
|
2903
2986
|
init_argv();
|
|
2904
2987
|
init_brand_design_unpack();
|
|
2905
2988
|
USAGE = "usage: odla-ai brand design unpack <bundle.html|-> [--out <dir>] [--json]";
|
|
@@ -3537,7 +3620,7 @@ var init_config_reconcile_digest = __esm({
|
|
|
3537
3620
|
function readPlan(path) {
|
|
3538
3621
|
let value2;
|
|
3539
3622
|
try {
|
|
3540
|
-
const raw = (0,
|
|
3623
|
+
const raw = (0, import_node_fs13.readFileSync)(path, "utf8");
|
|
3541
3624
|
if (Buffer.byteLength(raw) > 128 * 1024) throw new Error("plan exceeds 128 KiB");
|
|
3542
3625
|
value2 = JSON.parse(raw);
|
|
3543
3626
|
} catch (error) {
|
|
@@ -3653,13 +3736,13 @@ function invalidPlan(message2) {
|
|
|
3653
3736
|
function record3(value2) {
|
|
3654
3737
|
return !!value2 && typeof value2 === "object" && !Array.isArray(value2);
|
|
3655
3738
|
}
|
|
3656
|
-
var import_apps3,
|
|
3739
|
+
var import_apps3, import_node_fs13, DIGEST, REVISION, OPERATION_ID, ACTION_ID, ENV, SERVICE;
|
|
3657
3740
|
var init_config_operation_validate = __esm({
|
|
3658
3741
|
"src/config-operation-validate.ts"() {
|
|
3659
3742
|
"use strict";
|
|
3660
3743
|
init_cjs_shims();
|
|
3661
3744
|
import_apps3 = require("@odla-ai/apps");
|
|
3662
|
-
|
|
3745
|
+
import_node_fs13 = require("fs");
|
|
3663
3746
|
init_config_operation_error();
|
|
3664
3747
|
init_config_reconcile_digest();
|
|
3665
3748
|
DIGEST = /^sha256:[0-9a-f]{64}$/;
|
|
@@ -3947,7 +4030,7 @@ async function operationClient(cfg, options, purpose) {
|
|
|
3947
4030
|
platform: cfg.platformUrl,
|
|
3948
4031
|
scope: "app:config:write",
|
|
3949
4032
|
token: options.token,
|
|
3950
|
-
tokenFile: (0,
|
|
4033
|
+
tokenFile: (0, import_node_path10.join)(cfg.rootDir, ".odla", "admin-token.local.json"),
|
|
3951
4034
|
rootDir: cfg.rootDir,
|
|
3952
4035
|
email: options.email,
|
|
3953
4036
|
open: options.open,
|
|
@@ -3999,13 +4082,13 @@ function normalizeRequestError(error) {
|
|
|
3999
4082
|
function record4(value2) {
|
|
4000
4083
|
return !!value2 && typeof value2 === "object" && !Array.isArray(value2);
|
|
4001
4084
|
}
|
|
4002
|
-
var import_apps6,
|
|
4085
|
+
var import_apps6, import_node_path10, IDEMPOTENCY_KEY, DEFAULT_WAIT_SECONDS, DEFAULT_INTERVAL_SECONDS;
|
|
4003
4086
|
var init_config_operation_command = __esm({
|
|
4004
4087
|
"src/config-operation-command.ts"() {
|
|
4005
4088
|
"use strict";
|
|
4006
4089
|
init_cjs_shims();
|
|
4007
4090
|
import_apps6 = require("@odla-ai/apps");
|
|
4008
|
-
|
|
4091
|
+
import_node_path10 = require("path");
|
|
4009
4092
|
init_admin_ai_auth();
|
|
4010
4093
|
init_version();
|
|
4011
4094
|
init_config();
|
|
@@ -4328,7 +4411,7 @@ async function inspectConfig(options) {
|
|
|
4328
4411
|
platform: cfg.platformUrl,
|
|
4329
4412
|
scope: "app:config:read",
|
|
4330
4413
|
token: options.token,
|
|
4331
|
-
tokenFile: (0,
|
|
4414
|
+
tokenFile: (0, import_node_path11.join)(cfg.rootDir, ".odla", "admin-token.local.json"),
|
|
4332
4415
|
rootDir: cfg.rootDir,
|
|
4333
4416
|
email: options.email,
|
|
4334
4417
|
open: options.open,
|
|
@@ -4457,13 +4540,13 @@ function studioSettingsUrl(reconciliation) {
|
|
|
4457
4540
|
function quoteArg2(value2) {
|
|
4458
4541
|
return `'${value2.replace(/'/g, `'\\''`)}'`;
|
|
4459
4542
|
}
|
|
4460
|
-
var import_apps8,
|
|
4543
|
+
var import_apps8, import_node_path11;
|
|
4461
4544
|
var init_config_reconcile_command = __esm({
|
|
4462
4545
|
"src/config-reconcile-command.ts"() {
|
|
4463
4546
|
"use strict";
|
|
4464
4547
|
init_cjs_shims();
|
|
4465
4548
|
import_apps8 = require("@odla-ai/apps");
|
|
4466
|
-
|
|
4549
|
+
import_node_path11 = require("path");
|
|
4467
4550
|
init_admin_ai_auth();
|
|
4468
4551
|
init_config();
|
|
4469
4552
|
init_config_reconcile_digest();
|
|
@@ -4476,15 +4559,15 @@ var init_config_reconcile_command = __esm({
|
|
|
4476
4559
|
// src/wrangler.ts
|
|
4477
4560
|
function findWranglerConfig(rootDir) {
|
|
4478
4561
|
for (const name of WRANGLER_CONFIG_FILES) {
|
|
4479
|
-
const path = (0,
|
|
4480
|
-
if ((0,
|
|
4562
|
+
const path = (0, import_node_path12.join)(rootDir, name);
|
|
4563
|
+
if ((0, import_node_fs14.existsSync)(path)) return path;
|
|
4481
4564
|
}
|
|
4482
4565
|
return null;
|
|
4483
4566
|
}
|
|
4484
4567
|
function readWranglerConfig(path) {
|
|
4485
4568
|
if (path.endsWith(".toml")) return null;
|
|
4486
4569
|
try {
|
|
4487
|
-
return JSON.parse(stripJsonComments((0,
|
|
4570
|
+
return JSON.parse(stripJsonComments((0, import_node_fs14.readFileSync)(path, "utf8")));
|
|
4488
4571
|
} catch {
|
|
4489
4572
|
return null;
|
|
4490
4573
|
}
|
|
@@ -4588,14 +4671,14 @@ function wranglerBulkSecrets(run, opts) {
|
|
|
4588
4671
|
];
|
|
4589
4672
|
return run("npx", args, { input: JSON.stringify(opts.secrets), cwd: opts.cwd });
|
|
4590
4673
|
}
|
|
4591
|
-
var import_node_child_process2,
|
|
4674
|
+
var import_node_child_process2, import_node_fs14, import_node_path12, defaultRunner, WRANGLER_CONFIG_FILES;
|
|
4592
4675
|
var init_wrangler = __esm({
|
|
4593
4676
|
"src/wrangler.ts"() {
|
|
4594
4677
|
"use strict";
|
|
4595
4678
|
init_cjs_shims();
|
|
4596
4679
|
import_node_child_process2 = require("child_process");
|
|
4597
|
-
|
|
4598
|
-
|
|
4680
|
+
import_node_fs14 = require("fs");
|
|
4681
|
+
import_node_path12 = require("path");
|
|
4599
4682
|
defaultRunner = (cmd, args, opts) => new Promise((resolvePromise, reject) => {
|
|
4600
4683
|
const child = (0, import_node_child_process2.spawn)(cmd, args, { cwd: opts?.cwd, stdio: ["pipe", "pipe", "pipe"] });
|
|
4601
4684
|
let stdout = "";
|
|
@@ -4654,10 +4737,10 @@ function wranglerWarnings(rootDir) {
|
|
|
4654
4737
|
for (const { label, block } of blocks) {
|
|
4655
4738
|
const assets = block.assets;
|
|
4656
4739
|
if (assets?.directory) {
|
|
4657
|
-
const dir = (0,
|
|
4658
|
-
if (dir === (0,
|
|
4740
|
+
const dir = (0, import_node_path13.resolve)(rootDir, assets.directory);
|
|
4741
|
+
if (dir === (0, import_node_path13.resolve)(rootDir)) {
|
|
4659
4742
|
warnings.push(`${label}assets.directory is the project root \u2014 point it at a dedicated build dir (wrangler dev fails with "spawn EBADF")`);
|
|
4660
|
-
} else if ((0,
|
|
4743
|
+
} else if ((0, import_node_fs15.existsSync)((0, import_node_path13.join)(dir, "node_modules"))) {
|
|
4661
4744
|
warnings.push(`${label}assets.directory contains node_modules \u2014 wrangler dev's watcher will exhaust file descriptors`);
|
|
4662
4745
|
}
|
|
4663
4746
|
}
|
|
@@ -4692,13 +4775,13 @@ function o11yProjectWarnings(rootDir) {
|
|
|
4692
4775
|
warnings.push("cannot verify o11y Worker instrumentation \u2014 add a parseable wrangler.jsonc/json config");
|
|
4693
4776
|
return warnings;
|
|
4694
4777
|
}
|
|
4695
|
-
const main = typeof config.main === "string" ? (0,
|
|
4696
|
-
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)) {
|
|
4697
4780
|
warnings.push("cannot verify o11y Worker instrumentation \u2014 wrangler main is missing or unreadable");
|
|
4698
4781
|
} else {
|
|
4699
4782
|
let source = "";
|
|
4700
4783
|
try {
|
|
4701
|
-
source = (0,
|
|
4784
|
+
source = (0, import_node_fs15.readFileSync)(main, "utf8");
|
|
4702
4785
|
} catch {
|
|
4703
4786
|
}
|
|
4704
4787
|
if (!/\bwithObservability\b/.test(source)) {
|
|
@@ -4722,19 +4805,19 @@ function calendarProjectWarnings(rootDir) {
|
|
|
4722
4805
|
}
|
|
4723
4806
|
function readPackageJson(rootDir) {
|
|
4724
4807
|
try {
|
|
4725
|
-
return JSON.parse((0,
|
|
4808
|
+
return JSON.parse((0, import_node_fs15.readFileSync)((0, import_node_path13.join)(rootDir, "package.json"), "utf8"));
|
|
4726
4809
|
} catch {
|
|
4727
4810
|
return null;
|
|
4728
4811
|
}
|
|
4729
4812
|
}
|
|
4730
|
-
var import_node_child_process3,
|
|
4813
|
+
var import_node_child_process3, import_node_fs15, import_node_path13, defaultExec;
|
|
4731
4814
|
var init_doctor_checks = __esm({
|
|
4732
4815
|
"src/doctor-checks.ts"() {
|
|
4733
4816
|
"use strict";
|
|
4734
4817
|
init_cjs_shims();
|
|
4735
4818
|
import_node_child_process3 = require("child_process");
|
|
4736
|
-
|
|
4737
|
-
|
|
4819
|
+
import_node_fs15 = require("fs");
|
|
4820
|
+
import_node_path13 = require("path");
|
|
4738
4821
|
init_redact();
|
|
4739
4822
|
init_local();
|
|
4740
4823
|
init_wrangler();
|
|
@@ -5071,9 +5154,9 @@ var init_harness_options = __esm({
|
|
|
5071
5154
|
// src/init.ts
|
|
5072
5155
|
function initProject(options) {
|
|
5073
5156
|
const out = options.stdout ?? console;
|
|
5074
|
-
const rootDir = (0,
|
|
5075
|
-
const configPath = (0,
|
|
5076
|
-
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) {
|
|
5077
5160
|
throw new Error(`${configPath} already exists. Pass --force to overwrite.`);
|
|
5078
5161
|
}
|
|
5079
5162
|
if (!/^[a-z0-9][a-z0-9-]*$/.test(options.appId)) {
|
|
@@ -5089,20 +5172,20 @@ function initProject(options) {
|
|
|
5089
5172
|
}
|
|
5090
5173
|
}
|
|
5091
5174
|
const aiProvider = options.aiProvider;
|
|
5092
|
-
(0,
|
|
5093
|
-
(0,
|
|
5094
|
-
(0,
|
|
5095
|
-
(0,
|
|
5096
|
-
writeIfMissing((0,
|
|
5097
|
-
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());
|
|
5098
5181
|
ensureGitignore(rootDir);
|
|
5099
5182
|
out.log(`created ${relativeDisplay(configPath, rootDir)}`);
|
|
5100
5183
|
out.log("created src/odla/schema.mjs and src/odla/rules.mjs");
|
|
5101
5184
|
out.log("updated .gitignore for local odla credentials");
|
|
5102
5185
|
}
|
|
5103
5186
|
function writeIfMissing(path, text3) {
|
|
5104
|
-
if ((0,
|
|
5105
|
-
(0,
|
|
5187
|
+
if ((0, import_node_fs16.existsSync)(path)) return;
|
|
5188
|
+
(0, import_node_fs16.writeFileSync)(path, text3);
|
|
5106
5189
|
}
|
|
5107
5190
|
function configTemplate(input) {
|
|
5108
5191
|
const calendar = input.services.includes("calendar") ? ` calendar: {
|
|
@@ -5205,13 +5288,13 @@ function defaultKeyEnv(provider) {
|
|
|
5205
5288
|
function relativeDisplay(path, rootDir) {
|
|
5206
5289
|
return path.startsWith(rootDir) ? path.slice(rootDir.length + 1) : path;
|
|
5207
5290
|
}
|
|
5208
|
-
var
|
|
5291
|
+
var import_node_fs16, import_node_path14, import_apps9;
|
|
5209
5292
|
var init_init = __esm({
|
|
5210
5293
|
"src/init.ts"() {
|
|
5211
5294
|
"use strict";
|
|
5212
5295
|
init_cjs_shims();
|
|
5213
|
-
|
|
5214
|
-
|
|
5296
|
+
import_node_fs16 = require("fs");
|
|
5297
|
+
import_node_path14 = require("path");
|
|
5215
5298
|
import_apps9 = require("@odla-ai/apps");
|
|
5216
5299
|
init_local();
|
|
5217
5300
|
}
|
|
@@ -5544,8 +5627,8 @@ function installSkill(options = {}) {
|
|
|
5544
5627
|
const files = listFiles(sourceDir);
|
|
5545
5628
|
if (files.length === 0) throw new Error(`no bundled skills found at ${sourceDir}`);
|
|
5546
5629
|
const harnesses = normalizeHarnesses(options.harnesses, options.global === true);
|
|
5547
|
-
const root = (0,
|
|
5548
|
-
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)());
|
|
5549
5632
|
const plans = /* @__PURE__ */ new Map();
|
|
5550
5633
|
const targets = /* @__PURE__ */ new Map();
|
|
5551
5634
|
const rememberTarget = (harness, target) => {
|
|
@@ -5559,48 +5642,48 @@ function installSkill(options = {}) {
|
|
|
5559
5642
|
plans.set(target, { target, content: content2, boundary, managedMerge });
|
|
5560
5643
|
};
|
|
5561
5644
|
const planSkillTree = (targetDir2, boundary = root) => {
|
|
5562
|
-
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);
|
|
5563
5646
|
};
|
|
5564
5647
|
let targetDir;
|
|
5565
5648
|
if (options.global) {
|
|
5566
|
-
const claudeRoot = (0,
|
|
5567
|
-
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");
|
|
5568
5651
|
targetDir = harnesses[0] === "codex" ? codexRoot : claudeRoot;
|
|
5569
5652
|
for (const harness of harnesses) {
|
|
5570
5653
|
const skillRoot = harness === "claude" ? claudeRoot : codexRoot;
|
|
5571
|
-
planSkillTree(skillRoot, harness === "claude" ? home : (0,
|
|
5654
|
+
planSkillTree(skillRoot, harness === "claude" ? home : (0, import_node_path15.dirname)((0, import_node_path15.dirname)(codexRoot)));
|
|
5572
5655
|
rememberTarget(harness, skillRoot);
|
|
5573
5656
|
}
|
|
5574
5657
|
} else {
|
|
5575
|
-
const sharedRoot = (0,
|
|
5658
|
+
const sharedRoot = (0, import_node_path15.join)(root, ".agents", "skills");
|
|
5576
5659
|
planSkillTree(sharedRoot);
|
|
5577
|
-
const claudeRoot = (0,
|
|
5660
|
+
const claudeRoot = (0, import_node_path15.join)(root, ".claude", "skills");
|
|
5578
5661
|
targetDir = harnesses.includes("claude") ? claudeRoot : sharedRoot;
|
|
5579
5662
|
for (const harness of harnesses) rememberTarget(harness, sharedRoot);
|
|
5580
5663
|
if (harnesses.includes("claude")) {
|
|
5581
5664
|
for (const skill of skillNames(files)) {
|
|
5582
|
-
const canonical2 = (0,
|
|
5583
|
-
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));
|
|
5584
5667
|
}
|
|
5585
5668
|
rememberTarget("claude", claudeRoot);
|
|
5586
5669
|
}
|
|
5587
5670
|
if (harnesses.includes("cursor")) {
|
|
5588
|
-
const cursorRule = (0,
|
|
5671
|
+
const cursorRule = (0, import_node_path15.join)(root, ".cursor", "rules", "odla.mdc");
|
|
5589
5672
|
plan(cursorRule, CURSOR_RULE);
|
|
5590
5673
|
rememberTarget("cursor", cursorRule);
|
|
5591
5674
|
}
|
|
5592
5675
|
if (harnesses.includes("agents")) {
|
|
5593
|
-
const agentsFile = (0,
|
|
5676
|
+
const agentsFile = (0, import_node_path15.join)(root, "AGENTS.md");
|
|
5594
5677
|
plan(agentsFile, managedFileContent(agentsFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
|
|
5595
5678
|
rememberTarget("agents", agentsFile);
|
|
5596
5679
|
}
|
|
5597
5680
|
if (harnesses.includes("copilot")) {
|
|
5598
|
-
const copilotFile = (0,
|
|
5681
|
+
const copilotFile = (0, import_node_path15.join)(root, ".github", "copilot-instructions.md");
|
|
5599
5682
|
plan(copilotFile, managedFileContent(copilotFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
|
|
5600
5683
|
rememberTarget("copilot", copilotFile);
|
|
5601
5684
|
}
|
|
5602
5685
|
if (harnesses.includes("gemini")) {
|
|
5603
|
-
const geminiFile = (0,
|
|
5686
|
+
const geminiFile = (0, import_node_path15.join)(root, "GEMINI.md");
|
|
5604
5687
|
plan(geminiFile, managedFileContent(geminiFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
|
|
5605
5688
|
rememberTarget("gemini", geminiFile);
|
|
5606
5689
|
}
|
|
@@ -5614,11 +5697,11 @@ function installSkill(options = {}) {
|
|
|
5614
5697
|
conflicts.push(`${file.target} (redirected by symbolic link ${symlink})`);
|
|
5615
5698
|
continue;
|
|
5616
5699
|
}
|
|
5617
|
-
if (!(0,
|
|
5700
|
+
if (!(0, import_node_fs17.existsSync)(file.target)) {
|
|
5618
5701
|
writtenPaths.add(file.target);
|
|
5619
5702
|
continue;
|
|
5620
5703
|
}
|
|
5621
|
-
const current = (0,
|
|
5704
|
+
const current = (0, import_node_fs17.readFileSync)(file.target, "utf8");
|
|
5622
5705
|
if (current === file.content) {
|
|
5623
5706
|
unchangedPaths.add(file.target);
|
|
5624
5707
|
} else if (file.managedMerge || options.force) {
|
|
@@ -5635,9 +5718,9 @@ ${conflicts.map((f) => ` - ${f}`).join("\n")}`
|
|
|
5635
5718
|
);
|
|
5636
5719
|
}
|
|
5637
5720
|
for (const file of plans.values()) {
|
|
5638
|
-
if (!(0,
|
|
5639
|
-
(0,
|
|
5640
|
-
(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);
|
|
5641
5724
|
}
|
|
5642
5725
|
}
|
|
5643
5726
|
const skills = skillNames(files);
|
|
@@ -5656,7 +5739,7 @@ ${conflicts.map((f) => ` - ${f}`).join("\n")}`
|
|
|
5656
5739
|
};
|
|
5657
5740
|
}
|
|
5658
5741
|
function pathsUnder(root, paths) {
|
|
5659
|
-
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();
|
|
5660
5743
|
}
|
|
5661
5744
|
function normalizeHarnesses(values, global) {
|
|
5662
5745
|
const requested = values?.length ? values : ["claude"];
|
|
@@ -5678,9 +5761,9 @@ function normalizeHarnesses(values, global) {
|
|
|
5678
5761
|
function managedFileContent(path, block, force, boundary) {
|
|
5679
5762
|
const symlink = symlinkedComponent(boundary, path);
|
|
5680
5763
|
if (symlink) throw new Error(`refusing to manage ${path}: symbolic link component ${symlink}`);
|
|
5681
|
-
if (!(0,
|
|
5764
|
+
if (!(0, import_node_fs17.existsSync)(path)) return `${block}
|
|
5682
5765
|
`;
|
|
5683
|
-
const current = (0,
|
|
5766
|
+
const current = (0, import_node_fs17.readFileSync)(path, "utf8");
|
|
5684
5767
|
const start = "<!-- odla-ai agent setup:start -->";
|
|
5685
5768
|
const end = "<!-- odla-ai agent setup:end -->";
|
|
5686
5769
|
const startAt = current.indexOf(start);
|
|
@@ -5701,15 +5784,15 @@ function managedFileContent(path, block, force, boundary) {
|
|
|
5701
5784
|
return `${current.slice(0, startAt)}${block}${current.slice(afterEnd)}`;
|
|
5702
5785
|
}
|
|
5703
5786
|
function symlinkedComponent(boundary, target) {
|
|
5704
|
-
const rel = (0,
|
|
5705
|
-
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)) {
|
|
5706
5789
|
throw new Error(`agent setup target escapes its install root: ${target}`);
|
|
5707
5790
|
}
|
|
5708
5791
|
let current = boundary;
|
|
5709
|
-
for (const part of rel.split(
|
|
5710
|
-
current = (0,
|
|
5792
|
+
for (const part of rel.split(import_node_path15.sep).filter(Boolean)) {
|
|
5793
|
+
current = (0, import_node_path15.join)(current, part);
|
|
5711
5794
|
try {
|
|
5712
|
-
if ((0,
|
|
5795
|
+
if ((0, import_node_fs17.lstatSync)(current).isSymbolicLink()) return current;
|
|
5713
5796
|
} catch (error) {
|
|
5714
5797
|
if (error.code !== "ENOENT") throw error;
|
|
5715
5798
|
}
|
|
@@ -5720,26 +5803,26 @@ function skillNames(files) {
|
|
|
5720
5803
|
return [...new Set(files.filter((file) => /(^|[\\/])SKILL\.md$/.test(file)).map((file) => file.split(/[\\/]/)[0]))].sort();
|
|
5721
5804
|
}
|
|
5722
5805
|
function listFiles(dir) {
|
|
5723
|
-
if (!(0,
|
|
5806
|
+
if (!(0, import_node_fs17.existsSync)(dir)) return [];
|
|
5724
5807
|
const results = [];
|
|
5725
5808
|
const walk = (current) => {
|
|
5726
|
-
for (const entry of (0,
|
|
5727
|
-
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);
|
|
5728
5811
|
if (entry.isDirectory()) walk(path);
|
|
5729
|
-
else results.push((0,
|
|
5812
|
+
else results.push((0, import_node_path15.relative)(dir, path));
|
|
5730
5813
|
}
|
|
5731
5814
|
};
|
|
5732
5815
|
walk(dir);
|
|
5733
5816
|
return results.sort();
|
|
5734
5817
|
}
|
|
5735
|
-
var
|
|
5818
|
+
var import_node_fs17, import_node_os3, import_node_path15, import_node_url2, AGENT_HARNESSES;
|
|
5736
5819
|
var init_skill = __esm({
|
|
5737
5820
|
"src/skill.ts"() {
|
|
5738
5821
|
"use strict";
|
|
5739
5822
|
init_cjs_shims();
|
|
5740
|
-
|
|
5741
|
-
|
|
5742
|
-
|
|
5823
|
+
import_node_fs17 = require("fs");
|
|
5824
|
+
import_node_os3 = require("os");
|
|
5825
|
+
import_node_path15 = require("path");
|
|
5743
5826
|
import_node_url2 = require("url");
|
|
5744
5827
|
init_skill_adapters();
|
|
5745
5828
|
AGENT_HARNESSES = ["claude", "codex", "cursor", "copilot", "gemini", "agents"];
|
|
@@ -7290,8 +7373,8 @@ function rollup(graph, kind, options = {}) {
|
|
|
7290
7373
|
for (const node of nodesOfKind(graph, kind)) {
|
|
7291
7374
|
if (options.prefix && !node.name.startsWith(options.prefix)) continue;
|
|
7292
7375
|
const key = node.name.split(separator).slice(0, depth).join(separator);
|
|
7293
|
-
const
|
|
7294
|
-
if (
|
|
7376
|
+
const list3 = groups.get(key);
|
|
7377
|
+
if (list3) list3.push(node);
|
|
7295
7378
|
else groups.set(key, [node]);
|
|
7296
7379
|
}
|
|
7297
7380
|
return [...groups].map(([prefix, nodes]) => ({
|
|
@@ -7316,7 +7399,7 @@ function dirname9(path) {
|
|
|
7316
7399
|
const at = path.lastIndexOf("/");
|
|
7317
7400
|
return at <= 0 ? "." : path.slice(0, at);
|
|
7318
7401
|
}
|
|
7319
|
-
function
|
|
7402
|
+
function join13(base, specifier) {
|
|
7320
7403
|
const parts = [];
|
|
7321
7404
|
const segments = `${base === "." ? "" : `${base}/`}${specifier}`.split("/");
|
|
7322
7405
|
for (const segment of segments) {
|
|
@@ -7328,7 +7411,7 @@ function join12(base, specifier) {
|
|
|
7328
7411
|
}
|
|
7329
7412
|
function resolveImport(fromPath, specifier, known) {
|
|
7330
7413
|
if (!specifier.startsWith(".")) return null;
|
|
7331
|
-
const base =
|
|
7414
|
+
const base = join13(dirname9(fromPath), specifier);
|
|
7332
7415
|
const candidates = [
|
|
7333
7416
|
base,
|
|
7334
7417
|
base.replace(/\.js$/, ".ts"),
|
|
@@ -10254,8 +10337,8 @@ var init_code_runtime_config = __esm({
|
|
|
10254
10337
|
// src/code-connect.ts
|
|
10255
10338
|
async function codeConnect(options) {
|
|
10256
10339
|
const cwd = options.cwd ?? process.cwd();
|
|
10257
|
-
const configPath = (0,
|
|
10258
|
-
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;
|
|
10259
10342
|
const requestedAppId = options.appId?.trim();
|
|
10260
10343
|
if (requestedAppId && !/^[a-z0-9][a-z0-9-]{1,62}$/.test(requestedAppId)) {
|
|
10261
10344
|
throw new Error("--app-id must be a valid odla app id");
|
|
@@ -10284,7 +10367,7 @@ async function codeConnect(options) {
|
|
|
10284
10367
|
const doFetch = options.fetch ?? fetch;
|
|
10285
10368
|
const engine = await (options.selectEngine ?? selectContainerEngine)(options.engine ?? "auto");
|
|
10286
10369
|
const hostPlatform = process.platform === "darwin" ? "macos" : "linux";
|
|
10287
|
-
const hostName = (options.name ?? (0,
|
|
10370
|
+
const hostName = (options.name ?? (0, import_node_os4.hostname)()).trim();
|
|
10288
10371
|
if (!hostName || hostName.length > 120) throw new Error("--name must contain 1 to 120 characters");
|
|
10289
10372
|
const repository = await inferGitHubRepository(cwd, options.readGitOrigin);
|
|
10290
10373
|
const localSource = await (options.prepareLocalSource ?? prepareCodeLocalSource)(
|
|
@@ -10321,8 +10404,8 @@ async function codeConnect(options) {
|
|
|
10321
10404
|
platform: hostPlatform,
|
|
10322
10405
|
arch: process.arch,
|
|
10323
10406
|
engines: [engine],
|
|
10324
|
-
cpuCount: (0,
|
|
10325
|
-
memoryBytes: (0,
|
|
10407
|
+
cpuCount: (0, import_node_os4.cpus)().length,
|
|
10408
|
+
memoryBytes: (0, import_node_os4.totalmem)(),
|
|
10326
10409
|
source: descriptor2,
|
|
10327
10410
|
images: {
|
|
10328
10411
|
ready: true,
|
|
@@ -10419,14 +10502,14 @@ function apiFailure(action2, status, value2) {
|
|
|
10419
10502
|
function record6(value2) {
|
|
10420
10503
|
return value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
|
|
10421
10504
|
}
|
|
10422
|
-
var
|
|
10505
|
+
var import_node_fs18, import_node_os4, import_node_path16;
|
|
10423
10506
|
var init_code_connect = __esm({
|
|
10424
10507
|
"src/code-connect.ts"() {
|
|
10425
10508
|
"use strict";
|
|
10426
10509
|
init_cjs_shims();
|
|
10427
|
-
|
|
10428
|
-
|
|
10429
|
-
|
|
10510
|
+
import_node_fs18 = require("fs");
|
|
10511
|
+
import_node_os4 = require("os");
|
|
10512
|
+
import_node_path16 = require("path");
|
|
10430
10513
|
init_node();
|
|
10431
10514
|
init_admin_ai_auth();
|
|
10432
10515
|
init_config();
|
|
@@ -10743,7 +10826,7 @@ function developerTokenStatus(context, parsed, now = Date.now()) {
|
|
|
10743
10826
|
const cacheStatus = !cached?.token ? "missing" : cached.platform !== context.platform.value ? "other-platform" : (cached.expiresAt ?? 0) <= now + 6e4 ? "expired" : "valid";
|
|
10744
10827
|
const source = clean3(
|
|
10745
10828
|
stringOpt(parsed.options.token)
|
|
10746
|
-
) ? "flag" : clean3(
|
|
10829
|
+
) ? "flag" : clean3(import_node_process12.default.env.ODLA_DEV_TOKEN) ? "environment" : cacheStatus === "valid" ? "cache" : "missing";
|
|
10747
10830
|
return {
|
|
10748
10831
|
source,
|
|
10749
10832
|
cacheFile: context.cfg.local.tokenFile,
|
|
@@ -10754,12 +10837,12 @@ function clean3(value2) {
|
|
|
10754
10837
|
const normalized = value2?.trim();
|
|
10755
10838
|
return normalized || void 0;
|
|
10756
10839
|
}
|
|
10757
|
-
var
|
|
10840
|
+
var import_node_process12;
|
|
10758
10841
|
var init_operator_credentials = __esm({
|
|
10759
10842
|
"src/operator-credentials.ts"() {
|
|
10760
10843
|
"use strict";
|
|
10761
10844
|
init_cjs_shims();
|
|
10762
|
-
|
|
10845
|
+
import_node_process12 = __toESM(require("process"), 1);
|
|
10763
10846
|
init_argv();
|
|
10764
10847
|
init_local();
|
|
10765
10848
|
}
|
|
@@ -11100,6 +11183,9 @@ Usage:
|
|
|
11100
11183
|
odla-ai security run [target] --ack-redacted-source [--env dev] [--profile odla] [--fail-on high]
|
|
11101
11184
|
odla-ai security run [target] --self --ack-redacted-source
|
|
11102
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]
|
|
11103
11189
|
odla-ai credentials list [--config odla.config.mjs] [--env dev] [--all] [--json]
|
|
11104
11190
|
odla-ai credentials revoke <receipt-id> [--config odla.config.mjs] [--json]
|
|
11105
11191
|
odla-ai smoke [--config odla.config.mjs] [--env dev] [--runtime] [--email <odla-account>] [--no-open]
|
|
@@ -11207,6 +11293,10 @@ Commands:
|
|
|
11207
11293
|
stable status, incident, and report JSON to agents and CI.
|
|
11208
11294
|
platform Read canonical fleet health, releases, provider load/freshness,
|
|
11209
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.
|
|
11210
11300
|
provision Register services, compose integrations, persist credentials, optionally push secrets.
|
|
11211
11301
|
"provision --live --yes" initializes only the live instance of
|
|
11212
11302
|
an existing sandbox app and enables every configured service;
|
|
@@ -12581,14 +12671,14 @@ function readPmProjectContext(rootDir) {
|
|
|
12581
12671
|
function writePmProjectContext(rootDir, value2) {
|
|
12582
12672
|
writePrivateJson(pmProjectContextFile(rootDir), { ...value2, selectedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
12583
12673
|
}
|
|
12584
|
-
var
|
|
12674
|
+
var import_node_path17, pmProjectContextFile;
|
|
12585
12675
|
var init_pm_project_context = __esm({
|
|
12586
12676
|
"src/pm-project-context.ts"() {
|
|
12587
12677
|
"use strict";
|
|
12588
12678
|
init_cjs_shims();
|
|
12589
|
-
|
|
12679
|
+
import_node_path17 = require("path");
|
|
12590
12680
|
init_local();
|
|
12591
|
-
pmProjectContextFile = (rootDir) => (0,
|
|
12681
|
+
pmProjectContextFile = (rootDir) => (0, import_node_path17.resolve)(rootDir, ".odla", "pm-project.local.json");
|
|
12592
12682
|
}
|
|
12593
12683
|
});
|
|
12594
12684
|
|
|
@@ -14160,7 +14250,7 @@ async function provision(options) {
|
|
|
14160
14250
|
await provisionIntegrationSeeds(doFetch, cfg.dbEndpoint, tenantId, dbKey, database.integrations, env, out);
|
|
14161
14251
|
}
|
|
14162
14252
|
if (cfg.services.includes("ai") && cfg.ai?.provider && cfg.ai.keyEnv) {
|
|
14163
|
-
const key =
|
|
14253
|
+
const key = import_node_process13.default.env[cfg.ai.keyEnv];
|
|
14164
14254
|
if (key) {
|
|
14165
14255
|
const secretName = cfg.ai.secretName ?? defaultSecretName(cfg.ai.provider);
|
|
14166
14256
|
await (0, import_ai5.putSecret)({ endpoint: cfg.dbEndpoint, token, fetch: doFetch }, tenantId, secretName, key);
|
|
@@ -14199,14 +14289,14 @@ async function provision(options) {
|
|
|
14199
14289
|
}
|
|
14200
14290
|
}
|
|
14201
14291
|
}
|
|
14202
|
-
var import_apps13, import_ai5,
|
|
14292
|
+
var import_apps13, import_ai5, import_node_process13;
|
|
14203
14293
|
var init_provision = __esm({
|
|
14204
14294
|
"src/provision.ts"() {
|
|
14205
14295
|
"use strict";
|
|
14206
14296
|
init_cjs_shims();
|
|
14207
14297
|
import_apps13 = require("@odla-ai/apps");
|
|
14208
14298
|
import_ai5 = require("@odla-ai/ai");
|
|
14209
|
-
|
|
14299
|
+
import_node_process13 = __toESM(require("process"), 1);
|
|
14210
14300
|
init_config();
|
|
14211
14301
|
init_calendar();
|
|
14212
14302
|
init_calendar_errors();
|
|
@@ -14332,6 +14422,7 @@ var init_surface = __esm({
|
|
|
14332
14422
|
config: { diff: {}, plan: {}, apply: {} },
|
|
14333
14423
|
context: { show: {}, list: {}, save: {}, remove: {} },
|
|
14334
14424
|
credentials: { list: {}, revoke: {} },
|
|
14425
|
+
device: { enroll: {}, list: {}, revoke: {} },
|
|
14335
14426
|
// `watch`, `read`, `reply`, and `resolve` take a topic id from there on.
|
|
14336
14427
|
discuss: {
|
|
14337
14428
|
groups: {},
|
|
@@ -14400,7 +14491,7 @@ var init_surface = __esm({
|
|
|
14400
14491
|
|
|
14401
14492
|
// src/record.ts
|
|
14402
14493
|
function recordInvocation(parsed) {
|
|
14403
|
-
const file =
|
|
14494
|
+
const file = import_node_process14.default.env.ODLA_CLI_RECORD;
|
|
14404
14495
|
if (!file) return;
|
|
14405
14496
|
try {
|
|
14406
14497
|
const entry = {
|
|
@@ -14408,22 +14499,165 @@ function recordInvocation(parsed) {
|
|
|
14408
14499
|
options: Object.entries(parsed.options).map(([name, value2]) => value2 === false ? `no-${name}` : name).sort()
|
|
14409
14500
|
};
|
|
14410
14501
|
if (!entry.path.length) return;
|
|
14411
|
-
(0,
|
|
14502
|
+
(0, import_node_fs19.appendFileSync)(file, `${JSON.stringify(entry)}
|
|
14412
14503
|
`);
|
|
14413
14504
|
} catch {
|
|
14414
14505
|
}
|
|
14415
14506
|
}
|
|
14416
|
-
var
|
|
14507
|
+
var import_node_fs19, import_node_process14;
|
|
14417
14508
|
var init_record = __esm({
|
|
14418
14509
|
"src/record.ts"() {
|
|
14419
14510
|
"use strict";
|
|
14420
14511
|
init_cjs_shims();
|
|
14421
|
-
|
|
14422
|
-
|
|
14512
|
+
import_node_fs19 = require("fs");
|
|
14513
|
+
import_node_process14 = __toESM(require("process"), 1);
|
|
14423
14514
|
init_surface();
|
|
14424
14515
|
}
|
|
14425
14516
|
});
|
|
14426
14517
|
|
|
14518
|
+
// src/advisory-output.ts
|
|
14519
|
+
function advisoryCollectingFetch(inner, sink) {
|
|
14520
|
+
return (async (input, init) => {
|
|
14521
|
+
const response2 = await inner(input, init);
|
|
14522
|
+
try {
|
|
14523
|
+
sink.push(...(0, import_apps14.parseAdvisories)(response2));
|
|
14524
|
+
} catch {
|
|
14525
|
+
}
|
|
14526
|
+
return response2;
|
|
14527
|
+
});
|
|
14528
|
+
}
|
|
14529
|
+
function renderAdvisories(out, advisories, env = process.env) {
|
|
14530
|
+
if (env.ODLA_NO_ADVISORIES) return;
|
|
14531
|
+
const seen = /* @__PURE__ */ new Set();
|
|
14532
|
+
for (const advisory of advisories) {
|
|
14533
|
+
const key = `${advisory.code}:${advisory.message}`;
|
|
14534
|
+
if (seen.has(key)) continue;
|
|
14535
|
+
seen.add(key);
|
|
14536
|
+
out.error((0, import_apps14.formatAdvisory)(advisory));
|
|
14537
|
+
}
|
|
14538
|
+
}
|
|
14539
|
+
var import_apps14;
|
|
14540
|
+
var init_advisory_output = __esm({
|
|
14541
|
+
"src/advisory-output.ts"() {
|
|
14542
|
+
"use strict";
|
|
14543
|
+
init_cjs_shims();
|
|
14544
|
+
import_apps14 = require("@odla-ai/apps");
|
|
14545
|
+
}
|
|
14546
|
+
});
|
|
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
|
+
|
|
14427
14661
|
// src/runbook-actions.ts
|
|
14428
14662
|
async function call(ctx, method, path, body) {
|
|
14429
14663
|
const res = await ctx.doFetch(`${ctx.platformUrl.replace(/\/$/, "")}/registry/pm${path}`, {
|
|
@@ -14470,7 +14704,7 @@ async function bySlug(ctx, slug) {
|
|
|
14470
14704
|
function readBody(file, inline) {
|
|
14471
14705
|
if (inline !== void 0) return inline;
|
|
14472
14706
|
if (file === void 0) throw new Error("supply the new text with --file <path>, --file - (stdin), or --body");
|
|
14473
|
-
return (0,
|
|
14707
|
+
return (0, import_node_fs21.readFileSync)(file === "-" ? 0 : file, "utf8");
|
|
14474
14708
|
}
|
|
14475
14709
|
async function runbookList(ctx, all, query) {
|
|
14476
14710
|
const params = new URLSearchParams();
|
|
@@ -14559,12 +14793,12 @@ async function runbookRemove(ctx, slug) {
|
|
|
14559
14793
|
await call(ctx, "DELETE", `/runbook/${encodeURIComponent(runbook.id)}`);
|
|
14560
14794
|
ctx.out.log(`removed ${slug}`);
|
|
14561
14795
|
}
|
|
14562
|
-
var
|
|
14796
|
+
var import_node_fs21, PLATFORM_SCOPE, stamp;
|
|
14563
14797
|
var init_runbook_actions = __esm({
|
|
14564
14798
|
"src/runbook-actions.ts"() {
|
|
14565
14799
|
"use strict";
|
|
14566
14800
|
init_cjs_shims();
|
|
14567
|
-
|
|
14801
|
+
import_node_fs21 = require("fs");
|
|
14568
14802
|
init_version();
|
|
14569
14803
|
init_runbook_requires();
|
|
14570
14804
|
PLATFORM_SCOPE = "$platform";
|
|
@@ -14597,12 +14831,12 @@ function parseRunbook(text3, slug) {
|
|
|
14597
14831
|
};
|
|
14598
14832
|
}
|
|
14599
14833
|
function readRunbookDir(dir) {
|
|
14600
|
-
if (!(0,
|
|
14601
|
-
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();
|
|
14602
14836
|
if (!files.length) throw new Error(`no .md files in ${dir}`);
|
|
14603
14837
|
return files.map((file) => {
|
|
14604
|
-
const slug = (0,
|
|
14605
|
-
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);
|
|
14606
14840
|
return { file, slug, ...parsed, words: parsed.body.split(/\s+/).filter(Boolean).length };
|
|
14607
14841
|
});
|
|
14608
14842
|
}
|
|
@@ -14672,13 +14906,13 @@ async function upsert(ctx, r, visibility) {
|
|
|
14672
14906
|
);
|
|
14673
14907
|
return "updated";
|
|
14674
14908
|
}
|
|
14675
|
-
var
|
|
14909
|
+
var import_node_fs22, import_node_path19;
|
|
14676
14910
|
var init_runbook_import = __esm({
|
|
14677
14911
|
"src/runbook-import.ts"() {
|
|
14678
14912
|
"use strict";
|
|
14679
14913
|
init_cjs_shims();
|
|
14680
|
-
|
|
14681
|
-
|
|
14914
|
+
import_node_fs22 = require("fs");
|
|
14915
|
+
import_node_path19 = require("path");
|
|
14682
14916
|
init_runbook_actions();
|
|
14683
14917
|
}
|
|
14684
14918
|
});
|
|
@@ -14856,10 +15090,10 @@ ${body.split("\n").map((line2) => `+${line2}`).join("\n")}
|
|
|
14856
15090
|
}
|
|
14857
15091
|
function manifestLabeller(root) {
|
|
14858
15092
|
return (workspace) => {
|
|
14859
|
-
const manifest = (0,
|
|
14860
|
-
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;
|
|
14861
15095
|
try {
|
|
14862
|
-
const name = JSON.parse((0,
|
|
15096
|
+
const name = JSON.parse((0, import_node_fs23.readFileSync)(manifest, "utf8")).name;
|
|
14863
15097
|
return typeof name === "string" ? name : void 0;
|
|
14864
15098
|
} catch {
|
|
14865
15099
|
return void 0;
|
|
@@ -14925,7 +15159,7 @@ function report4(ctx, impacts) {
|
|
|
14925
15159
|
async function runbookImpact(ctx, options, deps = {}) {
|
|
14926
15160
|
const cwd = deps.cwd ?? process.cwd();
|
|
14927
15161
|
const runGit = deps.runGit ?? gitRunner(cwd);
|
|
14928
|
-
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"));
|
|
14929
15163
|
const surfaces = changedSurfaces(collectDiff(runGit, options.base, read3), manifestLabeller(cwd));
|
|
14930
15164
|
if (!surfaces.length) {
|
|
14931
15165
|
return ctx.out.log(
|
|
@@ -14936,14 +15170,14 @@ async function runbookImpact(ctx, options, deps = {}) {
|
|
|
14936
15170
|
if (ctx.json) return ctx.out.log(JSON.stringify({ base: options.base, impacts }, null, 2));
|
|
14937
15171
|
report4(ctx, impacts);
|
|
14938
15172
|
}
|
|
14939
|
-
var import_node_child_process6,
|
|
15173
|
+
var import_node_child_process6, import_node_fs23, import_node_path20, SOURCE3, editHint;
|
|
14940
15174
|
var init_runbook_impact = __esm({
|
|
14941
15175
|
"src/runbook-impact.ts"() {
|
|
14942
15176
|
"use strict";
|
|
14943
15177
|
init_cjs_shims();
|
|
14944
15178
|
import_node_child_process6 = require("child_process");
|
|
14945
|
-
|
|
14946
|
-
|
|
15179
|
+
import_node_fs23 = require("fs");
|
|
15180
|
+
import_node_path20 = require("path");
|
|
14947
15181
|
init_runbook_impact_scan();
|
|
14948
15182
|
init_runbook_actions();
|
|
14949
15183
|
SOURCE3 = /\.(ts|tsx|js|jsx|mts|cts)$/;
|
|
@@ -15089,7 +15323,7 @@ var init_runbook_search_command = __esm({
|
|
|
15089
15323
|
});
|
|
15090
15324
|
|
|
15091
15325
|
// src/runbook-editor.ts
|
|
15092
|
-
function resolveEditor(env =
|
|
15326
|
+
function resolveEditor(env = import_node_process16.default.env) {
|
|
15093
15327
|
for (const name of EDITOR_ENV) {
|
|
15094
15328
|
const value2 = env[name];
|
|
15095
15329
|
if (value2 && value2.trim()) return value2.trim();
|
|
@@ -15103,8 +15337,8 @@ function defaultRun(command, path) {
|
|
|
15103
15337
|
return result.status ?? 0;
|
|
15104
15338
|
}
|
|
15105
15339
|
function editText(initial, slug, deps = {}) {
|
|
15106
|
-
const env = deps.env ??
|
|
15107
|
-
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));
|
|
15108
15342
|
const editor = resolveEditor(env);
|
|
15109
15343
|
if (!editor)
|
|
15110
15344
|
throw new Error(
|
|
@@ -15112,28 +15346,28 @@ function editText(initial, slug, deps = {}) {
|
|
|
15112
15346
|
);
|
|
15113
15347
|
if (!interactive())
|
|
15114
15348
|
throw new Error(`cannot open an editor without a terminal \u2014 pass --file <path> or --body "\u2026" instead`);
|
|
15115
|
-
const dir = (0,
|
|
15116
|
-
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`);
|
|
15117
15351
|
try {
|
|
15118
|
-
(0,
|
|
15352
|
+
(0, import_node_fs24.writeFileSync)(file, initial, { mode: 384 });
|
|
15119
15353
|
const code = defaultRunOrInjected(deps)(editor, file);
|
|
15120
15354
|
if (code !== 0) throw new Error(`editor "${editor}" exited with ${code}; nothing was written`);
|
|
15121
|
-
const edited = (0,
|
|
15355
|
+
const edited = (0, import_node_fs24.readFileSync)(file, "utf8");
|
|
15122
15356
|
return edited === initial ? null : edited;
|
|
15123
15357
|
} finally {
|
|
15124
|
-
(0,
|
|
15358
|
+
(0, import_node_fs24.rmSync)(dir, { recursive: true, force: true });
|
|
15125
15359
|
}
|
|
15126
15360
|
}
|
|
15127
|
-
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;
|
|
15128
15362
|
var init_runbook_editor = __esm({
|
|
15129
15363
|
"src/runbook-editor.ts"() {
|
|
15130
15364
|
"use strict";
|
|
15131
15365
|
init_cjs_shims();
|
|
15132
15366
|
import_node_child_process7 = require("child_process");
|
|
15133
|
-
|
|
15134
|
-
|
|
15135
|
-
|
|
15136
|
-
|
|
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);
|
|
15137
15371
|
EDITOR_ENV = ["ODLA_EDITOR", "VISUAL", "EDITOR"];
|
|
15138
15372
|
defaultRunOrInjected = (deps) => deps.run ?? defaultRun;
|
|
15139
15373
|
}
|
|
@@ -15528,9 +15762,9 @@ async function runHostedSecurity(options) {
|
|
|
15528
15762
|
const appId = selfAudit ? "odla-ai" : cfg.app.id;
|
|
15529
15763
|
const env = selfAudit ? "prod" : selectEnv(options.env, cfg.envs, cfg.configPath, cfg.rootDir);
|
|
15530
15764
|
const platform = options.platform ?? cfg?.platformUrl ?? "https://odla.ai";
|
|
15531
|
-
const target = (0,
|
|
15532
|
-
const output = (0,
|
|
15533
|
-
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("/");
|
|
15534
15768
|
if (!outputRelative) throw new Error("Hosted security output cannot be the repository root");
|
|
15535
15769
|
const profile = profileFor(options.profile ?? "odla", options.maxHuntTasks ?? 12);
|
|
15536
15770
|
const tokenRequest = {
|
|
@@ -15542,7 +15776,7 @@ async function runHostedSecurity(options) {
|
|
|
15542
15776
|
};
|
|
15543
15777
|
const token = await injectedToken(options, tokenRequest);
|
|
15544
15778
|
const snapshot = await (0, import_node3.snapshotDirectory)(target, {
|
|
15545
|
-
exclude: !outputRelative.startsWith("../") && !(0,
|
|
15779
|
+
exclude: !outputRelative.startsWith("../") && !(0, import_node_path22.isAbsolute)(outputRelative) ? [outputRelative] : []
|
|
15546
15780
|
});
|
|
15547
15781
|
const hosted = await (0, import_security.createPlatformSecurityReasoners)({
|
|
15548
15782
|
platform,
|
|
@@ -15560,7 +15794,7 @@ async function runHostedSecurity(options) {
|
|
|
15560
15794
|
});
|
|
15561
15795
|
const harness = (0, import_security.createSecurityHarness)({
|
|
15562
15796
|
profile,
|
|
15563
|
-
store: new import_node3.FileRunStore((0,
|
|
15797
|
+
store: new import_node3.FileRunStore((0, import_node_path22.resolve)(output, "state")),
|
|
15564
15798
|
discoveryReasoner: hosted.discoveryReasoner,
|
|
15565
15799
|
validationReasoner: hosted.validationReasoner,
|
|
15566
15800
|
policy: {
|
|
@@ -15584,7 +15818,7 @@ async function runHostedSecurity(options) {
|
|
|
15584
15818
|
function selectEnv(requested, declared, configPath, rootDir) {
|
|
15585
15819
|
const env = requested ?? (declared.includes("dev") ? "dev" : declared[0]);
|
|
15586
15820
|
if (!env || !declared.includes(env)) {
|
|
15587
|
-
const shown = (0,
|
|
15821
|
+
const shown = (0, import_node_path22.relative)(rootDir, configPath) || configPath;
|
|
15588
15822
|
throw new Error(`env "${env ?? ""}" is not declared in ${shown}`);
|
|
15589
15823
|
}
|
|
15590
15824
|
return env;
|
|
@@ -15613,17 +15847,17 @@ function printSummary(out, appId, env, run, report5, output) {
|
|
|
15613
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}`);
|
|
15614
15848
|
if (report5.callBudget) out.log(` calls: discovery=${formatBudget(report5.callBudget.discovery)} validation=${formatBudget(report5.callBudget.validation)}`);
|
|
15615
15849
|
out.log(` findings: confirmed=${report5.metrics.confirmed} needs_reproduction=${report5.metrics.needsReproduction} candidates=${report5.metrics.candidates}`);
|
|
15616
|
-
out.log(` report: ${(0,
|
|
15850
|
+
out.log(` report: ${(0, import_node_path22.resolve)(output, "REPORT.md")}`);
|
|
15617
15851
|
}
|
|
15618
15852
|
function formatBudget(usage) {
|
|
15619
15853
|
return usage ? `${usage.usedCalls}/${usage.maxCalls} skipped=${usage.skippedCalls}` : "caller-managed";
|
|
15620
15854
|
}
|
|
15621
|
-
var
|
|
15855
|
+
var import_node_path22, import_security, import_node3;
|
|
15622
15856
|
var init_security = __esm({
|
|
15623
15857
|
"src/security.ts"() {
|
|
15624
15858
|
"use strict";
|
|
15625
15859
|
init_cjs_shims();
|
|
15626
|
-
|
|
15860
|
+
import_node_path22 = require("path");
|
|
15627
15861
|
import_security = require("@odla-ai/security");
|
|
15628
15862
|
import_node3 = require("@odla-ai/security/node");
|
|
15629
15863
|
init_config();
|
|
@@ -16111,6 +16345,23 @@ __export(cli_exports, {
|
|
|
16111
16345
|
runCli: () => runCli
|
|
16112
16346
|
});
|
|
16113
16347
|
async function runCli(argv2 = process.argv.slice(2), dependencies = {}) {
|
|
16348
|
+
const out = redactingOutput(dependencies.stdout ?? console);
|
|
16349
|
+
const advisories = [];
|
|
16350
|
+
const withAdvisoryReader = {
|
|
16351
|
+
...dependencies,
|
|
16352
|
+
fetch: advisoryCollectingFetch(dependencies.fetch ?? fetch, advisories)
|
|
16353
|
+
};
|
|
16354
|
+
try {
|
|
16355
|
+
return await dispatchCli(argv2, withAdvisoryReader);
|
|
16356
|
+
} catch (error) {
|
|
16357
|
+
const explanation = explainRejectedCredential(error);
|
|
16358
|
+
if (explanation) out.error(explanation);
|
|
16359
|
+
throw error;
|
|
16360
|
+
} finally {
|
|
16361
|
+
renderAdvisories(out, advisories);
|
|
16362
|
+
}
|
|
16363
|
+
}
|
|
16364
|
+
async function dispatchCli(argv2, dependencies) {
|
|
16114
16365
|
const runtime = {
|
|
16115
16366
|
...dependencies,
|
|
16116
16367
|
stdout: redactingOutput(dependencies.stdout ?? console)
|
|
@@ -16140,6 +16391,10 @@ async function runCli(argv2 = process.argv.slice(2), dependencies = {}) {
|
|
|
16140
16391
|
await contextCommand(parsed, runtime);
|
|
16141
16392
|
return;
|
|
16142
16393
|
}
|
|
16394
|
+
if (command === "device") {
|
|
16395
|
+
await deviceCommand(parsed, runtime);
|
|
16396
|
+
return;
|
|
16397
|
+
}
|
|
16143
16398
|
if (command === "credentials") {
|
|
16144
16399
|
await credentialCommand(parsed, runtime);
|
|
16145
16400
|
return;
|
|
@@ -16300,6 +16555,9 @@ var init_cli = __esm({
|
|
|
16300
16555
|
init_monitor_command();
|
|
16301
16556
|
init_provision();
|
|
16302
16557
|
init_record();
|
|
16558
|
+
init_advisory_output();
|
|
16559
|
+
init_cached_credential();
|
|
16560
|
+
init_device_command();
|
|
16303
16561
|
init_redact();
|
|
16304
16562
|
init_runbook_command();
|
|
16305
16563
|
init_security_command();
|