@odla-ai/cli 0.44.0 → 0.46.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.cjs +408 -303
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +12 -14
- package/dist/bin.js.map +1 -1
- package/dist/chunk-5MEO3BVF.js +282 -0
- package/dist/chunk-5MEO3BVF.js.map +1 -0
- package/dist/{chunk-7MUWCSGP.js → chunk-JHB6352A.js} +179 -212
- package/dist/chunk-JHB6352A.js.map +1 -0
- package/dist/cli-2FTL245Y.js +12 -0
- package/dist/index.cjs +97 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/package.json +1 -1
- package/skills/odla/references/sdks.md +85 -0
- package/dist/chunk-7MUWCSGP.js.map +0 -1
- package/dist/chunk-UKLSRQ5J.js +0 -129
- package/dist/chunk-UKLSRQ5J.js.map +0 -1
- package/dist/cli-ZQBZT6YF.js +0 -12
- /package/dist/{cli-ZQBZT6YF.js.map → cli-2FTL245Y.js.map} +0 -0
|
@@ -1,21 +1,29 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
adoptRepoLocalCache,
|
|
4
|
+
appCredentialsFile,
|
|
5
|
+
appTokenFile,
|
|
3
6
|
cliVersion,
|
|
4
7
|
describeUnmet,
|
|
8
|
+
deviceSessionFile,
|
|
9
|
+
identityFile,
|
|
5
10
|
looksSecret,
|
|
6
11
|
parseRequires,
|
|
12
|
+
pmContextFile,
|
|
7
13
|
redactSecrets,
|
|
8
14
|
redactingOutput,
|
|
9
|
-
|
|
10
|
-
|
|
15
|
+
scopedTokenFile,
|
|
16
|
+
unmetRequirements,
|
|
17
|
+
updateNotice
|
|
18
|
+
} from "./chunk-5MEO3BVF.js";
|
|
11
19
|
|
|
12
20
|
// src/admin-ai.ts
|
|
13
|
-
import
|
|
21
|
+
import process12 from "process";
|
|
14
22
|
|
|
15
23
|
// src/token.ts
|
|
16
24
|
import { OdlaError, requestToken } from "@odla-ai/db";
|
|
17
25
|
import { createHash } from "crypto";
|
|
18
|
-
import
|
|
26
|
+
import process9 from "process";
|
|
19
27
|
|
|
20
28
|
// src/handshake-approval.ts
|
|
21
29
|
import process3 from "process";
|
|
@@ -150,10 +158,10 @@ function handshakeWaitMs(waitSeconds, interactive = process4.stdout.isTTY === tr
|
|
|
150
158
|
}
|
|
151
159
|
|
|
152
160
|
// src/cached-credential.ts
|
|
153
|
-
import { rmSync as
|
|
161
|
+
import { rmSync as rmSync2 } from "fs";
|
|
154
162
|
|
|
155
163
|
// src/auth-guidance.ts
|
|
156
|
-
import
|
|
164
|
+
import process6 from "process";
|
|
157
165
|
|
|
158
166
|
// src/device-session.ts
|
|
159
167
|
import { existsSync, readFileSync } from "fs";
|
|
@@ -202,63 +210,9 @@ async function mintDeviceSession(platformUrl, credential2, doFetch) {
|
|
|
202
210
|
};
|
|
203
211
|
}
|
|
204
212
|
|
|
205
|
-
// src/odla-home.ts
|
|
206
|
-
import { chmodSync, copyFileSync, existsSync as existsSync2, mkdirSync, rmSync as rmSync2 } from "fs";
|
|
207
|
-
import { homedir as homedir2 } from "os";
|
|
208
|
-
import { dirname as dirname2, join as join3 } from "path";
|
|
209
|
-
import process6 from "process";
|
|
210
|
-
function odlaHome(env = process6.env) {
|
|
211
|
-
if (env.VITEST && !env.ODLA_HOME) {
|
|
212
|
-
throw new Error(
|
|
213
|
-
"ODLA_HOME must be set under test \u2014 resolving the real ~/.odla would write to the developer's machine"
|
|
214
|
-
);
|
|
215
|
-
}
|
|
216
|
-
return env.ODLA_HOME ?? join3(env.HOME ?? homedir2(), ".odla");
|
|
217
|
-
}
|
|
218
|
-
function odlaHomePath(segments, env = process6.env) {
|
|
219
|
-
return join3(odlaHome(env), ...segments);
|
|
220
|
-
}
|
|
221
|
-
function identityFile(env) {
|
|
222
|
-
return odlaHomePath(["identity.json"], env);
|
|
223
|
-
}
|
|
224
|
-
function deviceSessionFile(env) {
|
|
225
|
-
return odlaHomePath(["session.json"], env);
|
|
226
|
-
}
|
|
227
|
-
function appTokenFile(appId, env) {
|
|
228
|
-
return odlaHomePath(["apps", safeSegment(appId), "dev-token.json"], env);
|
|
229
|
-
}
|
|
230
|
-
function appCredentialsFile(appId, env) {
|
|
231
|
-
return odlaHomePath(["apps", safeSegment(appId), "credentials.json"], env);
|
|
232
|
-
}
|
|
233
|
-
function scopedTokenFile(env) {
|
|
234
|
-
return odlaHomePath(["admin-token.local.json"], env);
|
|
235
|
-
}
|
|
236
|
-
function pmContextFile(env) {
|
|
237
|
-
return odlaHomePath(["pm-context.json"], env);
|
|
238
|
-
}
|
|
239
|
-
function adoptRepoLocalCache(legacyPath, machinePath, out) {
|
|
240
|
-
if (!existsSync2(legacyPath) || legacyPath === machinePath) return false;
|
|
241
|
-
const superseded2 = existsSync2(machinePath);
|
|
242
|
-
if (!superseded2) {
|
|
243
|
-
mkdirSync(dirname2(machinePath), { recursive: true });
|
|
244
|
-
copyFileSync(legacyPath, machinePath);
|
|
245
|
-
chmodSync(machinePath, 384);
|
|
246
|
-
}
|
|
247
|
-
rmSync2(legacyPath, { force: true });
|
|
248
|
-
out?.error(
|
|
249
|
-
superseded2 ? `auth: removed superseded ${legacyPath}; this machine's credentials live in ${odlaHome()}` : `auth: moved ${legacyPath} into ${machinePath}; credentials are per machine now, not per worktree`
|
|
250
|
-
);
|
|
251
|
-
return true;
|
|
252
|
-
}
|
|
253
|
-
function safeSegment(value2) {
|
|
254
|
-
const clean4 = value2.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/^-+|-+$/g, "");
|
|
255
|
-
if (!clean4) throw new Error(`"${value2}" is not a usable app id`);
|
|
256
|
-
return clean4;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
213
|
// src/local.ts
|
|
260
|
-
import { chmodSync
|
|
261
|
-
import { dirname as
|
|
214
|
+
import { chmodSync, existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, renameSync, writeFileSync } from "fs";
|
|
215
|
+
import { dirname as dirname2, isAbsolute, relative, resolve } from "path";
|
|
262
216
|
var GITIGNORE_LINES = [".odla/*.local.json", ".odla/dev-token.json", ".dev.vars"];
|
|
263
217
|
function readJsonFile(path) {
|
|
264
218
|
try {
|
|
@@ -272,7 +226,7 @@ function writePrivateJson(path, value2) {
|
|
|
272
226
|
`);
|
|
273
227
|
}
|
|
274
228
|
function readCredentials(path) {
|
|
275
|
-
if (!
|
|
229
|
+
if (!existsSync2(path)) return null;
|
|
276
230
|
let value2;
|
|
277
231
|
try {
|
|
278
232
|
value2 = JSON.parse(readFileSync2(path, "utf8"));
|
|
@@ -306,7 +260,7 @@ function mergeCredential(current, update) {
|
|
|
306
260
|
}
|
|
307
261
|
function ensureGitignore(rootDir, localPaths = []) {
|
|
308
262
|
const path = resolve(rootDir, ".gitignore");
|
|
309
|
-
const existing =
|
|
263
|
+
const existing = existsSync2(path) ? readFileSync2(path, "utf8") : "";
|
|
310
264
|
const configured = localPaths.map((localPath) => gitignoreEntry(rootDir, localPath)).filter((line2) => !!line2);
|
|
311
265
|
const wanted = [.../* @__PURE__ */ new Set([...GITIGNORE_LINES, ...configured])];
|
|
312
266
|
const missing = wanted.filter((line2) => !existing.split(/\r?\n/).includes(line2));
|
|
@@ -326,7 +280,7 @@ function o11yDevVars(cfg) {
|
|
|
326
280
|
function resolveWriteDevVarsTarget(cfg, requested) {
|
|
327
281
|
if (!requested) return null;
|
|
328
282
|
if (requested === true) return cfg.local.devVarsFile;
|
|
329
|
-
return resolve(
|
|
283
|
+
return resolve(dirname2(cfg.configPath), requested);
|
|
330
284
|
}
|
|
331
285
|
function writeDevVars(path, credentials, env, o11y) {
|
|
332
286
|
const entry = credentials.envs[env];
|
|
@@ -340,7 +294,7 @@ function writeDevVars(path, credentials, env, o11y) {
|
|
|
340
294
|
if (o11y.version) lines.push(`ODLA_O11Y_VERSION="${o11y.version}"`);
|
|
341
295
|
if (entry.o11yToken) lines.push(`ODLA_O11Y_TOKEN="${entry.o11yToken}"`);
|
|
342
296
|
}
|
|
343
|
-
const existing =
|
|
297
|
+
const existing = existsSync2(path) ? readFileSync2(path, "utf8") : "";
|
|
344
298
|
const retained = existing.split(/\r?\n/).filter((line2) => !isManagedDevVar(line2));
|
|
345
299
|
while (retained.at(-1) === "") retained.pop();
|
|
346
300
|
const prefix = retained.length ? `${retained.join("\n")}
|
|
@@ -366,10 +320,10 @@ function isManagedDevVar(line2) {
|
|
|
366
320
|
return !!match?.[1] && MANAGED_DEV_VARS.has(match[1]);
|
|
367
321
|
}
|
|
368
322
|
function writePrivateText(path, text4) {
|
|
369
|
-
|
|
323
|
+
mkdirSync(dirname2(path), { recursive: true });
|
|
370
324
|
const temporary = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
371
325
|
writeFileSync(temporary, text4, { mode: 384 });
|
|
372
|
-
|
|
326
|
+
chmodSync(temporary, 384);
|
|
373
327
|
renameSync(temporary, path);
|
|
374
328
|
}
|
|
375
329
|
function gitignoreEntry(rootDir, path) {
|
|
@@ -385,7 +339,7 @@ function displayPath(path, rootDir = process.cwd()) {
|
|
|
385
339
|
// src/auth-guidance.ts
|
|
386
340
|
var ENROL_EVERYTHING = "npx odla-ai device enroll --no-open --wait 600";
|
|
387
341
|
var ENROL_PLATFORM_WIDE = "npx odla-ai device enroll --platform-wide --device-ttl 6w --no-open --wait 600";
|
|
388
|
-
function machineAuthState(audience, env =
|
|
342
|
+
function machineAuthState(audience, env = process6.env) {
|
|
389
343
|
const device = readDeviceCredential(audience, env);
|
|
390
344
|
if (!device) return { enrolled: false };
|
|
391
345
|
const session = readJsonFile(deviceSessionFile(env));
|
|
@@ -443,7 +397,7 @@ function explainRejectedCredential(error) {
|
|
|
443
397
|
const tokenFile = noted;
|
|
444
398
|
if (!tokenFile || !isCredentialRejection(error)) return null;
|
|
445
399
|
noted = null;
|
|
446
|
-
|
|
400
|
+
rmSync2(tokenFile, { force: true });
|
|
447
401
|
return [
|
|
448
402
|
"auth: the cached credential was rejected by odla, so it was revoked before its cached expiry.",
|
|
449
403
|
" The usual cause is a newer sign-in for this account: collecting a handshake retires the",
|
|
@@ -454,9 +408,9 @@ function explainRejectedCredential(error) {
|
|
|
454
408
|
}
|
|
455
409
|
|
|
456
410
|
// src/device-session-cache.ts
|
|
457
|
-
import
|
|
411
|
+
import process7 from "process";
|
|
458
412
|
var SKEW_MS = 6e4;
|
|
459
|
-
async function deviceSessionToken(platformUrl, audience, credential2, doFetch, env =
|
|
413
|
+
async function deviceSessionToken(platformUrl, audience, credential2, doFetch, env = process7.env) {
|
|
460
414
|
const path = deviceSessionFile(env);
|
|
461
415
|
const cached = readJsonFile(path);
|
|
462
416
|
if (cached?.token && cached.platform === audience && cached.deviceId === credential2.deviceId && (cached.expiresAt ?? 0) > Date.now() + SKEW_MS) return cached;
|
|
@@ -471,13 +425,13 @@ async function deviceSessionToken(platformUrl, audience, credential2, doFetch, e
|
|
|
471
425
|
}
|
|
472
426
|
|
|
473
427
|
// src/machine-identity.ts
|
|
474
|
-
import
|
|
475
|
-
function readMachineIdentity(audience, env =
|
|
428
|
+
import process8 from "process";
|
|
429
|
+
function readMachineIdentity(audience, env = process8.env) {
|
|
476
430
|
const stored = readJsonFile(identityFile(env));
|
|
477
431
|
if (!stored || typeof stored.email !== "string" || !stored.email) return null;
|
|
478
432
|
return stored.platform === audience ? { platform: audience, email: stored.email } : null;
|
|
479
433
|
}
|
|
480
|
-
function rememberMachineIdentity(audience, email, env =
|
|
434
|
+
function rememberMachineIdentity(audience, email, env = process8.env) {
|
|
481
435
|
if (!email) return;
|
|
482
436
|
const existing = readMachineIdentity(audience, env);
|
|
483
437
|
if (existing?.email === email) return;
|
|
@@ -492,14 +446,14 @@ async function getDeveloperToken(cfg, options, doFetch, out, grantRequest = {})
|
|
|
492
446
|
const cached = readJsonFile(cfg.local.tokenFile);
|
|
493
447
|
if (!grantRequest.forceReview && !grantRequest.freshLogin) {
|
|
494
448
|
if (options.token) return options.token;
|
|
495
|
-
if (
|
|
496
|
-
const declared =
|
|
449
|
+
if (process9.env.ODLA_DEV_TOKEN) {
|
|
450
|
+
const declared = process9.env.ODLA_DEV_TOKEN_AUDIENCE;
|
|
497
451
|
if (declared) {
|
|
498
452
|
if (platformAudience(declared) !== audience) throw new Error("ODLA_DEV_TOKEN_AUDIENCE does not match the configured platform");
|
|
499
453
|
} else if (audience !== "https://odla.ai") {
|
|
500
454
|
throw new Error("ODLA_DEV_TOKEN_AUDIENCE is required for a non-default platform");
|
|
501
455
|
}
|
|
502
|
-
return
|
|
456
|
+
return process9.env.ODLA_DEV_TOKEN;
|
|
503
457
|
}
|
|
504
458
|
const device = readDeviceCredential(audience);
|
|
505
459
|
if (device) {
|
|
@@ -614,7 +568,7 @@ function stillPending(pending, email) {
|
|
|
614
568
|
);
|
|
615
569
|
}
|
|
616
570
|
function handshakeEmail(value2, cached) {
|
|
617
|
-
const email = (value2 ??
|
|
571
|
+
const email = (value2 ?? process9.env.ODLA_USER_EMAIL ?? cached ?? "").trim().toLowerCase();
|
|
618
572
|
if (/@users\.noreply\.github\.com$/i.test(email)) {
|
|
619
573
|
throw new Error(
|
|
620
574
|
`"${email}" is a GitHub commit identity, not an odla account email; use --email <signed-in-odla-account> or ODLA_USER_EMAIL`
|
|
@@ -645,12 +599,12 @@ function platformAudience(value2) {
|
|
|
645
599
|
}
|
|
646
600
|
|
|
647
601
|
// src/secret-input.ts
|
|
648
|
-
import
|
|
602
|
+
import process10 from "process";
|
|
649
603
|
var MAX_BYTES = 64 * 1024;
|
|
650
604
|
async function secretInputValue(options, kind = "credential") {
|
|
651
605
|
if (options.fromEnv && options.stdin) throw new Error("choose exactly one of --from-env or --stdin");
|
|
652
606
|
let value2;
|
|
653
|
-
if (options.fromEnv) value2 =
|
|
607
|
+
if (options.fromEnv) value2 = process10.env[options.fromEnv];
|
|
654
608
|
else if (options.stdin) value2 = await (options.readStdin ?? (() => readSecretStream(kind)))();
|
|
655
609
|
else throw new Error(`${kind} input required: use --from-env <NAME> or --stdin; values are never accepted as arguments`);
|
|
656
610
|
value2 = value2?.replace(/[\r\n]+$/, "");
|
|
@@ -658,7 +612,7 @@ async function secretInputValue(options, kind = "credential") {
|
|
|
658
612
|
if (new TextEncoder().encode(value2).byteLength > MAX_BYTES) throw new Error(`${kind} exceeds 64 KiB`);
|
|
659
613
|
return value2;
|
|
660
614
|
}
|
|
661
|
-
async function readSecretStream(kind, stream =
|
|
615
|
+
async function readSecretStream(kind, stream = process10.stdin) {
|
|
662
616
|
let value2 = "";
|
|
663
617
|
for await (const chunk of stream) {
|
|
664
618
|
value2 += String(chunk);
|
|
@@ -668,8 +622,8 @@ async function readSecretStream(kind, stream = process11.stdin) {
|
|
|
668
622
|
}
|
|
669
623
|
|
|
670
624
|
// src/admin-ai-auth.ts
|
|
671
|
-
import { join as
|
|
672
|
-
import
|
|
625
|
+
import { join as join3 } from "path";
|
|
626
|
+
import process11 from "process";
|
|
673
627
|
import { requestToken as requestToken2 } from "@odla-ai/db";
|
|
674
628
|
async function getScopedPlatformToken(options) {
|
|
675
629
|
return resolveAdminPlatformToken(options);
|
|
@@ -677,7 +631,7 @@ async function getScopedPlatformToken(options) {
|
|
|
677
631
|
async function resolveAdminPlatformToken(options) {
|
|
678
632
|
const audience = platformAudience(options.platform);
|
|
679
633
|
if (options.token) return options.token;
|
|
680
|
-
const fromEnv =
|
|
634
|
+
const fromEnv = process11.env.ODLA_ADMIN_TOKEN;
|
|
681
635
|
if (fromEnv) return audienceBoundEnvToken(fromEnv, audience);
|
|
682
636
|
return scopedToken(
|
|
683
637
|
audience,
|
|
@@ -689,7 +643,7 @@ async function resolveAdminPlatformToken(options) {
|
|
|
689
643
|
}
|
|
690
644
|
function audienceBoundEnvToken(token, platform) {
|
|
691
645
|
const audience = platformAudience(platform);
|
|
692
|
-
const declared =
|
|
646
|
+
const declared = process11.env.ODLA_ADMIN_TOKEN_AUDIENCE;
|
|
693
647
|
if (declared) {
|
|
694
648
|
if (platformAudience(declared) !== audience) throw new Error("ODLA_ADMIN_TOKEN_AUDIENCE does not match the configured platform");
|
|
695
649
|
} else if (audience !== "https://odla.ai") {
|
|
@@ -716,9 +670,9 @@ var SCOPE_PURPOSE = {
|
|
|
716
670
|
};
|
|
717
671
|
async function scopedToken(platform, scope, options, doFetch, out) {
|
|
718
672
|
const audience = platformAudience(platform);
|
|
719
|
-
const rootDir = options.rootDir ??
|
|
673
|
+
const rootDir = options.rootDir ?? process11.cwd();
|
|
720
674
|
const tokenFile = options.tokenFile ?? scopedTokenFile();
|
|
721
|
-
adoptRepoLocalCache(
|
|
675
|
+
adoptRepoLocalCache(join3(rootDir, ".odla/admin-token.local.json"), tokenFile, out);
|
|
722
676
|
const device = readDeviceCredential(audience);
|
|
723
677
|
if (device && options.cache !== false) {
|
|
724
678
|
const session = await deviceSessionToken(platform, audience, device, doFetch);
|
|
@@ -934,7 +888,7 @@ function isRecord2(value2) {
|
|
|
934
888
|
|
|
935
889
|
// src/admin-ai.ts
|
|
936
890
|
async function adminAi(options) {
|
|
937
|
-
const platform = platformAudience(options.platform ??
|
|
891
|
+
const platform = platformAudience(options.platform ?? process12.env.ODLA_PLATFORM ?? "https://odla.ai");
|
|
938
892
|
const doFetch = options.fetch ?? fetch;
|
|
939
893
|
const out = options.stdout ?? console;
|
|
940
894
|
const usageQuery = options.action === "usage" ? adminAiUsageQuery(options) : void 0;
|
|
@@ -1446,13 +1400,13 @@ async function adminSpend(parsed, ctx) {
|
|
|
1446
1400
|
}
|
|
1447
1401
|
|
|
1448
1402
|
// src/operator-context.ts
|
|
1449
|
-
import { existsSync as
|
|
1450
|
-
import { join as
|
|
1451
|
-
import
|
|
1403
|
+
import { existsSync as existsSync5 } from "fs";
|
|
1404
|
+
import { join as join5, resolve as resolve4 } from "path";
|
|
1405
|
+
import process14 from "process";
|
|
1452
1406
|
|
|
1453
1407
|
// src/config.ts
|
|
1454
|
-
import { existsSync as
|
|
1455
|
-
import { dirname as
|
|
1408
|
+
import { existsSync as existsSync3, rmSync as rmSync3, readFileSync as readFileSync3 } from "fs";
|
|
1409
|
+
import { dirname as dirname3, isAbsolute as isAbsolute2, resolve as resolve2 } from "path";
|
|
1456
1410
|
import { pathToFileURL } from "url";
|
|
1457
1411
|
import { appServiceDefinition, appServiceIds } from "@odla-ai/apps";
|
|
1458
1412
|
|
|
@@ -1862,11 +1816,11 @@ var stderr = { error: (message2) => {
|
|
|
1862
1816
|
} };
|
|
1863
1817
|
async function loadProjectConfig(configPath = "odla.config.mjs", options = {}) {
|
|
1864
1818
|
const resolved = resolve2(configPath);
|
|
1865
|
-
if (!
|
|
1819
|
+
if (!existsSync3(resolved)) {
|
|
1866
1820
|
throw new Error(`config not found: ${configPath}. Run "odla-ai init" first or pass --config.`);
|
|
1867
1821
|
}
|
|
1868
1822
|
const raw = await loadConfigModule(resolved);
|
|
1869
|
-
const rootDir =
|
|
1823
|
+
const rootDir = dirname3(resolved);
|
|
1870
1824
|
validateRawConfig(raw, resolved);
|
|
1871
1825
|
const platformUrl = trimSlash(process.env.ODLA_PLATFORM_URL || raw.platformUrl || DEFAULT_PLATFORM);
|
|
1872
1826
|
const dbEndpoint = trimSlash(process.env.ODLA_DB_ENDPOINT || raw.dbEndpoint || platformUrl);
|
|
@@ -1883,7 +1837,7 @@ async function loadProjectConfig(configPath = "odla.config.mjs", options = {}) {
|
|
|
1883
1837
|
};
|
|
1884
1838
|
adoptRepoLocalCache(resolve2(rootDir, ".odla/dev-token.json"), local.tokenFile, stderr);
|
|
1885
1839
|
adoptRepoLocalCache(resolve2(rootDir, ".odla/credentials.local.json"), local.credentialsFile, stderr);
|
|
1886
|
-
|
|
1840
|
+
rmSync3(resolve2(rootDir, ".odla/handshake.local.json"), { force: true });
|
|
1887
1841
|
return {
|
|
1888
1842
|
...raw,
|
|
1889
1843
|
configPath: resolved,
|
|
@@ -1991,18 +1945,18 @@ function unique3(values) {
|
|
|
1991
1945
|
}
|
|
1992
1946
|
|
|
1993
1947
|
// src/operator-profiles.ts
|
|
1994
|
-
import { existsSync as
|
|
1995
|
-
import { homedir as
|
|
1996
|
-
import { dirname as
|
|
1997
|
-
import
|
|
1948
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
|
|
1949
|
+
import { homedir as homedir2 } from "os";
|
|
1950
|
+
import { dirname as dirname4, join as join4, resolve as resolve3 } from "path";
|
|
1951
|
+
import process13 from "process";
|
|
1998
1952
|
function operatorProfileFile() {
|
|
1999
1953
|
return resolve3(
|
|
2000
|
-
clean(
|
|
1954
|
+
clean(process13.env.ODLA_CONTEXT_FILE) ?? join4(homedir2(), ".odla", "contexts.json")
|
|
2001
1955
|
);
|
|
2002
1956
|
}
|
|
2003
1957
|
function resolveOperatorProfile(parsed) {
|
|
2004
1958
|
const fromFlag = clean(stringOpt(parsed.options.context));
|
|
2005
|
-
const fromEnvironment = clean(
|
|
1959
|
+
const fromEnvironment = clean(process13.env.ODLA_CONTEXT);
|
|
2006
1960
|
const name = fromFlag ?? fromEnvironment ?? null;
|
|
2007
1961
|
const file = operatorProfileFile();
|
|
2008
1962
|
if (!name) {
|
|
@@ -2042,10 +1996,10 @@ function removeOperatorProfile(name, file = operatorProfileFile()) {
|
|
|
2042
1996
|
return true;
|
|
2043
1997
|
}
|
|
2044
1998
|
function operatorCredentialFiles(selection) {
|
|
2045
|
-
const base = selection.name ?
|
|
1999
|
+
const base = selection.name ? join4(dirname4(selection.file), "profiles", selection.name) : join4(homedir2(), ".odla");
|
|
2046
2000
|
return {
|
|
2047
|
-
developer:
|
|
2048
|
-
scoped:
|
|
2001
|
+
developer: join4(base, "dev-token.json"),
|
|
2002
|
+
scoped: join4(base, "admin-token.local.json")
|
|
2049
2003
|
};
|
|
2050
2004
|
}
|
|
2051
2005
|
function assertOperatorName(value2, label) {
|
|
@@ -2056,7 +2010,7 @@ function assertOperatorName(value2, label) {
|
|
|
2056
2010
|
}
|
|
2057
2011
|
}
|
|
2058
2012
|
function readOperatorProfiles(file) {
|
|
2059
|
-
if (!
|
|
2013
|
+
if (!existsSync4(file)) return emptyProfiles();
|
|
2060
2014
|
let raw;
|
|
2061
2015
|
try {
|
|
2062
2016
|
raw = JSON.parse(readFileSync4(file, "utf8"));
|
|
@@ -2125,19 +2079,19 @@ async function resolveOperatorContext(parsed, options = {}) {
|
|
|
2125
2079
|
const configArgument = stringOpt(parsed.options.config) ?? "odla.config.mjs";
|
|
2126
2080
|
const configPath = resolve4(configArgument);
|
|
2127
2081
|
const explicitConfig = parsed.options.config !== void 0;
|
|
2128
|
-
const hasConfig =
|
|
2082
|
+
const hasConfig = existsSync5(configPath);
|
|
2129
2083
|
if (!hasConfig && (!options.allowMissingConfig || explicitConfig)) {
|
|
2130
2084
|
await loadProjectConfig(configArgument);
|
|
2131
2085
|
}
|
|
2132
2086
|
const loaded = hasConfig ? await loadProjectConfig(configArgument) : void 0;
|
|
2133
2087
|
const platformFlag = clean2(stringOpt(parsed.options.platform));
|
|
2134
|
-
const platformEnvironment = clean2(
|
|
2088
|
+
const platformEnvironment = clean2(process14.env.ODLA_PLATFORM_URL);
|
|
2135
2089
|
const platformValue = platformAudience(
|
|
2136
2090
|
platformFlag ?? platformEnvironment ?? profile.value?.platform ?? loaded?.platformUrl ?? DEFAULT_PLATFORM2
|
|
2137
2091
|
);
|
|
2138
2092
|
const platformSource = platformFlag ? "flag" : platformEnvironment ? "environment" : profile.value ? "profile" : loaded ? "config" : "default";
|
|
2139
2093
|
const appFlag = clean2(stringOpt(parsed.options.app));
|
|
2140
|
-
const appEnvironment = clean2(
|
|
2094
|
+
const appEnvironment = clean2(process14.env.ODLA_APP_ID);
|
|
2141
2095
|
const appValue = appFlag ?? appEnvironment ?? profile.value?.app ?? loaded?.app.id ?? null;
|
|
2142
2096
|
const appSource = appFlag ? "flag" : appEnvironment ? "environment" : profile.value?.app ? "profile" : loaded ? "config" : "unresolved";
|
|
2143
2097
|
if (appValue) {
|
|
@@ -2151,16 +2105,16 @@ async function resolveOperatorContext(parsed, options = {}) {
|
|
|
2151
2105
|
);
|
|
2152
2106
|
}
|
|
2153
2107
|
const envFlag = clean2(stringOpt(parsed.options.env));
|
|
2154
|
-
const envEnvironment = clean2(
|
|
2108
|
+
const envEnvironment = clean2(process14.env.ODLA_ENV);
|
|
2155
2109
|
const environmentValue = envFlag ?? envEnvironment ?? profile.value?.environment ?? options.defaultEnvironment ?? null;
|
|
2156
2110
|
const environmentSource = envFlag ? "flag" : envEnvironment ? "environment" : profile.value?.environment ? "profile" : options.defaultEnvironment ? "default" : "unresolved";
|
|
2157
2111
|
if (environmentValue) {
|
|
2158
2112
|
assertOperatorName(environmentValue, "environment");
|
|
2159
2113
|
}
|
|
2160
|
-
const rootDir = loaded?.rootDir ??
|
|
2114
|
+
const rootDir = loaded?.rootDir ?? process14.cwd();
|
|
2161
2115
|
const profileCredentials = operatorCredentialFiles(profile);
|
|
2162
|
-
const tokenFile = clean2(
|
|
2163
|
-
const scopedTokenFile2 = clean2(
|
|
2116
|
+
const tokenFile = clean2(process14.env.ODLA_DEV_TOKEN_FILE) ? resolve4(process14.env.ODLA_DEV_TOKEN_FILE) : profile.name ? profileCredentials.developer : loaded?.local.tokenFile ?? profileCredentials.developer;
|
|
2117
|
+
const scopedTokenFile2 = clean2(process14.env.ODLA_ADMIN_TOKEN_FILE) ? resolve4(process14.env.ODLA_ADMIN_TOKEN_FILE) : profile.name ? profileCredentials.scoped : loaded ? join5(loaded.rootDir, ".odla", "admin-token.local.json") : profileCredentials.scoped;
|
|
2164
2118
|
const cfg = loaded ? {
|
|
2165
2119
|
...loaded,
|
|
2166
2120
|
platformUrl: platformValue,
|
|
@@ -2182,8 +2136,8 @@ async function resolveOperatorContext(parsed, options = {}) {
|
|
|
2182
2136
|
services: [],
|
|
2183
2137
|
local: {
|
|
2184
2138
|
tokenFile,
|
|
2185
|
-
credentialsFile:
|
|
2186
|
-
devVarsFile:
|
|
2139
|
+
credentialsFile: join5(rootDir, ".odla", "credentials.local.json"),
|
|
2140
|
+
devVarsFile: join5(rootDir, ".dev.vars"),
|
|
2187
2141
|
gitignore: true
|
|
2188
2142
|
}
|
|
2189
2143
|
};
|
|
@@ -2307,7 +2261,7 @@ async function adminCommand(parsed, deps = {}) {
|
|
|
2307
2261
|
}
|
|
2308
2262
|
|
|
2309
2263
|
// src/auth-command.ts
|
|
2310
|
-
import
|
|
2264
|
+
import process15 from "process";
|
|
2311
2265
|
|
|
2312
2266
|
// src/whoami-command.ts
|
|
2313
2267
|
var text2 = (value2) => typeof value2 === "string" && value2.trim() ? value2.trim() : null;
|
|
@@ -2491,7 +2445,7 @@ async function authCommand(parsed, deps = {}) {
|
|
|
2491
2445
|
const { cfg } = context;
|
|
2492
2446
|
const out = deps.stdout ?? console;
|
|
2493
2447
|
const doFetch = deps.fetch ?? fetch;
|
|
2494
|
-
const email = stringOpt(parsed.options.email) ??
|
|
2448
|
+
const email = stringOpt(parsed.options.email) ?? process15.env.ODLA_USER_EMAIL?.trim();
|
|
2495
2449
|
if (!email) {
|
|
2496
2450
|
throw new Error(
|
|
2497
2451
|
"auth login requires --email <odla-account> or ODLA_USER_EMAIL; confirm the signed-in odla email instead of using git or GitHub identity"
|
|
@@ -2859,7 +2813,7 @@ async function appCommand(parsed, dependencies = {}) {
|
|
|
2859
2813
|
|
|
2860
2814
|
// src/brand-command.ts
|
|
2861
2815
|
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
2862
|
-
import { dirname as
|
|
2816
|
+
import { dirname as dirname5, resolve as resolve5 } from "path";
|
|
2863
2817
|
|
|
2864
2818
|
// src/brand-design-unpack.ts
|
|
2865
2819
|
import { gunzipSync } from "zlib";
|
|
@@ -2974,7 +2928,7 @@ async function readBundle(source, deps) {
|
|
|
2974
2928
|
async function writeAll(result, outDir) {
|
|
2975
2929
|
for (const file of result.files) {
|
|
2976
2930
|
const target = resolve5(outDir, file.path);
|
|
2977
|
-
await mkdir(
|
|
2931
|
+
await mkdir(dirname5(target), { recursive: true });
|
|
2978
2932
|
await writeFile(target, file.bytes);
|
|
2979
2933
|
}
|
|
2980
2934
|
}
|
|
@@ -3541,7 +3495,7 @@ import {
|
|
|
3541
3495
|
AppsError,
|
|
3542
3496
|
createAppsClient
|
|
3543
3497
|
} from "@odla-ai/apps";
|
|
3544
|
-
import { join as
|
|
3498
|
+
import { join as join6 } from "path";
|
|
3545
3499
|
|
|
3546
3500
|
// src/config-operation-error.ts
|
|
3547
3501
|
var ConfigOperationCommandError = class extends Error {
|
|
@@ -3966,7 +3920,7 @@ async function operationClient(cfg, options, purpose) {
|
|
|
3966
3920
|
platform: cfg.platformUrl,
|
|
3967
3921
|
scope: "app:config:write",
|
|
3968
3922
|
token: options.token,
|
|
3969
|
-
tokenFile:
|
|
3923
|
+
tokenFile: join6(cfg.rootDir, ".odla", "admin-token.local.json"),
|
|
3970
3924
|
rootDir: cfg.rootDir,
|
|
3971
3925
|
email: options.email,
|
|
3972
3926
|
open: options.open,
|
|
@@ -4021,7 +3975,7 @@ function record4(value2) {
|
|
|
4021
3975
|
|
|
4022
3976
|
// src/config-reconcile-command.ts
|
|
4023
3977
|
import { createAppsClient as createAppsClient2, studioAppSettingsPath } from "@odla-ai/apps";
|
|
4024
|
-
import { join as
|
|
3978
|
+
import { join as join7 } from "path";
|
|
4025
3979
|
|
|
4026
3980
|
// src/config-reconcile.ts
|
|
4027
3981
|
import { appServiceIds as appServiceIds2, orderAppServices as orderAppServices2 } from "@odla-ai/apps";
|
|
@@ -4317,7 +4271,7 @@ async function inspectConfig(options) {
|
|
|
4317
4271
|
platform: cfg.platformUrl,
|
|
4318
4272
|
scope: "app:config:read",
|
|
4319
4273
|
token: options.token,
|
|
4320
|
-
tokenFile:
|
|
4274
|
+
tokenFile: join7(cfg.rootDir, ".odla", "admin-token.local.json"),
|
|
4321
4275
|
rootDir: cfg.rootDir,
|
|
4322
4276
|
email: options.email,
|
|
4323
4277
|
open: options.open,
|
|
@@ -4449,13 +4403,13 @@ function quoteArg2(value2) {
|
|
|
4449
4403
|
|
|
4450
4404
|
// src/doctor-checks.ts
|
|
4451
4405
|
import { execFileSync } from "child_process";
|
|
4452
|
-
import { existsSync as
|
|
4453
|
-
import { join as
|
|
4406
|
+
import { existsSync as existsSync7, readFileSync as readFileSync8 } from "fs";
|
|
4407
|
+
import { join as join9, resolve as resolve6 } from "path";
|
|
4454
4408
|
|
|
4455
4409
|
// src/wrangler.ts
|
|
4456
4410
|
import { spawn as spawn2 } from "child_process";
|
|
4457
|
-
import { existsSync as
|
|
4458
|
-
import { join as
|
|
4411
|
+
import { existsSync as existsSync6, readFileSync as readFileSync7 } from "fs";
|
|
4412
|
+
import { join as join8 } from "path";
|
|
4459
4413
|
var defaultRunner = (cmd, args, opts) => new Promise((resolvePromise, reject) => {
|
|
4460
4414
|
const child = spawn2(cmd, args, { cwd: opts?.cwd, stdio: ["pipe", "pipe", "pipe"] });
|
|
4461
4415
|
let stdout = "";
|
|
@@ -4469,8 +4423,8 @@ var defaultRunner = (cmd, args, opts) => new Promise((resolvePromise, reject) =>
|
|
|
4469
4423
|
var WRANGLER_CONFIG_FILES = ["wrangler.jsonc", "wrangler.json", "wrangler.toml"];
|
|
4470
4424
|
function findWranglerConfig(rootDir) {
|
|
4471
4425
|
for (const name of WRANGLER_CONFIG_FILES) {
|
|
4472
|
-
const path =
|
|
4473
|
-
if (
|
|
4426
|
+
const path = join8(rootDir, name);
|
|
4427
|
+
if (existsSync6(path)) return path;
|
|
4474
4428
|
}
|
|
4475
4429
|
return null;
|
|
4476
4430
|
}
|
|
@@ -4630,7 +4584,7 @@ function wranglerWarnings(rootDir) {
|
|
|
4630
4584
|
const dir = resolve6(rootDir, assets.directory);
|
|
4631
4585
|
if (dir === resolve6(rootDir)) {
|
|
4632
4586
|
warnings.push(`${label}assets.directory is the project root \u2014 point it at a dedicated build dir (wrangler dev fails with "spawn EBADF")`);
|
|
4633
|
-
} else if (
|
|
4587
|
+
} else if (existsSync7(join9(dir, "node_modules"))) {
|
|
4634
4588
|
warnings.push(`${label}assets.directory contains node_modules \u2014 wrangler dev's watcher will exhaust file descriptors`);
|
|
4635
4589
|
}
|
|
4636
4590
|
}
|
|
@@ -4666,7 +4620,7 @@ function o11yProjectWarnings(rootDir) {
|
|
|
4666
4620
|
return warnings;
|
|
4667
4621
|
}
|
|
4668
4622
|
const main = typeof config.main === "string" ? resolve6(rootDir, config.main) : null;
|
|
4669
|
-
if (!main || !
|
|
4623
|
+
if (!main || !existsSync7(main)) {
|
|
4670
4624
|
warnings.push("cannot verify o11y Worker instrumentation \u2014 wrangler main is missing or unreadable");
|
|
4671
4625
|
} else {
|
|
4672
4626
|
let source = "";
|
|
@@ -4695,7 +4649,7 @@ function calendarProjectWarnings(rootDir) {
|
|
|
4695
4649
|
}
|
|
4696
4650
|
function readPackageJson(rootDir) {
|
|
4697
4651
|
try {
|
|
4698
|
-
return JSON.parse(readFileSync8(
|
|
4652
|
+
return JSON.parse(readFileSync8(join9(rootDir, "package.json"), "utf8"));
|
|
4699
4653
|
} catch {
|
|
4700
4654
|
return null;
|
|
4701
4655
|
}
|
|
@@ -4989,14 +4943,14 @@ function harnessOption(value2, flag) {
|
|
|
4989
4943
|
}
|
|
4990
4944
|
|
|
4991
4945
|
// src/init.ts
|
|
4992
|
-
import { existsSync as
|
|
4993
|
-
import { dirname as
|
|
4946
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
4947
|
+
import { dirname as dirname6, resolve as resolve7 } from "path";
|
|
4994
4948
|
import { appServiceDefinition as appServiceDefinition3, appServiceIds as appServiceIds3 } from "@odla-ai/apps";
|
|
4995
4949
|
function initProject(options) {
|
|
4996
4950
|
const out = options.stdout ?? console;
|
|
4997
4951
|
const rootDir = resolve7(options.rootDir ?? process.cwd());
|
|
4998
4952
|
const configPath = resolve7(rootDir, options.configPath ?? "odla.config.mjs");
|
|
4999
|
-
if (
|
|
4953
|
+
if (existsSync8(configPath) && !options.force) {
|
|
5000
4954
|
throw new Error(`${configPath} already exists. Pass --force to overwrite.`);
|
|
5001
4955
|
}
|
|
5002
4956
|
if (!/^[a-z0-9][a-z0-9-]*$/.test(options.appId)) {
|
|
@@ -5012,9 +4966,9 @@ function initProject(options) {
|
|
|
5012
4966
|
}
|
|
5013
4967
|
}
|
|
5014
4968
|
const aiProvider = options.aiProvider;
|
|
5015
|
-
|
|
5016
|
-
|
|
5017
|
-
|
|
4969
|
+
mkdirSync2(dirname6(configPath), { recursive: true });
|
|
4970
|
+
mkdirSync2(resolve7(rootDir, "src/odla"), { recursive: true });
|
|
4971
|
+
mkdirSync2(resolve7(rootDir, ".odla"), { recursive: true });
|
|
5018
4972
|
writeFileSync2(configPath, configTemplate({ appId: options.appId, name: options.name, envs, services, aiProvider }));
|
|
5019
4973
|
writeIfMissing(resolve7(rootDir, "src/odla/schema.mjs"), schemaTemplate());
|
|
5020
4974
|
writeIfMissing(resolve7(rootDir, "src/odla/rules.mjs"), rulesTemplate());
|
|
@@ -5024,7 +4978,7 @@ function initProject(options) {
|
|
|
5024
4978
|
out.log("updated .gitignore for local odla credentials");
|
|
5025
4979
|
}
|
|
5026
4980
|
function writeIfMissing(path, text4) {
|
|
5027
|
-
if (
|
|
4981
|
+
if (existsSync8(path)) return;
|
|
5028
4982
|
writeFileSync2(path, text4);
|
|
5029
4983
|
}
|
|
5030
4984
|
function configTemplate(input) {
|
|
@@ -5323,9 +5277,9 @@ function printReport(report5, out) {
|
|
|
5323
5277
|
}
|
|
5324
5278
|
|
|
5325
5279
|
// src/skill.ts
|
|
5326
|
-
import { existsSync as
|
|
5327
|
-
import { homedir as
|
|
5328
|
-
import { dirname as
|
|
5280
|
+
import { existsSync as existsSync9, lstatSync, mkdirSync as mkdirSync3, readFileSync as readFileSync9, readdirSync, writeFileSync as writeFileSync3 } from "fs";
|
|
5281
|
+
import { homedir as homedir3 } from "os";
|
|
5282
|
+
import { dirname as dirname7, isAbsolute as isAbsolute3, join as join10, relative as relative2, resolve as resolve8, sep } from "path";
|
|
5329
5283
|
import { fileURLToPath } from "url";
|
|
5330
5284
|
|
|
5331
5285
|
// src/skill-adapters.ts
|
|
@@ -5425,7 +5379,7 @@ function installSkill(options = {}) {
|
|
|
5425
5379
|
if (files.length === 0) throw new Error(`no bundled skills found at ${sourceDir}`);
|
|
5426
5380
|
const harnesses = normalizeHarnesses(options.harnesses, options.global === true);
|
|
5427
5381
|
const root = resolve8(options.dir ?? process.cwd());
|
|
5428
|
-
const home = resolve8(options.homeDir ??
|
|
5382
|
+
const home = resolve8(options.homeDir ?? homedir3());
|
|
5429
5383
|
const plans = /* @__PURE__ */ new Map();
|
|
5430
5384
|
const targets = /* @__PURE__ */ new Map();
|
|
5431
5385
|
const rememberTarget = (harness, target) => {
|
|
@@ -5439,48 +5393,48 @@ function installSkill(options = {}) {
|
|
|
5439
5393
|
plans.set(target, { target, content: content2, boundary, managedMerge });
|
|
5440
5394
|
};
|
|
5441
5395
|
const planSkillTree = (targetDir2, boundary = root) => {
|
|
5442
|
-
for (const rel of files) plan(
|
|
5396
|
+
for (const rel of files) plan(join10(targetDir2, rel), readFileSync9(join10(sourceDir, rel), "utf8"), false, boundary);
|
|
5443
5397
|
};
|
|
5444
5398
|
let targetDir;
|
|
5445
5399
|
if (options.global) {
|
|
5446
|
-
const claudeRoot =
|
|
5447
|
-
const codexRoot = resolve8(options.codexHomeDir ?? process.env.CODEX_HOME ??
|
|
5400
|
+
const claudeRoot = join10(home, ".claude", "skills");
|
|
5401
|
+
const codexRoot = resolve8(options.codexHomeDir ?? process.env.CODEX_HOME ?? join10(home, ".codex"), "skills");
|
|
5448
5402
|
targetDir = harnesses[0] === "codex" ? codexRoot : claudeRoot;
|
|
5449
5403
|
for (const harness of harnesses) {
|
|
5450
5404
|
const skillRoot = harness === "claude" ? claudeRoot : codexRoot;
|
|
5451
|
-
planSkillTree(skillRoot, harness === "claude" ? home :
|
|
5405
|
+
planSkillTree(skillRoot, harness === "claude" ? home : dirname7(dirname7(codexRoot)));
|
|
5452
5406
|
rememberTarget(harness, skillRoot);
|
|
5453
5407
|
}
|
|
5454
5408
|
} else {
|
|
5455
|
-
const sharedRoot =
|
|
5409
|
+
const sharedRoot = join10(root, ".agents", "skills");
|
|
5456
5410
|
planSkillTree(sharedRoot);
|
|
5457
|
-
const claudeRoot =
|
|
5411
|
+
const claudeRoot = join10(root, ".claude", "skills");
|
|
5458
5412
|
targetDir = harnesses.includes("claude") ? claudeRoot : sharedRoot;
|
|
5459
5413
|
for (const harness of harnesses) rememberTarget(harness, sharedRoot);
|
|
5460
5414
|
if (harnesses.includes("claude")) {
|
|
5461
5415
|
for (const skill of skillNames(files)) {
|
|
5462
|
-
const canonical2 = readFileSync9(
|
|
5463
|
-
plan(
|
|
5416
|
+
const canonical2 = readFileSync9(join10(sourceDir, skill, "SKILL.md"), "utf8");
|
|
5417
|
+
plan(join10(claudeRoot, skill, "SKILL.md"), claudeAdapter(skill, canonical2));
|
|
5464
5418
|
}
|
|
5465
5419
|
rememberTarget("claude", claudeRoot);
|
|
5466
5420
|
}
|
|
5467
5421
|
if (harnesses.includes("cursor")) {
|
|
5468
|
-
const cursorRule =
|
|
5422
|
+
const cursorRule = join10(root, ".cursor", "rules", "odla.mdc");
|
|
5469
5423
|
plan(cursorRule, CURSOR_RULE);
|
|
5470
5424
|
rememberTarget("cursor", cursorRule);
|
|
5471
5425
|
}
|
|
5472
5426
|
if (harnesses.includes("agents")) {
|
|
5473
|
-
const agentsFile =
|
|
5427
|
+
const agentsFile = join10(root, "AGENTS.md");
|
|
5474
5428
|
plan(agentsFile, managedFileContent(agentsFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
|
|
5475
5429
|
rememberTarget("agents", agentsFile);
|
|
5476
5430
|
}
|
|
5477
5431
|
if (harnesses.includes("copilot")) {
|
|
5478
|
-
const copilotFile =
|
|
5432
|
+
const copilotFile = join10(root, ".github", "copilot-instructions.md");
|
|
5479
5433
|
plan(copilotFile, managedFileContent(copilotFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
|
|
5480
5434
|
rememberTarget("copilot", copilotFile);
|
|
5481
5435
|
}
|
|
5482
5436
|
if (harnesses.includes("gemini")) {
|
|
5483
|
-
const geminiFile =
|
|
5437
|
+
const geminiFile = join10(root, "GEMINI.md");
|
|
5484
5438
|
plan(geminiFile, managedFileContent(geminiFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
|
|
5485
5439
|
rememberTarget("gemini", geminiFile);
|
|
5486
5440
|
}
|
|
@@ -5494,7 +5448,7 @@ function installSkill(options = {}) {
|
|
|
5494
5448
|
conflicts.push(`${file.target} (redirected by symbolic link ${symlink})`);
|
|
5495
5449
|
continue;
|
|
5496
5450
|
}
|
|
5497
|
-
if (!
|
|
5451
|
+
if (!existsSync9(file.target)) {
|
|
5498
5452
|
writtenPaths.add(file.target);
|
|
5499
5453
|
continue;
|
|
5500
5454
|
}
|
|
@@ -5515,8 +5469,8 @@ ${conflicts.map((f) => ` - ${f}`).join("\n")}`
|
|
|
5515
5469
|
);
|
|
5516
5470
|
}
|
|
5517
5471
|
for (const file of plans.values()) {
|
|
5518
|
-
if (!
|
|
5519
|
-
|
|
5472
|
+
if (!existsSync9(file.target) || readFileSync9(file.target, "utf8") !== file.content) {
|
|
5473
|
+
mkdirSync3(dirname7(file.target), { recursive: true });
|
|
5520
5474
|
writeFileSync3(file.target, file.content);
|
|
5521
5475
|
}
|
|
5522
5476
|
}
|
|
@@ -5558,7 +5512,7 @@ function normalizeHarnesses(values, global) {
|
|
|
5558
5512
|
function managedFileContent(path, block2, force, boundary) {
|
|
5559
5513
|
const symlink = symlinkedComponent(boundary, path);
|
|
5560
5514
|
if (symlink) throw new Error(`refusing to manage ${path}: symbolic link component ${symlink}`);
|
|
5561
|
-
if (!
|
|
5515
|
+
if (!existsSync9(path)) return `${block2}
|
|
5562
5516
|
`;
|
|
5563
5517
|
const current = readFileSync9(path, "utf8");
|
|
5564
5518
|
const start = "<!-- odla-ai agent setup:start -->";
|
|
@@ -5587,7 +5541,7 @@ function symlinkedComponent(boundary, target) {
|
|
|
5587
5541
|
}
|
|
5588
5542
|
let current = boundary;
|
|
5589
5543
|
for (const part of rel.split(sep).filter(Boolean)) {
|
|
5590
|
-
current =
|
|
5544
|
+
current = join10(current, part);
|
|
5591
5545
|
try {
|
|
5592
5546
|
if (lstatSync(current).isSymbolicLink()) return current;
|
|
5593
5547
|
} catch (error) {
|
|
@@ -5600,11 +5554,11 @@ function skillNames(files) {
|
|
|
5600
5554
|
return [...new Set(files.filter((file) => /(^|[\\/])SKILL\.md$/.test(file)).map((file) => file.split(/[\\/]/)[0]))].sort();
|
|
5601
5555
|
}
|
|
5602
5556
|
function listFiles(dir) {
|
|
5603
|
-
if (!
|
|
5557
|
+
if (!existsSync9(dir)) return [];
|
|
5604
5558
|
const results = [];
|
|
5605
5559
|
const walk = (current) => {
|
|
5606
5560
|
for (const entry of readdirSync(current, { withFileTypes: true })) {
|
|
5607
|
-
const path =
|
|
5561
|
+
const path = join10(current, entry.name);
|
|
5608
5562
|
if (entry.isDirectory()) walk(path);
|
|
5609
5563
|
else results.push(relative2(dir, path));
|
|
5610
5564
|
}
|
|
@@ -5957,7 +5911,7 @@ async function projectCommand(command, parsed, deps) {
|
|
|
5957
5911
|
}
|
|
5958
5912
|
|
|
5959
5913
|
// src/code-connect.ts
|
|
5960
|
-
import { existsSync as
|
|
5914
|
+
import { existsSync as existsSync10 } from "fs";
|
|
5961
5915
|
import { cpus, hostname, totalmem } from "os";
|
|
5962
5916
|
import { resolve as resolve11 } from "path";
|
|
5963
5917
|
|
|
@@ -5968,7 +5922,7 @@ var HARNESS_PROTOCOL_VERSION = 1;
|
|
|
5968
5922
|
import { execFile, spawn as spawn3 } from "child_process";
|
|
5969
5923
|
import { constants } from "fs";
|
|
5970
5924
|
import { access } from "fs/promises";
|
|
5971
|
-
import { delimiter, join as
|
|
5925
|
+
import { delimiter, join as join11 } from "path";
|
|
5972
5926
|
import { getgid, getuid } from "process";
|
|
5973
5927
|
import { mkdir as mkdir2, mkdtemp, realpath, rm, writeFile as writeFile2 } from "fs/promises";
|
|
5974
5928
|
import { tmpdir } from "os";
|
|
@@ -5986,7 +5940,7 @@ function assertPinnedImage(image) {
|
|
|
5986
5940
|
async function commandAvailable(engine) {
|
|
5987
5941
|
for (const directory of (process.env.PATH ?? "").split(delimiter).filter(Boolean)) {
|
|
5988
5942
|
try {
|
|
5989
|
-
await access(
|
|
5943
|
+
await access(join11(directory, engine), constants.X_OK);
|
|
5990
5944
|
return true;
|
|
5991
5945
|
} catch {
|
|
5992
5946
|
}
|
|
@@ -6684,10 +6638,10 @@ import { randomUUID } from "crypto";
|
|
|
6684
6638
|
import { createHash as createHash22, randomUUID as randomUUID2 } from "crypto";
|
|
6685
6639
|
import { createReadStream } from "fs";
|
|
6686
6640
|
import { lstat as lstat22 } from "fs/promises";
|
|
6687
|
-
import { join as
|
|
6641
|
+
import { join as join13 } from "path";
|
|
6688
6642
|
import { mkdir as mkdir3, mkdtemp as mkdtemp3, rm as rm3, writeFile as writeFile3 } from "fs/promises";
|
|
6689
6643
|
import { tmpdir as tmpdir3 } from "os";
|
|
6690
|
-
import { dirname as
|
|
6644
|
+
import { dirname as dirname9, join as join23, resolve as resolve32, sep as sep23 } from "path";
|
|
6691
6645
|
import {
|
|
6692
6646
|
keepRecentExchanges,
|
|
6693
6647
|
runAgent
|
|
@@ -7085,11 +7039,11 @@ function rollup(graph, kind, options = {}) {
|
|
|
7085
7039
|
}
|
|
7086
7040
|
|
|
7087
7041
|
// ../graph/dist/code/index.js
|
|
7088
|
-
function
|
|
7042
|
+
function dirname8(path) {
|
|
7089
7043
|
const at = path.lastIndexOf("/");
|
|
7090
7044
|
return at <= 0 ? "." : path.slice(0, at);
|
|
7091
7045
|
}
|
|
7092
|
-
function
|
|
7046
|
+
function join12(base, specifier) {
|
|
7093
7047
|
const parts = [];
|
|
7094
7048
|
const segments = `${base === "." ? "" : `${base}/`}${specifier}`.split("/");
|
|
7095
7049
|
for (const segment of segments) {
|
|
@@ -7113,7 +7067,7 @@ var BARE_IMPORT = /^\s*import\s*["']([^"']+)["']/gm;
|
|
|
7113
7067
|
var isSourcePath = (path) => SOURCE.test(path);
|
|
7114
7068
|
function resolveImport(fromPath, specifier, known) {
|
|
7115
7069
|
if (!specifier.startsWith(".")) return null;
|
|
7116
|
-
const base =
|
|
7070
|
+
const base = join12(dirname8(fromPath), specifier);
|
|
7117
7071
|
const candidates = [
|
|
7118
7072
|
base,
|
|
7119
7073
|
base.replace(/\.js$/, ".ts"),
|
|
@@ -7917,7 +7871,7 @@ async function inspectArtifacts(workspaceDir, recipe2) {
|
|
|
7917
7871
|
const receipts = [];
|
|
7918
7872
|
for (const artifact of recipe2.expectedArtifacts ?? []) {
|
|
7919
7873
|
try {
|
|
7920
|
-
const path =
|
|
7874
|
+
const path = join13(workspaceDir, artifact.path);
|
|
7921
7875
|
const info = await lstat22(path);
|
|
7922
7876
|
if (!info.isFile() || info.isSymbolicLink()) {
|
|
7923
7877
|
receipts.push({ artifactId: artifact.id, status: "invalid", bytes: null, digest: null });
|
|
@@ -8206,7 +8160,7 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = tmpdir3()) {
|
|
|
8206
8160
|
if (bytes > SOURCE_MAX_BYTES) throw new TypeError("Code source exceeds its byte bound");
|
|
8207
8161
|
const target = resolve32(sourceDir, file.path);
|
|
8208
8162
|
if (!target.startsWith(`${resolve32(sourceDir)}${sep23}`)) throw new TypeError("Code source path escapes its root");
|
|
8209
|
-
await mkdir3(
|
|
8163
|
+
await mkdir3(dirname9(target), { recursive: true });
|
|
8210
8164
|
await writeFile3(target, file.content, { flag: "wx", mode: 420 });
|
|
8211
8165
|
}
|
|
8212
8166
|
for (const reference of snapshot.references ?? []) {
|
|
@@ -8221,7 +8175,7 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = tmpdir3()) {
|
|
|
8221
8175
|
if (bytes > SOURCE_SET_MAX_BYTES) throw new TypeError("Code source set exceeds its byte bound");
|
|
8222
8176
|
const target = resolve32(sourceDir, path);
|
|
8223
8177
|
if (!target.startsWith(`${resolve32(sourceDir)}${sep23}`)) throw new TypeError("Code reference path escapes its root");
|
|
8224
|
-
await mkdir3(
|
|
8178
|
+
await mkdir3(dirname9(target), { recursive: true });
|
|
8225
8179
|
await writeFile3(target, file.content, { flag: "wx", mode: 292 });
|
|
8226
8180
|
}
|
|
8227
8181
|
}
|
|
@@ -8248,7 +8202,7 @@ async function attachCodeRuntimeReferences(workspace, references) {
|
|
|
8248
8202
|
for (const root of [workspace.baselineDir, workspace.workspaceDir]) {
|
|
8249
8203
|
const target = resolve32(root, path);
|
|
8250
8204
|
if (!target.startsWith(`${resolve32(root)}${sep23}`)) throw new TypeError("Code reference path escapes its root");
|
|
8251
|
-
await mkdir3(
|
|
8205
|
+
await mkdir3(dirname9(target), { recursive: true });
|
|
8252
8206
|
await writeFile3(target, file.content, { flag: "wx", mode: 292 });
|
|
8253
8207
|
}
|
|
8254
8208
|
}
|
|
@@ -10311,7 +10265,7 @@ var CODE_BUILD_RECIPES = Object.freeze([{
|
|
|
10311
10265
|
async function codeConnect(options) {
|
|
10312
10266
|
const cwd = options.cwd ?? process.cwd();
|
|
10313
10267
|
const configPath = resolve11(cwd, options.configPath);
|
|
10314
|
-
const cfg =
|
|
10268
|
+
const cfg = existsSync10(configPath) ? await loadProjectConfig(configPath) : null;
|
|
10315
10269
|
const requestedAppId = options.appId?.trim();
|
|
10316
10270
|
if (requestedAppId && !/^[a-z0-9][a-z0-9-]{1,62}$/.test(requestedAppId)) {
|
|
10317
10271
|
throw new Error("--app-id must be a valid odla app id");
|
|
@@ -10740,13 +10694,13 @@ async function codeCommand(parsed, dependencies) {
|
|
|
10740
10694
|
}
|
|
10741
10695
|
|
|
10742
10696
|
// src/operator-credentials.ts
|
|
10743
|
-
import
|
|
10697
|
+
import process16 from "process";
|
|
10744
10698
|
function developerTokenStatus(context, parsed, now = Date.now()) {
|
|
10745
10699
|
const cached = readJsonFile(context.cfg.local.tokenFile);
|
|
10746
10700
|
const cacheStatus = !cached?.token ? "missing" : cached.platform !== context.platform.value ? "other-platform" : (cached.expiresAt ?? 0) <= now + 6e4 ? "expired" : "valid";
|
|
10747
10701
|
const source = clean3(
|
|
10748
10702
|
stringOpt(parsed.options.token)
|
|
10749
|
-
) ? "flag" : clean3(
|
|
10703
|
+
) ? "flag" : clean3(process16.env.ODLA_DEV_TOKEN) ? "environment" : cacheStatus === "valid" ? "cache" : "missing";
|
|
10750
10704
|
return {
|
|
10751
10705
|
source,
|
|
10752
10706
|
cacheFile: context.cfg.local.tokenFile,
|
|
@@ -11303,9 +11257,17 @@ Safety:
|
|
|
11303
11257
|
grant, run it once with --request-grant. That flag ignores ODLA_DEV_TOKEN and
|
|
11304
11258
|
the local cache, prints and opens a fresh exact-project owner-review URL, then
|
|
11305
11259
|
continues provisioning with the approved replacement credential.
|
|
11306
|
-
|
|
11307
|
-
|
|
11308
|
-
|
|
11260
|
+
Every command says so on STDERR when this CLI is older than the one npm
|
|
11261
|
+
serves, naming the repair for how this executable was launched \u2014 rebuild the
|
|
11262
|
+
worktree, npm i the dependency, or npx the scoped package. The answer is read
|
|
11263
|
+
from a cache under ~/.odla refreshed in the background at most every three
|
|
11264
|
+
hours, so no invocation waits on the registry and stdout is never touched.
|
|
11265
|
+
Set ODLA_CLI_UPDATE_CHECK=0 to switch it off.
|
|
11266
|
+
Before a non-dry-run provision, the executable additionally checks npm LIVE
|
|
11267
|
+
rather than trusting that cache: a security-sensitive grant shape must not
|
|
11268
|
+
ride on a three-hour-old answer. A confirmed stale client stops with a safe
|
|
11269
|
+
npx rerun command; a workspace-linked client also identifies the worktree
|
|
11270
|
+
that must be updated.
|
|
11309
11271
|
Run Code from a GitHub checkout already connected to an app in Studio; an
|
|
11310
11272
|
odla.config.mjs may select the app explicitly but is not required. With an
|
|
11311
11273
|
enrolled code.session device, the Studio repository selection authorizes the
|
|
@@ -12640,7 +12602,7 @@ async function pmWatch(ctx, parsed) {
|
|
|
12640
12602
|
}
|
|
12641
12603
|
|
|
12642
12604
|
// src/pm-project-context.ts
|
|
12643
|
-
import { existsSync as
|
|
12605
|
+
import { existsSync as existsSync11, rmSync as rmSync4 } from "fs";
|
|
12644
12606
|
import { resolve as resolve12 } from "path";
|
|
12645
12607
|
var pmProjectContextFile = () => pmContextFile();
|
|
12646
12608
|
function readPmProjectContext(rootDir) {
|
|
@@ -12657,9 +12619,9 @@ function writePmProjectContext(rootDir, value2) {
|
|
|
12657
12619
|
}
|
|
12658
12620
|
function adoptLegacySelection(rootDir) {
|
|
12659
12621
|
const legacy = resolve12(rootDir, ".odla", "pm-project.local.json");
|
|
12660
|
-
if (!
|
|
12622
|
+
if (!existsSync11(legacy)) return;
|
|
12661
12623
|
const previous = readJsonFile(legacy);
|
|
12662
|
-
|
|
12624
|
+
rmSync4(legacy, { force: true });
|
|
12663
12625
|
if (!isSelection(previous)) return;
|
|
12664
12626
|
const selections = readSelections();
|
|
12665
12627
|
if (selections[previous.appId]) return;
|
|
@@ -13689,7 +13651,7 @@ function percent(value2) {
|
|
|
13689
13651
|
// src/provision.ts
|
|
13690
13652
|
import { AppsError as AppsError2, createAppsClient as createAppsClient3, orderAppServices as orderAppServices3, tenantIdFor as tenantIdFor6 } from "@odla-ai/apps";
|
|
13691
13653
|
import { putSecret as putSecret2 } from "@odla-ai/ai";
|
|
13692
|
-
import
|
|
13654
|
+
import process17 from "process";
|
|
13693
13655
|
|
|
13694
13656
|
// src/integration-provision.ts
|
|
13695
13657
|
import { uuidv7 } from "@odla-ai/db";
|
|
@@ -14127,7 +14089,7 @@ async function provision(options) {
|
|
|
14127
14089
|
await provisionIntegrationSeeds(doFetch, cfg.dbEndpoint, tenantId, dbKey, database.integrations, env, out);
|
|
14128
14090
|
}
|
|
14129
14091
|
if (cfg.services.includes("ai") && cfg.ai?.provider && cfg.ai.keyEnv) {
|
|
14130
|
-
const key =
|
|
14092
|
+
const key = process17.env[cfg.ai.keyEnv];
|
|
14131
14093
|
if (key) {
|
|
14132
14094
|
const secretName = cfg.ai.secretName ?? defaultSecretName(cfg.ai.provider);
|
|
14133
14095
|
await putSecret2({ endpoint: cfg.dbEndpoint, token, fetch: doFetch }, tenantId, secretName, key);
|
|
@@ -14169,9 +14131,9 @@ async function provision(options) {
|
|
|
14169
14131
|
|
|
14170
14132
|
// src/record.ts
|
|
14171
14133
|
import { appendFileSync } from "fs";
|
|
14172
|
-
import
|
|
14134
|
+
import process18 from "process";
|
|
14173
14135
|
function recordInvocation(parsed) {
|
|
14174
|
-
const file =
|
|
14136
|
+
const file = process18.env.ODLA_CLI_RECORD;
|
|
14175
14137
|
if (!file) return;
|
|
14176
14138
|
try {
|
|
14177
14139
|
const entry = {
|
|
@@ -14242,9 +14204,9 @@ function parseDeviceTtl(raw) {
|
|
|
14242
14204
|
}
|
|
14243
14205
|
|
|
14244
14206
|
// src/device-command.ts
|
|
14245
|
-
import { chmodSync as
|
|
14246
|
-
import { dirname as
|
|
14247
|
-
import
|
|
14207
|
+
import { chmodSync as chmodSync2, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
14208
|
+
import { dirname as dirname10 } from "path";
|
|
14209
|
+
import process19 from "process";
|
|
14248
14210
|
async function deviceCommand(parsed, deps) {
|
|
14249
14211
|
assertArgs(parsed, [
|
|
14250
14212
|
"app",
|
|
@@ -14302,7 +14264,7 @@ async function enroll(parsed, deps, cfg, doFetch, out, json) {
|
|
|
14302
14264
|
headers: { authorization: `Bearer ${token}`, "content-type": "application/json" },
|
|
14303
14265
|
body: JSON.stringify({
|
|
14304
14266
|
name,
|
|
14305
|
-
platform:
|
|
14267
|
+
platform: process19.platform,
|
|
14306
14268
|
appIds: apps,
|
|
14307
14269
|
...capabilities ? { capabilities } : {},
|
|
14308
14270
|
...scopes ? { scopes } : {},
|
|
@@ -14314,14 +14276,14 @@ async function enroll(parsed, deps, cfg, doFetch, out, json) {
|
|
|
14314
14276
|
throw new Error(`device enroll failed: ${body.error?.message ?? `registry returned ${response2.status}`} (${response2.status})`);
|
|
14315
14277
|
}
|
|
14316
14278
|
const path = deviceCredentialPath();
|
|
14317
|
-
|
|
14279
|
+
mkdirSync4(dirname10(path), { recursive: true });
|
|
14318
14280
|
writeFileSync4(path, JSON.stringify({
|
|
14319
14281
|
token: body.token,
|
|
14320
14282
|
platform: cfg.platformUrl.replace(/\/$/, ""),
|
|
14321
14283
|
deviceId: body.device.deviceId,
|
|
14322
14284
|
name
|
|
14323
14285
|
}, null, 2));
|
|
14324
|
-
|
|
14286
|
+
chmodSync2(path, 384);
|
|
14325
14287
|
rememberMachineIdentity(cfg.platformUrl.replace(/\/$/, ""), stringOpt(parsed.options.email));
|
|
14326
14288
|
supersedeAdvisory("credential.expiring");
|
|
14327
14289
|
const reach = body.device.appIds.includes(ALL_OWNED_APPS) ? "every app you own, now and later" : body.device.appIds.join(", ");
|
|
@@ -14421,7 +14383,7 @@ async function scopedToken2(parsed, deps, cfg, doFetch, out, label, scope = "app
|
|
|
14421
14383
|
});
|
|
14422
14384
|
}
|
|
14423
14385
|
function defaultDeviceName() {
|
|
14424
|
-
return `${
|
|
14386
|
+
return `${process19.env.HOSTNAME ?? process19.env.HOST ?? "machine"}-${process19.platform}`;
|
|
14425
14387
|
}
|
|
14426
14388
|
|
|
14427
14389
|
// src/runbook-actions.ts
|
|
@@ -14565,7 +14527,7 @@ async function runbookRemove(ctx, slug) {
|
|
|
14565
14527
|
|
|
14566
14528
|
// src/runbook-import.ts
|
|
14567
14529
|
import { readFileSync as readFileSync11, readdirSync as readdirSync2, statSync } from "fs";
|
|
14568
|
-
import { basename as basename2, join as
|
|
14530
|
+
import { basename as basename2, join as join14 } from "path";
|
|
14569
14531
|
function parseRunbook(text4, slug) {
|
|
14570
14532
|
let rest = text4;
|
|
14571
14533
|
const meta = {};
|
|
@@ -14595,7 +14557,7 @@ function readRunbookDir(dir) {
|
|
|
14595
14557
|
if (!files.length) throw new Error(`no .md files in ${dir}`);
|
|
14596
14558
|
return files.map((file) => {
|
|
14597
14559
|
const slug = basename2(file, ".md");
|
|
14598
|
-
const parsed = parseRunbook(readFileSync11(
|
|
14560
|
+
const parsed = parseRunbook(readFileSync11(join14(dir, file), "utf8"), slug);
|
|
14599
14561
|
return { file, slug, ...parsed, words: parsed.body.split(/\s+/).filter(Boolean).length };
|
|
14600
14562
|
});
|
|
14601
14563
|
}
|
|
@@ -14663,8 +14625,8 @@ async function upsert(ctx, r, visibility) {
|
|
|
14663
14625
|
|
|
14664
14626
|
// src/runbook-impact.ts
|
|
14665
14627
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
14666
|
-
import { existsSync as
|
|
14667
|
-
import { join as
|
|
14628
|
+
import { existsSync as existsSync12, readFileSync as readFileSync12 } from "fs";
|
|
14629
|
+
import { join as join15 } from "path";
|
|
14668
14630
|
|
|
14669
14631
|
// src/runbook-impact-scan.ts
|
|
14670
14632
|
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$]*)/;
|
|
@@ -14833,8 +14795,8 @@ ${body.split("\n").map((line2) => `+${line2}`).join("\n")}
|
|
|
14833
14795
|
}
|
|
14834
14796
|
function manifestLabeller(root) {
|
|
14835
14797
|
return (workspace) => {
|
|
14836
|
-
const manifest =
|
|
14837
|
-
if (!
|
|
14798
|
+
const manifest = join15(root, workspace, "package.json");
|
|
14799
|
+
if (!existsSync12(manifest)) return void 0;
|
|
14838
14800
|
try {
|
|
14839
14801
|
const name = JSON.parse(readFileSync12(manifest, "utf8")).name;
|
|
14840
14802
|
return typeof name === "string" ? name : void 0;
|
|
@@ -14903,7 +14865,7 @@ function report4(ctx, impacts) {
|
|
|
14903
14865
|
async function runbookImpact(ctx, options, deps = {}) {
|
|
14904
14866
|
const cwd = deps.cwd ?? process.cwd();
|
|
14905
14867
|
const runGit = deps.runGit ?? gitRunner(cwd);
|
|
14906
|
-
const read3 = deps.readRepoFile ?? ((path) => readFileSync12(
|
|
14868
|
+
const read3 = deps.readRepoFile ?? ((path) => readFileSync12(join15(cwd, path), "utf8"));
|
|
14907
14869
|
const surfaces = changedSurfaces(collectDiff(runGit, options.base, read3), manifestLabeller(cwd));
|
|
14908
14870
|
if (!surfaces.length) {
|
|
14909
14871
|
return ctx.out.log(
|
|
@@ -15030,12 +14992,12 @@ async function runbookComment(ctx, slug, body) {
|
|
|
15030
14992
|
|
|
15031
14993
|
// src/runbook-editor.ts
|
|
15032
14994
|
import { spawnSync } from "child_process";
|
|
15033
|
-
import { mkdtempSync, readFileSync as readFileSync13, rmSync as
|
|
14995
|
+
import { mkdtempSync, readFileSync as readFileSync13, rmSync as rmSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
15034
14996
|
import { tmpdir as tmpdir4 } from "os";
|
|
15035
|
-
import { join as
|
|
15036
|
-
import
|
|
14997
|
+
import { join as join16 } from "path";
|
|
14998
|
+
import process20 from "process";
|
|
15037
14999
|
var EDITOR_ENV = ["ODLA_EDITOR", "VISUAL", "EDITOR"];
|
|
15038
|
-
function resolveEditor(env =
|
|
15000
|
+
function resolveEditor(env = process20.env) {
|
|
15039
15001
|
for (const name of EDITOR_ENV) {
|
|
15040
15002
|
const value2 = env[name];
|
|
15041
15003
|
if (value2 && value2.trim()) return value2.trim();
|
|
@@ -15049,8 +15011,8 @@ function defaultRun(command, path) {
|
|
|
15049
15011
|
return result.status ?? 0;
|
|
15050
15012
|
}
|
|
15051
15013
|
function editText(initial, slug, deps = {}) {
|
|
15052
|
-
const env = deps.env ??
|
|
15053
|
-
const interactive = deps.interactive ?? (() => Boolean(
|
|
15014
|
+
const env = deps.env ?? process20.env;
|
|
15015
|
+
const interactive = deps.interactive ?? (() => Boolean(process20.stdin.isTTY));
|
|
15054
15016
|
const editor = resolveEditor(env);
|
|
15055
15017
|
if (!editor)
|
|
15056
15018
|
throw new Error(
|
|
@@ -15058,8 +15020,8 @@ function editText(initial, slug, deps = {}) {
|
|
|
15058
15020
|
);
|
|
15059
15021
|
if (!interactive())
|
|
15060
15022
|
throw new Error(`cannot open an editor without a terminal \u2014 pass --file <path> or --body "\u2026" instead`);
|
|
15061
|
-
const dir = mkdtempSync(
|
|
15062
|
-
const file =
|
|
15023
|
+
const dir = mkdtempSync(join16(tmpdir4(), "odla-runbook-"));
|
|
15024
|
+
const file = join16(dir, `${slug}.md`);
|
|
15063
15025
|
try {
|
|
15064
15026
|
writeFileSync5(file, initial, { mode: 384 });
|
|
15065
15027
|
const code = defaultRunOrInjected(deps)(editor, file);
|
|
@@ -15067,7 +15029,7 @@ function editText(initial, slug, deps = {}) {
|
|
|
15067
15029
|
const edited = readFileSync13(file, "utf8");
|
|
15068
15030
|
return edited === initial ? null : edited;
|
|
15069
15031
|
} finally {
|
|
15070
|
-
|
|
15032
|
+
rmSync5(dir, { recursive: true, force: true });
|
|
15071
15033
|
}
|
|
15072
15034
|
}
|
|
15073
15035
|
var defaultRunOrInjected = (deps) => deps.run ?? defaultRun;
|
|
@@ -15960,6 +15922,11 @@ async function runCli(argv = process.argv.slice(2), dependencies = {}) {
|
|
|
15960
15922
|
throw error;
|
|
15961
15923
|
} finally {
|
|
15962
15924
|
renderAdvisories(out, advisories);
|
|
15925
|
+
try {
|
|
15926
|
+
const behind = updateNotice();
|
|
15927
|
+
if (behind) out.error(behind);
|
|
15928
|
+
} catch {
|
|
15929
|
+
}
|
|
15963
15930
|
}
|
|
15964
15931
|
}
|
|
15965
15932
|
async function dispatchCli(argv, dependencies) {
|
|
@@ -16194,4 +16161,4 @@ export {
|
|
|
16194
16161
|
isTerminalHostedSecurityStatus,
|
|
16195
16162
|
runCli
|
|
16196
16163
|
};
|
|
16197
|
-
//# sourceMappingURL=chunk-
|
|
16164
|
+
//# sourceMappingURL=chunk-JHB6352A.js.map
|