@odla-ai/cli 0.38.3 → 0.39.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 +675 -292
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-WJAN5CZ2.js → chunk-PJ3RATDD.js} +551 -215
- package/dist/chunk-PJ3RATDD.js.map +1 -0
- package/dist/{cli-A5R2DMM4.js → cli-ALS5OZPB.js} +2 -2
- package/dist/index.cjs +585 -254
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +2 -2
- package/skills/odla/SKILL.md +50 -11
- package/skills/odla/references/agent-identity.md +94 -111
- package/dist/chunk-WJAN5CZ2.js.map +0 -1
- /package/dist/{cli-A5R2DMM4.js.map → cli-ALS5OZPB.js.map} +0 -0
package/dist/bin.cjs
CHANGED
|
@@ -347,45 +347,15 @@ 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
350
|
// src/device-session.ts
|
|
381
351
|
function deviceCredentialPath(env = import_node_process4.default.env) {
|
|
382
352
|
return env.ODLA_DEVICE_CREDENTIAL ?? (0, import_node_path3.join)(env.HOME ?? (0, import_node_os.homedir)(), ".odla", "device.json");
|
|
383
353
|
}
|
|
384
354
|
function readDeviceCredential(platform, env = import_node_process4.default.env) {
|
|
385
355
|
const path = deviceCredentialPath(env);
|
|
386
|
-
if (!(0,
|
|
356
|
+
if (!(0, import_node_fs5.existsSync)(path)) return null;
|
|
387
357
|
try {
|
|
388
|
-
const parsed = JSON.parse((0,
|
|
358
|
+
const parsed = JSON.parse((0, import_node_fs5.readFileSync)(path, "utf8"));
|
|
389
359
|
if (typeof parsed.token !== "string" || !parsed.token.startsWith("odla_device_")) return null;
|
|
390
360
|
if (parsed.platform !== platform) return null;
|
|
391
361
|
return { ...parsed, token: parsed.token, platform: parsed.platform };
|
|
@@ -407,20 +377,86 @@ async function mintDeviceSession(platformUrl, credential2, doFetch) {
|
|
|
407
377
|
`device session failed: ${detail} (${response2.status})` + (revocable ? " \u2014 if this machine's enrollment was revoked or has expired, enroll it again in Studio" : "")
|
|
408
378
|
);
|
|
409
379
|
}
|
|
410
|
-
return {
|
|
380
|
+
return {
|
|
381
|
+
token: body.token,
|
|
382
|
+
expiresAt: body.expiresAt ?? Date.now(),
|
|
383
|
+
// Absent from a registry that predates rolling expiry and scoped devices.
|
|
384
|
+
// Left undefined rather than defaulted, so a caller can tell "the platform
|
|
385
|
+
// did not say" from "the platform said none".
|
|
386
|
+
...typeof body.deviceExpiresAt === "number" ? { deviceExpiresAt: body.deviceExpiresAt } : {},
|
|
387
|
+
...Array.isArray(body.appIds) ? { appIds: body.appIds } : {},
|
|
388
|
+
...Array.isArray(body.capabilities) ? { capabilities: body.capabilities } : {},
|
|
389
|
+
...Array.isArray(body.scopes) ? { scopes: body.scopes } : {}
|
|
390
|
+
};
|
|
411
391
|
}
|
|
412
|
-
var
|
|
392
|
+
var import_node_fs5, import_node_os, import_node_path3, import_node_process4;
|
|
413
393
|
var init_device_session = __esm({
|
|
414
394
|
"src/device-session.ts"() {
|
|
415
395
|
"use strict";
|
|
416
396
|
init_cjs_shims();
|
|
417
|
-
|
|
397
|
+
import_node_fs5 = require("fs");
|
|
418
398
|
import_node_os = require("os");
|
|
419
399
|
import_node_path3 = require("path");
|
|
420
400
|
import_node_process4 = __toESM(require("process"), 1);
|
|
421
401
|
}
|
|
422
402
|
});
|
|
423
403
|
|
|
404
|
+
// src/odla-home.ts
|
|
405
|
+
function odlaHome(env = import_node_process5.default.env) {
|
|
406
|
+
return env.ODLA_HOME ?? (0, import_node_path4.join)(env.HOME ?? (0, import_node_os2.homedir)(), ".odla");
|
|
407
|
+
}
|
|
408
|
+
function odlaHomePath(segments, env = import_node_process5.default.env) {
|
|
409
|
+
return (0, import_node_path4.join)(odlaHome(env), ...segments);
|
|
410
|
+
}
|
|
411
|
+
function identityFile(env) {
|
|
412
|
+
return odlaHomePath(["identity.json"], env);
|
|
413
|
+
}
|
|
414
|
+
function deviceSessionFile(env) {
|
|
415
|
+
return odlaHomePath(["session.json"], env);
|
|
416
|
+
}
|
|
417
|
+
function appTokenFile(appId, env) {
|
|
418
|
+
return odlaHomePath(["apps", safeSegment(appId), "dev-token.json"], env);
|
|
419
|
+
}
|
|
420
|
+
function appCredentialsFile(appId, env) {
|
|
421
|
+
return odlaHomePath(["apps", safeSegment(appId), "credentials.json"], env);
|
|
422
|
+
}
|
|
423
|
+
function scopedTokenFile(env) {
|
|
424
|
+
return odlaHomePath(["admin-token.local.json"], env);
|
|
425
|
+
}
|
|
426
|
+
function pmContextFile(env) {
|
|
427
|
+
return odlaHomePath(["pm-context.json"], env);
|
|
428
|
+
}
|
|
429
|
+
function adoptRepoLocalCache(legacyPath, machinePath, out) {
|
|
430
|
+
if (!(0, import_node_fs6.existsSync)(legacyPath) || legacyPath === machinePath) return false;
|
|
431
|
+
const superseded = (0, import_node_fs6.existsSync)(machinePath);
|
|
432
|
+
if (!superseded) {
|
|
433
|
+
(0, import_node_fs6.mkdirSync)((0, import_node_path4.dirname)(machinePath), { recursive: true });
|
|
434
|
+
(0, import_node_fs6.copyFileSync)(legacyPath, machinePath);
|
|
435
|
+
(0, import_node_fs6.chmodSync)(machinePath, 384);
|
|
436
|
+
}
|
|
437
|
+
(0, import_node_fs6.rmSync)(legacyPath, { force: true });
|
|
438
|
+
out?.error(
|
|
439
|
+
superseded ? `auth: removed superseded ${legacyPath}; this machine's credentials live in ${odlaHome()}` : `auth: moved ${legacyPath} into ${machinePath}; credentials are per machine now, not per worktree`
|
|
440
|
+
);
|
|
441
|
+
return true;
|
|
442
|
+
}
|
|
443
|
+
function safeSegment(value2) {
|
|
444
|
+
const clean4 = value2.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/^-+|-+$/g, "");
|
|
445
|
+
if (!clean4) throw new Error(`"${value2}" is not a usable app id`);
|
|
446
|
+
return clean4;
|
|
447
|
+
}
|
|
448
|
+
var import_node_fs6, import_node_os2, import_node_path4, import_node_process5;
|
|
449
|
+
var init_odla_home = __esm({
|
|
450
|
+
"src/odla-home.ts"() {
|
|
451
|
+
"use strict";
|
|
452
|
+
init_cjs_shims();
|
|
453
|
+
import_node_fs6 = require("fs");
|
|
454
|
+
import_node_os2 = require("os");
|
|
455
|
+
import_node_path4 = require("path");
|
|
456
|
+
import_node_process5 = __toESM(require("process"), 1);
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
|
|
424
460
|
// src/local.ts
|
|
425
461
|
function readJsonFile(path) {
|
|
426
462
|
try {
|
|
@@ -467,7 +503,7 @@ function mergeCredential(current, update) {
|
|
|
467
503
|
return next;
|
|
468
504
|
}
|
|
469
505
|
function ensureGitignore(rootDir, localPaths = []) {
|
|
470
|
-
const path = (0,
|
|
506
|
+
const path = (0, import_node_path5.resolve)(rootDir, ".gitignore");
|
|
471
507
|
const existing = (0, import_node_fs7.existsSync)(path) ? (0, import_node_fs7.readFileSync)(path, "utf8") : "";
|
|
472
508
|
const configured = localPaths.map((localPath) => gitignoreEntry(rootDir, localPath)).filter((line2) => !!line2);
|
|
473
509
|
const wanted = [.../* @__PURE__ */ new Set([...GITIGNORE_LINES, ...configured])];
|
|
@@ -488,7 +524,7 @@ function o11yDevVars(cfg) {
|
|
|
488
524
|
function resolveWriteDevVarsTarget(cfg, requested) {
|
|
489
525
|
if (!requested) return null;
|
|
490
526
|
if (requested === true) return cfg.local.devVarsFile;
|
|
491
|
-
return (0,
|
|
527
|
+
return (0, import_node_path5.resolve)((0, import_node_path5.dirname)(cfg.configPath), requested);
|
|
492
528
|
}
|
|
493
529
|
function writeDevVars(path, credentials, env, o11y) {
|
|
494
530
|
const entry = credentials.envs[env];
|
|
@@ -516,28 +552,28 @@ function isManagedDevVar(line2) {
|
|
|
516
552
|
return !!match?.[1] && MANAGED_DEV_VARS.has(match[1]);
|
|
517
553
|
}
|
|
518
554
|
function writePrivateText(path, text3) {
|
|
519
|
-
(0, import_node_fs7.mkdirSync)((0,
|
|
555
|
+
(0, import_node_fs7.mkdirSync)((0, import_node_path5.dirname)(path), { recursive: true });
|
|
520
556
|
const temporary = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
521
557
|
(0, import_node_fs7.writeFileSync)(temporary, text3, { mode: 384 });
|
|
522
558
|
(0, import_node_fs7.chmodSync)(temporary, 384);
|
|
523
559
|
(0, import_node_fs7.renameSync)(temporary, path);
|
|
524
560
|
}
|
|
525
561
|
function gitignoreEntry(rootDir, path) {
|
|
526
|
-
const rel = (0,
|
|
527
|
-
if (!rel || rel === ".." || rel.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) || (0,
|
|
562
|
+
const rel = (0, import_node_path5.relative)((0, import_node_path5.resolve)(rootDir), (0, import_node_path5.resolve)(path));
|
|
563
|
+
if (!rel || rel === ".." || rel.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) || (0, import_node_path5.isAbsolute)(rel)) return null;
|
|
528
564
|
return rel.replaceAll("\\", "/");
|
|
529
565
|
}
|
|
530
566
|
function displayPath(path, rootDir = process.cwd()) {
|
|
531
|
-
const rel = (0,
|
|
567
|
+
const rel = (0, import_node_path5.relative)(rootDir, path);
|
|
532
568
|
return rel && !rel.startsWith("..") ? rel : path;
|
|
533
569
|
}
|
|
534
|
-
var import_node_fs7,
|
|
570
|
+
var import_node_fs7, import_node_path5, GITIGNORE_LINES, MANAGED_DEV_VARS;
|
|
535
571
|
var init_local = __esm({
|
|
536
572
|
"src/local.ts"() {
|
|
537
573
|
"use strict";
|
|
538
574
|
init_cjs_shims();
|
|
539
575
|
import_node_fs7 = require("fs");
|
|
540
|
-
|
|
576
|
+
import_node_path5 = require("path");
|
|
541
577
|
GITIGNORE_LINES = [".odla/*.local.json", ".odla/dev-token.json", ".dev.vars"];
|
|
542
578
|
MANAGED_DEV_VARS = /* @__PURE__ */ new Set([
|
|
543
579
|
"ODLA_PLATFORM",
|
|
@@ -554,6 +590,133 @@ var init_local = __esm({
|
|
|
554
590
|
}
|
|
555
591
|
});
|
|
556
592
|
|
|
593
|
+
// src/auth-guidance.ts
|
|
594
|
+
function machineAuthState(audience, env = import_node_process6.default.env) {
|
|
595
|
+
const device = readDeviceCredential(audience, env);
|
|
596
|
+
if (!device) return { enrolled: false };
|
|
597
|
+
const session = readJsonFile(deviceSessionFile(env));
|
|
598
|
+
const current = session?.platform === audience && session.deviceId === device.deviceId ? session : void 0;
|
|
599
|
+
return {
|
|
600
|
+
enrolled: true,
|
|
601
|
+
...device.name ? { deviceName: device.name } : {},
|
|
602
|
+
...current?.appIds ? { appIds: current.appIds } : {},
|
|
603
|
+
...current?.capabilities ? { capabilities: current.capabilities } : {},
|
|
604
|
+
...current?.scopes ? { scopes: current.scopes } : {},
|
|
605
|
+
...current?.deviceExpiresAt ? { lapsesAt: current.deviceExpiresAt } : {}
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
function scopeInterruptionNotice(scope, state2) {
|
|
609
|
+
const platformScope = scope.startsWith("platform:");
|
|
610
|
+
const reason = !state2.enrolled ? "this machine is not enrolled, so every privileged command needs its own browser approval" : `this machine is enrolled but its approval did not include "${scope}"`;
|
|
611
|
+
return [
|
|
612
|
+
`odla: ${reason}.`,
|
|
613
|
+
` Approve this one now, then end the interruptions with:`,
|
|
614
|
+
` ${platformScope ? ENROL_PLATFORM_WIDE : ENROL_EVERYTHING}`,
|
|
615
|
+
platformScope ? " A platform scope needs an administrator's approval; an app owner's cannot carry it." : " One approval, every app you own, and it rolls forward while you keep working."
|
|
616
|
+
].join("\n");
|
|
617
|
+
}
|
|
618
|
+
function lapseNotice(state2, now = Date.now()) {
|
|
619
|
+
if (!state2.enrolled || !state2.lapsesAt) return null;
|
|
620
|
+
const days = Math.floor((state2.lapsesAt - now) / (24 * 60 * 60 * 1e3));
|
|
621
|
+
if (days < 0) return "this machine's enrollment has lapsed; the next command will ask for approval";
|
|
622
|
+
return `idle for ${days} more day${days === 1 ? "" : "s"} before this machine needs approving again (using it resets the clock)`;
|
|
623
|
+
}
|
|
624
|
+
var import_node_process6, ENROL_EVERYTHING, ENROL_PLATFORM_WIDE;
|
|
625
|
+
var init_auth_guidance = __esm({
|
|
626
|
+
"src/auth-guidance.ts"() {
|
|
627
|
+
"use strict";
|
|
628
|
+
init_cjs_shims();
|
|
629
|
+
import_node_process6 = __toESM(require("process"), 1);
|
|
630
|
+
init_device_session();
|
|
631
|
+
init_odla_home();
|
|
632
|
+
init_local();
|
|
633
|
+
ENROL_EVERYTHING = "npx odla-ai device enroll --all-apps --capability all --no-open --wait 600";
|
|
634
|
+
ENROL_PLATFORM_WIDE = "npx odla-ai device enroll --platform-wide --device-ttl 6w --no-open --wait 600";
|
|
635
|
+
}
|
|
636
|
+
});
|
|
637
|
+
|
|
638
|
+
// src/cached-credential.ts
|
|
639
|
+
function noteCachedCredential(tokenFile) {
|
|
640
|
+
noted = tokenFile;
|
|
641
|
+
}
|
|
642
|
+
function isCredentialRejection(error) {
|
|
643
|
+
const message2 = error instanceof Error ? error.message : String(error ?? "");
|
|
644
|
+
return /\((401|403)\)\s*$/.test(message2.trim());
|
|
645
|
+
}
|
|
646
|
+
function explainRejectedCredential(error) {
|
|
647
|
+
const tokenFile = noted;
|
|
648
|
+
if (!tokenFile || !isCredentialRejection(error)) return null;
|
|
649
|
+
noted = null;
|
|
650
|
+
(0, import_node_fs8.rmSync)(tokenFile, { force: true });
|
|
651
|
+
return [
|
|
652
|
+
"auth: the cached credential was rejected by odla, so it was revoked before its cached expiry.",
|
|
653
|
+
" The usual cause is a newer sign-in for this account: collecting a handshake retires the",
|
|
654
|
+
" principal's other collected credentials, so a second machine supersedes this one.",
|
|
655
|
+
` Discarded ${tokenFile}; re-run this command to request a fresh approval.`,
|
|
656
|
+
` To stop needing one: ${ENROL_EVERYTHING}`
|
|
657
|
+
].join("\n");
|
|
658
|
+
}
|
|
659
|
+
var import_node_fs8, noted;
|
|
660
|
+
var init_cached_credential = __esm({
|
|
661
|
+
"src/cached-credential.ts"() {
|
|
662
|
+
"use strict";
|
|
663
|
+
init_cjs_shims();
|
|
664
|
+
import_node_fs8 = require("fs");
|
|
665
|
+
init_auth_guidance();
|
|
666
|
+
noted = null;
|
|
667
|
+
}
|
|
668
|
+
});
|
|
669
|
+
|
|
670
|
+
// src/device-session-cache.ts
|
|
671
|
+
async function deviceSessionToken(platformUrl, audience, credential2, doFetch, env = import_node_process7.default.env) {
|
|
672
|
+
const path = deviceSessionFile(env);
|
|
673
|
+
const cached = readJsonFile(path);
|
|
674
|
+
if (cached?.token && cached.platform === audience && cached.deviceId === credential2.deviceId && (cached.expiresAt ?? 0) > Date.now() + SKEW_MS) return cached;
|
|
675
|
+
const minted = await mintDeviceSession(platformUrl, credential2, doFetch);
|
|
676
|
+
const session = {
|
|
677
|
+
...minted,
|
|
678
|
+
platform: audience,
|
|
679
|
+
...credential2.deviceId ? { deviceId: credential2.deviceId } : {}
|
|
680
|
+
};
|
|
681
|
+
writePrivateJson(path, session);
|
|
682
|
+
return session;
|
|
683
|
+
}
|
|
684
|
+
var import_node_process7, SKEW_MS;
|
|
685
|
+
var init_device_session_cache = __esm({
|
|
686
|
+
"src/device-session-cache.ts"() {
|
|
687
|
+
"use strict";
|
|
688
|
+
init_cjs_shims();
|
|
689
|
+
import_node_process7 = __toESM(require("process"), 1);
|
|
690
|
+
init_odla_home();
|
|
691
|
+
init_local();
|
|
692
|
+
init_device_session();
|
|
693
|
+
SKEW_MS = 6e4;
|
|
694
|
+
}
|
|
695
|
+
});
|
|
696
|
+
|
|
697
|
+
// src/machine-identity.ts
|
|
698
|
+
function readMachineIdentity(audience, env = import_node_process8.default.env) {
|
|
699
|
+
const stored = readJsonFile(identityFile(env));
|
|
700
|
+
if (!stored || typeof stored.email !== "string" || !stored.email) return null;
|
|
701
|
+
return stored.platform === audience ? { platform: audience, email: stored.email } : null;
|
|
702
|
+
}
|
|
703
|
+
function rememberMachineIdentity(audience, email, env = import_node_process8.default.env) {
|
|
704
|
+
if (!email) return;
|
|
705
|
+
const existing = readMachineIdentity(audience, env);
|
|
706
|
+
if (existing?.email === email) return;
|
|
707
|
+
writePrivateJson(identityFile(env), { platform: audience, email });
|
|
708
|
+
}
|
|
709
|
+
var import_node_process8;
|
|
710
|
+
var init_machine_identity = __esm({
|
|
711
|
+
"src/machine-identity.ts"() {
|
|
712
|
+
"use strict";
|
|
713
|
+
init_cjs_shims();
|
|
714
|
+
import_node_process8 = __toESM(require("process"), 1);
|
|
715
|
+
init_odla_home();
|
|
716
|
+
init_local();
|
|
717
|
+
}
|
|
718
|
+
});
|
|
719
|
+
|
|
557
720
|
// src/token.ts
|
|
558
721
|
async function getDeveloperToken(cfg, options, doFetch, out, grantRequest = {}) {
|
|
559
722
|
const audience = platformAudience(cfg.platformUrl);
|
|
@@ -562,19 +725,19 @@ async function getDeveloperToken(cfg, options, doFetch, out, grantRequest = {})
|
|
|
562
725
|
const cached = readJsonFile(cfg.local.tokenFile);
|
|
563
726
|
if (!grantRequest.forceReview && !grantRequest.freshLogin) {
|
|
564
727
|
if (options.token) return options.token;
|
|
565
|
-
if (
|
|
566
|
-
const declared =
|
|
728
|
+
if (import_node_process9.default.env.ODLA_DEV_TOKEN) {
|
|
729
|
+
const declared = import_node_process9.default.env.ODLA_DEV_TOKEN_AUDIENCE;
|
|
567
730
|
if (declared) {
|
|
568
731
|
if (platformAudience(declared) !== audience) throw new Error("ODLA_DEV_TOKEN_AUDIENCE does not match the configured platform");
|
|
569
732
|
} else if (audience !== "https://odla.ai") {
|
|
570
733
|
throw new Error("ODLA_DEV_TOKEN_AUDIENCE is required for a non-default platform");
|
|
571
734
|
}
|
|
572
|
-
return
|
|
735
|
+
return import_node_process9.default.env.ODLA_DEV_TOKEN;
|
|
573
736
|
}
|
|
574
737
|
const device = readDeviceCredential(audience);
|
|
575
738
|
if (device) {
|
|
576
|
-
const session = await
|
|
577
|
-
out.error(`auth: session
|
|
739
|
+
const session = await deviceSessionToken(cfg.platformUrl, audience, device, doFetch);
|
|
740
|
+
out.error(`auth: session held by this enrolled device (${displayPath(deviceCredentialPath(), cfg.rootDir)})`);
|
|
578
741
|
return session.token;
|
|
579
742
|
}
|
|
580
743
|
if (cached?.token && cached.platform === audience && (cached.expiresAt ?? 0) > Date.now() + 6e4 && cachedGrantCovers(cached, grantIntent)) {
|
|
@@ -594,7 +757,10 @@ async function getDeveloperToken(cfg, options, doFetch, out, grantRequest = {})
|
|
|
594
757
|
doFetch,
|
|
595
758
|
out,
|
|
596
759
|
audience,
|
|
597
|
-
email: handshakeEmail(
|
|
760
|
+
email: handshakeEmail(
|
|
761
|
+
options.email,
|
|
762
|
+
(cached?.platform === audience ? cached.email : void 0) ?? readMachineIdentity(audience)?.email
|
|
763
|
+
),
|
|
598
764
|
pendingFile: handshakeFile(cfg),
|
|
599
765
|
grantIntent
|
|
600
766
|
};
|
|
@@ -611,6 +777,7 @@ async function getDeveloperToken(cfg, options, doFetch, out, grantRequest = {})
|
|
|
611
777
|
expiresAt
|
|
612
778
|
});
|
|
613
779
|
out.error(`auth: developer token cached (${displayPath(cfg.local.tokenFile, cfg.rootDir)})`);
|
|
780
|
+
rememberMachineIdentity(audience, ctx.email);
|
|
614
781
|
return token;
|
|
615
782
|
}
|
|
616
783
|
async function freshHandshake(ctx, waitMs) {
|
|
@@ -680,7 +847,7 @@ function stillPending(pending, email) {
|
|
|
680
847
|
);
|
|
681
848
|
}
|
|
682
849
|
function handshakeEmail(value2, cached) {
|
|
683
|
-
const email = (value2 ??
|
|
850
|
+
const email = (value2 ?? import_node_process9.default.env.ODLA_USER_EMAIL ?? cached ?? "").trim().toLowerCase();
|
|
684
851
|
if (/@users\.noreply\.github\.com$/i.test(email)) {
|
|
685
852
|
throw new Error(
|
|
686
853
|
`"${email}" is a GitHub commit identity, not an odla account email; use --email <signed-in-odla-account> or ODLA_USER_EMAIL`
|
|
@@ -709,18 +876,20 @@ function platformAudience(value2) {
|
|
|
709
876
|
}
|
|
710
877
|
return url.origin;
|
|
711
878
|
}
|
|
712
|
-
var import_db, import_node_crypto,
|
|
879
|
+
var import_db, import_node_crypto, import_node_process9;
|
|
713
880
|
var init_token = __esm({
|
|
714
881
|
"src/token.ts"() {
|
|
715
882
|
"use strict";
|
|
716
883
|
init_cjs_shims();
|
|
717
884
|
import_db = require("@odla-ai/db");
|
|
718
885
|
import_node_crypto = require("crypto");
|
|
719
|
-
|
|
886
|
+
import_node_process9 = __toESM(require("process"), 1);
|
|
720
887
|
init_handshake_approval();
|
|
721
888
|
init_handshake_state();
|
|
722
889
|
init_cached_credential();
|
|
723
890
|
init_device_session();
|
|
891
|
+
init_device_session_cache();
|
|
892
|
+
init_machine_identity();
|
|
724
893
|
init_local();
|
|
725
894
|
}
|
|
726
895
|
});
|
|
@@ -729,7 +898,7 @@ var init_token = __esm({
|
|
|
729
898
|
async function secretInputValue(options, kind = "credential") {
|
|
730
899
|
if (options.fromEnv && options.stdin) throw new Error("choose exactly one of --from-env or --stdin");
|
|
731
900
|
let value2;
|
|
732
|
-
if (options.fromEnv) value2 =
|
|
901
|
+
if (options.fromEnv) value2 = import_node_process10.default.env[options.fromEnv];
|
|
733
902
|
else if (options.stdin) value2 = await (options.readStdin ?? (() => readSecretStream(kind)))();
|
|
734
903
|
else throw new Error(`${kind} input required: use --from-env <NAME> or --stdin; values are never accepted as arguments`);
|
|
735
904
|
value2 = value2?.replace(/[\r\n]+$/, "");
|
|
@@ -737,7 +906,7 @@ async function secretInputValue(options, kind = "credential") {
|
|
|
737
906
|
if (new TextEncoder().encode(value2).byteLength > MAX_BYTES) throw new Error(`${kind} exceeds 64 KiB`);
|
|
738
907
|
return value2;
|
|
739
908
|
}
|
|
740
|
-
async function readSecretStream(kind, stream =
|
|
909
|
+
async function readSecretStream(kind, stream = import_node_process10.default.stdin) {
|
|
741
910
|
let value2 = "";
|
|
742
911
|
for await (const chunk of stream) {
|
|
743
912
|
value2 += String(chunk);
|
|
@@ -745,12 +914,12 @@ async function readSecretStream(kind, stream = import_node_process6.default.stdi
|
|
|
745
914
|
}
|
|
746
915
|
return value2;
|
|
747
916
|
}
|
|
748
|
-
var
|
|
917
|
+
var import_node_process10, MAX_BYTES;
|
|
749
918
|
var init_secret_input = __esm({
|
|
750
919
|
"src/secret-input.ts"() {
|
|
751
920
|
"use strict";
|
|
752
921
|
init_cjs_shims();
|
|
753
|
-
|
|
922
|
+
import_node_process10 = __toESM(require("process"), 1);
|
|
754
923
|
MAX_BYTES = 64 * 1024;
|
|
755
924
|
}
|
|
756
925
|
});
|
|
@@ -762,7 +931,7 @@ async function getScopedPlatformToken(options) {
|
|
|
762
931
|
async function resolveAdminPlatformToken(options) {
|
|
763
932
|
const audience = platformAudience(options.platform);
|
|
764
933
|
if (options.token) return options.token;
|
|
765
|
-
const fromEnv =
|
|
934
|
+
const fromEnv = import_node_process11.default.env.ODLA_ADMIN_TOKEN;
|
|
766
935
|
if (fromEnv) return audienceBoundEnvToken(fromEnv, audience);
|
|
767
936
|
return scopedToken(
|
|
768
937
|
audience,
|
|
@@ -774,7 +943,7 @@ async function resolveAdminPlatformToken(options) {
|
|
|
774
943
|
}
|
|
775
944
|
function audienceBoundEnvToken(token, platform) {
|
|
776
945
|
const audience = platformAudience(platform);
|
|
777
|
-
const declared =
|
|
946
|
+
const declared = import_node_process11.default.env.ODLA_ADMIN_TOKEN_AUDIENCE;
|
|
778
947
|
if (declared) {
|
|
779
948
|
if (platformAudience(declared) !== audience) throw new Error("ODLA_ADMIN_TOKEN_AUDIENCE does not match the configured platform");
|
|
780
949
|
} else if (audience !== "https://odla.ai") {
|
|
@@ -784,15 +953,28 @@ function audienceBoundEnvToken(token, platform) {
|
|
|
784
953
|
}
|
|
785
954
|
async function scopedToken(platform, scope, options, doFetch, out) {
|
|
786
955
|
const audience = platformAudience(platform);
|
|
787
|
-
const rootDir = options.rootDir ??
|
|
788
|
-
const tokenFile = options.tokenFile ?? (
|
|
956
|
+
const rootDir = options.rootDir ?? import_node_process11.default.cwd();
|
|
957
|
+
const tokenFile = options.tokenFile ?? scopedTokenFile();
|
|
958
|
+
adoptRepoLocalCache((0, import_node_path6.join)(rootDir, ".odla/admin-token.local.json"), tokenFile, out);
|
|
959
|
+
const device = readDeviceCredential(audience);
|
|
960
|
+
if (device && options.cache !== false) {
|
|
961
|
+
const session = await deviceSessionToken(platform, audience, device, doFetch);
|
|
962
|
+
if (session.scopes?.includes(scope)) {
|
|
963
|
+
out.error(`auth: ${scope} held by this enrolled device`);
|
|
964
|
+
return session.token;
|
|
965
|
+
}
|
|
966
|
+
}
|
|
789
967
|
const cache2 = options.cache === false ? null : readJsonFile(tokenFile);
|
|
790
968
|
const cached = cache2?.platform === audience ? cache2.tokens?.[scope] : void 0;
|
|
791
969
|
if (cached?.token && (cached.expiresAt ?? 0) > Date.now() + 6e4) {
|
|
792
970
|
out.error(`auth: using cached ${scope} grant (${tokenFile})`);
|
|
793
971
|
return cached.token;
|
|
794
972
|
}
|
|
795
|
-
|
|
973
|
+
out.error(scopeInterruptionNotice(scope, machineAuthState(audience)));
|
|
974
|
+
const email = handshakeEmail(
|
|
975
|
+
options.email,
|
|
976
|
+
(cache2?.platform === audience ? cache2.email : void 0) ?? readMachineIdentity(audience)?.email
|
|
977
|
+
);
|
|
796
978
|
const { token, expiresAt } = await (0, import_db2.requestToken)({
|
|
797
979
|
endpoint: audience,
|
|
798
980
|
email,
|
|
@@ -812,26 +994,30 @@ async function scopedToken(platform, scope, options, doFetch, out) {
|
|
|
812
994
|
if (options.cache !== false) {
|
|
813
995
|
const tokens = cache2?.platform === audience ? { ...cache2.tokens ?? {} } : {};
|
|
814
996
|
tokens[scope] = { token, expiresAt };
|
|
815
|
-
if ((0, import_node_fs8.existsSync)((0, import_node_path5.join)(rootDir, ".git"))) ensureGitignore(rootDir, [tokenFile]);
|
|
816
997
|
writePrivateJson(tokenFile, { platform: audience, email, tokens });
|
|
998
|
+
rememberMachineIdentity(audience, email);
|
|
817
999
|
out.error(`auth: cached ${scope} grant (${tokenFile}; mode 0600)`);
|
|
818
1000
|
} else {
|
|
819
1001
|
out.error(`auth: ${scope} grant is in memory only; its credential record remains in odla-ai/db`);
|
|
820
1002
|
}
|
|
821
1003
|
return token;
|
|
822
1004
|
}
|
|
823
|
-
var
|
|
1005
|
+
var import_node_path6, import_node_process11, import_db2, SCOPE_PURPOSE;
|
|
824
1006
|
var init_admin_ai_auth = __esm({
|
|
825
1007
|
"src/admin-ai-auth.ts"() {
|
|
826
1008
|
"use strict";
|
|
827
1009
|
init_cjs_shims();
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
import_node_process7 = __toESM(require("process"), 1);
|
|
1010
|
+
import_node_path6 = require("path");
|
|
1011
|
+
import_node_process11 = __toESM(require("process"), 1);
|
|
831
1012
|
import_db2 = require("@odla-ai/db");
|
|
832
1013
|
init_local();
|
|
833
1014
|
init_handshake_approval();
|
|
834
1015
|
init_token();
|
|
1016
|
+
init_auth_guidance();
|
|
1017
|
+
init_device_session_cache();
|
|
1018
|
+
init_device_session();
|
|
1019
|
+
init_machine_identity();
|
|
1020
|
+
init_odla_home();
|
|
835
1021
|
SCOPE_PURPOSE = {
|
|
836
1022
|
"platform:status:read": "read the platform fleet health and deployment snapshot",
|
|
837
1023
|
"app:config:read": "compare checked-in intent with an exact-id app Registry configuration",
|
|
@@ -1047,7 +1233,7 @@ var init_admin_ai_usage = __esm({
|
|
|
1047
1233
|
|
|
1048
1234
|
// src/admin-ai.ts
|
|
1049
1235
|
async function adminAi(options) {
|
|
1050
|
-
const platform = platformAudience(options.platform ??
|
|
1236
|
+
const platform = platformAudience(options.platform ?? import_node_process12.default.env.ODLA_PLATFORM ?? "https://odla.ai");
|
|
1051
1237
|
const doFetch = options.fetch ?? fetch;
|
|
1052
1238
|
const out = options.stdout ?? console;
|
|
1053
1239
|
const usageQuery = options.action === "usage" ? adminAiUsageQuery(options) : void 0;
|
|
@@ -1225,12 +1411,12 @@ function apiError3(action2, status, body) {
|
|
|
1225
1411
|
function isRecord3(value2) {
|
|
1226
1412
|
return Boolean(value2) && typeof value2 === "object" && !Array.isArray(value2);
|
|
1227
1413
|
}
|
|
1228
|
-
var
|
|
1414
|
+
var import_node_process12;
|
|
1229
1415
|
var init_admin_ai = __esm({
|
|
1230
1416
|
"src/admin-ai.ts"() {
|
|
1231
1417
|
"use strict";
|
|
1232
1418
|
init_cjs_shims();
|
|
1233
|
-
|
|
1419
|
+
import_node_process12 = __toESM(require("process"), 1);
|
|
1234
1420
|
init_token();
|
|
1235
1421
|
init_secret_input();
|
|
1236
1422
|
init_admin_ai_auth();
|
|
@@ -1812,12 +1998,12 @@ var init_monitoring_validation = __esm({
|
|
|
1812
1998
|
|
|
1813
1999
|
// src/config.ts
|
|
1814
2000
|
async function loadProjectConfig(configPath = "odla.config.mjs", options = {}) {
|
|
1815
|
-
const resolved = (0,
|
|
2001
|
+
const resolved = (0, import_node_path7.resolve)(configPath);
|
|
1816
2002
|
if (!(0, import_node_fs9.existsSync)(resolved)) {
|
|
1817
2003
|
throw new Error(`config not found: ${configPath}. Run "odla-ai init" first or pass --config.`);
|
|
1818
2004
|
}
|
|
1819
2005
|
const raw = await loadConfigModule(resolved);
|
|
1820
|
-
const rootDir = (0,
|
|
2006
|
+
const rootDir = (0, import_node_path7.dirname)(resolved);
|
|
1821
2007
|
validateRawConfig(raw, resolved);
|
|
1822
2008
|
const platformUrl = trimSlash(process.env.ODLA_PLATFORM_URL || raw.platformUrl || DEFAULT_PLATFORM);
|
|
1823
2009
|
const dbEndpoint = trimSlash(process.env.ODLA_DB_ENDPOINT || raw.dbEndpoint || platformUrl);
|
|
@@ -1827,11 +2013,14 @@ async function loadProjectConfig(configPath = "odla.config.mjs", options = {}) {
|
|
|
1827
2013
|
validateCalendarConfig(raw, unique3([...envs, ...options.additionalEnvs ?? []]), services, resolved);
|
|
1828
2014
|
validateMonitoringConfig(raw, unique3([...envs, ...options.additionalEnvs ?? []]), services, resolved);
|
|
1829
2015
|
const local = {
|
|
1830
|
-
tokenFile: (0,
|
|
1831
|
-
credentialsFile: (0,
|
|
1832
|
-
devVarsFile: (0,
|
|
2016
|
+
tokenFile: raw.local?.tokenFile ? (0, import_node_path7.resolve)(rootDir, raw.local.tokenFile) : appTokenFile(raw.app.id),
|
|
2017
|
+
credentialsFile: raw.local?.credentialsFile ? (0, import_node_path7.resolve)(rootDir, raw.local.credentialsFile) : appCredentialsFile(raw.app.id),
|
|
2018
|
+
devVarsFile: (0, import_node_path7.resolve)(rootDir, raw.local?.devVarsFile ?? ".dev.vars"),
|
|
1833
2019
|
gitignore: raw.local?.gitignore ?? true
|
|
1834
2020
|
};
|
|
2021
|
+
adoptRepoLocalCache((0, import_node_path7.resolve)(rootDir, ".odla/dev-token.json"), local.tokenFile, stderr);
|
|
2022
|
+
adoptRepoLocalCache((0, import_node_path7.resolve)(rootDir, ".odla/credentials.local.json"), local.credentialsFile, stderr);
|
|
2023
|
+
(0, import_node_fs9.rmSync)((0, import_node_path7.resolve)(rootDir, ".odla/handshake.local.json"), { force: true });
|
|
1835
2024
|
return {
|
|
1836
2025
|
...raw,
|
|
1837
2026
|
configPath: resolved,
|
|
@@ -1846,7 +2035,7 @@ async function loadProjectConfig(configPath = "odla.config.mjs", options = {}) {
|
|
|
1846
2035
|
async function resolveDataExport(cfg, value2, names) {
|
|
1847
2036
|
if (value2 === void 0 || value2 === null || value2 === false) return void 0;
|
|
1848
2037
|
if (typeof value2 !== "string") return value2;
|
|
1849
|
-
const target = (0,
|
|
2038
|
+
const target = (0, import_node_path7.isAbsolute)(value2) ? value2 : (0, import_node_path7.resolve)(cfg.rootDir, value2);
|
|
1850
2039
|
if (target.endsWith(".json")) {
|
|
1851
2040
|
return JSON.parse((0, import_node_fs9.readFileSync)(target, "utf8"));
|
|
1852
2041
|
}
|
|
@@ -1937,37 +2126,42 @@ function trimSlash(value2) {
|
|
|
1937
2126
|
function unique3(values) {
|
|
1938
2127
|
return [...new Set(values.filter(Boolean))];
|
|
1939
2128
|
}
|
|
1940
|
-
var import_node_fs9,
|
|
2129
|
+
var import_node_fs9, import_node_path7, import_node_url, import_apps, DEFAULT_PLATFORM, DEFAULT_ENVS, DEFAULT_SERVICES, configImportSerial, GOOGLE_CALENDAR_EVENTS_SCOPE, stderr;
|
|
1941
2130
|
var init_config = __esm({
|
|
1942
2131
|
"src/config.ts"() {
|
|
1943
2132
|
"use strict";
|
|
1944
2133
|
init_cjs_shims();
|
|
1945
2134
|
import_node_fs9 = require("fs");
|
|
1946
|
-
|
|
2135
|
+
import_node_path7 = require("path");
|
|
1947
2136
|
import_node_url = require("url");
|
|
1948
2137
|
import_apps = require("@odla-ai/apps");
|
|
1949
2138
|
init_ai_config_validation();
|
|
1950
2139
|
init_calendar_config();
|
|
1951
2140
|
init_integration_validation();
|
|
1952
2141
|
init_monitoring_validation();
|
|
2142
|
+
init_odla_home();
|
|
1953
2143
|
init_calendar_config();
|
|
1954
2144
|
DEFAULT_PLATFORM = "https://odla.ai";
|
|
1955
2145
|
DEFAULT_ENVS = ["dev"];
|
|
1956
2146
|
DEFAULT_SERVICES = ["db", "ai"];
|
|
1957
2147
|
configImportSerial = 0;
|
|
1958
2148
|
GOOGLE_CALENDAR_EVENTS_SCOPE = "https://www.googleapis.com/auth/calendar.events";
|
|
2149
|
+
stderr = { error: (message2) => {
|
|
2150
|
+
process.stderr.write(`${message2}
|
|
2151
|
+
`);
|
|
2152
|
+
} };
|
|
1959
2153
|
}
|
|
1960
2154
|
});
|
|
1961
2155
|
|
|
1962
2156
|
// src/operator-profiles.ts
|
|
1963
2157
|
function operatorProfileFile() {
|
|
1964
|
-
return (0,
|
|
1965
|
-
clean(
|
|
2158
|
+
return (0, import_node_path8.resolve)(
|
|
2159
|
+
clean(import_node_process13.default.env.ODLA_CONTEXT_FILE) ?? (0, import_node_path8.join)((0, import_node_os3.homedir)(), ".odla", "contexts.json")
|
|
1966
2160
|
);
|
|
1967
2161
|
}
|
|
1968
2162
|
function resolveOperatorProfile(parsed) {
|
|
1969
2163
|
const fromFlag = clean(stringOpt(parsed.options.context));
|
|
1970
|
-
const fromEnvironment = clean(
|
|
2164
|
+
const fromEnvironment = clean(import_node_process13.default.env.ODLA_CONTEXT);
|
|
1971
2165
|
const name = fromFlag ?? fromEnvironment ?? null;
|
|
1972
2166
|
const file = operatorProfileFile();
|
|
1973
2167
|
if (!name) {
|
|
@@ -2007,10 +2201,10 @@ function removeOperatorProfile(name, file = operatorProfileFile()) {
|
|
|
2007
2201
|
return true;
|
|
2008
2202
|
}
|
|
2009
2203
|
function operatorCredentialFiles(selection) {
|
|
2010
|
-
const base = selection.name ? (0,
|
|
2204
|
+
const base = selection.name ? (0, import_node_path8.join)((0, import_node_path8.dirname)(selection.file), "profiles", selection.name) : (0, import_node_path8.join)((0, import_node_os3.homedir)(), ".odla");
|
|
2011
2205
|
return {
|
|
2012
|
-
developer: (0,
|
|
2013
|
-
scoped: (0,
|
|
2206
|
+
developer: (0, import_node_path8.join)(base, "dev-token.json"),
|
|
2207
|
+
scoped: (0, import_node_path8.join)(base, "admin-token.local.json")
|
|
2014
2208
|
};
|
|
2015
2209
|
}
|
|
2016
2210
|
function assertOperatorName(value2, label) {
|
|
@@ -2082,15 +2276,15 @@ function clean(value2) {
|
|
|
2082
2276
|
const normalized = value2?.trim();
|
|
2083
2277
|
return normalized || void 0;
|
|
2084
2278
|
}
|
|
2085
|
-
var import_node_fs10,
|
|
2279
|
+
var import_node_fs10, import_node_os3, import_node_path8, import_node_process13;
|
|
2086
2280
|
var init_operator_profiles = __esm({
|
|
2087
2281
|
"src/operator-profiles.ts"() {
|
|
2088
2282
|
"use strict";
|
|
2089
2283
|
init_cjs_shims();
|
|
2090
2284
|
import_node_fs10 = require("fs");
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2285
|
+
import_node_os3 = require("os");
|
|
2286
|
+
import_node_path8 = require("path");
|
|
2287
|
+
import_node_process13 = __toESM(require("process"), 1);
|
|
2094
2288
|
init_argv();
|
|
2095
2289
|
init_local();
|
|
2096
2290
|
init_token();
|
|
@@ -2101,7 +2295,7 @@ var init_operator_profiles = __esm({
|
|
|
2101
2295
|
async function resolveOperatorContext(parsed, options = {}) {
|
|
2102
2296
|
const profile = resolveOperatorProfile(parsed);
|
|
2103
2297
|
const configArgument = stringOpt(parsed.options.config) ?? "odla.config.mjs";
|
|
2104
|
-
const configPath = (0,
|
|
2298
|
+
const configPath = (0, import_node_path9.resolve)(configArgument);
|
|
2105
2299
|
const explicitConfig = parsed.options.config !== void 0;
|
|
2106
2300
|
const hasConfig = (0, import_node_fs11.existsSync)(configPath);
|
|
2107
2301
|
if (!hasConfig && (!options.allowMissingConfig || explicitConfig)) {
|
|
@@ -2109,13 +2303,13 @@ async function resolveOperatorContext(parsed, options = {}) {
|
|
|
2109
2303
|
}
|
|
2110
2304
|
const loaded = hasConfig ? await loadProjectConfig(configArgument) : void 0;
|
|
2111
2305
|
const platformFlag = clean2(stringOpt(parsed.options.platform));
|
|
2112
|
-
const platformEnvironment = clean2(
|
|
2306
|
+
const platformEnvironment = clean2(import_node_process14.default.env.ODLA_PLATFORM_URL);
|
|
2113
2307
|
const platformValue = platformAudience(
|
|
2114
2308
|
platformFlag ?? platformEnvironment ?? profile.value?.platform ?? loaded?.platformUrl ?? DEFAULT_PLATFORM2
|
|
2115
2309
|
);
|
|
2116
2310
|
const platformSource = platformFlag ? "flag" : platformEnvironment ? "environment" : profile.value ? "profile" : loaded ? "config" : "default";
|
|
2117
2311
|
const appFlag = clean2(stringOpt(parsed.options.app));
|
|
2118
|
-
const appEnvironment = clean2(
|
|
2312
|
+
const appEnvironment = clean2(import_node_process14.default.env.ODLA_APP_ID);
|
|
2119
2313
|
const appValue = appFlag ?? appEnvironment ?? profile.value?.app ?? loaded?.app.id ?? null;
|
|
2120
2314
|
const appSource = appFlag ? "flag" : appEnvironment ? "environment" : profile.value?.app ? "profile" : loaded ? "config" : "unresolved";
|
|
2121
2315
|
if (appValue) {
|
|
@@ -2129,16 +2323,16 @@ async function resolveOperatorContext(parsed, options = {}) {
|
|
|
2129
2323
|
);
|
|
2130
2324
|
}
|
|
2131
2325
|
const envFlag = clean2(stringOpt(parsed.options.env));
|
|
2132
|
-
const envEnvironment = clean2(
|
|
2326
|
+
const envEnvironment = clean2(import_node_process14.default.env.ODLA_ENV);
|
|
2133
2327
|
const environmentValue = envFlag ?? envEnvironment ?? profile.value?.environment ?? options.defaultEnvironment ?? null;
|
|
2134
2328
|
const environmentSource = envFlag ? "flag" : envEnvironment ? "environment" : profile.value?.environment ? "profile" : options.defaultEnvironment ? "default" : "unresolved";
|
|
2135
2329
|
if (environmentValue) {
|
|
2136
2330
|
assertOperatorName(environmentValue, "environment");
|
|
2137
2331
|
}
|
|
2138
|
-
const rootDir = loaded?.rootDir ??
|
|
2332
|
+
const rootDir = loaded?.rootDir ?? import_node_process14.default.cwd();
|
|
2139
2333
|
const profileCredentials = operatorCredentialFiles(profile);
|
|
2140
|
-
const tokenFile = clean2(
|
|
2141
|
-
const
|
|
2334
|
+
const tokenFile = clean2(import_node_process14.default.env.ODLA_DEV_TOKEN_FILE) ? (0, import_node_path9.resolve)(import_node_process14.default.env.ODLA_DEV_TOKEN_FILE) : profile.name ? profileCredentials.developer : loaded?.local.tokenFile ?? profileCredentials.developer;
|
|
2335
|
+
const scopedTokenFile2 = clean2(import_node_process14.default.env.ODLA_ADMIN_TOKEN_FILE) ? (0, import_node_path9.resolve)(import_node_process14.default.env.ODLA_ADMIN_TOKEN_FILE) : profile.name ? profileCredentials.scoped : loaded ? (0, import_node_path9.join)(loaded.rootDir, ".odla", "admin-token.local.json") : profileCredentials.scoped;
|
|
2142
2336
|
const cfg = loaded ? {
|
|
2143
2337
|
...loaded,
|
|
2144
2338
|
platformUrl: platformValue,
|
|
@@ -2160,8 +2354,8 @@ async function resolveOperatorContext(parsed, options = {}) {
|
|
|
2160
2354
|
services: [],
|
|
2161
2355
|
local: {
|
|
2162
2356
|
tokenFile,
|
|
2163
|
-
credentialsFile: (0,
|
|
2164
|
-
devVarsFile: (0,
|
|
2357
|
+
credentialsFile: (0, import_node_path9.join)(rootDir, ".odla", "credentials.local.json"),
|
|
2358
|
+
devVarsFile: (0, import_node_path9.join)(rootDir, ".dev.vars"),
|
|
2165
2359
|
gitignore: true
|
|
2166
2360
|
}
|
|
2167
2361
|
};
|
|
@@ -2185,7 +2379,7 @@ async function resolveOperatorContext(parsed, options = {}) {
|
|
|
2185
2379
|
},
|
|
2186
2380
|
credentials: {
|
|
2187
2381
|
developerTokenFile: tokenFile,
|
|
2188
|
-
scopedTokenFile
|
|
2382
|
+
scopedTokenFile: scopedTokenFile2
|
|
2189
2383
|
}
|
|
2190
2384
|
};
|
|
2191
2385
|
}
|
|
@@ -2193,14 +2387,14 @@ function clean2(value2) {
|
|
|
2193
2387
|
const normalized = value2?.trim();
|
|
2194
2388
|
return normalized || void 0;
|
|
2195
2389
|
}
|
|
2196
|
-
var import_node_fs11,
|
|
2390
|
+
var import_node_fs11, import_node_path9, import_node_process14, DEFAULT_PLATFORM2;
|
|
2197
2391
|
var init_operator_context = __esm({
|
|
2198
2392
|
"src/operator-context.ts"() {
|
|
2199
2393
|
"use strict";
|
|
2200
2394
|
init_cjs_shims();
|
|
2201
2395
|
import_node_fs11 = require("fs");
|
|
2202
|
-
|
|
2203
|
-
|
|
2396
|
+
import_node_path9 = require("path");
|
|
2397
|
+
import_node_process14 = __toESM(require("process"), 1);
|
|
2204
2398
|
init_argv();
|
|
2205
2399
|
init_config();
|
|
2206
2400
|
init_operator_profiles();
|
|
@@ -2442,6 +2636,7 @@ async function whoamiCommand(parsed, deps = {}) {
|
|
|
2442
2636
|
} else {
|
|
2443
2637
|
out.log("projects: (none \u2014 every pm and discuss call will be refused)");
|
|
2444
2638
|
}
|
|
2639
|
+
printMachineBlock(cfg.platformUrl, out);
|
|
2445
2640
|
if (!identity.admin) {
|
|
2446
2641
|
if (identity.scopes.includes("platform:runbook:write")) {
|
|
2447
2642
|
out.log("\nThis exact scope can read and edit all platform runbook content.");
|
|
@@ -2452,6 +2647,21 @@ async function whoamiCommand(parsed, deps = {}) {
|
|
|
2452
2647
|
}
|
|
2453
2648
|
}
|
|
2454
2649
|
}
|
|
2650
|
+
function printMachineBlock(platformUrl, out) {
|
|
2651
|
+
const state2 = machineAuthState(platformAudience(platformUrl));
|
|
2652
|
+
if (!state2.enrolled) {
|
|
2653
|
+
out.log("\nmachine: not enrolled \u2014 every privileged command needs its own browser approval.");
|
|
2654
|
+
out.log(` End that with:
|
|
2655
|
+
${ENROL_EVERYTHING}`);
|
|
2656
|
+
return;
|
|
2657
|
+
}
|
|
2658
|
+
const reach = state2.appIds?.includes("*") ? "every app you own" : state2.appIds?.join(", ");
|
|
2659
|
+
out.log(`
|
|
2660
|
+
machine: enrolled${state2.deviceName ? ` as "${state2.deviceName}"` : ""}${reach ? ` for ${reach}` : ""}`);
|
|
2661
|
+
if (state2.scopes?.length) out.log(` carrying ${state2.scopes.join(", ")}`);
|
|
2662
|
+
const lapse = lapseNotice(state2);
|
|
2663
|
+
if (lapse) out.log(` ${lapse}`);
|
|
2664
|
+
}
|
|
2455
2665
|
var text2;
|
|
2456
2666
|
var init_whoami_command = __esm({
|
|
2457
2667
|
"src/whoami-command.ts"() {
|
|
@@ -2460,6 +2670,8 @@ var init_whoami_command = __esm({
|
|
|
2460
2670
|
init_argv();
|
|
2461
2671
|
init_operator_context();
|
|
2462
2672
|
init_token();
|
|
2673
|
+
init_auth_guidance();
|
|
2674
|
+
init_token();
|
|
2463
2675
|
text2 = (value2) => typeof value2 === "string" && value2.trim() ? value2.trim() : null;
|
|
2464
2676
|
}
|
|
2465
2677
|
});
|
|
@@ -2487,7 +2699,7 @@ async function authCommand(parsed, deps = {}) {
|
|
|
2487
2699
|
const { cfg } = context;
|
|
2488
2700
|
const out = deps.stdout ?? console;
|
|
2489
2701
|
const doFetch = deps.fetch ?? fetch;
|
|
2490
|
-
const email = stringOpt(parsed.options.email) ??
|
|
2702
|
+
const email = stringOpt(parsed.options.email) ?? import_node_process15.default.env.ODLA_USER_EMAIL?.trim();
|
|
2491
2703
|
if (!email) {
|
|
2492
2704
|
throw new Error(
|
|
2493
2705
|
"auth login requires --email <odla-account> or ODLA_USER_EMAIL; confirm the signed-in odla email instead of using git or GitHub identity"
|
|
@@ -2521,12 +2733,12 @@ async function authCommand(parsed, deps = {}) {
|
|
|
2521
2733
|
out.log(`Authorized ${identity.displayName}${handle} for ${cfg.app.id}.`);
|
|
2522
2734
|
out.log(`odla account: ${identity.email ?? "not returned"}`);
|
|
2523
2735
|
}
|
|
2524
|
-
var
|
|
2736
|
+
var import_node_process15;
|
|
2525
2737
|
var init_auth_command = __esm({
|
|
2526
2738
|
"src/auth-command.ts"() {
|
|
2527
2739
|
"use strict";
|
|
2528
2740
|
init_cjs_shims();
|
|
2529
|
-
|
|
2741
|
+
import_node_process15 = __toESM(require("process"), 1);
|
|
2530
2742
|
init_argv();
|
|
2531
2743
|
init_operator_context();
|
|
2532
2744
|
init_token();
|
|
@@ -3043,15 +3255,15 @@ var init_brand_design_unpack = __esm({
|
|
|
3043
3255
|
|
|
3044
3256
|
// src/brand-command.ts
|
|
3045
3257
|
async function readBundle(source, deps) {
|
|
3046
|
-
if (source !== "-") return (0, import_promises.readFile)((0,
|
|
3258
|
+
if (source !== "-") return (0, import_promises.readFile)((0, import_node_path10.resolve)(source), "utf8");
|
|
3047
3259
|
const readStdin = deps.readStdin;
|
|
3048
3260
|
if (!readStdin) throw new Error("reading a bundle from stdin is not supported here");
|
|
3049
3261
|
return readStdin();
|
|
3050
3262
|
}
|
|
3051
3263
|
async function writeAll(result, outDir) {
|
|
3052
3264
|
for (const file of result.files) {
|
|
3053
|
-
const target = (0,
|
|
3054
|
-
await (0, import_promises.mkdir)((0,
|
|
3265
|
+
const target = (0, import_node_path10.resolve)(outDir, file.path);
|
|
3266
|
+
await (0, import_promises.mkdir)((0, import_node_path10.dirname)(target), { recursive: true });
|
|
3055
3267
|
await (0, import_promises.writeFile)(target, file.bytes);
|
|
3056
3268
|
}
|
|
3057
3269
|
}
|
|
@@ -3059,7 +3271,7 @@ async function designUnpack(parsed, deps) {
|
|
|
3059
3271
|
assertArgs(parsed, ["out", "json"], 4);
|
|
3060
3272
|
const source = parsed.positionals[3];
|
|
3061
3273
|
if (!source) throw new Error(USAGE);
|
|
3062
|
-
const outDir = (0,
|
|
3274
|
+
const outDir = (0, import_node_path10.resolve)(stringOpt(parsed.options.out) ?? "design");
|
|
3063
3275
|
const result = unpackDesign(await readBundle(source, deps));
|
|
3064
3276
|
await writeAll(result, outDir);
|
|
3065
3277
|
const out = deps.stdout ?? console;
|
|
@@ -3085,13 +3297,13 @@ async function brandCommand(parsed, deps) {
|
|
|
3085
3297
|
}
|
|
3086
3298
|
throw new Error(USAGE);
|
|
3087
3299
|
}
|
|
3088
|
-
var import_promises,
|
|
3300
|
+
var import_promises, import_node_path10, USAGE;
|
|
3089
3301
|
var init_brand_command = __esm({
|
|
3090
3302
|
"src/brand-command.ts"() {
|
|
3091
3303
|
"use strict";
|
|
3092
3304
|
init_cjs_shims();
|
|
3093
3305
|
import_promises = require("fs/promises");
|
|
3094
|
-
|
|
3306
|
+
import_node_path10 = require("path");
|
|
3095
3307
|
init_argv();
|
|
3096
3308
|
init_brand_design_unpack();
|
|
3097
3309
|
USAGE = "usage: odla-ai brand design unpack <bundle.html|-> [--out <dir>] [--json]";
|
|
@@ -4139,7 +4351,7 @@ async function operationClient(cfg, options, purpose) {
|
|
|
4139
4351
|
platform: cfg.platformUrl,
|
|
4140
4352
|
scope: "app:config:write",
|
|
4141
4353
|
token: options.token,
|
|
4142
|
-
tokenFile: (0,
|
|
4354
|
+
tokenFile: (0, import_node_path11.join)(cfg.rootDir, ".odla", "admin-token.local.json"),
|
|
4143
4355
|
rootDir: cfg.rootDir,
|
|
4144
4356
|
email: options.email,
|
|
4145
4357
|
open: options.open,
|
|
@@ -4191,13 +4403,13 @@ function normalizeRequestError(error) {
|
|
|
4191
4403
|
function record4(value2) {
|
|
4192
4404
|
return !!value2 && typeof value2 === "object" && !Array.isArray(value2);
|
|
4193
4405
|
}
|
|
4194
|
-
var import_apps6,
|
|
4406
|
+
var import_apps6, import_node_path11, IDEMPOTENCY_KEY, DEFAULT_WAIT_SECONDS, DEFAULT_INTERVAL_SECONDS;
|
|
4195
4407
|
var init_config_operation_command = __esm({
|
|
4196
4408
|
"src/config-operation-command.ts"() {
|
|
4197
4409
|
"use strict";
|
|
4198
4410
|
init_cjs_shims();
|
|
4199
4411
|
import_apps6 = require("@odla-ai/apps");
|
|
4200
|
-
|
|
4412
|
+
import_node_path11 = require("path");
|
|
4201
4413
|
init_admin_ai_auth();
|
|
4202
4414
|
init_version();
|
|
4203
4415
|
init_config();
|
|
@@ -4520,7 +4732,7 @@ async function inspectConfig(options) {
|
|
|
4520
4732
|
platform: cfg.platformUrl,
|
|
4521
4733
|
scope: "app:config:read",
|
|
4522
4734
|
token: options.token,
|
|
4523
|
-
tokenFile: (0,
|
|
4735
|
+
tokenFile: (0, import_node_path12.join)(cfg.rootDir, ".odla", "admin-token.local.json"),
|
|
4524
4736
|
rootDir: cfg.rootDir,
|
|
4525
4737
|
email: options.email,
|
|
4526
4738
|
open: options.open,
|
|
@@ -4649,13 +4861,13 @@ function studioSettingsUrl(reconciliation) {
|
|
|
4649
4861
|
function quoteArg2(value2) {
|
|
4650
4862
|
return `'${value2.replace(/'/g, `'\\''`)}'`;
|
|
4651
4863
|
}
|
|
4652
|
-
var import_apps8,
|
|
4864
|
+
var import_apps8, import_node_path12;
|
|
4653
4865
|
var init_config_reconcile_command = __esm({
|
|
4654
4866
|
"src/config-reconcile-command.ts"() {
|
|
4655
4867
|
"use strict";
|
|
4656
4868
|
init_cjs_shims();
|
|
4657
4869
|
import_apps8 = require("@odla-ai/apps");
|
|
4658
|
-
|
|
4870
|
+
import_node_path12 = require("path");
|
|
4659
4871
|
init_admin_ai_auth();
|
|
4660
4872
|
init_config();
|
|
4661
4873
|
init_config_reconcile_digest();
|
|
@@ -4668,7 +4880,7 @@ var init_config_reconcile_command = __esm({
|
|
|
4668
4880
|
// src/wrangler.ts
|
|
4669
4881
|
function findWranglerConfig(rootDir) {
|
|
4670
4882
|
for (const name of WRANGLER_CONFIG_FILES) {
|
|
4671
|
-
const path = (0,
|
|
4883
|
+
const path = (0, import_node_path13.join)(rootDir, name);
|
|
4672
4884
|
if ((0, import_node_fs14.existsSync)(path)) return path;
|
|
4673
4885
|
}
|
|
4674
4886
|
return null;
|
|
@@ -4780,22 +4992,22 @@ function wranglerBulkSecrets(run, opts) {
|
|
|
4780
4992
|
];
|
|
4781
4993
|
return run("npx", args, { input: JSON.stringify(opts.secrets), cwd: opts.cwd });
|
|
4782
4994
|
}
|
|
4783
|
-
var import_node_child_process2, import_node_fs14,
|
|
4995
|
+
var import_node_child_process2, import_node_fs14, import_node_path13, defaultRunner, WRANGLER_CONFIG_FILES;
|
|
4784
4996
|
var init_wrangler = __esm({
|
|
4785
4997
|
"src/wrangler.ts"() {
|
|
4786
4998
|
"use strict";
|
|
4787
4999
|
init_cjs_shims();
|
|
4788
5000
|
import_node_child_process2 = require("child_process");
|
|
4789
5001
|
import_node_fs14 = require("fs");
|
|
4790
|
-
|
|
5002
|
+
import_node_path13 = require("path");
|
|
4791
5003
|
defaultRunner = (cmd, args, opts) => new Promise((resolvePromise, reject) => {
|
|
4792
5004
|
const child = (0, import_node_child_process2.spawn)(cmd, args, { cwd: opts?.cwd, stdio: ["pipe", "pipe", "pipe"] });
|
|
4793
5005
|
let stdout = "";
|
|
4794
|
-
let
|
|
5006
|
+
let stderr2 = "";
|
|
4795
5007
|
child.stdout.on("data", (chunk) => stdout += chunk.toString());
|
|
4796
|
-
child.stderr.on("data", (chunk) =>
|
|
5008
|
+
child.stderr.on("data", (chunk) => stderr2 += chunk.toString());
|
|
4797
5009
|
child.on("error", reject);
|
|
4798
|
-
child.on("close", (code) => resolvePromise({ code: code ?? 1, stdout, stderr }));
|
|
5010
|
+
child.on("close", (code) => resolvePromise({ code: code ?? 1, stdout, stderr: stderr2 }));
|
|
4799
5011
|
child.stdin.end(opts?.input ?? "");
|
|
4800
5012
|
});
|
|
4801
5013
|
WRANGLER_CONFIG_FILES = ["wrangler.jsonc", "wrangler.json", "wrangler.toml"];
|
|
@@ -4839,21 +5051,21 @@ function wranglerWarnings(rootDir) {
|
|
|
4839
5051
|
const blocks = [{ label: "", block: config }];
|
|
4840
5052
|
const envs = config.env;
|
|
4841
5053
|
if (envs && typeof envs === "object") {
|
|
4842
|
-
for (const [name,
|
|
4843
|
-
if (
|
|
5054
|
+
for (const [name, block2] of Object.entries(envs)) {
|
|
5055
|
+
if (block2 && typeof block2 === "object") blocks.push({ label: `env.${name}.`, block: block2 });
|
|
4844
5056
|
}
|
|
4845
5057
|
}
|
|
4846
|
-
for (const { label, block } of blocks) {
|
|
4847
|
-
const assets =
|
|
5058
|
+
for (const { label, block: block2 } of blocks) {
|
|
5059
|
+
const assets = block2.assets;
|
|
4848
5060
|
if (assets?.directory) {
|
|
4849
|
-
const dir = (0,
|
|
4850
|
-
if (dir === (0,
|
|
5061
|
+
const dir = (0, import_node_path14.resolve)(rootDir, assets.directory);
|
|
5062
|
+
if (dir === (0, import_node_path14.resolve)(rootDir)) {
|
|
4851
5063
|
warnings.push(`${label}assets.directory is the project root \u2014 point it at a dedicated build dir (wrangler dev fails with "spawn EBADF")`);
|
|
4852
|
-
} else if ((0, import_node_fs15.existsSync)((0,
|
|
5064
|
+
} else if ((0, import_node_fs15.existsSync)((0, import_node_path14.join)(dir, "node_modules"))) {
|
|
4853
5065
|
warnings.push(`${label}assets.directory contains node_modules \u2014 wrangler dev's watcher will exhaust file descriptors`);
|
|
4854
5066
|
}
|
|
4855
5067
|
}
|
|
4856
|
-
const vars =
|
|
5068
|
+
const vars = block2.vars;
|
|
4857
5069
|
if (vars && typeof vars === "object") {
|
|
4858
5070
|
for (const [name, value2] of Object.entries(vars)) {
|
|
4859
5071
|
if (name === "ODLA_API_KEY" || name === "ODLA_O11Y_TOKEN" || typeof value2 === "string" && looksSecret(value2)) {
|
|
@@ -4884,7 +5096,7 @@ function o11yProjectWarnings(rootDir) {
|
|
|
4884
5096
|
warnings.push("cannot verify o11y Worker instrumentation \u2014 add a parseable wrangler.jsonc/json config");
|
|
4885
5097
|
return warnings;
|
|
4886
5098
|
}
|
|
4887
|
-
const main = typeof config.main === "string" ? (0,
|
|
5099
|
+
const main = typeof config.main === "string" ? (0, import_node_path14.resolve)(rootDir, config.main) : null;
|
|
4888
5100
|
if (!main || !(0, import_node_fs15.existsSync)(main)) {
|
|
4889
5101
|
warnings.push("cannot verify o11y Worker instrumentation \u2014 wrangler main is missing or unreadable");
|
|
4890
5102
|
} else {
|
|
@@ -4914,19 +5126,19 @@ function calendarProjectWarnings(rootDir) {
|
|
|
4914
5126
|
}
|
|
4915
5127
|
function readPackageJson(rootDir) {
|
|
4916
5128
|
try {
|
|
4917
|
-
return JSON.parse((0, import_node_fs15.readFileSync)((0,
|
|
5129
|
+
return JSON.parse((0, import_node_fs15.readFileSync)((0, import_node_path14.join)(rootDir, "package.json"), "utf8"));
|
|
4918
5130
|
} catch {
|
|
4919
5131
|
return null;
|
|
4920
5132
|
}
|
|
4921
5133
|
}
|
|
4922
|
-
var import_node_child_process3, import_node_fs15,
|
|
5134
|
+
var import_node_child_process3, import_node_fs15, import_node_path14, defaultExec;
|
|
4923
5135
|
var init_doctor_checks = __esm({
|
|
4924
5136
|
"src/doctor-checks.ts"() {
|
|
4925
5137
|
"use strict";
|
|
4926
5138
|
init_cjs_shims();
|
|
4927
5139
|
import_node_child_process3 = require("child_process");
|
|
4928
5140
|
import_node_fs15 = require("fs");
|
|
4929
|
-
|
|
5141
|
+
import_node_path14 = require("path");
|
|
4930
5142
|
init_redact();
|
|
4931
5143
|
init_local();
|
|
4932
5144
|
init_wrangler();
|
|
@@ -5263,8 +5475,8 @@ var init_harness_options = __esm({
|
|
|
5263
5475
|
// src/init.ts
|
|
5264
5476
|
function initProject(options) {
|
|
5265
5477
|
const out = options.stdout ?? console;
|
|
5266
|
-
const rootDir = (0,
|
|
5267
|
-
const configPath = (0,
|
|
5478
|
+
const rootDir = (0, import_node_path15.resolve)(options.rootDir ?? process.cwd());
|
|
5479
|
+
const configPath = (0, import_node_path15.resolve)(rootDir, options.configPath ?? "odla.config.mjs");
|
|
5268
5480
|
if ((0, import_node_fs16.existsSync)(configPath) && !options.force) {
|
|
5269
5481
|
throw new Error(`${configPath} already exists. Pass --force to overwrite.`);
|
|
5270
5482
|
}
|
|
@@ -5281,12 +5493,12 @@ function initProject(options) {
|
|
|
5281
5493
|
}
|
|
5282
5494
|
}
|
|
5283
5495
|
const aiProvider = options.aiProvider;
|
|
5284
|
-
(0, import_node_fs16.mkdirSync)((0,
|
|
5285
|
-
(0, import_node_fs16.mkdirSync)((0,
|
|
5286
|
-
(0, import_node_fs16.mkdirSync)((0,
|
|
5496
|
+
(0, import_node_fs16.mkdirSync)((0, import_node_path15.dirname)(configPath), { recursive: true });
|
|
5497
|
+
(0, import_node_fs16.mkdirSync)((0, import_node_path15.resolve)(rootDir, "src/odla"), { recursive: true });
|
|
5498
|
+
(0, import_node_fs16.mkdirSync)((0, import_node_path15.resolve)(rootDir, ".odla"), { recursive: true });
|
|
5287
5499
|
(0, import_node_fs16.writeFileSync)(configPath, configTemplate({ appId: options.appId, name: options.name, envs, services, aiProvider }));
|
|
5288
|
-
writeIfMissing((0,
|
|
5289
|
-
writeIfMissing((0,
|
|
5500
|
+
writeIfMissing((0, import_node_path15.resolve)(rootDir, "src/odla/schema.mjs"), schemaTemplate());
|
|
5501
|
+
writeIfMissing((0, import_node_path15.resolve)(rootDir, "src/odla/rules.mjs"), rulesTemplate());
|
|
5290
5502
|
ensureGitignore(rootDir);
|
|
5291
5503
|
out.log(`created ${relativeDisplay(configPath, rootDir)}`);
|
|
5292
5504
|
out.log("created src/odla/schema.mjs and src/odla/rules.mjs");
|
|
@@ -5353,8 +5565,10 @@ ${calendar}
|
|
|
5353
5565
|
// prod: "https://example.com",
|
|
5354
5566
|
},
|
|
5355
5567
|
local: {
|
|
5356
|
-
|
|
5357
|
-
|
|
5568
|
+
// Credentials live in ~/.odla, per machine, so every worktree of this app
|
|
5569
|
+
// shares one approval instead of asking for its own. Pinning tokenFile or
|
|
5570
|
+
// credentialsFile here still works and still overrides that \u2014 it just puts
|
|
5571
|
+
// this checkout back on its own island.
|
|
5358
5572
|
devVarsFile: ".dev.vars",
|
|
5359
5573
|
},
|
|
5360
5574
|
};
|
|
@@ -5397,13 +5611,13 @@ function defaultKeyEnv(provider) {
|
|
|
5397
5611
|
function relativeDisplay(path, rootDir) {
|
|
5398
5612
|
return path.startsWith(rootDir) ? path.slice(rootDir.length + 1) : path;
|
|
5399
5613
|
}
|
|
5400
|
-
var import_node_fs16,
|
|
5614
|
+
var import_node_fs16, import_node_path15, import_apps9;
|
|
5401
5615
|
var init_init = __esm({
|
|
5402
5616
|
"src/init.ts"() {
|
|
5403
5617
|
"use strict";
|
|
5404
5618
|
init_cjs_shims();
|
|
5405
5619
|
import_node_fs16 = require("fs");
|
|
5406
|
-
|
|
5620
|
+
import_node_path15 = require("path");
|
|
5407
5621
|
import_apps9 = require("@odla-ai/apps");
|
|
5408
5622
|
init_local();
|
|
5409
5623
|
}
|
|
@@ -5736,8 +5950,8 @@ function installSkill(options = {}) {
|
|
|
5736
5950
|
const files = listFiles(sourceDir);
|
|
5737
5951
|
if (files.length === 0) throw new Error(`no bundled skills found at ${sourceDir}`);
|
|
5738
5952
|
const harnesses = normalizeHarnesses(options.harnesses, options.global === true);
|
|
5739
|
-
const root = (0,
|
|
5740
|
-
const home = (0,
|
|
5953
|
+
const root = (0, import_node_path16.resolve)(options.dir ?? process.cwd());
|
|
5954
|
+
const home = (0, import_node_path16.resolve)(options.homeDir ?? (0, import_node_os4.homedir)());
|
|
5741
5955
|
const plans = /* @__PURE__ */ new Map();
|
|
5742
5956
|
const targets = /* @__PURE__ */ new Map();
|
|
5743
5957
|
const rememberTarget = (harness, target) => {
|
|
@@ -5751,48 +5965,48 @@ function installSkill(options = {}) {
|
|
|
5751
5965
|
plans.set(target, { target, content: content2, boundary, managedMerge });
|
|
5752
5966
|
};
|
|
5753
5967
|
const planSkillTree = (targetDir2, boundary = root) => {
|
|
5754
|
-
for (const rel of files) plan((0,
|
|
5968
|
+
for (const rel of files) plan((0, import_node_path16.join)(targetDir2, rel), (0, import_node_fs17.readFileSync)((0, import_node_path16.join)(sourceDir, rel), "utf8"), false, boundary);
|
|
5755
5969
|
};
|
|
5756
5970
|
let targetDir;
|
|
5757
5971
|
if (options.global) {
|
|
5758
|
-
const claudeRoot = (0,
|
|
5759
|
-
const codexRoot = (0,
|
|
5972
|
+
const claudeRoot = (0, import_node_path16.join)(home, ".claude", "skills");
|
|
5973
|
+
const codexRoot = (0, import_node_path16.resolve)(options.codexHomeDir ?? process.env.CODEX_HOME ?? (0, import_node_path16.join)(home, ".codex"), "skills");
|
|
5760
5974
|
targetDir = harnesses[0] === "codex" ? codexRoot : claudeRoot;
|
|
5761
5975
|
for (const harness of harnesses) {
|
|
5762
5976
|
const skillRoot = harness === "claude" ? claudeRoot : codexRoot;
|
|
5763
|
-
planSkillTree(skillRoot, harness === "claude" ? home : (0,
|
|
5977
|
+
planSkillTree(skillRoot, harness === "claude" ? home : (0, import_node_path16.dirname)((0, import_node_path16.dirname)(codexRoot)));
|
|
5764
5978
|
rememberTarget(harness, skillRoot);
|
|
5765
5979
|
}
|
|
5766
5980
|
} else {
|
|
5767
|
-
const sharedRoot = (0,
|
|
5981
|
+
const sharedRoot = (0, import_node_path16.join)(root, ".agents", "skills");
|
|
5768
5982
|
planSkillTree(sharedRoot);
|
|
5769
|
-
const claudeRoot = (0,
|
|
5983
|
+
const claudeRoot = (0, import_node_path16.join)(root, ".claude", "skills");
|
|
5770
5984
|
targetDir = harnesses.includes("claude") ? claudeRoot : sharedRoot;
|
|
5771
5985
|
for (const harness of harnesses) rememberTarget(harness, sharedRoot);
|
|
5772
5986
|
if (harnesses.includes("claude")) {
|
|
5773
5987
|
for (const skill of skillNames(files)) {
|
|
5774
|
-
const canonical2 = (0, import_node_fs17.readFileSync)((0,
|
|
5775
|
-
plan((0,
|
|
5988
|
+
const canonical2 = (0, import_node_fs17.readFileSync)((0, import_node_path16.join)(sourceDir, skill, "SKILL.md"), "utf8");
|
|
5989
|
+
plan((0, import_node_path16.join)(claudeRoot, skill, "SKILL.md"), claudeAdapter(skill, canonical2));
|
|
5776
5990
|
}
|
|
5777
5991
|
rememberTarget("claude", claudeRoot);
|
|
5778
5992
|
}
|
|
5779
5993
|
if (harnesses.includes("cursor")) {
|
|
5780
|
-
const cursorRule = (0,
|
|
5994
|
+
const cursorRule = (0, import_node_path16.join)(root, ".cursor", "rules", "odla.mdc");
|
|
5781
5995
|
plan(cursorRule, CURSOR_RULE);
|
|
5782
5996
|
rememberTarget("cursor", cursorRule);
|
|
5783
5997
|
}
|
|
5784
5998
|
if (harnesses.includes("agents")) {
|
|
5785
|
-
const agentsFile = (0,
|
|
5999
|
+
const agentsFile = (0, import_node_path16.join)(root, "AGENTS.md");
|
|
5786
6000
|
plan(agentsFile, managedFileContent(agentsFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
|
|
5787
6001
|
rememberTarget("agents", agentsFile);
|
|
5788
6002
|
}
|
|
5789
6003
|
if (harnesses.includes("copilot")) {
|
|
5790
|
-
const copilotFile = (0,
|
|
6004
|
+
const copilotFile = (0, import_node_path16.join)(root, ".github", "copilot-instructions.md");
|
|
5791
6005
|
plan(copilotFile, managedFileContent(copilotFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
|
|
5792
6006
|
rememberTarget("copilot", copilotFile);
|
|
5793
6007
|
}
|
|
5794
6008
|
if (harnesses.includes("gemini")) {
|
|
5795
|
-
const geminiFile = (0,
|
|
6009
|
+
const geminiFile = (0, import_node_path16.join)(root, "GEMINI.md");
|
|
5796
6010
|
plan(geminiFile, managedFileContent(geminiFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
|
|
5797
6011
|
rememberTarget("gemini", geminiFile);
|
|
5798
6012
|
}
|
|
@@ -5828,7 +6042,7 @@ ${conflicts.map((f) => ` - ${f}`).join("\n")}`
|
|
|
5828
6042
|
}
|
|
5829
6043
|
for (const file of plans.values()) {
|
|
5830
6044
|
if (!(0, import_node_fs17.existsSync)(file.target) || (0, import_node_fs17.readFileSync)(file.target, "utf8") !== file.content) {
|
|
5831
|
-
(0, import_node_fs17.mkdirSync)((0,
|
|
6045
|
+
(0, import_node_fs17.mkdirSync)((0, import_node_path16.dirname)(file.target), { recursive: true });
|
|
5832
6046
|
(0, import_node_fs17.writeFileSync)(file.target, file.content);
|
|
5833
6047
|
}
|
|
5834
6048
|
}
|
|
@@ -5848,7 +6062,7 @@ ${conflicts.map((f) => ` - ${f}`).join("\n")}`
|
|
|
5848
6062
|
};
|
|
5849
6063
|
}
|
|
5850
6064
|
function pathsUnder(root, paths) {
|
|
5851
|
-
return [...paths].map((path) => (0,
|
|
6065
|
+
return [...paths].map((path) => (0, import_node_path16.relative)(root, path)).filter((path) => path !== ".." && !path.startsWith(`..${import_node_path16.sep}`) && !(0, import_node_path16.isAbsolute)(path)).sort();
|
|
5852
6066
|
}
|
|
5853
6067
|
function normalizeHarnesses(values, global) {
|
|
5854
6068
|
const requested = values?.length ? values : ["claude"];
|
|
@@ -5867,10 +6081,10 @@ function normalizeHarnesses(values, global) {
|
|
|
5867
6081
|
}
|
|
5868
6082
|
return expanded;
|
|
5869
6083
|
}
|
|
5870
|
-
function managedFileContent(path,
|
|
6084
|
+
function managedFileContent(path, block2, force, boundary) {
|
|
5871
6085
|
const symlink = symlinkedComponent(boundary, path);
|
|
5872
6086
|
if (symlink) throw new Error(`refusing to manage ${path}: symbolic link component ${symlink}`);
|
|
5873
|
-
if (!(0, import_node_fs17.existsSync)(path)) return `${
|
|
6087
|
+
if (!(0, import_node_fs17.existsSync)(path)) return `${block2}
|
|
5874
6088
|
`;
|
|
5875
6089
|
const current = (0, import_node_fs17.readFileSync)(path, "utf8");
|
|
5876
6090
|
const start = "<!-- odla-ai agent setup:start -->";
|
|
@@ -5882,24 +6096,24 @@ function managedFileContent(path, block, force, boundary) {
|
|
|
5882
6096
|
}
|
|
5883
6097
|
if (startAt === -1) {
|
|
5884
6098
|
const separator = current.length === 0 || current.endsWith("\n\n") ? "" : current.endsWith("\n") ? "\n" : "\n\n";
|
|
5885
|
-
return `${current}${separator}${
|
|
6099
|
+
return `${current}${separator}${block2}
|
|
5886
6100
|
`;
|
|
5887
6101
|
}
|
|
5888
6102
|
const afterEnd = endAt + end.length;
|
|
5889
6103
|
const existing = current.slice(startAt, afterEnd);
|
|
5890
|
-
if (existing !==
|
|
6104
|
+
if (existing !== block2 && !force) {
|
|
5891
6105
|
throw new Error(`odla-managed section modified locally in ${path}; re-run with --force to replace that section`);
|
|
5892
6106
|
}
|
|
5893
|
-
return `${current.slice(0, startAt)}${
|
|
6107
|
+
return `${current.slice(0, startAt)}${block2}${current.slice(afterEnd)}`;
|
|
5894
6108
|
}
|
|
5895
6109
|
function symlinkedComponent(boundary, target) {
|
|
5896
|
-
const rel = (0,
|
|
5897
|
-
if (rel === ".." || rel.startsWith(`..${
|
|
6110
|
+
const rel = (0, import_node_path16.relative)(boundary, target);
|
|
6111
|
+
if (rel === ".." || rel.startsWith(`..${import_node_path16.sep}`) || (0, import_node_path16.isAbsolute)(rel)) {
|
|
5898
6112
|
throw new Error(`agent setup target escapes its install root: ${target}`);
|
|
5899
6113
|
}
|
|
5900
6114
|
let current = boundary;
|
|
5901
|
-
for (const part of rel.split(
|
|
5902
|
-
current = (0,
|
|
6115
|
+
for (const part of rel.split(import_node_path16.sep).filter(Boolean)) {
|
|
6116
|
+
current = (0, import_node_path16.join)(current, part);
|
|
5903
6117
|
try {
|
|
5904
6118
|
if ((0, import_node_fs17.lstatSync)(current).isSymbolicLink()) return current;
|
|
5905
6119
|
} catch (error) {
|
|
@@ -5916,22 +6130,22 @@ function listFiles(dir) {
|
|
|
5916
6130
|
const results = [];
|
|
5917
6131
|
const walk = (current) => {
|
|
5918
6132
|
for (const entry of (0, import_node_fs17.readdirSync)(current, { withFileTypes: true })) {
|
|
5919
|
-
const path = (0,
|
|
6133
|
+
const path = (0, import_node_path16.join)(current, entry.name);
|
|
5920
6134
|
if (entry.isDirectory()) walk(path);
|
|
5921
|
-
else results.push((0,
|
|
6135
|
+
else results.push((0, import_node_path16.relative)(dir, path));
|
|
5922
6136
|
}
|
|
5923
6137
|
};
|
|
5924
6138
|
walk(dir);
|
|
5925
6139
|
return results.sort();
|
|
5926
6140
|
}
|
|
5927
|
-
var import_node_fs17,
|
|
6141
|
+
var import_node_fs17, import_node_os4, import_node_path16, import_node_url2, AGENT_HARNESSES;
|
|
5928
6142
|
var init_skill = __esm({
|
|
5929
6143
|
"src/skill.ts"() {
|
|
5930
6144
|
"use strict";
|
|
5931
6145
|
init_cjs_shims();
|
|
5932
6146
|
import_node_fs17 = require("fs");
|
|
5933
|
-
|
|
5934
|
-
|
|
6147
|
+
import_node_os4 = require("os");
|
|
6148
|
+
import_node_path16 = require("path");
|
|
5935
6149
|
import_node_url2 = require("url");
|
|
5936
6150
|
init_skill_adapters();
|
|
5937
6151
|
AGENT_HARNESSES = ["claude", "codex", "cursor", "copilot", "gemini", "agents"];
|
|
@@ -6405,7 +6619,7 @@ function allowedWorkspacePath(relativePath) {
|
|
|
6405
6619
|
async function gitOutput(cwd, args, maxBytes) {
|
|
6406
6620
|
const child = (0, import_child_process2.spawn)("git", args, { cwd, stdio: ["ignore", "pipe", "pipe"], shell: false });
|
|
6407
6621
|
const stdout = [];
|
|
6408
|
-
const
|
|
6622
|
+
const stderr2 = [];
|
|
6409
6623
|
let bytes = 0;
|
|
6410
6624
|
child.stdout.on("data", (chunk) => {
|
|
6411
6625
|
bytes += chunk.byteLength;
|
|
@@ -6413,20 +6627,20 @@ async function gitOutput(cwd, args, maxBytes) {
|
|
|
6413
6627
|
else stdout.push(chunk);
|
|
6414
6628
|
});
|
|
6415
6629
|
child.stderr.on("data", (chunk) => {
|
|
6416
|
-
if (
|
|
6630
|
+
if (stderr2.reduce((sum, value2) => sum + value2.byteLength, 0) < 16384) stderr2.push(chunk);
|
|
6417
6631
|
});
|
|
6418
6632
|
const code = await new Promise((accept, reject) => {
|
|
6419
6633
|
child.once("error", reject);
|
|
6420
6634
|
child.once("exit", accept);
|
|
6421
6635
|
});
|
|
6422
6636
|
if (bytes > maxBytes) throw new Error(`git output exceeds ${maxBytes} bytes`);
|
|
6423
|
-
if (code !== 0) throw new Error(`git command failed: ${Buffer.concat(
|
|
6637
|
+
if (code !== 0) throw new Error(`git command failed: ${Buffer.concat(stderr2).toString("utf8").slice(0, 1e3)}`);
|
|
6424
6638
|
return Buffer.concat(stdout);
|
|
6425
6639
|
}
|
|
6426
6640
|
async function gitBlobs(cwd, entries, maxBytes) {
|
|
6427
6641
|
const child = (0, import_child_process2.spawn)("git", ["cat-file", "--batch"], { cwd, stdio: ["pipe", "pipe", "pipe"], shell: false });
|
|
6428
6642
|
const stdout = [];
|
|
6429
|
-
const
|
|
6643
|
+
const stderr2 = [];
|
|
6430
6644
|
let bytes = 0;
|
|
6431
6645
|
child.stdout.on("data", (chunk) => {
|
|
6432
6646
|
bytes += chunk.byteLength;
|
|
@@ -6434,7 +6648,7 @@ async function gitBlobs(cwd, entries, maxBytes) {
|
|
|
6434
6648
|
else stdout.push(chunk);
|
|
6435
6649
|
});
|
|
6436
6650
|
child.stderr.on("data", (chunk) => {
|
|
6437
|
-
if (
|
|
6651
|
+
if (stderr2.reduce((sum, value2) => sum + value2.byteLength, 0) < 16384) stderr2.push(chunk);
|
|
6438
6652
|
});
|
|
6439
6653
|
child.stdin.end(`${entries.map((entry) => entry.hash).join("\n")}
|
|
6440
6654
|
`);
|
|
@@ -6443,7 +6657,7 @@ async function gitBlobs(cwd, entries, maxBytes) {
|
|
|
6443
6657
|
child.once("exit", accept);
|
|
6444
6658
|
});
|
|
6445
6659
|
if (bytes > maxBytes + entries.length * 100) throw new Error(`Git tree exceeds ${maxBytes} bytes`);
|
|
6446
|
-
if (code !== 0) throw new Error(`git object read failed: ${Buffer.concat(
|
|
6660
|
+
if (code !== 0) throw new Error(`git object read failed: ${Buffer.concat(stderr2).toString("utf8").slice(0, 1e3)}`);
|
|
6447
6661
|
const output = Buffer.concat(stdout);
|
|
6448
6662
|
const blobs = [];
|
|
6449
6663
|
let offset = 0;
|
|
@@ -6540,7 +6754,7 @@ async function gitSourceFiles(sourceDir, maxFiles, maxBytes) {
|
|
|
6540
6754
|
shell: false
|
|
6541
6755
|
});
|
|
6542
6756
|
const stdout = [];
|
|
6543
|
-
const
|
|
6757
|
+
const stderr2 = [];
|
|
6544
6758
|
let outputBytes = 0;
|
|
6545
6759
|
child.stdout.on("data", (chunk) => {
|
|
6546
6760
|
outputBytes += chunk.byteLength;
|
|
@@ -6548,14 +6762,14 @@ async function gitSourceFiles(sourceDir, maxFiles, maxBytes) {
|
|
|
6548
6762
|
else stdout.push(chunk);
|
|
6549
6763
|
});
|
|
6550
6764
|
child.stderr.on("data", (chunk) => {
|
|
6551
|
-
if (
|
|
6765
|
+
if (stderr2.reduce((sum, value2) => sum + value2.byteLength, 0) < 16384) stderr2.push(chunk);
|
|
6552
6766
|
});
|
|
6553
6767
|
const code = await new Promise((accept, reject) => {
|
|
6554
6768
|
child.once("error", reject);
|
|
6555
6769
|
child.once("exit", accept);
|
|
6556
6770
|
});
|
|
6557
6771
|
if (outputBytes > 8 * 1024 * 1024) throw new Error("git file inventory exceeds 8 MiB");
|
|
6558
|
-
if (code !== 0) throw new Error(`git file inventory failed: ${Buffer.concat(
|
|
6772
|
+
if (code !== 0) throw new Error(`git file inventory failed: ${Buffer.concat(stderr2).toString("utf8").slice(0, 1e3)}`);
|
|
6559
6773
|
const paths = Buffer.concat(stdout).toString("utf8").split("\0").filter(Boolean).sort();
|
|
6560
6774
|
if (paths.length > maxFiles) throw new Error(`workspace exceeds ${maxFiles} files`);
|
|
6561
6775
|
const root = (0, import_path4.resolve)(sourceDir);
|
|
@@ -6600,7 +6814,7 @@ async function captureGitDiff(root, maxBytes) {
|
|
|
6600
6814
|
"workspace"
|
|
6601
6815
|
], { cwd: root, stdio: ["ignore", "pipe", "pipe"], shell: false });
|
|
6602
6816
|
const stdout = [];
|
|
6603
|
-
const
|
|
6817
|
+
const stderr2 = [];
|
|
6604
6818
|
let bytes = 0;
|
|
6605
6819
|
child.stdout.on("data", (chunk) => {
|
|
6606
6820
|
bytes += chunk.byteLength;
|
|
@@ -6608,7 +6822,7 @@ async function captureGitDiff(root, maxBytes) {
|
|
|
6608
6822
|
else stdout.push(chunk);
|
|
6609
6823
|
});
|
|
6610
6824
|
child.stderr.on("data", (chunk) => {
|
|
6611
|
-
if (
|
|
6825
|
+
if (stderr2.reduce((sum, value2) => sum + value2.byteLength, 0) < 16384) stderr2.push(chunk);
|
|
6612
6826
|
});
|
|
6613
6827
|
const code = await new Promise((accept, reject) => {
|
|
6614
6828
|
child.once("error", reject);
|
|
@@ -6616,7 +6830,7 @@ async function captureGitDiff(root, maxBytes) {
|
|
|
6616
6830
|
});
|
|
6617
6831
|
if (bytes > maxBytes) throw new Error(`patch exceeds ${maxBytes} bytes`);
|
|
6618
6832
|
if (code !== 0 && code !== 1) {
|
|
6619
|
-
throw new Error(`git diff failed: ${Buffer.concat(
|
|
6833
|
+
throw new Error(`git diff failed: ${Buffer.concat(stderr2).toString("utf8").slice(0, 1e3)}`);
|
|
6620
6834
|
}
|
|
6621
6835
|
return Buffer.concat(stdout).toString("utf8").replaceAll("a/baseline/", "a/").replaceAll("a/workspace/", "a/").replaceAll("b/baseline/", "b/").replaceAll("b/workspace/", "b/").replaceAll("--- a/baseline", "--- a").replaceAll("+++ b/workspace", "+++ b");
|
|
6622
6836
|
}
|
|
@@ -7504,11 +7718,11 @@ var init_dist2 = __esm({
|
|
|
7504
7718
|
});
|
|
7505
7719
|
|
|
7506
7720
|
// ../graph/dist/code/index.js
|
|
7507
|
-
function
|
|
7721
|
+
function dirname10(path) {
|
|
7508
7722
|
const at = path.lastIndexOf("/");
|
|
7509
7723
|
return at <= 0 ? "." : path.slice(0, at);
|
|
7510
7724
|
}
|
|
7511
|
-
function
|
|
7725
|
+
function join14(base, specifier) {
|
|
7512
7726
|
const parts = [];
|
|
7513
7727
|
const segments = `${base === "." ? "" : `${base}/`}${specifier}`.split("/");
|
|
7514
7728
|
for (const segment of segments) {
|
|
@@ -7520,7 +7734,7 @@ function join13(base, specifier) {
|
|
|
7520
7734
|
}
|
|
7521
7735
|
function resolveImport(fromPath, specifier, known) {
|
|
7522
7736
|
if (!specifier.startsWith(".")) return null;
|
|
7523
|
-
const base =
|
|
7737
|
+
const base = join14(dirname10(fromPath), specifier);
|
|
7524
7738
|
const candidates = [
|
|
7525
7739
|
base,
|
|
7526
7740
|
base.replace(/\.js$/, ".ts"),
|
|
@@ -8023,13 +8237,13 @@ function gitApply(cwd, patch2, check) {
|
|
|
8023
8237
|
stdio: ["pipe", "ignore", "pipe"],
|
|
8024
8238
|
env: { PATH: process.env.PATH ?? "", GIT_CONFIG_NOSYSTEM: "1", GIT_CONFIG_GLOBAL: "/dev/null" }
|
|
8025
8239
|
});
|
|
8026
|
-
let
|
|
8240
|
+
let stderr2 = "";
|
|
8027
8241
|
child.stderr.setEncoding("utf8");
|
|
8028
8242
|
child.stderr.on("data", (text3) => {
|
|
8029
|
-
if (
|
|
8243
|
+
if (stderr2.length < 4e3) stderr2 += text3.slice(0, 4e3);
|
|
8030
8244
|
});
|
|
8031
8245
|
child.once("error", reject);
|
|
8032
|
-
child.once("exit", (code) => code === 0 ? accept() : reject(new TypeError(describePatchFailure(patch2,
|
|
8246
|
+
child.once("exit", (code) => code === 0 ? accept() : reject(new TypeError(describePatchFailure(patch2, stderr2.trim().slice(0, 500)))));
|
|
8033
8247
|
child.stdin.end(patch2);
|
|
8034
8248
|
});
|
|
8035
8249
|
}
|
|
@@ -8140,7 +8354,7 @@ function execute(engine, args, name, recipe2, signal) {
|
|
|
8140
8354
|
const started = Date.now();
|
|
8141
8355
|
const child = (0, import_child_process5.spawn)(engine, args, { shell: false, stdio: ["ignore", "pipe", "pipe"] });
|
|
8142
8356
|
const stdout = [];
|
|
8143
|
-
const
|
|
8357
|
+
const stderr2 = [];
|
|
8144
8358
|
let bytes = 0;
|
|
8145
8359
|
let outputLimitExceeded = false;
|
|
8146
8360
|
let timedOut = false;
|
|
@@ -8161,7 +8375,7 @@ function execute(engine, args, name, recipe2, signal) {
|
|
|
8161
8375
|
else target.push(chunk);
|
|
8162
8376
|
};
|
|
8163
8377
|
child.stdout.on("data", collect(stdout));
|
|
8164
|
-
child.stderr.on("data", collect(
|
|
8378
|
+
child.stderr.on("data", collect(stderr2));
|
|
8165
8379
|
const abort = () => stop("abort");
|
|
8166
8380
|
signal?.addEventListener("abort", abort, { once: true });
|
|
8167
8381
|
if (signal?.aborted) abort();
|
|
@@ -8177,7 +8391,7 @@ function execute(engine, args, name, recipe2, signal) {
|
|
|
8177
8391
|
accept({
|
|
8178
8392
|
exitCode: code ?? 1,
|
|
8179
8393
|
stdout: Buffer.concat(stdout).toString("utf8"),
|
|
8180
|
-
stderr: Buffer.concat(
|
|
8394
|
+
stderr: Buffer.concat(stderr2).toString("utf8"),
|
|
8181
8395
|
durationMs: Date.now() - started,
|
|
8182
8396
|
outputLimitExceeded,
|
|
8183
8397
|
timedOut
|
|
@@ -8323,11 +8537,11 @@ function checkedResult(result, maximumOutputBytes) {
|
|
|
8323
8537
|
}
|
|
8324
8538
|
function boundedLogs(result, maximum) {
|
|
8325
8539
|
const stdout = Buffer.from(result.stdout);
|
|
8326
|
-
const
|
|
8540
|
+
const stderr2 = Buffer.from(result.stderr);
|
|
8327
8541
|
const first = stdout.subarray(0, maximum);
|
|
8328
8542
|
return {
|
|
8329
8543
|
stdout: first.toString("utf8"),
|
|
8330
|
-
stderr:
|
|
8544
|
+
stderr: stderr2.subarray(0, Math.max(0, maximum - first.byteLength)).toString("utf8")
|
|
8331
8545
|
};
|
|
8332
8546
|
}
|
|
8333
8547
|
function digestPolicy(policy) {
|
|
@@ -10461,7 +10675,7 @@ var init_code_runtime_config = __esm({
|
|
|
10461
10675
|
// src/code-connect.ts
|
|
10462
10676
|
async function codeConnect(options) {
|
|
10463
10677
|
const cwd = options.cwd ?? process.cwd();
|
|
10464
|
-
const configPath = (0,
|
|
10678
|
+
const configPath = (0, import_node_path17.resolve)(cwd, options.configPath);
|
|
10465
10679
|
const cfg = (0, import_node_fs18.existsSync)(configPath) ? await loadProjectConfig(configPath) : null;
|
|
10466
10680
|
const requestedAppId = options.appId?.trim();
|
|
10467
10681
|
if (requestedAppId && !/^[a-z0-9][a-z0-9-]{1,62}$/.test(requestedAppId)) {
|
|
@@ -10491,7 +10705,7 @@ async function codeConnect(options) {
|
|
|
10491
10705
|
const doFetch = options.fetch ?? fetch;
|
|
10492
10706
|
const engine = await (options.selectEngine ?? selectContainerEngine)(options.engine ?? "auto");
|
|
10493
10707
|
const hostPlatform = process.platform === "darwin" ? "macos" : "linux";
|
|
10494
|
-
const hostName = (options.name ?? (0,
|
|
10708
|
+
const hostName = (options.name ?? (0, import_node_os5.hostname)()).trim();
|
|
10495
10709
|
if (!hostName || hostName.length > 120) throw new Error("--name must contain 1 to 120 characters");
|
|
10496
10710
|
const repository = await inferGitHubRepository(cwd, options.readGitOrigin);
|
|
10497
10711
|
const localSource = await (options.prepareLocalSource ?? prepareCodeLocalSource)(
|
|
@@ -10529,8 +10743,8 @@ async function codeConnect(options) {
|
|
|
10529
10743
|
platform: hostPlatform,
|
|
10530
10744
|
arch: process.arch,
|
|
10531
10745
|
engines: [engine],
|
|
10532
|
-
cpuCount: (0,
|
|
10533
|
-
memoryBytes: (0,
|
|
10746
|
+
cpuCount: (0, import_node_os5.cpus)().length,
|
|
10747
|
+
memoryBytes: (0, import_node_os5.totalmem)(),
|
|
10534
10748
|
source: descriptor2,
|
|
10535
10749
|
images: {
|
|
10536
10750
|
ready: true,
|
|
@@ -10627,14 +10841,14 @@ function apiFailure(action2, status, value2) {
|
|
|
10627
10841
|
function record6(value2) {
|
|
10628
10842
|
return value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
|
|
10629
10843
|
}
|
|
10630
|
-
var import_node_fs18,
|
|
10844
|
+
var import_node_fs18, import_node_os5, import_node_path17;
|
|
10631
10845
|
var init_code_connect = __esm({
|
|
10632
10846
|
"src/code-connect.ts"() {
|
|
10633
10847
|
"use strict";
|
|
10634
10848
|
init_cjs_shims();
|
|
10635
10849
|
import_node_fs18 = require("fs");
|
|
10636
|
-
|
|
10637
|
-
|
|
10850
|
+
import_node_os5 = require("os");
|
|
10851
|
+
import_node_path17 = require("path");
|
|
10638
10852
|
init_node();
|
|
10639
10853
|
init_admin_ai_auth();
|
|
10640
10854
|
init_config();
|
|
@@ -10952,7 +11166,7 @@ function developerTokenStatus(context, parsed, now = Date.now()) {
|
|
|
10952
11166
|
const cacheStatus = !cached?.token ? "missing" : cached.platform !== context.platform.value ? "other-platform" : (cached.expiresAt ?? 0) <= now + 6e4 ? "expired" : "valid";
|
|
10953
11167
|
const source = clean3(
|
|
10954
11168
|
stringOpt(parsed.options.token)
|
|
10955
|
-
) ? "flag" : clean3(
|
|
11169
|
+
) ? "flag" : clean3(import_node_process16.default.env.ODLA_DEV_TOKEN) ? "environment" : cacheStatus === "valid" ? "cache" : "missing";
|
|
10956
11170
|
return {
|
|
10957
11171
|
source,
|
|
10958
11172
|
cacheFile: context.cfg.local.tokenFile,
|
|
@@ -10963,12 +11177,12 @@ function clean3(value2) {
|
|
|
10963
11177
|
const normalized = value2?.trim();
|
|
10964
11178
|
return normalized || void 0;
|
|
10965
11179
|
}
|
|
10966
|
-
var
|
|
11180
|
+
var import_node_process16;
|
|
10967
11181
|
var init_operator_credentials = __esm({
|
|
10968
11182
|
"src/operator-credentials.ts"() {
|
|
10969
11183
|
"use strict";
|
|
10970
11184
|
init_cjs_shims();
|
|
10971
|
-
|
|
11185
|
+
import_node_process16 = __toESM(require("process"), 1);
|
|
10972
11186
|
init_argv();
|
|
10973
11187
|
init_local();
|
|
10974
11188
|
}
|
|
@@ -11176,11 +11390,32 @@ var init_credential_command = __esm({
|
|
|
11176
11390
|
});
|
|
11177
11391
|
|
|
11178
11392
|
// src/help-usage.ts
|
|
11179
|
-
var USAGE_SECTION;
|
|
11393
|
+
var AUTH_SECTION, USAGE_SECTION;
|
|
11180
11394
|
var init_help_usage = __esm({
|
|
11181
11395
|
"src/help-usage.ts"() {
|
|
11182
11396
|
"use strict";
|
|
11183
11397
|
init_cjs_shims();
|
|
11398
|
+
AUTH_SECTION = `
|
|
11399
|
+
Enrol this machine once, then stop asking:
|
|
11400
|
+
npx odla-ai device enroll --all-apps --capability all --no-open --wait 600
|
|
11401
|
+
One browser approval. Afterwards every worktree on this machine mints its
|
|
11402
|
+
own short-lived credentials with nobody's attention, for every app you
|
|
11403
|
+
own \u2014 including apps you create later. The window rolls forward each time
|
|
11404
|
+
you use it, so continuous work never interrupts anyone; only a real gap
|
|
11405
|
+
does. Give the human the printed /studio?code= URL, keep the process
|
|
11406
|
+
alive, and wait on it.
|
|
11407
|
+
|
|
11408
|
+
npx odla-ai device enroll --platform-wide --device-ttl 6w --no-open --wait 600
|
|
11409
|
+
The same thing across all of odla, for weeks. Needs a platform
|
|
11410
|
+
administrator's approval \u2014 an app owner's cannot carry platform scopes.
|
|
11411
|
+
|
|
11412
|
+
npx odla-ai whoami what this machine holds and when it lapses
|
|
11413
|
+
npx odla-ai device list every machine you have enrolled
|
|
11414
|
+
|
|
11415
|
+
Enrollment is the only human decision here. Revoking a machine, purging an app,
|
|
11416
|
+
transferring ownership, and rotating credentials still need a signed-in human in
|
|
11417
|
+
Studio, and no machine credential can do them however wide its approval was.
|
|
11418
|
+
`;
|
|
11184
11419
|
USAGE_SECTION = `
|
|
11185
11420
|
Start here:
|
|
11186
11421
|
odla-ai runbook ask "<question>" The current procedure, from odla's own
|
|
@@ -11219,7 +11454,7 @@ Usage:
|
|
|
11219
11454
|
odla-ai brand design unpack <bundle.html|-> [--out <dir>] [--json]
|
|
11220
11455
|
odla-ai pm project list [--app <product-id>] [--status <s>] [--json]
|
|
11221
11456
|
odla-ai pm project add --app <product-id> --name <name> [--description <text>] [--json]
|
|
11222
|
-
odla-ai pm project use <project-id> [--json] [saved
|
|
11457
|
+
odla-ai pm project use <project-id> [--json] [saved for this app, on this machine]
|
|
11223
11458
|
odla-ai pm goal list [--app <id>] [--project <id>] [--status <s>] [--q <text>] [--limit <n>] [--offset <n>] [--json]
|
|
11224
11459
|
odla-ai pm task list [--app <id>] [--column <backlog|ready|doing|review|done>] [--goal <id>] [--assignee <id>] [--q <text>] [--limit <n>] [--offset <n>] [--json]
|
|
11225
11460
|
odla-ai pm decision list [--app <id>] [--status <s>] [--q <text>] [--limit <n>] [--offset <n>] [--json]
|
|
@@ -11311,7 +11546,8 @@ Usage:
|
|
|
11311
11546
|
odla-ai security run [target] --ack-redacted-source [--env dev] [--profile odla] [--fail-on high]
|
|
11312
11547
|
odla-ai security run [target] --self --ack-redacted-source
|
|
11313
11548
|
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]
|
|
11314
|
-
odla-ai device enroll [--app <id>[,<id>...]] [--
|
|
11549
|
+
odla-ai device enroll [--all-apps|--app <id>[,<id>...]] [--capability all|<c>[,<c>...]] [--platform-wide]
|
|
11550
|
+
[--name <label>] [--device-ttl <30d|6w|2y|forever>] [--email <odla-account>] [--no-open] [--wait <seconds>] [--json]
|
|
11315
11551
|
odla-ai device list [--email <odla-account>] [--json]
|
|
11316
11552
|
odla-ai device revoke <device-id> [--email <odla-account>] [--json]
|
|
11317
11553
|
odla-ai credentials list [--config odla.config.mjs] [--env dev] [--all] [--json]
|
|
@@ -11327,8 +11563,11 @@ Usage:
|
|
|
11327
11563
|
|
|
11328
11564
|
// src/help.ts
|
|
11329
11565
|
function printHelp(output = console) {
|
|
11330
|
-
output.log(
|
|
11331
|
-
|
|
11566
|
+
output.log(helpText());
|
|
11567
|
+
}
|
|
11568
|
+
function helpText() {
|
|
11569
|
+
return `odla-ai
|
|
11570
|
+
${AUTH_SECTION}${USAGE_SECTION}
|
|
11332
11571
|
Commands:
|
|
11333
11572
|
auth Start a fresh, exact-project agent authorization for human review.
|
|
11334
11573
|
The email is the signed-in odla account, never git or GitHub
|
|
@@ -11401,8 +11640,9 @@ Commands:
|
|
|
11401
11640
|
security Connect GitHub sources and run commit-pinned hosted reviews, or scan a local snapshot.
|
|
11402
11641
|
pm Project management (via @odla-ai/pm): Products contain Projects;
|
|
11403
11642
|
projects contain goals, kanban tasks, decisions, and bugs. Use
|
|
11404
|
-
"pm project list|add|use"
|
|
11405
|
-
|
|
11643
|
+
"pm project list|add|use" selects a project for this app on this
|
|
11644
|
+
machine \u2014 every worktree shares it \u2014 or pass --app/--project
|
|
11645
|
+
explicitly. Same device-grant auth as "app".
|
|
11406
11646
|
Status changes and comments post to each item's @odla-ai/chat
|
|
11407
11647
|
discussion thread.
|
|
11408
11648
|
NOTE: "--column ready" is OWNER-ONLY. Creating a task in Ready
|
|
@@ -11431,9 +11671,20 @@ Commands:
|
|
|
11431
11671
|
platform Read canonical fleet health, releases, provider load/freshness,
|
|
11432
11672
|
explicit unknowns, and next actions through a read-only grant.
|
|
11433
11673
|
device Enrol THIS machine once, then stop asking. A human approves the
|
|
11434
|
-
enrollment in the browser; from then on
|
|
11435
|
-
own short-lived credentials
|
|
11436
|
-
attention, until the device
|
|
11674
|
+
enrollment in the browser; from then on EVERY worktree on this
|
|
11675
|
+
machine mints its own short-lived credentials with nobody's
|
|
11676
|
+
attention, until the device is revoked or goes unused.
|
|
11677
|
+
"--all-apps" covers every app you own, now and later, so creating
|
|
11678
|
+
an app costs no new approval. "--capability all" takes everything
|
|
11679
|
+
that approval is allowed to carry, so a capability you did not
|
|
11680
|
+
think to name is not a 403 next week. "--platform-wide" is the
|
|
11681
|
+
administrator's version, across all of odla.
|
|
11682
|
+
The expiry is a GAP, not a clock: each use rolls it forward, so
|
|
11683
|
+
only going quiet brings a human back into the loop \u2014 which is
|
|
11684
|
+
where anything that changed can be explained.
|
|
11685
|
+
"device list" shows what each machine holds and when it lapses;
|
|
11686
|
+
revoking one takes down every credential it ever minted, and is
|
|
11687
|
+
deliberately a signed-in human's decision in Studio.
|
|
11437
11688
|
provision Register services, compose integrations, persist credentials, optionally push secrets.
|
|
11438
11689
|
"provision --live --yes" initializes only the live instance of
|
|
11439
11690
|
an existing sandbox app and enables every configured service;
|
|
@@ -11529,7 +11780,7 @@ Safety:
|
|
|
11529
11780
|
Run security plan first to inspect the admin-selected providers, models,
|
|
11530
11781
|
per-route bounds, credential readiness, retention, no-execution boundary,
|
|
11531
11782
|
and digest that binds consent to that exact plan.
|
|
11532
|
-
|
|
11783
|
+
`;
|
|
11533
11784
|
}
|
|
11534
11785
|
var init_help = __esm({
|
|
11535
11786
|
"src/help.ts"() {
|
|
@@ -11539,6 +11790,53 @@ var init_help = __esm({
|
|
|
11539
11790
|
}
|
|
11540
11791
|
});
|
|
11541
11792
|
|
|
11793
|
+
// src/help-command.ts
|
|
11794
|
+
function printCommandHelp(command, output = console) {
|
|
11795
|
+
const lines = helpText().split("\n");
|
|
11796
|
+
const usage = allBlocks(lines, new RegExp(`^ odla-ai ${escapeRe(command)}(\\s|$)`));
|
|
11797
|
+
const prose = block(lines, (line2) => new RegExp(`^ ${escapeRe(command)}\\s\\s+\\S`).test(line2));
|
|
11798
|
+
if (usage.length === 0 && prose.length === 0) {
|
|
11799
|
+
output.log(`odla-ai: no command "${command}". Run "odla-ai help" for all of them.`);
|
|
11800
|
+
return;
|
|
11801
|
+
}
|
|
11802
|
+
output.log([
|
|
11803
|
+
...prose.length ? [prose.join("\n"), ""] : [],
|
|
11804
|
+
...usage.length ? ["Usage:", ...usage, ""] : [],
|
|
11805
|
+
AUTH_SECTION.trimEnd()
|
|
11806
|
+
].join("\n"));
|
|
11807
|
+
}
|
|
11808
|
+
function allBlocks(lines, pattern) {
|
|
11809
|
+
const out = [];
|
|
11810
|
+
for (let i = 0; i < lines.length; i++) {
|
|
11811
|
+
if (!pattern.test(lines[i])) continue;
|
|
11812
|
+
out.push(...block(lines.slice(i), (line2) => line2 === lines[i]));
|
|
11813
|
+
}
|
|
11814
|
+
return out;
|
|
11815
|
+
}
|
|
11816
|
+
function block(lines, starts) {
|
|
11817
|
+
const first = lines.findIndex(starts);
|
|
11818
|
+
if (first === -1) return [];
|
|
11819
|
+
const indent = lines[first].length - lines[first].trimStart().length;
|
|
11820
|
+
const out = [lines[first]];
|
|
11821
|
+
for (const line2 of lines.slice(first + 1)) {
|
|
11822
|
+
if (!line2.trim()) break;
|
|
11823
|
+
if (line2.length - line2.trimStart().length <= indent) break;
|
|
11824
|
+
out.push(line2);
|
|
11825
|
+
}
|
|
11826
|
+
return out;
|
|
11827
|
+
}
|
|
11828
|
+
function escapeRe(value2) {
|
|
11829
|
+
return value2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
11830
|
+
}
|
|
11831
|
+
var init_help_command = __esm({
|
|
11832
|
+
"src/help-command.ts"() {
|
|
11833
|
+
"use strict";
|
|
11834
|
+
init_cjs_shims();
|
|
11835
|
+
init_help_usage();
|
|
11836
|
+
init_help();
|
|
11837
|
+
}
|
|
11838
|
+
});
|
|
11839
|
+
|
|
11542
11840
|
// src/discuss-principals.ts
|
|
11543
11841
|
function mergeDiscussPrincipals(target, source) {
|
|
11544
11842
|
Object.assign(target.authors, source.authors ?? {});
|
|
@@ -12804,20 +13102,43 @@ var init_pm_watch = __esm({
|
|
|
12804
13102
|
|
|
12805
13103
|
// src/pm-project-context.ts
|
|
12806
13104
|
function readPmProjectContext(rootDir) {
|
|
12807
|
-
|
|
12808
|
-
|
|
13105
|
+
adoptLegacySelection(rootDir);
|
|
13106
|
+
const entries = Object.values(readSelections()).filter(isSelection);
|
|
13107
|
+
return entries.sort((a, b) => b.selectedAt.localeCompare(a.selectedAt))[0] ?? null;
|
|
12809
13108
|
}
|
|
12810
13109
|
function writePmProjectContext(rootDir, value2) {
|
|
12811
|
-
|
|
13110
|
+
adoptLegacySelection(rootDir);
|
|
13111
|
+
writePrivateJson(pmProjectContextFile(), {
|
|
13112
|
+
...readSelections(),
|
|
13113
|
+
[value2.appId]: { ...value2, selectedAt: (/* @__PURE__ */ new Date()).toISOString() }
|
|
13114
|
+
});
|
|
13115
|
+
}
|
|
13116
|
+
function adoptLegacySelection(rootDir) {
|
|
13117
|
+
const legacy = (0, import_node_path18.resolve)(rootDir, ".odla", "pm-project.local.json");
|
|
13118
|
+
if (!(0, import_node_fs19.existsSync)(legacy)) return;
|
|
13119
|
+
const previous = readJsonFile(legacy);
|
|
13120
|
+
(0, import_node_fs19.rmSync)(legacy, { force: true });
|
|
13121
|
+
if (!isSelection(previous)) return;
|
|
13122
|
+
const selections = readSelections();
|
|
13123
|
+
if (selections[previous.appId]) return;
|
|
13124
|
+
writePrivateJson(pmProjectContextFile(), { ...selections, [previous.appId]: previous });
|
|
12812
13125
|
}
|
|
12813
|
-
|
|
13126
|
+
function readSelections() {
|
|
13127
|
+
return readJsonFile(pmProjectContextFile()) ?? {};
|
|
13128
|
+
}
|
|
13129
|
+
function isSelection(value2) {
|
|
13130
|
+
return !!value2 && typeof value2.appId === "string" && typeof value2.projectId === "string" && typeof value2.selectedAt === "string";
|
|
13131
|
+
}
|
|
13132
|
+
var import_node_fs19, import_node_path18, pmProjectContextFile;
|
|
12814
13133
|
var init_pm_project_context = __esm({
|
|
12815
13134
|
"src/pm-project-context.ts"() {
|
|
12816
13135
|
"use strict";
|
|
12817
13136
|
init_cjs_shims();
|
|
12818
|
-
|
|
13137
|
+
import_node_fs19 = require("fs");
|
|
13138
|
+
import_node_path18 = require("path");
|
|
12819
13139
|
init_local();
|
|
12820
|
-
|
|
13140
|
+
init_odla_home();
|
|
13141
|
+
pmProjectContextFile = () => pmContextFile();
|
|
12821
13142
|
}
|
|
12822
13143
|
});
|
|
12823
13144
|
|
|
@@ -14391,7 +14712,7 @@ async function provision(options) {
|
|
|
14391
14712
|
await provisionIntegrationSeeds(doFetch, cfg.dbEndpoint, tenantId, dbKey, database.integrations, env, out);
|
|
14392
14713
|
}
|
|
14393
14714
|
if (cfg.services.includes("ai") && cfg.ai?.provider && cfg.ai.keyEnv) {
|
|
14394
|
-
const key =
|
|
14715
|
+
const key = import_node_process17.default.env[cfg.ai.keyEnv];
|
|
14395
14716
|
if (key) {
|
|
14396
14717
|
const secretName = cfg.ai.secretName ?? defaultSecretName(cfg.ai.provider);
|
|
14397
14718
|
await (0, import_ai5.putSecret)({ endpoint: cfg.dbEndpoint, token, fetch: doFetch }, tenantId, secretName, key);
|
|
@@ -14430,14 +14751,14 @@ async function provision(options) {
|
|
|
14430
14751
|
}
|
|
14431
14752
|
}
|
|
14432
14753
|
}
|
|
14433
|
-
var import_apps13, import_ai5,
|
|
14754
|
+
var import_apps13, import_ai5, import_node_process17;
|
|
14434
14755
|
var init_provision = __esm({
|
|
14435
14756
|
"src/provision.ts"() {
|
|
14436
14757
|
"use strict";
|
|
14437
14758
|
init_cjs_shims();
|
|
14438
14759
|
import_apps13 = require("@odla-ai/apps");
|
|
14439
14760
|
import_ai5 = require("@odla-ai/ai");
|
|
14440
|
-
|
|
14761
|
+
import_node_process17 = __toESM(require("process"), 1);
|
|
14441
14762
|
init_config();
|
|
14442
14763
|
init_calendar();
|
|
14443
14764
|
init_calendar_errors();
|
|
@@ -14632,7 +14953,7 @@ var init_surface = __esm({
|
|
|
14632
14953
|
|
|
14633
14954
|
// src/record.ts
|
|
14634
14955
|
function recordInvocation(parsed) {
|
|
14635
|
-
const file =
|
|
14956
|
+
const file = import_node_process18.default.env.ODLA_CLI_RECORD;
|
|
14636
14957
|
if (!file) return;
|
|
14637
14958
|
try {
|
|
14638
14959
|
const entry = {
|
|
@@ -14640,18 +14961,18 @@ function recordInvocation(parsed) {
|
|
|
14640
14961
|
options: Object.entries(parsed.options).map(([name, value2]) => value2 === false ? `no-${name}` : name).sort()
|
|
14641
14962
|
};
|
|
14642
14963
|
if (!entry.path.length) return;
|
|
14643
|
-
(0,
|
|
14964
|
+
(0, import_node_fs20.appendFileSync)(file, `${JSON.stringify(entry)}
|
|
14644
14965
|
`);
|
|
14645
14966
|
} catch {
|
|
14646
14967
|
}
|
|
14647
14968
|
}
|
|
14648
|
-
var
|
|
14969
|
+
var import_node_fs20, import_node_process18;
|
|
14649
14970
|
var init_record = __esm({
|
|
14650
14971
|
"src/record.ts"() {
|
|
14651
14972
|
"use strict";
|
|
14652
14973
|
init_cjs_shims();
|
|
14653
|
-
|
|
14654
|
-
|
|
14974
|
+
import_node_fs20 = require("fs");
|
|
14975
|
+
import_node_process18 = __toESM(require("process"), 1);
|
|
14655
14976
|
init_surface();
|
|
14656
14977
|
}
|
|
14657
14978
|
});
|
|
@@ -14713,6 +15034,22 @@ var init_device_ttl = __esm({
|
|
|
14713
15034
|
|
|
14714
15035
|
// src/device-command.ts
|
|
14715
15036
|
async function deviceCommand(parsed, deps) {
|
|
15037
|
+
assertArgs(parsed, [
|
|
15038
|
+
"app",
|
|
15039
|
+
"all-apps",
|
|
15040
|
+
"platform-wide",
|
|
15041
|
+
"name",
|
|
15042
|
+
"capability",
|
|
15043
|
+
"device-ttl",
|
|
15044
|
+
"email",
|
|
15045
|
+
"open",
|
|
15046
|
+
"json",
|
|
15047
|
+
"config",
|
|
15048
|
+
"token",
|
|
15049
|
+
"context",
|
|
15050
|
+
"platform",
|
|
15051
|
+
"wait"
|
|
15052
|
+
], 3);
|
|
14716
15053
|
const action2 = parsed.positionals[1] ?? "";
|
|
14717
15054
|
const out = deps.stdout ?? console;
|
|
14718
15055
|
const doFetch = deps.fetch ?? fetch;
|
|
@@ -14725,10 +15062,12 @@ async function deviceCommand(parsed, deps) {
|
|
|
14725
15062
|
}
|
|
14726
15063
|
async function enroll(parsed, deps, cfg, doFetch, out, json) {
|
|
14727
15064
|
const name = stringOpt(parsed.options.name) ?? defaultDeviceName();
|
|
14728
|
-
const
|
|
14729
|
-
|
|
15065
|
+
const platformWide = parsed.options["platform-wide"] === true;
|
|
15066
|
+
const apps = platformWide || parsed.options["all-apps"] === true ? [import_db4.ALL_OWNED_APPS] : (stringOpt(parsed.options.app) ?? cfg.app.id).split(",").map((id2) => id2.trim()).filter(Boolean);
|
|
15067
|
+
if (apps.length === 0) throw new Error("device enroll needs --app <id>[,<id>\u2026], or --all-apps");
|
|
14730
15068
|
const deviceTtlMs = parseDeviceTtl(parsed.options["device-ttl"]);
|
|
14731
|
-
const extended = deviceTtlMs !== void 0 && deviceTtlMs > OWNER_DEVICE_TTL_MS;
|
|
15069
|
+
const extended = platformWide || deviceTtlMs !== void 0 && deviceTtlMs > OWNER_DEVICE_TTL_MS;
|
|
15070
|
+
const { capabilities, scopes } = requestedEnvelope(parsed, platformWide);
|
|
14732
15071
|
const token = await scopedToken2(
|
|
14733
15072
|
parsed,
|
|
14734
15073
|
deps,
|
|
@@ -14743,9 +15082,10 @@ async function enroll(parsed, deps, cfg, doFetch, out, json) {
|
|
|
14743
15082
|
headers: { authorization: `Bearer ${token}`, "content-type": "application/json" },
|
|
14744
15083
|
body: JSON.stringify({
|
|
14745
15084
|
name,
|
|
14746
|
-
platform:
|
|
15085
|
+
platform: import_node_process19.default.platform,
|
|
14747
15086
|
appIds: apps,
|
|
14748
|
-
...
|
|
15087
|
+
...capabilities ? { capabilities } : {},
|
|
15088
|
+
...scopes ? { scopes } : {},
|
|
14749
15089
|
...deviceTtlMs === void 0 ? {} : { deviceTtlMs }
|
|
14750
15090
|
})
|
|
14751
15091
|
});
|
|
@@ -14754,19 +15094,52 @@ async function enroll(parsed, deps, cfg, doFetch, out, json) {
|
|
|
14754
15094
|
throw new Error(`device enroll failed: ${body.error?.message ?? `registry returned ${response2.status}`} (${response2.status})`);
|
|
14755
15095
|
}
|
|
14756
15096
|
const path = deviceCredentialPath();
|
|
14757
|
-
(0,
|
|
14758
|
-
(0,
|
|
15097
|
+
(0, import_node_fs21.mkdirSync)((0, import_node_path19.dirname)(path), { recursive: true });
|
|
15098
|
+
(0, import_node_fs21.writeFileSync)(path, JSON.stringify({
|
|
14759
15099
|
token: body.token,
|
|
14760
15100
|
platform: cfg.platformUrl.replace(/\/$/, ""),
|
|
14761
15101
|
deviceId: body.device.deviceId,
|
|
14762
15102
|
name
|
|
14763
15103
|
}, null, 2));
|
|
14764
|
-
(0,
|
|
14765
|
-
|
|
14766
|
-
|
|
15104
|
+
(0, import_node_fs21.chmodSync)(path, 384);
|
|
15105
|
+
rememberMachineIdentity(cfg.platformUrl.replace(/\/$/, ""), stringOpt(parsed.options.email));
|
|
15106
|
+
const reach = body.device.appIds.includes(import_db4.ALL_OWNED_APPS) ? "every app you own, now and later" : body.device.appIds.join(", ");
|
|
15107
|
+
out.error(`device: enrolled "${name}" for ${reach}; credential written to ${path}`);
|
|
15108
|
+
out.error(
|
|
15109
|
+
"device: every worktree on this machine mints its own credentials from now on \u2014 no further approvals,"
|
|
15110
|
+
);
|
|
15111
|
+
out.error(
|
|
15112
|
+
`device: and the clock resets each time you use it. Going quiet for ${describeWindow(body.device.expiresAt)} is what ends it.`
|
|
15113
|
+
);
|
|
14767
15114
|
if (json) {
|
|
14768
|
-
out.log(JSON.stringify({
|
|
15115
|
+
out.log(JSON.stringify({
|
|
15116
|
+
deviceId: body.device.deviceId,
|
|
15117
|
+
name,
|
|
15118
|
+
appIds: body.device.appIds,
|
|
15119
|
+
capabilities: body.device.capabilities ?? [],
|
|
15120
|
+
scopes: body.device.scopes ?? [],
|
|
15121
|
+
expiresAt: body.device.expiresAt,
|
|
15122
|
+
hardExpiresAt: body.device.hardExpiresAt ?? null
|
|
15123
|
+
}, null, 2));
|
|
15124
|
+
}
|
|
15125
|
+
}
|
|
15126
|
+
function requestedEnvelope(parsed, platformWide) {
|
|
15127
|
+
const raw = stringOpt(parsed.options.capability);
|
|
15128
|
+
const everything = platformWide || raw?.trim().toLowerCase() === "all";
|
|
15129
|
+
if (everything) {
|
|
15130
|
+
return {
|
|
15131
|
+
capabilities: [...import_db4.OPTIONAL_AGENT_PROJECT_CAPABILITIES],
|
|
15132
|
+
scopes: platformWide ? [...import_db4.ADMIN_DEVICE_SCOPES] : [...import_db4.OWNER_DEVICE_SCOPES]
|
|
15133
|
+
};
|
|
14769
15134
|
}
|
|
15135
|
+
const named = raw?.split(",").map((c) => c.trim()).filter(Boolean);
|
|
15136
|
+
return named?.length ? { capabilities: named } : {};
|
|
15137
|
+
}
|
|
15138
|
+
function describeWindow(expiresAt, now = Date.now()) {
|
|
15139
|
+
const days = Math.max(1, Math.round((expiresAt - now) / (24 * 60 * 60 * 1e3)));
|
|
15140
|
+
if (days >= 365) return `${Math.round(days / 365)} year${days >= 730 ? "s" : ""}`;
|
|
15141
|
+
if (days % 7 === 0) return `${days / 7} week${days > 7 ? "s" : ""}`;
|
|
15142
|
+
return `${days} day${days === 1 ? "" : "s"}`;
|
|
14770
15143
|
}
|
|
14771
15144
|
async function list2(parsed, deps, cfg, doFetch, out, json) {
|
|
14772
15145
|
const token = await scopedToken2(parsed, deps, cfg, doFetch, out, "odla CLI (device list)");
|
|
@@ -14781,12 +15154,17 @@ async function list2(parsed, deps, cfg, doFetch, out, json) {
|
|
|
14781
15154
|
if (body.devices.length === 0) return out.log("no enrolled devices");
|
|
14782
15155
|
for (const device of body.devices) {
|
|
14783
15156
|
const state2 = device.revokedAt ? "revoked" : device.expiresAt <= Date.now() ? "expired" : "active";
|
|
14784
|
-
|
|
15157
|
+
const reach = device.appIds.includes("*") ? "every app you own" : device.appIds.join(", ");
|
|
15158
|
+
const gap = state2 === "active" ? ` idle ${describeWindow(device.expiresAt)} left` : "";
|
|
15159
|
+
out.log(`${device.deviceId} ${state2.padEnd(7)} ${device.name} [${reach}]${gap}`);
|
|
14785
15160
|
}
|
|
14786
15161
|
}
|
|
14787
15162
|
async function revoke(parsed, deps, cfg, doFetch, out, json) {
|
|
14788
15163
|
const deviceId = parsed.positionals[2];
|
|
14789
15164
|
if (!deviceId) throw new Error("device revoke needs the device id from `odla-ai device list`");
|
|
15165
|
+
out.error(
|
|
15166
|
+
`device: revoking is a signed-in human's decision; if this is refused, open ${cfg.platformUrl}/studio and revoke it there.`
|
|
15167
|
+
);
|
|
14790
15168
|
const token = await scopedToken2(parsed, deps, cfg, doFetch, out, "odla CLI (device revoke)");
|
|
14791
15169
|
const response2 = await doFetch(`${cfg.platformUrl}/registry/devices/${encodeURIComponent(deviceId)}/revoke`, {
|
|
14792
15170
|
method: "POST",
|
|
@@ -14805,7 +15183,7 @@ async function scopedToken2(parsed, deps, cfg, doFetch, out, label, scope = "app
|
|
|
14805
15183
|
// A device is granted the apps named in ONE approval, so --app is a list here.
|
|
14806
15184
|
allowAppList: true
|
|
14807
15185
|
});
|
|
14808
|
-
const
|
|
15186
|
+
const scopedTokenFile2 = credentials.scopedTokenFile;
|
|
14809
15187
|
return getScopedPlatformToken({
|
|
14810
15188
|
platform: cfg.platformUrl,
|
|
14811
15189
|
scope,
|
|
@@ -14816,22 +15194,24 @@ async function scopedToken2(parsed, deps, cfg, doFetch, out, label, scope = "app
|
|
|
14816
15194
|
open: parsed.options.open === false ? false : parsed.options.open === true ? true : void 0,
|
|
14817
15195
|
openApprovalUrl: deps.openUrl,
|
|
14818
15196
|
rootDir: cfg.rootDir,
|
|
14819
|
-
tokenFile:
|
|
15197
|
+
tokenFile: scopedTokenFile2,
|
|
14820
15198
|
...stringOpt(parsed.options.token) ? { token: stringOpt(parsed.options.token) } : {}
|
|
14821
15199
|
});
|
|
14822
15200
|
}
|
|
14823
15201
|
function defaultDeviceName() {
|
|
14824
|
-
return `${
|
|
15202
|
+
return `${import_node_process19.default.env.HOSTNAME ?? import_node_process19.default.env.HOST ?? "machine"}-${import_node_process19.default.platform}`;
|
|
14825
15203
|
}
|
|
14826
|
-
var
|
|
15204
|
+
var import_db4, import_node_fs21, import_node_path19, import_node_process19;
|
|
14827
15205
|
var init_device_command = __esm({
|
|
14828
15206
|
"src/device-command.ts"() {
|
|
14829
15207
|
"use strict";
|
|
14830
15208
|
init_cjs_shims();
|
|
15209
|
+
import_db4 = require("@odla-ai/db");
|
|
14831
15210
|
init_device_ttl();
|
|
14832
|
-
|
|
14833
|
-
|
|
14834
|
-
|
|
15211
|
+
init_machine_identity();
|
|
15212
|
+
import_node_fs21 = require("fs");
|
|
15213
|
+
import_node_path19 = require("path");
|
|
15214
|
+
import_node_process19 = __toESM(require("process"), 1);
|
|
14835
15215
|
init_argv();
|
|
14836
15216
|
init_admin_ai_auth();
|
|
14837
15217
|
init_device_session();
|
|
@@ -14886,7 +15266,7 @@ async function bySlug(ctx, slug) {
|
|
|
14886
15266
|
function readBody(file, inline) {
|
|
14887
15267
|
if (inline !== void 0) return inline;
|
|
14888
15268
|
if (file === void 0) throw new Error("supply the new text with --file <path>, --file - (stdin), or --body");
|
|
14889
|
-
return (0,
|
|
15269
|
+
return (0, import_node_fs22.readFileSync)(file === "-" ? 0 : file, "utf8");
|
|
14890
15270
|
}
|
|
14891
15271
|
async function runbookList(ctx, all, query) {
|
|
14892
15272
|
const params = new URLSearchParams();
|
|
@@ -14975,12 +15355,12 @@ async function runbookRemove(ctx, slug) {
|
|
|
14975
15355
|
await call2(ctx, "DELETE", `/runbook/${encodeURIComponent(runbook.id)}`);
|
|
14976
15356
|
ctx.out.log(`removed ${slug}`);
|
|
14977
15357
|
}
|
|
14978
|
-
var
|
|
15358
|
+
var import_node_fs22, PLATFORM_SCOPE, stamp;
|
|
14979
15359
|
var init_runbook_actions = __esm({
|
|
14980
15360
|
"src/runbook-actions.ts"() {
|
|
14981
15361
|
"use strict";
|
|
14982
15362
|
init_cjs_shims();
|
|
14983
|
-
|
|
15363
|
+
import_node_fs22 = require("fs");
|
|
14984
15364
|
init_version();
|
|
14985
15365
|
init_runbook_requires();
|
|
14986
15366
|
PLATFORM_SCOPE = "$platform";
|
|
@@ -15013,12 +15393,12 @@ function parseRunbook(text3, slug) {
|
|
|
15013
15393
|
};
|
|
15014
15394
|
}
|
|
15015
15395
|
function readRunbookDir(dir) {
|
|
15016
|
-
if (!(0,
|
|
15017
|
-
const files = (0,
|
|
15396
|
+
if (!(0, import_node_fs23.statSync)(dir, { throwIfNoEntry: false })?.isDirectory()) throw new Error(`not a directory: ${dir}`);
|
|
15397
|
+
const files = (0, import_node_fs23.readdirSync)(dir).filter((f) => f.endsWith(".md")).sort();
|
|
15018
15398
|
if (!files.length) throw new Error(`no .md files in ${dir}`);
|
|
15019
15399
|
return files.map((file) => {
|
|
15020
|
-
const slug = (0,
|
|
15021
|
-
const parsed = parseRunbook((0,
|
|
15400
|
+
const slug = (0, import_node_path20.basename)(file, ".md");
|
|
15401
|
+
const parsed = parseRunbook((0, import_node_fs23.readFileSync)((0, import_node_path20.join)(dir, file), "utf8"), slug);
|
|
15022
15402
|
return { file, slug, ...parsed, words: parsed.body.split(/\s+/).filter(Boolean).length };
|
|
15023
15403
|
});
|
|
15024
15404
|
}
|
|
@@ -15083,13 +15463,13 @@ async function upsert(ctx, r, visibility) {
|
|
|
15083
15463
|
);
|
|
15084
15464
|
return "updated";
|
|
15085
15465
|
}
|
|
15086
|
-
var
|
|
15466
|
+
var import_node_fs23, import_node_path20;
|
|
15087
15467
|
var init_runbook_import = __esm({
|
|
15088
15468
|
"src/runbook-import.ts"() {
|
|
15089
15469
|
"use strict";
|
|
15090
15470
|
init_cjs_shims();
|
|
15091
|
-
|
|
15092
|
-
|
|
15471
|
+
import_node_fs23 = require("fs");
|
|
15472
|
+
import_node_path20 = require("path");
|
|
15093
15473
|
init_runbook_actions();
|
|
15094
15474
|
}
|
|
15095
15475
|
});
|
|
@@ -15267,10 +15647,10 @@ ${body.split("\n").map((line2) => `+${line2}`).join("\n")}
|
|
|
15267
15647
|
}
|
|
15268
15648
|
function manifestLabeller(root) {
|
|
15269
15649
|
return (workspace) => {
|
|
15270
|
-
const manifest = (0,
|
|
15271
|
-
if (!(0,
|
|
15650
|
+
const manifest = (0, import_node_path21.join)(root, workspace, "package.json");
|
|
15651
|
+
if (!(0, import_node_fs24.existsSync)(manifest)) return void 0;
|
|
15272
15652
|
try {
|
|
15273
|
-
const name = JSON.parse((0,
|
|
15653
|
+
const name = JSON.parse((0, import_node_fs24.readFileSync)(manifest, "utf8")).name;
|
|
15274
15654
|
return typeof name === "string" ? name : void 0;
|
|
15275
15655
|
} catch {
|
|
15276
15656
|
return void 0;
|
|
@@ -15336,7 +15716,7 @@ function report4(ctx, impacts) {
|
|
|
15336
15716
|
async function runbookImpact(ctx, options, deps = {}) {
|
|
15337
15717
|
const cwd = deps.cwd ?? process.cwd();
|
|
15338
15718
|
const runGit = deps.runGit ?? gitRunner(cwd);
|
|
15339
|
-
const read3 = deps.readRepoFile ?? ((path) => (0,
|
|
15719
|
+
const read3 = deps.readRepoFile ?? ((path) => (0, import_node_fs24.readFileSync)((0, import_node_path21.join)(cwd, path), "utf8"));
|
|
15340
15720
|
const surfaces = changedSurfaces(collectDiff(runGit, options.base, read3), manifestLabeller(cwd));
|
|
15341
15721
|
if (!surfaces.length) {
|
|
15342
15722
|
return ctx.out.log(
|
|
@@ -15347,14 +15727,14 @@ async function runbookImpact(ctx, options, deps = {}) {
|
|
|
15347
15727
|
if (ctx.json) return ctx.out.log(JSON.stringify({ base: options.base, impacts }, null, 2));
|
|
15348
15728
|
report4(ctx, impacts);
|
|
15349
15729
|
}
|
|
15350
|
-
var import_node_child_process6,
|
|
15730
|
+
var import_node_child_process6, import_node_fs24, import_node_path21, SOURCE3, editHint;
|
|
15351
15731
|
var init_runbook_impact = __esm({
|
|
15352
15732
|
"src/runbook-impact.ts"() {
|
|
15353
15733
|
"use strict";
|
|
15354
15734
|
init_cjs_shims();
|
|
15355
15735
|
import_node_child_process6 = require("child_process");
|
|
15356
|
-
|
|
15357
|
-
|
|
15736
|
+
import_node_fs24 = require("fs");
|
|
15737
|
+
import_node_path21 = require("path");
|
|
15358
15738
|
init_runbook_impact_scan();
|
|
15359
15739
|
init_runbook_actions();
|
|
15360
15740
|
SOURCE3 = /\.(ts|tsx|js|jsx|mts|cts)$/;
|
|
@@ -15494,7 +15874,7 @@ var init_runbook_search_command = __esm({
|
|
|
15494
15874
|
});
|
|
15495
15875
|
|
|
15496
15876
|
// src/runbook-editor.ts
|
|
15497
|
-
function resolveEditor(env =
|
|
15877
|
+
function resolveEditor(env = import_node_process20.default.env) {
|
|
15498
15878
|
for (const name of EDITOR_ENV) {
|
|
15499
15879
|
const value2 = env[name];
|
|
15500
15880
|
if (value2 && value2.trim()) return value2.trim();
|
|
@@ -15508,8 +15888,8 @@ function defaultRun(command, path) {
|
|
|
15508
15888
|
return result.status ?? 0;
|
|
15509
15889
|
}
|
|
15510
15890
|
function editText(initial, slug, deps = {}) {
|
|
15511
|
-
const env = deps.env ??
|
|
15512
|
-
const interactive = deps.interactive ?? (() => Boolean(
|
|
15891
|
+
const env = deps.env ?? import_node_process20.default.env;
|
|
15892
|
+
const interactive = deps.interactive ?? (() => Boolean(import_node_process20.default.stdin.isTTY));
|
|
15513
15893
|
const editor = resolveEditor(env);
|
|
15514
15894
|
if (!editor)
|
|
15515
15895
|
throw new Error(
|
|
@@ -15517,28 +15897,28 @@ function editText(initial, slug, deps = {}) {
|
|
|
15517
15897
|
);
|
|
15518
15898
|
if (!interactive())
|
|
15519
15899
|
throw new Error(`cannot open an editor without a terminal \u2014 pass --file <path> or --body "\u2026" instead`);
|
|
15520
|
-
const dir = (0,
|
|
15521
|
-
const file = (0,
|
|
15900
|
+
const dir = (0, import_node_fs25.mkdtempSync)((0, import_node_path22.join)((0, import_node_os6.tmpdir)(), "odla-runbook-"));
|
|
15901
|
+
const file = (0, import_node_path22.join)(dir, `${slug}.md`);
|
|
15522
15902
|
try {
|
|
15523
|
-
(0,
|
|
15903
|
+
(0, import_node_fs25.writeFileSync)(file, initial, { mode: 384 });
|
|
15524
15904
|
const code = defaultRunOrInjected(deps)(editor, file);
|
|
15525
15905
|
if (code !== 0) throw new Error(`editor "${editor}" exited with ${code}; nothing was written`);
|
|
15526
|
-
const edited = (0,
|
|
15906
|
+
const edited = (0, import_node_fs25.readFileSync)(file, "utf8");
|
|
15527
15907
|
return edited === initial ? null : edited;
|
|
15528
15908
|
} finally {
|
|
15529
|
-
(0,
|
|
15909
|
+
(0, import_node_fs25.rmSync)(dir, { recursive: true, force: true });
|
|
15530
15910
|
}
|
|
15531
15911
|
}
|
|
15532
|
-
var import_node_child_process7,
|
|
15912
|
+
var import_node_child_process7, import_node_fs25, import_node_os6, import_node_path22, import_node_process20, EDITOR_ENV, defaultRunOrInjected;
|
|
15533
15913
|
var init_runbook_editor = __esm({
|
|
15534
15914
|
"src/runbook-editor.ts"() {
|
|
15535
15915
|
"use strict";
|
|
15536
15916
|
init_cjs_shims();
|
|
15537
15917
|
import_node_child_process7 = require("child_process");
|
|
15538
|
-
|
|
15539
|
-
|
|
15540
|
-
|
|
15541
|
-
|
|
15918
|
+
import_node_fs25 = require("fs");
|
|
15919
|
+
import_node_os6 = require("os");
|
|
15920
|
+
import_node_path22 = require("path");
|
|
15921
|
+
import_node_process20 = __toESM(require("process"), 1);
|
|
15542
15922
|
EDITOR_ENV = ["ODLA_EDITOR", "VISUAL", "EDITOR"];
|
|
15543
15923
|
defaultRunOrInjected = (deps) => deps.run ?? defaultRun;
|
|
15544
15924
|
}
|
|
@@ -15927,9 +16307,9 @@ async function runHostedSecurity(options) {
|
|
|
15927
16307
|
const appId = selfAudit ? "odla-ai" : cfg.app.id;
|
|
15928
16308
|
const env = selfAudit ? "prod" : selectEnv(options.env, cfg.envs, cfg.configPath, cfg.rootDir);
|
|
15929
16309
|
const platform = options.platform ?? cfg?.platformUrl ?? "https://odla.ai";
|
|
15930
|
-
const target = (0,
|
|
15931
|
-
const output = (0,
|
|
15932
|
-
const outputRelative = (0,
|
|
16310
|
+
const target = (0, import_node_path23.resolve)(options.target ?? cfg?.rootDir ?? ".");
|
|
16311
|
+
const output = (0, import_node_path23.resolve)(options.out ?? (0, import_node_path23.resolve)(target, ".odla/security/hosted"));
|
|
16312
|
+
const outputRelative = (0, import_node_path23.relative)(target, output).split(import_node_path23.sep).join("/");
|
|
15933
16313
|
if (!outputRelative) throw new Error("Hosted security output cannot be the repository root");
|
|
15934
16314
|
const profile = profileFor(options.profile ?? "odla", options.maxHuntTasks ?? 12);
|
|
15935
16315
|
const tokenRequest = {
|
|
@@ -15941,7 +16321,7 @@ async function runHostedSecurity(options) {
|
|
|
15941
16321
|
};
|
|
15942
16322
|
const token = await injectedToken(options, tokenRequest);
|
|
15943
16323
|
const snapshot = await (0, import_node3.snapshotDirectory)(target, {
|
|
15944
|
-
exclude: !outputRelative.startsWith("../") && !(0,
|
|
16324
|
+
exclude: !outputRelative.startsWith("../") && !(0, import_node_path23.isAbsolute)(outputRelative) ? [outputRelative] : []
|
|
15945
16325
|
});
|
|
15946
16326
|
const hosted = await (0, import_security.createPlatformSecurityReasoners)({
|
|
15947
16327
|
platform,
|
|
@@ -15959,7 +16339,7 @@ async function runHostedSecurity(options) {
|
|
|
15959
16339
|
});
|
|
15960
16340
|
const harness = (0, import_security.createSecurityHarness)({
|
|
15961
16341
|
profile,
|
|
15962
|
-
store: new import_node3.FileRunStore((0,
|
|
16342
|
+
store: new import_node3.FileRunStore((0, import_node_path23.resolve)(output, "state")),
|
|
15963
16343
|
discoveryReasoner: hosted.discoveryReasoner,
|
|
15964
16344
|
validationReasoner: hosted.validationReasoner,
|
|
15965
16345
|
policy: {
|
|
@@ -15983,7 +16363,7 @@ async function runHostedSecurity(options) {
|
|
|
15983
16363
|
function selectEnv(requested, declared, configPath, rootDir) {
|
|
15984
16364
|
const env = requested ?? (declared.includes("dev") ? "dev" : declared[0]);
|
|
15985
16365
|
if (!env || !declared.includes(env)) {
|
|
15986
|
-
const shown = (0,
|
|
16366
|
+
const shown = (0, import_node_path23.relative)(rootDir, configPath) || configPath;
|
|
15987
16367
|
throw new Error(`env "${env ?? ""}" is not declared in ${shown}`);
|
|
15988
16368
|
}
|
|
15989
16369
|
return env;
|
|
@@ -16012,17 +16392,17 @@ function printSummary(out, appId, env, run, report5, output) {
|
|
|
16012
16392
|
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}`);
|
|
16013
16393
|
if (report5.callBudget) out.log(` calls: discovery=${formatBudget(report5.callBudget.discovery)} validation=${formatBudget(report5.callBudget.validation)}`);
|
|
16014
16394
|
out.log(` findings: confirmed=${report5.metrics.confirmed} needs_reproduction=${report5.metrics.needsReproduction} candidates=${report5.metrics.candidates}`);
|
|
16015
|
-
out.log(` report: ${(0,
|
|
16395
|
+
out.log(` report: ${(0, import_node_path23.resolve)(output, "REPORT.md")}`);
|
|
16016
16396
|
}
|
|
16017
16397
|
function formatBudget(usage) {
|
|
16018
16398
|
return usage ? `${usage.usedCalls}/${usage.maxCalls} skipped=${usage.skippedCalls}` : "caller-managed";
|
|
16019
16399
|
}
|
|
16020
|
-
var
|
|
16400
|
+
var import_node_path23, import_security, import_node3;
|
|
16021
16401
|
var init_security = __esm({
|
|
16022
16402
|
"src/security.ts"() {
|
|
16023
16403
|
"use strict";
|
|
16024
16404
|
init_cjs_shims();
|
|
16025
|
-
|
|
16405
|
+
import_node_path23 = require("path");
|
|
16026
16406
|
import_security = require("@odla-ai/security");
|
|
16027
16407
|
import_node3 = require("@odla-ai/security/node");
|
|
16028
16408
|
init_config();
|
|
@@ -16540,8 +16920,10 @@ async function dispatchCli(argv2, dependencies) {
|
|
|
16540
16920
|
return;
|
|
16541
16921
|
}
|
|
16542
16922
|
if (command === "help" || command === "--help" || command === "-h") {
|
|
16543
|
-
assertArgs(parsed, ["help"],
|
|
16544
|
-
|
|
16923
|
+
assertArgs(parsed, ["help"], 2);
|
|
16924
|
+
const topic = parsed.positionals[1];
|
|
16925
|
+
if (topic) printCommandHelp(topic, runtime.stdout);
|
|
16926
|
+
else printHelp(runtime.stdout);
|
|
16545
16927
|
return;
|
|
16546
16928
|
}
|
|
16547
16929
|
if (command === "whoami") {
|
|
@@ -16712,6 +17094,7 @@ var init_cli = __esm({
|
|
|
16712
17094
|
init_context_command();
|
|
16713
17095
|
init_credential_command();
|
|
16714
17096
|
init_help();
|
|
17097
|
+
init_help_command();
|
|
16715
17098
|
init_version();
|
|
16716
17099
|
init_discuss_command();
|
|
16717
17100
|
init_pm_command();
|