@odla-ai/cli 0.35.3 → 0.36.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.cjs +409 -231
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-PYR73XBD.js → chunk-4QJ5NS64.js} +293 -136
- package/dist/chunk-4QJ5NS64.js.map +1 -0
- package/dist/{cli-ZBNA7J4T.js → cli-BFGY7F6X.js} +2 -2
- package/dist/index.cjs +361 -204
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-PYR73XBD.js.map +0 -1
- /package/dist/{cli-ZBNA7J4T.js.map → cli-BFGY7F6X.js.map} +0 -0
|
@@ -10,12 +10,12 @@ import {
|
|
|
10
10
|
} from "./chunk-UKLSRQ5J.js";
|
|
11
11
|
|
|
12
12
|
// src/admin-ai.ts
|
|
13
|
-
import
|
|
13
|
+
import process9 from "process";
|
|
14
14
|
|
|
15
15
|
// src/token.ts
|
|
16
16
|
import { OdlaError, requestToken } from "@odla-ai/db";
|
|
17
17
|
import { createHash } from "crypto";
|
|
18
|
-
import
|
|
18
|
+
import process6 from "process";
|
|
19
19
|
|
|
20
20
|
// src/handshake-approval.ts
|
|
21
21
|
import process3 from "process";
|
|
@@ -172,13 +172,50 @@ function explainRejectedCredential(error) {
|
|
|
172
172
|
].join("\n");
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
+
// src/device-session.ts
|
|
176
|
+
import { existsSync, readFileSync } from "fs";
|
|
177
|
+
import { homedir } from "os";
|
|
178
|
+
import { join as join2 } from "path";
|
|
179
|
+
import process5 from "process";
|
|
180
|
+
function deviceCredentialPath(env = process5.env) {
|
|
181
|
+
return env.ODLA_DEVICE_CREDENTIAL ?? join2(env.HOME ?? homedir(), ".odla", "device.json");
|
|
182
|
+
}
|
|
183
|
+
function readDeviceCredential(platform, env = process5.env) {
|
|
184
|
+
const path = deviceCredentialPath(env);
|
|
185
|
+
if (!existsSync(path)) return null;
|
|
186
|
+
try {
|
|
187
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
188
|
+
if (typeof parsed.token !== "string" || !parsed.token.startsWith("odla_device_")) return null;
|
|
189
|
+
if (parsed.platform !== platform) return null;
|
|
190
|
+
return { ...parsed, token: parsed.token, platform: parsed.platform };
|
|
191
|
+
} catch {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
async function mintDeviceSession(platformUrl, credential2, doFetch) {
|
|
196
|
+
const response2 = await doFetch(`${platformUrl.replace(/\/$/, "")}/registry/devices/session`, {
|
|
197
|
+
method: "POST",
|
|
198
|
+
headers: { authorization: `Bearer ${credential2.token}`, "content-type": "application/json" },
|
|
199
|
+
body: "{}"
|
|
200
|
+
});
|
|
201
|
+
const body = await response2.json().catch(() => ({}));
|
|
202
|
+
if (!response2.ok || typeof body.token !== "string") {
|
|
203
|
+
const revocable = response2.status === 401 || response2.status === 403 || response2.status === 404;
|
|
204
|
+
const detail = body.error?.message ?? (response2.ok ? `registry returned ${response2.status} with no session token` : `registry returned ${response2.status}`);
|
|
205
|
+
throw new Error(
|
|
206
|
+
`device session failed: ${detail} (${response2.status})` + (revocable ? " \u2014 if this machine's enrollment was revoked or has expired, enroll it again in Studio" : "")
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
return { token: body.token, expiresAt: body.expiresAt ?? Date.now() };
|
|
210
|
+
}
|
|
211
|
+
|
|
175
212
|
// src/local.ts
|
|
176
|
-
import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "fs";
|
|
213
|
+
import { chmodSync, existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, renameSync, writeFileSync } from "fs";
|
|
177
214
|
import { dirname as dirname2, isAbsolute, relative, resolve } from "path";
|
|
178
215
|
var GITIGNORE_LINES = [".odla/*.local.json", ".odla/dev-token.json", ".dev.vars"];
|
|
179
216
|
function readJsonFile(path) {
|
|
180
217
|
try {
|
|
181
|
-
return JSON.parse(
|
|
218
|
+
return JSON.parse(readFileSync2(path, "utf8"));
|
|
182
219
|
} catch {
|
|
183
220
|
return null;
|
|
184
221
|
}
|
|
@@ -188,10 +225,10 @@ function writePrivateJson(path, value2) {
|
|
|
188
225
|
`);
|
|
189
226
|
}
|
|
190
227
|
function readCredentials(path) {
|
|
191
|
-
if (!
|
|
228
|
+
if (!existsSync2(path)) return null;
|
|
192
229
|
let value2;
|
|
193
230
|
try {
|
|
194
|
-
value2 = JSON.parse(
|
|
231
|
+
value2 = JSON.parse(readFileSync2(path, "utf8"));
|
|
195
232
|
} catch {
|
|
196
233
|
throw new Error(`credentials file ${path} is not valid JSON; fix or remove it before provisioning`);
|
|
197
234
|
}
|
|
@@ -222,7 +259,7 @@ function mergeCredential(current, update) {
|
|
|
222
259
|
}
|
|
223
260
|
function ensureGitignore(rootDir, localPaths = []) {
|
|
224
261
|
const path = resolve(rootDir, ".gitignore");
|
|
225
|
-
const existing =
|
|
262
|
+
const existing = existsSync2(path) ? readFileSync2(path, "utf8") : "";
|
|
226
263
|
const configured = localPaths.map((localPath) => gitignoreEntry(rootDir, localPath)).filter((line2) => !!line2);
|
|
227
264
|
const wanted = [.../* @__PURE__ */ new Set([...GITIGNORE_LINES, ...configured])];
|
|
228
265
|
const missing = wanted.filter((line2) => !existing.split(/\r?\n/).includes(line2));
|
|
@@ -256,7 +293,7 @@ function writeDevVars(path, credentials, env, o11y) {
|
|
|
256
293
|
if (o11y.version) lines.push(`ODLA_O11Y_VERSION="${o11y.version}"`);
|
|
257
294
|
if (entry.o11yToken) lines.push(`ODLA_O11Y_TOKEN="${entry.o11yToken}"`);
|
|
258
295
|
}
|
|
259
|
-
const existing =
|
|
296
|
+
const existing = existsSync2(path) ? readFileSync2(path, "utf8") : "";
|
|
260
297
|
const retained = existing.split(/\r?\n/).filter((line2) => !isManagedDevVar(line2));
|
|
261
298
|
while (retained.at(-1) === "") retained.pop();
|
|
262
299
|
const prefix = retained.length ? `${retained.join("\n")}
|
|
@@ -306,14 +343,20 @@ async function getDeveloperToken(cfg, options, doFetch, out, grantRequest = {})
|
|
|
306
343
|
const cached = readJsonFile(cfg.local.tokenFile);
|
|
307
344
|
if (!grantRequest.forceReview && !grantRequest.freshLogin) {
|
|
308
345
|
if (options.token) return options.token;
|
|
309
|
-
if (
|
|
310
|
-
const declared =
|
|
346
|
+
if (process6.env.ODLA_DEV_TOKEN) {
|
|
347
|
+
const declared = process6.env.ODLA_DEV_TOKEN_AUDIENCE;
|
|
311
348
|
if (declared) {
|
|
312
349
|
if (platformAudience(declared) !== audience) throw new Error("ODLA_DEV_TOKEN_AUDIENCE does not match the configured platform");
|
|
313
350
|
} else if (audience !== "https://odla.ai") {
|
|
314
351
|
throw new Error("ODLA_DEV_TOKEN_AUDIENCE is required for a non-default platform");
|
|
315
352
|
}
|
|
316
|
-
return
|
|
353
|
+
return process6.env.ODLA_DEV_TOKEN;
|
|
354
|
+
}
|
|
355
|
+
const device = readDeviceCredential(audience);
|
|
356
|
+
if (device) {
|
|
357
|
+
const session = await mintDeviceSession(cfg.platformUrl, device, doFetch);
|
|
358
|
+
out.error(`auth: session minted by this enrolled device (${displayPath(deviceCredentialPath(), cfg.rootDir)})`);
|
|
359
|
+
return session.token;
|
|
317
360
|
}
|
|
318
361
|
if (cached?.token && cached.platform === audience && (cached.expiresAt ?? 0) > Date.now() + 6e4 && cachedGrantCovers(cached, grantIntent)) {
|
|
319
362
|
out.error(`auth: using cached developer token (${displayPath(cfg.local.tokenFile, cfg.rootDir)})`);
|
|
@@ -418,7 +461,7 @@ function stillPending(pending, email) {
|
|
|
418
461
|
);
|
|
419
462
|
}
|
|
420
463
|
function handshakeEmail(value2, cached) {
|
|
421
|
-
const email = (value2 ??
|
|
464
|
+
const email = (value2 ?? process6.env.ODLA_USER_EMAIL ?? cached ?? "").trim().toLowerCase();
|
|
422
465
|
if (/@users\.noreply\.github\.com$/i.test(email)) {
|
|
423
466
|
throw new Error(
|
|
424
467
|
`"${email}" is a GitHub commit identity, not an odla account email; use --email <signed-in-odla-account> or ODLA_USER_EMAIL`
|
|
@@ -449,12 +492,12 @@ function platformAudience(value2) {
|
|
|
449
492
|
}
|
|
450
493
|
|
|
451
494
|
// src/secret-input.ts
|
|
452
|
-
import
|
|
495
|
+
import process7 from "process";
|
|
453
496
|
var MAX_BYTES = 64 * 1024;
|
|
454
497
|
async function secretInputValue(options, kind = "credential") {
|
|
455
498
|
if (options.fromEnv && options.stdin) throw new Error("choose exactly one of --from-env or --stdin");
|
|
456
499
|
let value2;
|
|
457
|
-
if (options.fromEnv) value2 =
|
|
500
|
+
if (options.fromEnv) value2 = process7.env[options.fromEnv];
|
|
458
501
|
else if (options.stdin) value2 = await (options.readStdin ?? (() => readSecretStream(kind)))();
|
|
459
502
|
else throw new Error(`${kind} input required: use --from-env <NAME> or --stdin; values are never accepted as arguments`);
|
|
460
503
|
value2 = value2?.replace(/[\r\n]+$/, "");
|
|
@@ -462,7 +505,7 @@ async function secretInputValue(options, kind = "credential") {
|
|
|
462
505
|
if (new TextEncoder().encode(value2).byteLength > MAX_BYTES) throw new Error(`${kind} exceeds 64 KiB`);
|
|
463
506
|
return value2;
|
|
464
507
|
}
|
|
465
|
-
async function readSecretStream(kind, stream =
|
|
508
|
+
async function readSecretStream(kind, stream = process7.stdin) {
|
|
466
509
|
let value2 = "";
|
|
467
510
|
for await (const chunk of stream) {
|
|
468
511
|
value2 += String(chunk);
|
|
@@ -472,9 +515,9 @@ async function readSecretStream(kind, stream = process6.stdin) {
|
|
|
472
515
|
}
|
|
473
516
|
|
|
474
517
|
// src/admin-ai-auth.ts
|
|
475
|
-
import { existsSync as
|
|
476
|
-
import { join as
|
|
477
|
-
import
|
|
518
|
+
import { existsSync as existsSync3 } from "fs";
|
|
519
|
+
import { join as join3 } from "path";
|
|
520
|
+
import process8 from "process";
|
|
478
521
|
import { requestToken as requestToken2 } from "@odla-ai/db";
|
|
479
522
|
async function getScopedPlatformToken(options) {
|
|
480
523
|
return resolveAdminPlatformToken(options);
|
|
@@ -482,7 +525,7 @@ async function getScopedPlatformToken(options) {
|
|
|
482
525
|
async function resolveAdminPlatformToken(options) {
|
|
483
526
|
const audience = platformAudience(options.platform);
|
|
484
527
|
if (options.token) return options.token;
|
|
485
|
-
const fromEnv =
|
|
528
|
+
const fromEnv = process8.env.ODLA_ADMIN_TOKEN;
|
|
486
529
|
if (fromEnv) return audienceBoundEnvToken(fromEnv, audience);
|
|
487
530
|
return scopedToken(
|
|
488
531
|
audience,
|
|
@@ -494,7 +537,7 @@ async function resolveAdminPlatformToken(options) {
|
|
|
494
537
|
}
|
|
495
538
|
function audienceBoundEnvToken(token, platform) {
|
|
496
539
|
const audience = platformAudience(platform);
|
|
497
|
-
const declared =
|
|
540
|
+
const declared = process8.env.ODLA_ADMIN_TOKEN_AUDIENCE;
|
|
498
541
|
if (declared) {
|
|
499
542
|
if (platformAudience(declared) !== audience) throw new Error("ODLA_ADMIN_TOKEN_AUDIENCE does not match the configured platform");
|
|
500
543
|
} else if (audience !== "https://odla.ai") {
|
|
@@ -507,6 +550,7 @@ var SCOPE_PURPOSE = {
|
|
|
507
550
|
"app:config:read": "compare checked-in intent with an exact-id app Registry configuration",
|
|
508
551
|
"app:config:write": "apply or inspect one revision-bound configuration operation for an app you own",
|
|
509
552
|
"platform:runbook:write": "read and edit all of odla's operational runbooks, including admin-visible content",
|
|
553
|
+
"app:device:enroll": "enrol this machine so it can mint its own short-lived credentials without asking you again",
|
|
510
554
|
"platform:ai:policy:write": "change System AI model routing",
|
|
511
555
|
"platform:ai:policy:read": "read System AI model routing",
|
|
512
556
|
"platform:ai:credential:write": "replace a stored AI provider key",
|
|
@@ -519,8 +563,8 @@ var SCOPE_PURPOSE = {
|
|
|
519
563
|
};
|
|
520
564
|
async function scopedToken(platform, scope, options, doFetch, out) {
|
|
521
565
|
const audience = platformAudience(platform);
|
|
522
|
-
const rootDir = options.rootDir ??
|
|
523
|
-
const tokenFile = options.tokenFile ??
|
|
566
|
+
const rootDir = options.rootDir ?? process8.cwd();
|
|
567
|
+
const tokenFile = options.tokenFile ?? join3(rootDir, ".odla/admin-token.local.json");
|
|
524
568
|
const cache2 = options.cache === false ? null : readJsonFile(tokenFile);
|
|
525
569
|
const cached = cache2?.platform === audience ? cache2.tokens?.[scope] : void 0;
|
|
526
570
|
if (cached?.token && (cached.expiresAt ?? 0) > Date.now() + 6e4) {
|
|
@@ -547,7 +591,7 @@ async function scopedToken(platform, scope, options, doFetch, out) {
|
|
|
547
591
|
if (options.cache !== false) {
|
|
548
592
|
const tokens = cache2?.platform === audience ? { ...cache2.tokens ?? {} } : {};
|
|
549
593
|
tokens[scope] = { token, expiresAt };
|
|
550
|
-
if (
|
|
594
|
+
if (existsSync3(join3(rootDir, ".git"))) ensureGitignore(rootDir, [tokenFile]);
|
|
551
595
|
writePrivateJson(tokenFile, { platform: audience, email, tokens });
|
|
552
596
|
out.error(`auth: cached ${scope} grant (${tokenFile}; mode 0600)`);
|
|
553
597
|
} else {
|
|
@@ -724,7 +768,7 @@ function isRecord2(value2) {
|
|
|
724
768
|
|
|
725
769
|
// src/admin-ai.ts
|
|
726
770
|
async function adminAi(options) {
|
|
727
|
-
const platform = platformAudience(options.platform ??
|
|
771
|
+
const platform = platformAudience(options.platform ?? process9.env.ODLA_PLATFORM ?? "https://odla.ai");
|
|
728
772
|
const doFetch = options.fetch ?? fetch;
|
|
729
773
|
const out = options.stdout ?? console;
|
|
730
774
|
const usageQuery = options.action === "usage" ? adminAiUsageQuery(options) : void 0;
|
|
@@ -979,12 +1023,12 @@ function addOption(options, name, value2) {
|
|
|
979
1023
|
}
|
|
980
1024
|
|
|
981
1025
|
// src/operator-context.ts
|
|
982
|
-
import { existsSync as
|
|
983
|
-
import { join as
|
|
984
|
-
import
|
|
1026
|
+
import { existsSync as existsSync6 } from "fs";
|
|
1027
|
+
import { join as join5, resolve as resolve4 } from "path";
|
|
1028
|
+
import process11 from "process";
|
|
985
1029
|
|
|
986
1030
|
// src/config.ts
|
|
987
|
-
import { existsSync as
|
|
1031
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
|
|
988
1032
|
import { dirname as dirname3, isAbsolute as isAbsolute2, resolve as resolve2 } from "path";
|
|
989
1033
|
import { pathToFileURL } from "url";
|
|
990
1034
|
import { appServiceDefinition, appServiceIds } from "@odla-ai/apps";
|
|
@@ -1391,7 +1435,7 @@ var configImportSerial = 0;
|
|
|
1391
1435
|
var GOOGLE_CALENDAR_EVENTS_SCOPE = "https://www.googleapis.com/auth/calendar.events";
|
|
1392
1436
|
async function loadProjectConfig(configPath = "odla.config.mjs", options = {}) {
|
|
1393
1437
|
const resolved = resolve2(configPath);
|
|
1394
|
-
if (!
|
|
1438
|
+
if (!existsSync4(resolved)) {
|
|
1395
1439
|
throw new Error(`config not found: ${configPath}. Run "odla-ai init" first or pass --config.`);
|
|
1396
1440
|
}
|
|
1397
1441
|
const raw = await loadConfigModule(resolved);
|
|
@@ -1426,7 +1470,7 @@ async function resolveDataExport(cfg, value2, names) {
|
|
|
1426
1470
|
if (typeof value2 !== "string") return value2;
|
|
1427
1471
|
const target = isAbsolute2(value2) ? value2 : resolve2(cfg.rootDir, value2);
|
|
1428
1472
|
if (target.endsWith(".json")) {
|
|
1429
|
-
return JSON.parse(
|
|
1473
|
+
return JSON.parse(readFileSync3(target, "utf8"));
|
|
1430
1474
|
}
|
|
1431
1475
|
const mod = await import(pathToFileURL(target).href);
|
|
1432
1476
|
for (const name of names) {
|
|
@@ -1502,7 +1546,7 @@ function validId2(value2) {
|
|
|
1502
1546
|
return typeof value2 === "string" && /^[a-z0-9][a-z0-9-]*$/.test(value2);
|
|
1503
1547
|
}
|
|
1504
1548
|
async function loadConfigModule(path) {
|
|
1505
|
-
if (path.endsWith(".json")) return JSON.parse(
|
|
1549
|
+
if (path.endsWith(".json")) return JSON.parse(readFileSync3(path, "utf8"));
|
|
1506
1550
|
const nonce = `${Date.now()}-${configImportSerial++}`;
|
|
1507
1551
|
const mod = await import(`${pathToFileURL(path).href}?reload=${nonce}`);
|
|
1508
1552
|
const value2 = mod.default ?? mod.config;
|
|
@@ -1517,18 +1561,18 @@ function unique3(values) {
|
|
|
1517
1561
|
}
|
|
1518
1562
|
|
|
1519
1563
|
// src/operator-profiles.ts
|
|
1520
|
-
import { existsSync as
|
|
1521
|
-
import { homedir } from "os";
|
|
1522
|
-
import { dirname as dirname4, join as
|
|
1523
|
-
import
|
|
1564
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
|
|
1565
|
+
import { homedir as homedir2 } from "os";
|
|
1566
|
+
import { dirname as dirname4, join as join4, resolve as resolve3 } from "path";
|
|
1567
|
+
import process10 from "process";
|
|
1524
1568
|
function operatorProfileFile() {
|
|
1525
1569
|
return resolve3(
|
|
1526
|
-
clean(
|
|
1570
|
+
clean(process10.env.ODLA_CONTEXT_FILE) ?? join4(homedir2(), ".odla", "contexts.json")
|
|
1527
1571
|
);
|
|
1528
1572
|
}
|
|
1529
1573
|
function resolveOperatorProfile(parsed) {
|
|
1530
1574
|
const fromFlag = clean(stringOpt(parsed.options.context));
|
|
1531
|
-
const fromEnvironment = clean(
|
|
1575
|
+
const fromEnvironment = clean(process10.env.ODLA_CONTEXT);
|
|
1532
1576
|
const name = fromFlag ?? fromEnvironment ?? null;
|
|
1533
1577
|
const file = operatorProfileFile();
|
|
1534
1578
|
if (!name) {
|
|
@@ -1568,10 +1612,10 @@ function removeOperatorProfile(name, file = operatorProfileFile()) {
|
|
|
1568
1612
|
return true;
|
|
1569
1613
|
}
|
|
1570
1614
|
function operatorCredentialFiles(selection) {
|
|
1571
|
-
const base = selection.name ?
|
|
1615
|
+
const base = selection.name ? join4(dirname4(selection.file), "profiles", selection.name) : join4(homedir2(), ".odla");
|
|
1572
1616
|
return {
|
|
1573
|
-
developer:
|
|
1574
|
-
scoped:
|
|
1617
|
+
developer: join4(base, "dev-token.json"),
|
|
1618
|
+
scoped: join4(base, "admin-token.local.json")
|
|
1575
1619
|
};
|
|
1576
1620
|
}
|
|
1577
1621
|
function assertOperatorName(value2, label) {
|
|
@@ -1582,10 +1626,10 @@ function assertOperatorName(value2, label) {
|
|
|
1582
1626
|
}
|
|
1583
1627
|
}
|
|
1584
1628
|
function readOperatorProfiles(file) {
|
|
1585
|
-
if (!
|
|
1629
|
+
if (!existsSync5(file)) return emptyProfiles();
|
|
1586
1630
|
let raw;
|
|
1587
1631
|
try {
|
|
1588
|
-
raw = JSON.parse(
|
|
1632
|
+
raw = JSON.parse(readFileSync4(file, "utf8"));
|
|
1589
1633
|
} catch {
|
|
1590
1634
|
throw new Error(`operator context file ${file} is not valid JSON`);
|
|
1591
1635
|
}
|
|
@@ -1651,19 +1695,19 @@ async function resolveOperatorContext(parsed, options = {}) {
|
|
|
1651
1695
|
const configArgument = stringOpt(parsed.options.config) ?? "odla.config.mjs";
|
|
1652
1696
|
const configPath = resolve4(configArgument);
|
|
1653
1697
|
const explicitConfig = parsed.options.config !== void 0;
|
|
1654
|
-
const hasConfig =
|
|
1698
|
+
const hasConfig = existsSync6(configPath);
|
|
1655
1699
|
if (!hasConfig && (!options.allowMissingConfig || explicitConfig)) {
|
|
1656
1700
|
await loadProjectConfig(configArgument);
|
|
1657
1701
|
}
|
|
1658
1702
|
const loaded = hasConfig ? await loadProjectConfig(configArgument) : void 0;
|
|
1659
1703
|
const platformFlag = clean2(stringOpt(parsed.options.platform));
|
|
1660
|
-
const platformEnvironment = clean2(
|
|
1704
|
+
const platformEnvironment = clean2(process11.env.ODLA_PLATFORM_URL);
|
|
1661
1705
|
const platformValue = platformAudience(
|
|
1662
1706
|
platformFlag ?? platformEnvironment ?? profile.value?.platform ?? loaded?.platformUrl ?? DEFAULT_PLATFORM2
|
|
1663
1707
|
);
|
|
1664
1708
|
const platformSource = platformFlag ? "flag" : platformEnvironment ? "environment" : profile.value ? "profile" : loaded ? "config" : "default";
|
|
1665
1709
|
const appFlag = clean2(stringOpt(parsed.options.app));
|
|
1666
|
-
const appEnvironment = clean2(
|
|
1710
|
+
const appEnvironment = clean2(process11.env.ODLA_APP_ID);
|
|
1667
1711
|
const appValue = appFlag ?? appEnvironment ?? profile.value?.app ?? loaded?.app.id ?? null;
|
|
1668
1712
|
const appSource = appFlag ? "flag" : appEnvironment ? "environment" : profile.value?.app ? "profile" : loaded ? "config" : "unresolved";
|
|
1669
1713
|
if (appValue) assertOperatorName(appValue, "app");
|
|
@@ -1673,16 +1717,16 @@ async function resolveOperatorContext(parsed, options = {}) {
|
|
|
1673
1717
|
);
|
|
1674
1718
|
}
|
|
1675
1719
|
const envFlag = clean2(stringOpt(parsed.options.env));
|
|
1676
|
-
const envEnvironment = clean2(
|
|
1720
|
+
const envEnvironment = clean2(process11.env.ODLA_ENV);
|
|
1677
1721
|
const environmentValue = envFlag ?? envEnvironment ?? profile.value?.environment ?? options.defaultEnvironment ?? null;
|
|
1678
1722
|
const environmentSource = envFlag ? "flag" : envEnvironment ? "environment" : profile.value?.environment ? "profile" : options.defaultEnvironment ? "default" : "unresolved";
|
|
1679
1723
|
if (environmentValue) {
|
|
1680
1724
|
assertOperatorName(environmentValue, "environment");
|
|
1681
1725
|
}
|
|
1682
|
-
const rootDir = loaded?.rootDir ??
|
|
1726
|
+
const rootDir = loaded?.rootDir ?? process11.cwd();
|
|
1683
1727
|
const profileCredentials = operatorCredentialFiles(profile);
|
|
1684
|
-
const tokenFile = clean2(
|
|
1685
|
-
const scopedTokenFile = clean2(
|
|
1728
|
+
const tokenFile = clean2(process11.env.ODLA_DEV_TOKEN_FILE) ? resolve4(process11.env.ODLA_DEV_TOKEN_FILE) : profile.name ? profileCredentials.developer : loaded?.local.tokenFile ?? profileCredentials.developer;
|
|
1729
|
+
const scopedTokenFile = clean2(process11.env.ODLA_ADMIN_TOKEN_FILE) ? resolve4(process11.env.ODLA_ADMIN_TOKEN_FILE) : profile.name ? profileCredentials.scoped : loaded ? join5(loaded.rootDir, ".odla", "admin-token.local.json") : profileCredentials.scoped;
|
|
1686
1730
|
const cfg = loaded ? {
|
|
1687
1731
|
...loaded,
|
|
1688
1732
|
platformUrl: platformValue,
|
|
@@ -1704,8 +1748,8 @@ async function resolveOperatorContext(parsed, options = {}) {
|
|
|
1704
1748
|
services: [],
|
|
1705
1749
|
local: {
|
|
1706
1750
|
tokenFile,
|
|
1707
|
-
credentialsFile:
|
|
1708
|
-
devVarsFile:
|
|
1751
|
+
credentialsFile: join5(rootDir, ".odla", "credentials.local.json"),
|
|
1752
|
+
devVarsFile: join5(rootDir, ".dev.vars"),
|
|
1709
1753
|
gitignore: true
|
|
1710
1754
|
}
|
|
1711
1755
|
};
|
|
@@ -1802,7 +1846,7 @@ async function adminCommand(parsed, deps = {}) {
|
|
|
1802
1846
|
}
|
|
1803
1847
|
|
|
1804
1848
|
// src/auth-command.ts
|
|
1805
|
-
import
|
|
1849
|
+
import process12 from "process";
|
|
1806
1850
|
|
|
1807
1851
|
// src/whoami-command.ts
|
|
1808
1852
|
var text2 = (value2) => typeof value2 === "string" && value2.trim() ? value2.trim() : null;
|
|
@@ -1964,7 +2008,7 @@ async function authCommand(parsed, deps = {}) {
|
|
|
1964
2008
|
const { cfg } = context;
|
|
1965
2009
|
const out = deps.stdout ?? console;
|
|
1966
2010
|
const doFetch = deps.fetch ?? fetch;
|
|
1967
|
-
const email = stringOpt(parsed.options.email) ??
|
|
2011
|
+
const email = stringOpt(parsed.options.email) ?? process12.env.ODLA_USER_EMAIL?.trim();
|
|
1968
2012
|
if (!email) {
|
|
1969
2013
|
throw new Error(
|
|
1970
2014
|
"auth login requires --email <odla-account> or ODLA_USER_EMAIL; confirm the signed-in odla email instead of using git or GitHub identity"
|
|
@@ -2117,7 +2161,7 @@ async function appExport(options) {
|
|
|
2117
2161
|
}
|
|
2118
2162
|
|
|
2119
2163
|
// src/app-import.ts
|
|
2120
|
-
import { readFileSync as
|
|
2164
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
2121
2165
|
import {
|
|
2122
2166
|
buildImportOps,
|
|
2123
2167
|
parseImport,
|
|
@@ -2139,7 +2183,7 @@ async function appImport(options) {
|
|
|
2139
2183
|
const out = options.stdout ?? console;
|
|
2140
2184
|
const say = options.json ? (line2) => out.error(line2) : (line2) => out.log(line2);
|
|
2141
2185
|
const { tenant } = resolveTenant(cfg, options.env);
|
|
2142
|
-
const text3 = options.file === "-" ? (options.readStdin ?? (() =>
|
|
2186
|
+
const text3 = options.file === "-" ? (options.readStdin ?? (() => readFileSync5(0, "utf8")))() : readFileSync5(options.file, "utf8");
|
|
2143
2187
|
const { format, sources } = parseImport(text3, options.ns);
|
|
2144
2188
|
if (format === "namespace-map" && options.ns) {
|
|
2145
2189
|
throw new Error("--ns cannot be combined with a {namespace: rows} file \u2014 the file already names each namespace");
|
|
@@ -3010,7 +3054,7 @@ import {
|
|
|
3010
3054
|
AppsError,
|
|
3011
3055
|
createAppsClient
|
|
3012
3056
|
} from "@odla-ai/apps";
|
|
3013
|
-
import { join as
|
|
3057
|
+
import { join as join6 } from "path";
|
|
3014
3058
|
|
|
3015
3059
|
// src/config-operation-error.ts
|
|
3016
3060
|
var ConfigOperationCommandError = class extends Error {
|
|
@@ -3026,7 +3070,7 @@ var ConfigOperationCommandError = class extends Error {
|
|
|
3026
3070
|
import {
|
|
3027
3071
|
appServiceDefinition as appServiceDefinition2
|
|
3028
3072
|
} from "@odla-ai/apps";
|
|
3029
|
-
import { readFileSync as
|
|
3073
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
3030
3074
|
|
|
3031
3075
|
// src/config-reconcile-digest.ts
|
|
3032
3076
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -3062,7 +3106,7 @@ var SERVICE = /^[a-z][a-z0-9-]{0,39}$/;
|
|
|
3062
3106
|
function readPlan(path) {
|
|
3063
3107
|
let value2;
|
|
3064
3108
|
try {
|
|
3065
|
-
const raw =
|
|
3109
|
+
const raw = readFileSync6(path, "utf8");
|
|
3066
3110
|
if (Buffer.byteLength(raw) > 128 * 1024) throw new Error("plan exceeds 128 KiB");
|
|
3067
3111
|
value2 = JSON.parse(raw);
|
|
3068
3112
|
} catch (error) {
|
|
@@ -3435,7 +3479,7 @@ async function operationClient(cfg, options, purpose) {
|
|
|
3435
3479
|
platform: cfg.platformUrl,
|
|
3436
3480
|
scope: "app:config:write",
|
|
3437
3481
|
token: options.token,
|
|
3438
|
-
tokenFile:
|
|
3482
|
+
tokenFile: join6(cfg.rootDir, ".odla", "admin-token.local.json"),
|
|
3439
3483
|
rootDir: cfg.rootDir,
|
|
3440
3484
|
email: options.email,
|
|
3441
3485
|
open: options.open,
|
|
@@ -3490,7 +3534,7 @@ function record4(value2) {
|
|
|
3490
3534
|
|
|
3491
3535
|
// src/config-reconcile-command.ts
|
|
3492
3536
|
import { createAppsClient as createAppsClient2, studioAppSettingsPath } from "@odla-ai/apps";
|
|
3493
|
-
import { join as
|
|
3537
|
+
import { join as join7 } from "path";
|
|
3494
3538
|
|
|
3495
3539
|
// src/config-reconcile.ts
|
|
3496
3540
|
import { appServiceIds as appServiceIds2, orderAppServices as orderAppServices2 } from "@odla-ai/apps";
|
|
@@ -3786,7 +3830,7 @@ async function inspectConfig(options) {
|
|
|
3786
3830
|
platform: cfg.platformUrl,
|
|
3787
3831
|
scope: "app:config:read",
|
|
3788
3832
|
token: options.token,
|
|
3789
|
-
tokenFile:
|
|
3833
|
+
tokenFile: join7(cfg.rootDir, ".odla", "admin-token.local.json"),
|
|
3790
3834
|
rootDir: cfg.rootDir,
|
|
3791
3835
|
email: options.email,
|
|
3792
3836
|
open: options.open,
|
|
@@ -3918,13 +3962,13 @@ function quoteArg2(value2) {
|
|
|
3918
3962
|
|
|
3919
3963
|
// src/doctor-checks.ts
|
|
3920
3964
|
import { execFileSync } from "child_process";
|
|
3921
|
-
import { existsSync as
|
|
3922
|
-
import { join as
|
|
3965
|
+
import { existsSync as existsSync8, readFileSync as readFileSync8 } from "fs";
|
|
3966
|
+
import { join as join9, resolve as resolve6 } from "path";
|
|
3923
3967
|
|
|
3924
3968
|
// src/wrangler.ts
|
|
3925
3969
|
import { spawn as spawn2 } from "child_process";
|
|
3926
|
-
import { existsSync as
|
|
3927
|
-
import { join as
|
|
3970
|
+
import { existsSync as existsSync7, readFileSync as readFileSync7 } from "fs";
|
|
3971
|
+
import { join as join8 } from "path";
|
|
3928
3972
|
var defaultRunner = (cmd, args, opts) => new Promise((resolvePromise, reject) => {
|
|
3929
3973
|
const child = spawn2(cmd, args, { cwd: opts?.cwd, stdio: ["pipe", "pipe", "pipe"] });
|
|
3930
3974
|
let stdout = "";
|
|
@@ -3938,15 +3982,15 @@ var defaultRunner = (cmd, args, opts) => new Promise((resolvePromise, reject) =>
|
|
|
3938
3982
|
var WRANGLER_CONFIG_FILES = ["wrangler.jsonc", "wrangler.json", "wrangler.toml"];
|
|
3939
3983
|
function findWranglerConfig(rootDir) {
|
|
3940
3984
|
for (const name of WRANGLER_CONFIG_FILES) {
|
|
3941
|
-
const path =
|
|
3942
|
-
if (
|
|
3985
|
+
const path = join8(rootDir, name);
|
|
3986
|
+
if (existsSync7(path)) return path;
|
|
3943
3987
|
}
|
|
3944
3988
|
return null;
|
|
3945
3989
|
}
|
|
3946
3990
|
function readWranglerConfig(path) {
|
|
3947
3991
|
if (path.endsWith(".toml")) return null;
|
|
3948
3992
|
try {
|
|
3949
|
-
return JSON.parse(stripJsonComments(
|
|
3993
|
+
return JSON.parse(stripJsonComments(readFileSync7(path, "utf8")));
|
|
3950
3994
|
} catch {
|
|
3951
3995
|
return null;
|
|
3952
3996
|
}
|
|
@@ -4099,7 +4143,7 @@ function wranglerWarnings(rootDir) {
|
|
|
4099
4143
|
const dir = resolve6(rootDir, assets.directory);
|
|
4100
4144
|
if (dir === resolve6(rootDir)) {
|
|
4101
4145
|
warnings.push(`${label}assets.directory is the project root \u2014 point it at a dedicated build dir (wrangler dev fails with "spawn EBADF")`);
|
|
4102
|
-
} else if (
|
|
4146
|
+
} else if (existsSync8(join9(dir, "node_modules"))) {
|
|
4103
4147
|
warnings.push(`${label}assets.directory contains node_modules \u2014 wrangler dev's watcher will exhaust file descriptors`);
|
|
4104
4148
|
}
|
|
4105
4149
|
}
|
|
@@ -4135,12 +4179,12 @@ function o11yProjectWarnings(rootDir) {
|
|
|
4135
4179
|
return warnings;
|
|
4136
4180
|
}
|
|
4137
4181
|
const main = typeof config.main === "string" ? resolve6(rootDir, config.main) : null;
|
|
4138
|
-
if (!main || !
|
|
4182
|
+
if (!main || !existsSync8(main)) {
|
|
4139
4183
|
warnings.push("cannot verify o11y Worker instrumentation \u2014 wrangler main is missing or unreadable");
|
|
4140
4184
|
} else {
|
|
4141
4185
|
let source = "";
|
|
4142
4186
|
try {
|
|
4143
|
-
source =
|
|
4187
|
+
source = readFileSync8(main, "utf8");
|
|
4144
4188
|
} catch {
|
|
4145
4189
|
}
|
|
4146
4190
|
if (!/\bwithObservability\b/.test(source)) {
|
|
@@ -4164,7 +4208,7 @@ function calendarProjectWarnings(rootDir) {
|
|
|
4164
4208
|
}
|
|
4165
4209
|
function readPackageJson(rootDir) {
|
|
4166
4210
|
try {
|
|
4167
|
-
return JSON.parse(
|
|
4211
|
+
return JSON.parse(readFileSync8(join9(rootDir, "package.json"), "utf8"));
|
|
4168
4212
|
} catch {
|
|
4169
4213
|
return null;
|
|
4170
4214
|
}
|
|
@@ -4458,14 +4502,14 @@ function harnessOption(value2, flag) {
|
|
|
4458
4502
|
}
|
|
4459
4503
|
|
|
4460
4504
|
// src/init.ts
|
|
4461
|
-
import { existsSync as
|
|
4505
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
4462
4506
|
import { dirname as dirname6, resolve as resolve7 } from "path";
|
|
4463
4507
|
import { appServiceDefinition as appServiceDefinition3, appServiceIds as appServiceIds3 } from "@odla-ai/apps";
|
|
4464
4508
|
function initProject(options) {
|
|
4465
4509
|
const out = options.stdout ?? console;
|
|
4466
4510
|
const rootDir = resolve7(options.rootDir ?? process.cwd());
|
|
4467
4511
|
const configPath = resolve7(rootDir, options.configPath ?? "odla.config.mjs");
|
|
4468
|
-
if (
|
|
4512
|
+
if (existsSync9(configPath) && !options.force) {
|
|
4469
4513
|
throw new Error(`${configPath} already exists. Pass --force to overwrite.`);
|
|
4470
4514
|
}
|
|
4471
4515
|
if (!/^[a-z0-9][a-z0-9-]*$/.test(options.appId)) {
|
|
@@ -4493,7 +4537,7 @@ function initProject(options) {
|
|
|
4493
4537
|
out.log("updated .gitignore for local odla credentials");
|
|
4494
4538
|
}
|
|
4495
4539
|
function writeIfMissing(path, text3) {
|
|
4496
|
-
if (
|
|
4540
|
+
if (existsSync9(path)) return;
|
|
4497
4541
|
writeFileSync2(path, text3);
|
|
4498
4542
|
}
|
|
4499
4543
|
function configTemplate(input) {
|
|
@@ -4790,9 +4834,9 @@ function printReport(report5, out) {
|
|
|
4790
4834
|
}
|
|
4791
4835
|
|
|
4792
4836
|
// src/skill.ts
|
|
4793
|
-
import { existsSync as
|
|
4794
|
-
import { homedir as
|
|
4795
|
-
import { dirname as dirname7, isAbsolute as isAbsolute3, join as
|
|
4837
|
+
import { existsSync as existsSync10, lstatSync, mkdirSync as mkdirSync3, readFileSync as readFileSync9, readdirSync, writeFileSync as writeFileSync3 } from "fs";
|
|
4838
|
+
import { homedir as homedir3 } from "os";
|
|
4839
|
+
import { dirname as dirname7, isAbsolute as isAbsolute3, join as join10, relative as relative2, resolve as resolve8, sep } from "path";
|
|
4796
4840
|
import { fileURLToPath } from "url";
|
|
4797
4841
|
|
|
4798
4842
|
// src/skill-adapters.ts
|
|
@@ -4892,7 +4936,7 @@ function installSkill(options = {}) {
|
|
|
4892
4936
|
if (files.length === 0) throw new Error(`no bundled skills found at ${sourceDir}`);
|
|
4893
4937
|
const harnesses = normalizeHarnesses(options.harnesses, options.global === true);
|
|
4894
4938
|
const root = resolve8(options.dir ?? process.cwd());
|
|
4895
|
-
const home = resolve8(options.homeDir ??
|
|
4939
|
+
const home = resolve8(options.homeDir ?? homedir3());
|
|
4896
4940
|
const plans = /* @__PURE__ */ new Map();
|
|
4897
4941
|
const targets = /* @__PURE__ */ new Map();
|
|
4898
4942
|
const rememberTarget = (harness, target) => {
|
|
@@ -4906,12 +4950,12 @@ function installSkill(options = {}) {
|
|
|
4906
4950
|
plans.set(target, { target, content: content2, boundary, managedMerge });
|
|
4907
4951
|
};
|
|
4908
4952
|
const planSkillTree = (targetDir2, boundary = root) => {
|
|
4909
|
-
for (const rel of files) plan(
|
|
4953
|
+
for (const rel of files) plan(join10(targetDir2, rel), readFileSync9(join10(sourceDir, rel), "utf8"), false, boundary);
|
|
4910
4954
|
};
|
|
4911
4955
|
let targetDir;
|
|
4912
4956
|
if (options.global) {
|
|
4913
|
-
const claudeRoot =
|
|
4914
|
-
const codexRoot = resolve8(options.codexHomeDir ?? process.env.CODEX_HOME ??
|
|
4957
|
+
const claudeRoot = join10(home, ".claude", "skills");
|
|
4958
|
+
const codexRoot = resolve8(options.codexHomeDir ?? process.env.CODEX_HOME ?? join10(home, ".codex"), "skills");
|
|
4915
4959
|
targetDir = harnesses[0] === "codex" ? codexRoot : claudeRoot;
|
|
4916
4960
|
for (const harness of harnesses) {
|
|
4917
4961
|
const skillRoot = harness === "claude" ? claudeRoot : codexRoot;
|
|
@@ -4919,35 +4963,35 @@ function installSkill(options = {}) {
|
|
|
4919
4963
|
rememberTarget(harness, skillRoot);
|
|
4920
4964
|
}
|
|
4921
4965
|
} else {
|
|
4922
|
-
const sharedRoot =
|
|
4966
|
+
const sharedRoot = join10(root, ".agents", "skills");
|
|
4923
4967
|
planSkillTree(sharedRoot);
|
|
4924
|
-
const claudeRoot =
|
|
4968
|
+
const claudeRoot = join10(root, ".claude", "skills");
|
|
4925
4969
|
targetDir = harnesses.includes("claude") ? claudeRoot : sharedRoot;
|
|
4926
4970
|
for (const harness of harnesses) rememberTarget(harness, sharedRoot);
|
|
4927
4971
|
if (harnesses.includes("claude")) {
|
|
4928
4972
|
for (const skill of skillNames(files)) {
|
|
4929
|
-
const canonical2 =
|
|
4930
|
-
plan(
|
|
4973
|
+
const canonical2 = readFileSync9(join10(sourceDir, skill, "SKILL.md"), "utf8");
|
|
4974
|
+
plan(join10(claudeRoot, skill, "SKILL.md"), claudeAdapter(skill, canonical2));
|
|
4931
4975
|
}
|
|
4932
4976
|
rememberTarget("claude", claudeRoot);
|
|
4933
4977
|
}
|
|
4934
4978
|
if (harnesses.includes("cursor")) {
|
|
4935
|
-
const cursorRule =
|
|
4979
|
+
const cursorRule = join10(root, ".cursor", "rules", "odla.mdc");
|
|
4936
4980
|
plan(cursorRule, CURSOR_RULE);
|
|
4937
4981
|
rememberTarget("cursor", cursorRule);
|
|
4938
4982
|
}
|
|
4939
4983
|
if (harnesses.includes("agents")) {
|
|
4940
|
-
const agentsFile =
|
|
4984
|
+
const agentsFile = join10(root, "AGENTS.md");
|
|
4941
4985
|
plan(agentsFile, managedFileContent(agentsFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
|
|
4942
4986
|
rememberTarget("agents", agentsFile);
|
|
4943
4987
|
}
|
|
4944
4988
|
if (harnesses.includes("copilot")) {
|
|
4945
|
-
const copilotFile =
|
|
4989
|
+
const copilotFile = join10(root, ".github", "copilot-instructions.md");
|
|
4946
4990
|
plan(copilotFile, managedFileContent(copilotFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
|
|
4947
4991
|
rememberTarget("copilot", copilotFile);
|
|
4948
4992
|
}
|
|
4949
4993
|
if (harnesses.includes("gemini")) {
|
|
4950
|
-
const geminiFile =
|
|
4994
|
+
const geminiFile = join10(root, "GEMINI.md");
|
|
4951
4995
|
plan(geminiFile, managedFileContent(geminiFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
|
|
4952
4996
|
rememberTarget("gemini", geminiFile);
|
|
4953
4997
|
}
|
|
@@ -4961,11 +5005,11 @@ function installSkill(options = {}) {
|
|
|
4961
5005
|
conflicts.push(`${file.target} (redirected by symbolic link ${symlink})`);
|
|
4962
5006
|
continue;
|
|
4963
5007
|
}
|
|
4964
|
-
if (!
|
|
5008
|
+
if (!existsSync10(file.target)) {
|
|
4965
5009
|
writtenPaths.add(file.target);
|
|
4966
5010
|
continue;
|
|
4967
5011
|
}
|
|
4968
|
-
const current =
|
|
5012
|
+
const current = readFileSync9(file.target, "utf8");
|
|
4969
5013
|
if (current === file.content) {
|
|
4970
5014
|
unchangedPaths.add(file.target);
|
|
4971
5015
|
} else if (file.managedMerge || options.force) {
|
|
@@ -4982,7 +5026,7 @@ ${conflicts.map((f) => ` - ${f}`).join("\n")}`
|
|
|
4982
5026
|
);
|
|
4983
5027
|
}
|
|
4984
5028
|
for (const file of plans.values()) {
|
|
4985
|
-
if (!
|
|
5029
|
+
if (!existsSync10(file.target) || readFileSync9(file.target, "utf8") !== file.content) {
|
|
4986
5030
|
mkdirSync3(dirname7(file.target), { recursive: true });
|
|
4987
5031
|
writeFileSync3(file.target, file.content);
|
|
4988
5032
|
}
|
|
@@ -5025,9 +5069,9 @@ function normalizeHarnesses(values, global) {
|
|
|
5025
5069
|
function managedFileContent(path, block, force, boundary) {
|
|
5026
5070
|
const symlink = symlinkedComponent(boundary, path);
|
|
5027
5071
|
if (symlink) throw new Error(`refusing to manage ${path}: symbolic link component ${symlink}`);
|
|
5028
|
-
if (!
|
|
5072
|
+
if (!existsSync10(path)) return `${block}
|
|
5029
5073
|
`;
|
|
5030
|
-
const current =
|
|
5074
|
+
const current = readFileSync9(path, "utf8");
|
|
5031
5075
|
const start = "<!-- odla-ai agent setup:start -->";
|
|
5032
5076
|
const end = "<!-- odla-ai agent setup:end -->";
|
|
5033
5077
|
const startAt = current.indexOf(start);
|
|
@@ -5054,7 +5098,7 @@ function symlinkedComponent(boundary, target) {
|
|
|
5054
5098
|
}
|
|
5055
5099
|
let current = boundary;
|
|
5056
5100
|
for (const part of rel.split(sep).filter(Boolean)) {
|
|
5057
|
-
current =
|
|
5101
|
+
current = join10(current, part);
|
|
5058
5102
|
try {
|
|
5059
5103
|
if (lstatSync(current).isSymbolicLink()) return current;
|
|
5060
5104
|
} catch (error) {
|
|
@@ -5067,11 +5111,11 @@ function skillNames(files) {
|
|
|
5067
5111
|
return [...new Set(files.filter((file) => /(^|[\\/])SKILL\.md$/.test(file)).map((file) => file.split(/[\\/]/)[0]))].sort();
|
|
5068
5112
|
}
|
|
5069
5113
|
function listFiles(dir) {
|
|
5070
|
-
if (!
|
|
5114
|
+
if (!existsSync10(dir)) return [];
|
|
5071
5115
|
const results = [];
|
|
5072
5116
|
const walk = (current) => {
|
|
5073
5117
|
for (const entry of readdirSync(current, { withFileTypes: true })) {
|
|
5074
|
-
const path =
|
|
5118
|
+
const path = join10(current, entry.name);
|
|
5075
5119
|
if (entry.isDirectory()) walk(path);
|
|
5076
5120
|
else results.push(relative2(dir, path));
|
|
5077
5121
|
}
|
|
@@ -5428,7 +5472,7 @@ async function projectCommand(command, parsed, deps) {
|
|
|
5428
5472
|
}
|
|
5429
5473
|
|
|
5430
5474
|
// src/code-connect.ts
|
|
5431
|
-
import { existsSync as
|
|
5475
|
+
import { existsSync as existsSync11 } from "fs";
|
|
5432
5476
|
import { cpus, hostname, totalmem } from "os";
|
|
5433
5477
|
import { resolve as resolve11 } from "path";
|
|
5434
5478
|
|
|
@@ -5439,7 +5483,7 @@ var HARNESS_PROTOCOL_VERSION = 1;
|
|
|
5439
5483
|
import { execFile, spawn as spawn3 } from "child_process";
|
|
5440
5484
|
import { constants } from "fs";
|
|
5441
5485
|
import { access } from "fs/promises";
|
|
5442
|
-
import { delimiter, join as
|
|
5486
|
+
import { delimiter, join as join11 } from "path";
|
|
5443
5487
|
import { getgid, getuid } from "process";
|
|
5444
5488
|
import { mkdir as mkdir2, mkdtemp, realpath, rm, writeFile as writeFile2 } from "fs/promises";
|
|
5445
5489
|
import { tmpdir } from "os";
|
|
@@ -5457,7 +5501,7 @@ function assertPinnedImage(image) {
|
|
|
5457
5501
|
async function commandAvailable(engine) {
|
|
5458
5502
|
for (const directory of (process.env.PATH ?? "").split(delimiter).filter(Boolean)) {
|
|
5459
5503
|
try {
|
|
5460
|
-
await access(
|
|
5504
|
+
await access(join11(directory, engine), constants.X_OK);
|
|
5461
5505
|
return true;
|
|
5462
5506
|
} catch {
|
|
5463
5507
|
}
|
|
@@ -6155,7 +6199,7 @@ import { randomUUID } from "crypto";
|
|
|
6155
6199
|
import { createHash as createHash22, randomUUID as randomUUID2 } from "crypto";
|
|
6156
6200
|
import { createReadStream } from "fs";
|
|
6157
6201
|
import { lstat as lstat22 } from "fs/promises";
|
|
6158
|
-
import { join as
|
|
6202
|
+
import { join as join13 } from "path";
|
|
6159
6203
|
import { mkdir as mkdir3, mkdtemp as mkdtemp3, rm as rm3, writeFile as writeFile3 } from "fs/promises";
|
|
6160
6204
|
import { tmpdir as tmpdir3 } from "os";
|
|
6161
6205
|
import { dirname as dirname9, join as join23, resolve as resolve32, sep as sep23 } from "path";
|
|
@@ -6542,8 +6586,8 @@ function rollup(graph, kind, options = {}) {
|
|
|
6542
6586
|
for (const node of nodesOfKind(graph, kind)) {
|
|
6543
6587
|
if (options.prefix && !node.name.startsWith(options.prefix)) continue;
|
|
6544
6588
|
const key = node.name.split(separator).slice(0, depth).join(separator);
|
|
6545
|
-
const
|
|
6546
|
-
if (
|
|
6589
|
+
const list3 = groups.get(key);
|
|
6590
|
+
if (list3) list3.push(node);
|
|
6547
6591
|
else groups.set(key, [node]);
|
|
6548
6592
|
}
|
|
6549
6593
|
return [...groups].map(([prefix, nodes]) => ({
|
|
@@ -6558,7 +6602,7 @@ function dirname8(path) {
|
|
|
6558
6602
|
const at = path.lastIndexOf("/");
|
|
6559
6603
|
return at <= 0 ? "." : path.slice(0, at);
|
|
6560
6604
|
}
|
|
6561
|
-
function
|
|
6605
|
+
function join12(base, specifier) {
|
|
6562
6606
|
const parts = [];
|
|
6563
6607
|
const segments = `${base === "." ? "" : `${base}/`}${specifier}`.split("/");
|
|
6564
6608
|
for (const segment of segments) {
|
|
@@ -6582,7 +6626,7 @@ var BARE_IMPORT = /^\s*import\s*["']([^"']+)["']/gm;
|
|
|
6582
6626
|
var isSourcePath = (path) => SOURCE.test(path);
|
|
6583
6627
|
function resolveImport(fromPath, specifier, known) {
|
|
6584
6628
|
if (!specifier.startsWith(".")) return null;
|
|
6585
|
-
const base =
|
|
6629
|
+
const base = join12(dirname8(fromPath), specifier);
|
|
6586
6630
|
const candidates = [
|
|
6587
6631
|
base,
|
|
6588
6632
|
base.replace(/\.js$/, ".ts"),
|
|
@@ -7385,7 +7429,7 @@ async function inspectArtifacts(workspaceDir, recipe2) {
|
|
|
7385
7429
|
const receipts = [];
|
|
7386
7430
|
for (const artifact of recipe2.expectedArtifacts ?? []) {
|
|
7387
7431
|
try {
|
|
7388
|
-
const path =
|
|
7432
|
+
const path = join13(workspaceDir, artifact.path);
|
|
7389
7433
|
const info = await lstat22(path);
|
|
7390
7434
|
if (!info.isFile() || info.isSymbolicLink()) {
|
|
7391
7435
|
receipts.push({ artifactId: artifact.id, status: "invalid", bytes: null, digest: null });
|
|
@@ -9481,7 +9525,7 @@ var CODE_BUILD_RECIPES = Object.freeze([{
|
|
|
9481
9525
|
async function codeConnect(options) {
|
|
9482
9526
|
const cwd = options.cwd ?? process.cwd();
|
|
9483
9527
|
const configPath = resolve11(cwd, options.configPath);
|
|
9484
|
-
const cfg =
|
|
9528
|
+
const cfg = existsSync11(configPath) ? await loadProjectConfig(configPath) : null;
|
|
9485
9529
|
const requestedAppId = options.appId?.trim();
|
|
9486
9530
|
if (requestedAppId && !/^[a-z0-9][a-z0-9-]{1,62}$/.test(requestedAppId)) {
|
|
9487
9531
|
throw new Error("--app-id must be a valid odla app id");
|
|
@@ -9915,13 +9959,13 @@ async function codeCommand(parsed, dependencies) {
|
|
|
9915
9959
|
}
|
|
9916
9960
|
|
|
9917
9961
|
// src/operator-credentials.ts
|
|
9918
|
-
import
|
|
9962
|
+
import process13 from "process";
|
|
9919
9963
|
function developerTokenStatus(context, parsed, now = Date.now()) {
|
|
9920
9964
|
const cached = readJsonFile(context.cfg.local.tokenFile);
|
|
9921
9965
|
const cacheStatus = !cached?.token ? "missing" : cached.platform !== context.platform.value ? "other-platform" : (cached.expiresAt ?? 0) <= now + 6e4 ? "expired" : "valid";
|
|
9922
9966
|
const source = clean3(
|
|
9923
9967
|
stringOpt(parsed.options.token)
|
|
9924
|
-
) ? "flag" : clean3(
|
|
9968
|
+
) ? "flag" : clean3(process13.env.ODLA_DEV_TOKEN) ? "environment" : cacheStatus === "valid" ? "cache" : "missing";
|
|
9925
9969
|
return {
|
|
9926
9970
|
source,
|
|
9927
9971
|
cacheFile: context.cfg.local.tokenFile,
|
|
@@ -10243,6 +10287,9 @@ Usage:
|
|
|
10243
10287
|
odla-ai security run [target] --ack-redacted-source [--env dev] [--profile odla] [--fail-on high]
|
|
10244
10288
|
odla-ai security run [target] --self --ack-redacted-source
|
|
10245
10289
|
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]
|
|
10290
|
+
odla-ai device enroll [--app <id>[,<id>...]] [--name <label>] [--capability <c>[,<c>...]] [--email <odla-account>] [--no-open] [--json]
|
|
10291
|
+
odla-ai device list [--email <odla-account>] [--json]
|
|
10292
|
+
odla-ai device revoke <device-id> [--email <odla-account>] [--json]
|
|
10246
10293
|
odla-ai credentials list [--config odla.config.mjs] [--env dev] [--all] [--json]
|
|
10247
10294
|
odla-ai credentials revoke <receipt-id> [--config odla.config.mjs] [--json]
|
|
10248
10295
|
odla-ai smoke [--config odla.config.mjs] [--env dev] [--runtime] [--email <odla-account>] [--no-open]
|
|
@@ -10348,6 +10395,10 @@ Commands:
|
|
|
10348
10395
|
stable status, incident, and report JSON to agents and CI.
|
|
10349
10396
|
platform Read canonical fleet health, releases, provider load/freshness,
|
|
10350
10397
|
explicit unknowns, and next actions through a read-only grant.
|
|
10398
|
+
device Enrol THIS machine once, then stop asking. A human approves the
|
|
10399
|
+
enrollment in the browser; from then on this terminal mints its
|
|
10400
|
+
own short-lived credentials for the named projects with nobody's
|
|
10401
|
+
attention, until the device expires or is revoked.
|
|
10351
10402
|
provision Register services, compose integrations, persist credentials, optionally push secrets.
|
|
10352
10403
|
"provision --live --yes" initializes only the live instance of
|
|
10353
10404
|
an existing sandbox app and enables every configured service;
|
|
@@ -12630,7 +12681,7 @@ function percent(value2) {
|
|
|
12630
12681
|
// src/provision.ts
|
|
12631
12682
|
import { AppsError as AppsError2, createAppsClient as createAppsClient3, orderAppServices as orderAppServices3, tenantIdFor as tenantIdFor6 } from "@odla-ai/apps";
|
|
12632
12683
|
import { putSecret as putSecret2 } from "@odla-ai/ai";
|
|
12633
|
-
import
|
|
12684
|
+
import process14 from "process";
|
|
12634
12685
|
|
|
12635
12686
|
// src/integration-provision.ts
|
|
12636
12687
|
import { uuidv7 } from "@odla-ai/db";
|
|
@@ -13066,7 +13117,7 @@ async function provision(options) {
|
|
|
13066
13117
|
await provisionIntegrationSeeds(doFetch, cfg.dbEndpoint, tenantId, dbKey, database.integrations, env, out);
|
|
13067
13118
|
}
|
|
13068
13119
|
if (cfg.services.includes("ai") && cfg.ai?.provider && cfg.ai.keyEnv) {
|
|
13069
|
-
const key =
|
|
13120
|
+
const key = process14.env[cfg.ai.keyEnv];
|
|
13070
13121
|
if (key) {
|
|
13071
13122
|
const secretName = cfg.ai.secretName ?? defaultSecretName(cfg.ai.provider);
|
|
13072
13123
|
await putSecret2({ endpoint: cfg.dbEndpoint, token, fetch: doFetch }, tenantId, secretName, key);
|
|
@@ -13108,7 +13159,7 @@ async function provision(options) {
|
|
|
13108
13159
|
|
|
13109
13160
|
// src/record.ts
|
|
13110
13161
|
import { appendFileSync } from "fs";
|
|
13111
|
-
import
|
|
13162
|
+
import process15 from "process";
|
|
13112
13163
|
|
|
13113
13164
|
// src/surface.ts
|
|
13114
13165
|
var PM_ACTIONS = {
|
|
@@ -13178,6 +13229,7 @@ var COMMAND_SURFACE = {
|
|
|
13178
13229
|
config: { diff: {}, plan: {}, apply: {} },
|
|
13179
13230
|
context: { show: {}, list: {}, save: {}, remove: {} },
|
|
13180
13231
|
credentials: { list: {}, revoke: {} },
|
|
13232
|
+
device: { enroll: {}, list: {}, revoke: {} },
|
|
13181
13233
|
// `watch`, `read`, `reply`, and `resolve` take a topic id from there on.
|
|
13182
13234
|
discuss: {
|
|
13183
13235
|
groups: {},
|
|
@@ -13289,7 +13341,7 @@ function surfacePaths(node = COMMAND_SURFACE, prefix = []) {
|
|
|
13289
13341
|
|
|
13290
13342
|
// src/record.ts
|
|
13291
13343
|
function recordInvocation(parsed) {
|
|
13292
|
-
const file =
|
|
13344
|
+
const file = process15.env.ODLA_CLI_RECORD;
|
|
13293
13345
|
if (!file) return;
|
|
13294
13346
|
try {
|
|
13295
13347
|
const entry = {
|
|
@@ -13326,8 +13378,109 @@ function renderAdvisories(out, advisories, env = process.env) {
|
|
|
13326
13378
|
}
|
|
13327
13379
|
}
|
|
13328
13380
|
|
|
13381
|
+
// src/device-command.ts
|
|
13382
|
+
import { chmodSync as chmodSync2, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
13383
|
+
import { dirname as dirname10 } from "path";
|
|
13384
|
+
import process16 from "process";
|
|
13385
|
+
async function deviceCommand(parsed, deps) {
|
|
13386
|
+
const action2 = parsed.positionals[1] ?? "";
|
|
13387
|
+
const out = deps.stdout ?? console;
|
|
13388
|
+
const doFetch = deps.fetch ?? fetch;
|
|
13389
|
+
const cfg = await loadProjectConfig(stringOpt(parsed.options.config));
|
|
13390
|
+
const json = parsed.options.json === true;
|
|
13391
|
+
if (action2 === "enroll") return enroll(parsed, deps, cfg, doFetch, out, json);
|
|
13392
|
+
if (action2 === "list") return list2(parsed, deps, cfg, doFetch, out, json);
|
|
13393
|
+
if (action2 === "revoke") return revoke(parsed, deps, cfg, doFetch, out, json);
|
|
13394
|
+
throw new Error('odla-ai device expects "enroll", "list", or "revoke"');
|
|
13395
|
+
}
|
|
13396
|
+
async function enroll(parsed, deps, cfg, doFetch, out, json) {
|
|
13397
|
+
const name = stringOpt(parsed.options.name) ?? defaultDeviceName();
|
|
13398
|
+
const apps = (stringOpt(parsed.options.app) ?? cfg.app.id).split(",").map((id2) => id2.trim()).filter(Boolean);
|
|
13399
|
+
if (apps.length === 0) throw new Error("device enroll needs --app <id>[,<id>\u2026]");
|
|
13400
|
+
const token = await scopedToken2(parsed, deps, cfg, doFetch, out, `odla CLI (enroll ${name})`);
|
|
13401
|
+
const response2 = await doFetch(`${cfg.platformUrl}/registry/devices`, {
|
|
13402
|
+
method: "POST",
|
|
13403
|
+
headers: { authorization: `Bearer ${token}`, "content-type": "application/json" },
|
|
13404
|
+
body: JSON.stringify({
|
|
13405
|
+
name,
|
|
13406
|
+
platform: process16.platform,
|
|
13407
|
+
appIds: apps,
|
|
13408
|
+
...parsed.options.capability ? { capabilities: String(parsed.options.capability).split(",").map((c) => c.trim()).filter(Boolean) } : {}
|
|
13409
|
+
})
|
|
13410
|
+
});
|
|
13411
|
+
const body = await response2.json().catch(() => ({}));
|
|
13412
|
+
if (!response2.ok || !body.token || !body.device) {
|
|
13413
|
+
throw new Error(`device enroll failed: ${body.error?.message ?? `registry returned ${response2.status}`} (${response2.status})`);
|
|
13414
|
+
}
|
|
13415
|
+
const path = deviceCredentialPath();
|
|
13416
|
+
mkdirSync4(dirname10(path), { recursive: true });
|
|
13417
|
+
writeFileSync4(path, JSON.stringify({
|
|
13418
|
+
token: body.token,
|
|
13419
|
+
platform: cfg.platformUrl.replace(/\/$/, ""),
|
|
13420
|
+
deviceId: body.device.deviceId,
|
|
13421
|
+
name
|
|
13422
|
+
}, null, 2));
|
|
13423
|
+
chmodSync2(path, 384);
|
|
13424
|
+
out.error(`device: enrolled "${name}" for ${body.device.appIds.join(", ")}; credential written to ${path}`);
|
|
13425
|
+
out.error("device: this terminal will mint its own credentials from now on \u2014 no further approvals.");
|
|
13426
|
+
if (json) {
|
|
13427
|
+
out.log(JSON.stringify({ deviceId: body.device.deviceId, name, appIds: body.device.appIds, expiresAt: body.device.expiresAt }, null, 2));
|
|
13428
|
+
}
|
|
13429
|
+
}
|
|
13430
|
+
async function list2(parsed, deps, cfg, doFetch, out, json) {
|
|
13431
|
+
const token = await scopedToken2(parsed, deps, cfg, doFetch, out, "odla CLI (device list)");
|
|
13432
|
+
const response2 = await doFetch(`${cfg.platformUrl}/registry/devices`, {
|
|
13433
|
+
headers: { authorization: `Bearer ${token}` }
|
|
13434
|
+
});
|
|
13435
|
+
const body = await response2.json().catch(() => ({}));
|
|
13436
|
+
if (!response2.ok || !body.devices) {
|
|
13437
|
+
throw new Error(`device list failed: ${body.error?.message ?? `registry returned ${response2.status}`} (${response2.status})`);
|
|
13438
|
+
}
|
|
13439
|
+
if (json) return out.log(JSON.stringify(body.devices, null, 2));
|
|
13440
|
+
if (body.devices.length === 0) return out.log("no enrolled devices");
|
|
13441
|
+
for (const device of body.devices) {
|
|
13442
|
+
const state2 = device.revokedAt ? "revoked" : device.expiresAt <= Date.now() ? "expired" : "active";
|
|
13443
|
+
out.log(`${device.deviceId} ${state2.padEnd(7)} ${device.name} [${device.appIds.join(", ")}]`);
|
|
13444
|
+
}
|
|
13445
|
+
}
|
|
13446
|
+
async function revoke(parsed, deps, cfg, doFetch, out, json) {
|
|
13447
|
+
const deviceId = parsed.positionals[2];
|
|
13448
|
+
if (!deviceId) throw new Error("device revoke needs the device id from `odla-ai device list`");
|
|
13449
|
+
const token = await scopedToken2(parsed, deps, cfg, doFetch, out, "odla CLI (device revoke)");
|
|
13450
|
+
const response2 = await doFetch(`${cfg.platformUrl}/registry/devices/${encodeURIComponent(deviceId)}/revoke`, {
|
|
13451
|
+
method: "POST",
|
|
13452
|
+
headers: { authorization: `Bearer ${token}` }
|
|
13453
|
+
});
|
|
13454
|
+
if (!response2.ok) {
|
|
13455
|
+
const body = await response2.json().catch(() => ({}));
|
|
13456
|
+
throw new Error(`device revoke failed: ${body.error?.message ?? `registry returned ${response2.status}`} (${response2.status})`);
|
|
13457
|
+
}
|
|
13458
|
+
out.error(`device: revoked ${deviceId}; every credential it minted is revoked with it`);
|
|
13459
|
+
if (json) out.log(JSON.stringify({ deviceId, revoked: true }, null, 2));
|
|
13460
|
+
}
|
|
13461
|
+
async function scopedToken2(parsed, deps, cfg, doFetch, out, label) {
|
|
13462
|
+
const { credentials } = await resolveOperatorContext(parsed, { allowMissingConfig: true });
|
|
13463
|
+
const scopedTokenFile = credentials.scopedTokenFile;
|
|
13464
|
+
return getScopedPlatformToken({
|
|
13465
|
+
platform: cfg.platformUrl,
|
|
13466
|
+
scope: "app:device:enroll",
|
|
13467
|
+
email: stringOpt(parsed.options.email),
|
|
13468
|
+
label,
|
|
13469
|
+
fetch: doFetch,
|
|
13470
|
+
stdout: out,
|
|
13471
|
+
open: parsed.options.open === false ? false : parsed.options.open === true ? true : void 0,
|
|
13472
|
+
openApprovalUrl: deps.openUrl,
|
|
13473
|
+
rootDir: cfg.rootDir,
|
|
13474
|
+
tokenFile: scopedTokenFile,
|
|
13475
|
+
...stringOpt(parsed.options.token) ? { token: stringOpt(parsed.options.token) } : {}
|
|
13476
|
+
});
|
|
13477
|
+
}
|
|
13478
|
+
function defaultDeviceName() {
|
|
13479
|
+
return `${process16.env.HOSTNAME ?? process16.env.HOST ?? "machine"}-${process16.platform}`;
|
|
13480
|
+
}
|
|
13481
|
+
|
|
13329
13482
|
// src/runbook-actions.ts
|
|
13330
|
-
import { readFileSync as
|
|
13483
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
13331
13484
|
var PLATFORM_SCOPE = "$platform";
|
|
13332
13485
|
async function call(ctx, method, path, body) {
|
|
13333
13486
|
const res = await ctx.doFetch(`${ctx.platformUrl.replace(/\/$/, "")}/registry/pm${path}`, {
|
|
@@ -13374,7 +13527,7 @@ async function bySlug(ctx, slug) {
|
|
|
13374
13527
|
function readBody(file, inline) {
|
|
13375
13528
|
if (inline !== void 0) return inline;
|
|
13376
13529
|
if (file === void 0) throw new Error("supply the new text with --file <path>, --file - (stdin), or --body");
|
|
13377
|
-
return
|
|
13530
|
+
return readFileSync10(file === "-" ? 0 : file, "utf8");
|
|
13378
13531
|
}
|
|
13379
13532
|
var stamp = (ms) => ms ? new Date(ms).toISOString().slice(0, 16).replace("T", " ") : "";
|
|
13380
13533
|
async function runbookList(ctx, all, query) {
|
|
@@ -13466,8 +13619,8 @@ async function runbookRemove(ctx, slug) {
|
|
|
13466
13619
|
}
|
|
13467
13620
|
|
|
13468
13621
|
// src/runbook-import.ts
|
|
13469
|
-
import { readFileSync as
|
|
13470
|
-
import { basename as basename2, join as
|
|
13622
|
+
import { readFileSync as readFileSync11, readdirSync as readdirSync2, statSync } from "fs";
|
|
13623
|
+
import { basename as basename2, join as join14 } from "path";
|
|
13471
13624
|
function parseRunbook(text3, slug) {
|
|
13472
13625
|
let rest = text3;
|
|
13473
13626
|
const meta = {};
|
|
@@ -13497,7 +13650,7 @@ function readRunbookDir(dir) {
|
|
|
13497
13650
|
if (!files.length) throw new Error(`no .md files in ${dir}`);
|
|
13498
13651
|
return files.map((file) => {
|
|
13499
13652
|
const slug = basename2(file, ".md");
|
|
13500
|
-
const parsed = parseRunbook(
|
|
13653
|
+
const parsed = parseRunbook(readFileSync11(join14(dir, file), "utf8"), slug);
|
|
13501
13654
|
return { file, slug, ...parsed, words: parsed.body.split(/\s+/).filter(Boolean).length };
|
|
13502
13655
|
});
|
|
13503
13656
|
}
|
|
@@ -13570,8 +13723,8 @@ async function upsert(ctx, r, visibility) {
|
|
|
13570
13723
|
|
|
13571
13724
|
// src/runbook-impact.ts
|
|
13572
13725
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
13573
|
-
import { existsSync as
|
|
13574
|
-
import { join as
|
|
13726
|
+
import { existsSync as existsSync12, readFileSync as readFileSync12 } from "fs";
|
|
13727
|
+
import { join as join15 } from "path";
|
|
13575
13728
|
|
|
13576
13729
|
// src/runbook-impact-scan.ts
|
|
13577
13730
|
var DECL = /^[+-]\s*export\s+(?:declare\s+)?(?:default\s+)?(?:abstract\s+)?(?:async\s+)?(?:const|let|var|function|class|interface|type|enum)\s+([A-Za-z_$][\w$]*)/;
|
|
@@ -13740,10 +13893,10 @@ ${body.split("\n").map((line2) => `+${line2}`).join("\n")}
|
|
|
13740
13893
|
}
|
|
13741
13894
|
function manifestLabeller(root) {
|
|
13742
13895
|
return (workspace) => {
|
|
13743
|
-
const manifest =
|
|
13744
|
-
if (!
|
|
13896
|
+
const manifest = join15(root, workspace, "package.json");
|
|
13897
|
+
if (!existsSync12(manifest)) return void 0;
|
|
13745
13898
|
try {
|
|
13746
|
-
const name = JSON.parse(
|
|
13899
|
+
const name = JSON.parse(readFileSync12(manifest, "utf8")).name;
|
|
13747
13900
|
return typeof name === "string" ? name : void 0;
|
|
13748
13901
|
} catch {
|
|
13749
13902
|
return void 0;
|
|
@@ -13810,7 +13963,7 @@ function report4(ctx, impacts) {
|
|
|
13810
13963
|
async function runbookImpact(ctx, options, deps = {}) {
|
|
13811
13964
|
const cwd = deps.cwd ?? process.cwd();
|
|
13812
13965
|
const runGit = deps.runGit ?? gitRunner(cwd);
|
|
13813
|
-
const read3 = deps.readRepoFile ?? ((path) =>
|
|
13966
|
+
const read3 = deps.readRepoFile ?? ((path) => readFileSync12(join15(cwd, path), "utf8"));
|
|
13814
13967
|
const surfaces = changedSurfaces(collectDiff(runGit, options.base, read3), manifestLabeller(cwd));
|
|
13815
13968
|
if (!surfaces.length) {
|
|
13816
13969
|
return ctx.out.log(
|
|
@@ -13943,12 +14096,12 @@ async function runbookComment(ctx, slug, body) {
|
|
|
13943
14096
|
|
|
13944
14097
|
// src/runbook-editor.ts
|
|
13945
14098
|
import { spawnSync } from "child_process";
|
|
13946
|
-
import { mkdtempSync, readFileSync as
|
|
14099
|
+
import { mkdtempSync, readFileSync as readFileSync13, rmSync as rmSync3, writeFileSync as writeFileSync5 } from "fs";
|
|
13947
14100
|
import { tmpdir as tmpdir4 } from "os";
|
|
13948
|
-
import { join as
|
|
13949
|
-
import
|
|
14101
|
+
import { join as join16 } from "path";
|
|
14102
|
+
import process17 from "process";
|
|
13950
14103
|
var EDITOR_ENV = ["ODLA_EDITOR", "VISUAL", "EDITOR"];
|
|
13951
|
-
function resolveEditor(env =
|
|
14104
|
+
function resolveEditor(env = process17.env) {
|
|
13952
14105
|
for (const name of EDITOR_ENV) {
|
|
13953
14106
|
const value2 = env[name];
|
|
13954
14107
|
if (value2 && value2.trim()) return value2.trim();
|
|
@@ -13962,8 +14115,8 @@ function defaultRun(command, path) {
|
|
|
13962
14115
|
return result.status ?? 0;
|
|
13963
14116
|
}
|
|
13964
14117
|
function editText(initial, slug, deps = {}) {
|
|
13965
|
-
const env = deps.env ??
|
|
13966
|
-
const interactive = deps.interactive ?? (() => Boolean(
|
|
14118
|
+
const env = deps.env ?? process17.env;
|
|
14119
|
+
const interactive = deps.interactive ?? (() => Boolean(process17.stdin.isTTY));
|
|
13967
14120
|
const editor = resolveEditor(env);
|
|
13968
14121
|
if (!editor)
|
|
13969
14122
|
throw new Error(
|
|
@@ -13971,13 +14124,13 @@ function editText(initial, slug, deps = {}) {
|
|
|
13971
14124
|
);
|
|
13972
14125
|
if (!interactive())
|
|
13973
14126
|
throw new Error(`cannot open an editor without a terminal \u2014 pass --file <path> or --body "\u2026" instead`);
|
|
13974
|
-
const dir = mkdtempSync(
|
|
13975
|
-
const file =
|
|
14127
|
+
const dir = mkdtempSync(join16(tmpdir4(), "odla-runbook-"));
|
|
14128
|
+
const file = join16(dir, `${slug}.md`);
|
|
13976
14129
|
try {
|
|
13977
|
-
|
|
14130
|
+
writeFileSync5(file, initial, { mode: 384 });
|
|
13978
14131
|
const code = defaultRunOrInjected(deps)(editor, file);
|
|
13979
14132
|
if (code !== 0) throw new Error(`editor "${editor}" exited with ${code}; nothing was written`);
|
|
13980
|
-
const edited =
|
|
14133
|
+
const edited = readFileSync13(file, "utf8");
|
|
13981
14134
|
return edited === initial ? null : edited;
|
|
13982
14135
|
} finally {
|
|
13983
14136
|
rmSync3(dir, { recursive: true, force: true });
|
|
@@ -14914,6 +15067,10 @@ async function dispatchCli(argv, dependencies) {
|
|
|
14914
15067
|
await contextCommand(parsed, runtime);
|
|
14915
15068
|
return;
|
|
14916
15069
|
}
|
|
15070
|
+
if (command === "device") {
|
|
15071
|
+
await deviceCommand(parsed, runtime);
|
|
15072
|
+
return;
|
|
15073
|
+
}
|
|
14917
15074
|
if (command === "credentials") {
|
|
14918
15075
|
await credentialCommand(parsed, runtime);
|
|
14919
15076
|
return;
|
|
@@ -15109,4 +15266,4 @@ export {
|
|
|
15109
15266
|
isTerminalHostedSecurityStatus,
|
|
15110
15267
|
runCli
|
|
15111
15268
|
};
|
|
15112
|
-
//# sourceMappingURL=chunk-
|
|
15269
|
+
//# sourceMappingURL=chunk-4QJ5NS64.js.map
|