@odla-ai/cli 0.43.1 → 0.45.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 +671 -552
- 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-T4OPSRME.js → chunk-JHB6352A.js} +429 -471
- package/dist/chunk-JHB6352A.js.map +1 -0
- package/dist/cli-2FTL245Y.js +12 -0
- package/dist/index.cjs +341 -256
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-T4OPSRME.js.map +0 -1
- package/dist/chunk-UKLSRQ5J.js +0 -129
- package/dist/chunk-UKLSRQ5J.js.map +0 -1
- package/dist/cli-3JOB4OUV.js +0 -12
- /package/dist/{cli-3JOB4OUV.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;
|
|
@@ -1147,10 +1101,17 @@ function parseArgv(argv) {
|
|
|
1147
1101
|
function assertArgs(parsed, allowedOptions2, maxPositionals) {
|
|
1148
1102
|
const allowed = new Set(allowedOptions2);
|
|
1149
1103
|
for (const name of Object.keys(parsed.options)) {
|
|
1150
|
-
if (
|
|
1104
|
+
if (allowed.has(name)) continue;
|
|
1105
|
+
const accepts = [...allowed].sort().map((option) => `--${option}`).join(" ");
|
|
1106
|
+
throw new Error(
|
|
1107
|
+
`unknown option "--${name}"` + (accepts ? ` \u2014 this command accepts: ${accepts}` : " \u2014 this command takes no options")
|
|
1108
|
+
);
|
|
1151
1109
|
}
|
|
1152
1110
|
if (parsed.positionals.length > maxPositionals) {
|
|
1153
|
-
|
|
1111
|
+
const taken = parsed.positionals.slice(0, maxPositionals).join(" ");
|
|
1112
|
+
throw new Error(
|
|
1113
|
+
`unexpected argument "${parsed.positionals[maxPositionals]}" \u2014 ` + (maxPositionals ? `"odla-ai ${taken}" takes no further arguments` : "this command takes no arguments")
|
|
1114
|
+
);
|
|
1154
1115
|
}
|
|
1155
1116
|
}
|
|
1156
1117
|
function requiredString(value2, name) {
|
|
@@ -1188,6 +1149,198 @@ function addOption(options, name, value2) {
|
|
|
1188
1149
|
else options[name] = [String(current), String(value2)];
|
|
1189
1150
|
}
|
|
1190
1151
|
|
|
1152
|
+
// src/surface.ts
|
|
1153
|
+
var PM_ACTIONS = {
|
|
1154
|
+
list: {},
|
|
1155
|
+
add: {},
|
|
1156
|
+
create: {},
|
|
1157
|
+
get: {},
|
|
1158
|
+
set: {},
|
|
1159
|
+
update: {},
|
|
1160
|
+
status: {},
|
|
1161
|
+
move: {},
|
|
1162
|
+
done: {},
|
|
1163
|
+
comment: {},
|
|
1164
|
+
comments: {},
|
|
1165
|
+
history: {},
|
|
1166
|
+
link: {},
|
|
1167
|
+
ref: {},
|
|
1168
|
+
rm: {},
|
|
1169
|
+
delete: {}
|
|
1170
|
+
};
|
|
1171
|
+
var PM_TASK_ACTIONS = {
|
|
1172
|
+
...PM_ACTIONS,
|
|
1173
|
+
ready: {},
|
|
1174
|
+
claim: {},
|
|
1175
|
+
release: {}
|
|
1176
|
+
};
|
|
1177
|
+
var PM_ENTITIES = {
|
|
1178
|
+
...Object.fromEntries(
|
|
1179
|
+
["goal", "conformance", "decision", "bug"].map((entity) => [entity, PM_ACTIONS])
|
|
1180
|
+
),
|
|
1181
|
+
task: PM_TASK_ACTIONS,
|
|
1182
|
+
kanban: PM_TASK_ACTIONS
|
|
1183
|
+
};
|
|
1184
|
+
var COMMAND_SURFACE = {
|
|
1185
|
+
agent: { jobs: {}, retry: {} },
|
|
1186
|
+
ai: { models: {} },
|
|
1187
|
+
admin: {
|
|
1188
|
+
ai: {
|
|
1189
|
+
show: {},
|
|
1190
|
+
set: {},
|
|
1191
|
+
credentials: {},
|
|
1192
|
+
models: {},
|
|
1193
|
+
usage: {},
|
|
1194
|
+
audit: {},
|
|
1195
|
+
credential: { set: {} }
|
|
1196
|
+
},
|
|
1197
|
+
spend: { show: {}, reset: {} }
|
|
1198
|
+
},
|
|
1199
|
+
app: {
|
|
1200
|
+
archive: {},
|
|
1201
|
+
restore: {},
|
|
1202
|
+
export: {},
|
|
1203
|
+
import: {},
|
|
1204
|
+
rename: {},
|
|
1205
|
+
"refresh-sandbox": {},
|
|
1206
|
+
"go-live": {},
|
|
1207
|
+
promote: {},
|
|
1208
|
+
owners: { list: {}, add: {}, remove: {} }
|
|
1209
|
+
},
|
|
1210
|
+
auth: { login: {} },
|
|
1211
|
+
brand: { design: { unpack: {} } },
|
|
1212
|
+
bug: { create: {}, list: {}, report: {} },
|
|
1213
|
+
calendar: { status: {}, calendars: {}, connect: {}, disconnect: {} },
|
|
1214
|
+
capabilities: {},
|
|
1215
|
+
code: {
|
|
1216
|
+
connect: {},
|
|
1217
|
+
grant: { request: {}, list: {}, approve: {}, revoke: {} },
|
|
1218
|
+
repository: { show: {}, list: {}, bind: {} }
|
|
1219
|
+
},
|
|
1220
|
+
config: { diff: {}, plan: {}, apply: {} },
|
|
1221
|
+
context: { show: {}, list: {}, save: {}, remove: {} },
|
|
1222
|
+
credentials: { list: {}, revoke: {} },
|
|
1223
|
+
device: { enroll: {}, list: {}, revoke: {} },
|
|
1224
|
+
// `watch`, `read`, `reply`, and `resolve` take a topic id from there on.
|
|
1225
|
+
discuss: {
|
|
1226
|
+
groups: {},
|
|
1227
|
+
list: {},
|
|
1228
|
+
topics: {},
|
|
1229
|
+
read: {},
|
|
1230
|
+
post: {},
|
|
1231
|
+
reply: {},
|
|
1232
|
+
resolve: {},
|
|
1233
|
+
who: {},
|
|
1234
|
+
watch: {}
|
|
1235
|
+
},
|
|
1236
|
+
doctor: {},
|
|
1237
|
+
help: {},
|
|
1238
|
+
init: {},
|
|
1239
|
+
monitor: { plan: {}, apply: {}, run: {}, status: {}, incidents: {}, report: {} },
|
|
1240
|
+
o11y: { status: {} },
|
|
1241
|
+
operations: { get: {}, wait: {} },
|
|
1242
|
+
platform: {
|
|
1243
|
+
status: {}
|
|
1244
|
+
},
|
|
1245
|
+
pm: {
|
|
1246
|
+
...PM_ENTITIES,
|
|
1247
|
+
project: { list: {}, add: {}, create: {}, use: {} },
|
|
1248
|
+
handoff: {},
|
|
1249
|
+
next: {},
|
|
1250
|
+
start: {},
|
|
1251
|
+
watch: {}
|
|
1252
|
+
},
|
|
1253
|
+
provision: {},
|
|
1254
|
+
runbook: {
|
|
1255
|
+
ask: {},
|
|
1256
|
+
search: {},
|
|
1257
|
+
impact: {},
|
|
1258
|
+
list: {},
|
|
1259
|
+
get: {},
|
|
1260
|
+
cat: {},
|
|
1261
|
+
new: {},
|
|
1262
|
+
edit: {},
|
|
1263
|
+
comment: {},
|
|
1264
|
+
import: {},
|
|
1265
|
+
visibility: {},
|
|
1266
|
+
publish: {},
|
|
1267
|
+
archive: {},
|
|
1268
|
+
history: {},
|
|
1269
|
+
revert: {},
|
|
1270
|
+
rm: {},
|
|
1271
|
+
lint: {}
|
|
1272
|
+
},
|
|
1273
|
+
secrets: { push: {}, status: {}, set: {}, "set-clerk-key": {} },
|
|
1274
|
+
security: {
|
|
1275
|
+
plan: {},
|
|
1276
|
+
sources: {},
|
|
1277
|
+
run: {},
|
|
1278
|
+
status: {},
|
|
1279
|
+
report: {},
|
|
1280
|
+
github: { connect: {}, disconnect: {} }
|
|
1281
|
+
},
|
|
1282
|
+
setup: {},
|
|
1283
|
+
skill: { install: {} },
|
|
1284
|
+
smoke: {},
|
|
1285
|
+
version: {},
|
|
1286
|
+
whoami: {}
|
|
1287
|
+
};
|
|
1288
|
+
function acceptedAfter(path) {
|
|
1289
|
+
let node = COMMAND_SURFACE;
|
|
1290
|
+
for (const word of path) {
|
|
1291
|
+
node = node?.[word];
|
|
1292
|
+
if (!node) return [];
|
|
1293
|
+
}
|
|
1294
|
+
return Object.keys(node).sort();
|
|
1295
|
+
}
|
|
1296
|
+
function validateInvocation(words2) {
|
|
1297
|
+
let node = COMMAND_SURFACE;
|
|
1298
|
+
const walked = [];
|
|
1299
|
+
for (const word of words2) {
|
|
1300
|
+
if (Object.keys(node).length === 0) return null;
|
|
1301
|
+
const next = node[word];
|
|
1302
|
+
if (!next) return { validPrefix: walked.join(" "), word, accepted: Object.keys(node).sort() };
|
|
1303
|
+
walked.push(word);
|
|
1304
|
+
node = next;
|
|
1305
|
+
}
|
|
1306
|
+
return null;
|
|
1307
|
+
}
|
|
1308
|
+
function describeProblem(problem) {
|
|
1309
|
+
const where = problem.validPrefix ? `after "${problem.validPrefix}"` : "as a command";
|
|
1310
|
+
if (!problem.accepted.length) return `"${problem.word}" is not accepted ${where}. Run "odla-ai help"`;
|
|
1311
|
+
if (!problem.word) return `"odla-ai ${problem.validPrefix}" needs one of: ${problem.accepted.join(", ")}`;
|
|
1312
|
+
return `"${problem.word}" is not accepted ${where} \u2014 try: ${problem.accepted.join(", ")}`;
|
|
1313
|
+
}
|
|
1314
|
+
function rejectWord(path, word, note) {
|
|
1315
|
+
const sentence = describeProblem({
|
|
1316
|
+
validPrefix: path.join(" "),
|
|
1317
|
+
word: word ?? "",
|
|
1318
|
+
accepted: acceptedAfter(path)
|
|
1319
|
+
});
|
|
1320
|
+
throw new Error(note ? `${sentence}. ${note}` : sentence);
|
|
1321
|
+
}
|
|
1322
|
+
function invocationPath(words2) {
|
|
1323
|
+
let node = COMMAND_SURFACE;
|
|
1324
|
+
const path = [];
|
|
1325
|
+
for (const word of words2) {
|
|
1326
|
+
const next = node[word];
|
|
1327
|
+
if (!next) break;
|
|
1328
|
+
path.push(word);
|
|
1329
|
+
node = next;
|
|
1330
|
+
if (Object.keys(node).length === 0) break;
|
|
1331
|
+
}
|
|
1332
|
+
return path;
|
|
1333
|
+
}
|
|
1334
|
+
function surfacePaths(node = COMMAND_SURFACE, prefix = []) {
|
|
1335
|
+
const paths2 = [];
|
|
1336
|
+
for (const [word, child] of Object.entries(node)) {
|
|
1337
|
+
const path = [...prefix, word];
|
|
1338
|
+
paths2.push(path);
|
|
1339
|
+
paths2.push(...surfacePaths(child, path));
|
|
1340
|
+
}
|
|
1341
|
+
return paths2;
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1191
1344
|
// src/admin-spend.ts
|
|
1192
1345
|
async function call(ctx, method, scope) {
|
|
1193
1346
|
const url = `${ctx.platformUrl.replace(/\/$/, "")}/registry/platform/spend?scope=${encodeURIComponent(scope)}`;
|
|
@@ -1237,9 +1390,7 @@ async function spendReset(ctx, scope) {
|
|
|
1237
1390
|
async function adminSpend(parsed, ctx) {
|
|
1238
1391
|
const action2 = parsed.positionals[2];
|
|
1239
1392
|
const scope = parsed.positionals[3] ?? stringOpt(parsed.options.scope);
|
|
1240
|
-
if (action2 !== "show" && action2 !== "reset")
|
|
1241
|
-
throw new Error('unknown spend command. Try "odla-ai admin spend show <scope>".');
|
|
1242
|
-
}
|
|
1393
|
+
if (action2 !== "show" && action2 !== "reset") rejectWord(["admin", "spend"], action2);
|
|
1243
1394
|
if (!scope) {
|
|
1244
1395
|
throw new Error(
|
|
1245
1396
|
`"admin spend ${action2}" needs a scope, e.g. odla-ai admin spend ${action2} app:my-app:<incarnation>`
|
|
@@ -1249,13 +1400,13 @@ async function adminSpend(parsed, ctx) {
|
|
|
1249
1400
|
}
|
|
1250
1401
|
|
|
1251
1402
|
// src/operator-context.ts
|
|
1252
|
-
import { existsSync as
|
|
1253
|
-
import { join as
|
|
1254
|
-
import
|
|
1403
|
+
import { existsSync as existsSync5 } from "fs";
|
|
1404
|
+
import { join as join5, resolve as resolve4 } from "path";
|
|
1405
|
+
import process14 from "process";
|
|
1255
1406
|
|
|
1256
1407
|
// src/config.ts
|
|
1257
|
-
import { existsSync as
|
|
1258
|
-
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";
|
|
1259
1410
|
import { pathToFileURL } from "url";
|
|
1260
1411
|
import { appServiceDefinition, appServiceIds } from "@odla-ai/apps";
|
|
1261
1412
|
|
|
@@ -1665,11 +1816,11 @@ var stderr = { error: (message2) => {
|
|
|
1665
1816
|
} };
|
|
1666
1817
|
async function loadProjectConfig(configPath = "odla.config.mjs", options = {}) {
|
|
1667
1818
|
const resolved = resolve2(configPath);
|
|
1668
|
-
if (!
|
|
1819
|
+
if (!existsSync3(resolved)) {
|
|
1669
1820
|
throw new Error(`config not found: ${configPath}. Run "odla-ai init" first or pass --config.`);
|
|
1670
1821
|
}
|
|
1671
1822
|
const raw = await loadConfigModule(resolved);
|
|
1672
|
-
const rootDir =
|
|
1823
|
+
const rootDir = dirname3(resolved);
|
|
1673
1824
|
validateRawConfig(raw, resolved);
|
|
1674
1825
|
const platformUrl = trimSlash(process.env.ODLA_PLATFORM_URL || raw.platformUrl || DEFAULT_PLATFORM);
|
|
1675
1826
|
const dbEndpoint = trimSlash(process.env.ODLA_DB_ENDPOINT || raw.dbEndpoint || platformUrl);
|
|
@@ -1686,7 +1837,7 @@ async function loadProjectConfig(configPath = "odla.config.mjs", options = {}) {
|
|
|
1686
1837
|
};
|
|
1687
1838
|
adoptRepoLocalCache(resolve2(rootDir, ".odla/dev-token.json"), local.tokenFile, stderr);
|
|
1688
1839
|
adoptRepoLocalCache(resolve2(rootDir, ".odla/credentials.local.json"), local.credentialsFile, stderr);
|
|
1689
|
-
|
|
1840
|
+
rmSync3(resolve2(rootDir, ".odla/handshake.local.json"), { force: true });
|
|
1690
1841
|
return {
|
|
1691
1842
|
...raw,
|
|
1692
1843
|
configPath: resolved,
|
|
@@ -1794,18 +1945,18 @@ function unique3(values) {
|
|
|
1794
1945
|
}
|
|
1795
1946
|
|
|
1796
1947
|
// src/operator-profiles.ts
|
|
1797
|
-
import { existsSync as
|
|
1798
|
-
import { homedir as
|
|
1799
|
-
import { dirname as
|
|
1800
|
-
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";
|
|
1801
1952
|
function operatorProfileFile() {
|
|
1802
1953
|
return resolve3(
|
|
1803
|
-
clean(
|
|
1954
|
+
clean(process13.env.ODLA_CONTEXT_FILE) ?? join4(homedir2(), ".odla", "contexts.json")
|
|
1804
1955
|
);
|
|
1805
1956
|
}
|
|
1806
1957
|
function resolveOperatorProfile(parsed) {
|
|
1807
1958
|
const fromFlag = clean(stringOpt(parsed.options.context));
|
|
1808
|
-
const fromEnvironment = clean(
|
|
1959
|
+
const fromEnvironment = clean(process13.env.ODLA_CONTEXT);
|
|
1809
1960
|
const name = fromFlag ?? fromEnvironment ?? null;
|
|
1810
1961
|
const file = operatorProfileFile();
|
|
1811
1962
|
if (!name) {
|
|
@@ -1845,10 +1996,10 @@ function removeOperatorProfile(name, file = operatorProfileFile()) {
|
|
|
1845
1996
|
return true;
|
|
1846
1997
|
}
|
|
1847
1998
|
function operatorCredentialFiles(selection) {
|
|
1848
|
-
const base = selection.name ?
|
|
1999
|
+
const base = selection.name ? join4(dirname4(selection.file), "profiles", selection.name) : join4(homedir2(), ".odla");
|
|
1849
2000
|
return {
|
|
1850
|
-
developer:
|
|
1851
|
-
scoped:
|
|
2001
|
+
developer: join4(base, "dev-token.json"),
|
|
2002
|
+
scoped: join4(base, "admin-token.local.json")
|
|
1852
2003
|
};
|
|
1853
2004
|
}
|
|
1854
2005
|
function assertOperatorName(value2, label) {
|
|
@@ -1859,7 +2010,7 @@ function assertOperatorName(value2, label) {
|
|
|
1859
2010
|
}
|
|
1860
2011
|
}
|
|
1861
2012
|
function readOperatorProfiles(file) {
|
|
1862
|
-
if (!
|
|
2013
|
+
if (!existsSync4(file)) return emptyProfiles();
|
|
1863
2014
|
let raw;
|
|
1864
2015
|
try {
|
|
1865
2016
|
raw = JSON.parse(readFileSync4(file, "utf8"));
|
|
@@ -1928,19 +2079,19 @@ async function resolveOperatorContext(parsed, options = {}) {
|
|
|
1928
2079
|
const configArgument = stringOpt(parsed.options.config) ?? "odla.config.mjs";
|
|
1929
2080
|
const configPath = resolve4(configArgument);
|
|
1930
2081
|
const explicitConfig = parsed.options.config !== void 0;
|
|
1931
|
-
const hasConfig =
|
|
2082
|
+
const hasConfig = existsSync5(configPath);
|
|
1932
2083
|
if (!hasConfig && (!options.allowMissingConfig || explicitConfig)) {
|
|
1933
2084
|
await loadProjectConfig(configArgument);
|
|
1934
2085
|
}
|
|
1935
2086
|
const loaded = hasConfig ? await loadProjectConfig(configArgument) : void 0;
|
|
1936
2087
|
const platformFlag = clean2(stringOpt(parsed.options.platform));
|
|
1937
|
-
const platformEnvironment = clean2(
|
|
2088
|
+
const platformEnvironment = clean2(process14.env.ODLA_PLATFORM_URL);
|
|
1938
2089
|
const platformValue = platformAudience(
|
|
1939
2090
|
platformFlag ?? platformEnvironment ?? profile.value?.platform ?? loaded?.platformUrl ?? DEFAULT_PLATFORM2
|
|
1940
2091
|
);
|
|
1941
2092
|
const platformSource = platformFlag ? "flag" : platformEnvironment ? "environment" : profile.value ? "profile" : loaded ? "config" : "default";
|
|
1942
2093
|
const appFlag = clean2(stringOpt(parsed.options.app));
|
|
1943
|
-
const appEnvironment = clean2(
|
|
2094
|
+
const appEnvironment = clean2(process14.env.ODLA_APP_ID);
|
|
1944
2095
|
const appValue = appFlag ?? appEnvironment ?? profile.value?.app ?? loaded?.app.id ?? null;
|
|
1945
2096
|
const appSource = appFlag ? "flag" : appEnvironment ? "environment" : profile.value?.app ? "profile" : loaded ? "config" : "unresolved";
|
|
1946
2097
|
if (appValue) {
|
|
@@ -1954,16 +2105,16 @@ async function resolveOperatorContext(parsed, options = {}) {
|
|
|
1954
2105
|
);
|
|
1955
2106
|
}
|
|
1956
2107
|
const envFlag = clean2(stringOpt(parsed.options.env));
|
|
1957
|
-
const envEnvironment = clean2(
|
|
2108
|
+
const envEnvironment = clean2(process14.env.ODLA_ENV);
|
|
1958
2109
|
const environmentValue = envFlag ?? envEnvironment ?? profile.value?.environment ?? options.defaultEnvironment ?? null;
|
|
1959
2110
|
const environmentSource = envFlag ? "flag" : envEnvironment ? "environment" : profile.value?.environment ? "profile" : options.defaultEnvironment ? "default" : "unresolved";
|
|
1960
2111
|
if (environmentValue) {
|
|
1961
2112
|
assertOperatorName(environmentValue, "environment");
|
|
1962
2113
|
}
|
|
1963
|
-
const rootDir = loaded?.rootDir ??
|
|
2114
|
+
const rootDir = loaded?.rootDir ?? process14.cwd();
|
|
1964
2115
|
const profileCredentials = operatorCredentialFiles(profile);
|
|
1965
|
-
const tokenFile = clean2(
|
|
1966
|
-
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;
|
|
1967
2118
|
const cfg = loaded ? {
|
|
1968
2119
|
...loaded,
|
|
1969
2120
|
platformUrl: platformValue,
|
|
@@ -1985,8 +2136,8 @@ async function resolveOperatorContext(parsed, options = {}) {
|
|
|
1985
2136
|
services: [],
|
|
1986
2137
|
local: {
|
|
1987
2138
|
tokenFile,
|
|
1988
|
-
credentialsFile:
|
|
1989
|
-
devVarsFile:
|
|
2139
|
+
credentialsFile: join5(rootDir, ".odla", "credentials.local.json"),
|
|
2140
|
+
devVarsFile: join5(rootDir, ".dev.vars"),
|
|
1990
2141
|
gitignore: true
|
|
1991
2142
|
}
|
|
1992
2143
|
};
|
|
@@ -2038,6 +2189,11 @@ var SET_OPTIONS = [
|
|
|
2038
2189
|
async function adminCommand(parsed, deps = {}) {
|
|
2039
2190
|
const area = parsed.positionals[1];
|
|
2040
2191
|
const action2 = parsed.positionals[2];
|
|
2192
|
+
if (area !== "ai" && area !== "spend") rejectWord(["admin"], area);
|
|
2193
|
+
if (!acceptedAfter(["admin", area]).includes(action2 ?? "")) rejectWord(["admin", area], action2);
|
|
2194
|
+
if (action2 === "credential" && !acceptedAfter(["admin", "ai", "credential"]).includes(parsed.positionals[3] ?? "")) {
|
|
2195
|
+
rejectWord(["admin", "ai", "credential"], parsed.positionals[3]);
|
|
2196
|
+
}
|
|
2041
2197
|
if (area === "spend") {
|
|
2042
2198
|
assertArgs(parsed, JSON_OPTIONS, 4);
|
|
2043
2199
|
const context2 = await resolveOperatorContext(parsed, { allowMissingConfig: true });
|
|
@@ -2063,14 +2219,11 @@ async function adminCommand(parsed, deps = {}) {
|
|
|
2063
2219
|
out
|
|
2064
2220
|
});
|
|
2065
2221
|
}
|
|
2066
|
-
const credentialSet = action2 === "credential"
|
|
2222
|
+
const credentialSet = action2 === "credential";
|
|
2067
2223
|
const credentials = action2 === "credentials";
|
|
2068
2224
|
const models = action2 === "models";
|
|
2069
2225
|
const usage = action2 === "usage";
|
|
2070
2226
|
const audit = action2 === "audit";
|
|
2071
|
-
if (area !== "ai" || action2 !== "show" && action2 !== "set" && !credentialSet && !credentials && !models && !usage && !audit) {
|
|
2072
|
-
throw new Error('unknown admin command. Try "odla-ai admin ai show".');
|
|
2073
|
-
}
|
|
2074
2227
|
const allowed = credentialSet ? [...CONTEXT_OPTIONS, "from-env", "stdin"] : action2 === "set" ? SET_OPTIONS : models ? [...JSON_OPTIONS, "provider"] : usage ? [...JSON_OPTIONS, "app-id", "env", "run-id", "limit"] : audit ? [...JSON_OPTIONS, "limit"] : JSON_OPTIONS;
|
|
2075
2228
|
assertArgs(parsed, allowed, credentialSet ? 5 : action2 === "set" ? 4 : 3);
|
|
2076
2229
|
const context = await resolveOperatorContext(parsed, { allowMissingConfig: true });
|
|
@@ -2108,7 +2261,7 @@ async function adminCommand(parsed, deps = {}) {
|
|
|
2108
2261
|
}
|
|
2109
2262
|
|
|
2110
2263
|
// src/auth-command.ts
|
|
2111
|
-
import
|
|
2264
|
+
import process15 from "process";
|
|
2112
2265
|
|
|
2113
2266
|
// src/whoami-command.ts
|
|
2114
2267
|
var text2 = (value2) => typeof value2 === "string" && value2.trim() ? value2.trim() : null;
|
|
@@ -2284,9 +2437,7 @@ async function authCommand(parsed, deps = {}) {
|
|
|
2284
2437
|
"json"
|
|
2285
2438
|
], 2);
|
|
2286
2439
|
const action2 = parsed.positionals[1] ?? "login";
|
|
2287
|
-
if (action2 !== "login")
|
|
2288
|
-
throw new Error(`unknown auth action "${action2}". Try "odla-ai auth login --app <id> --email <odla-account>".`);
|
|
2289
|
-
}
|
|
2440
|
+
if (action2 !== "login") rejectWord(["auth"], action2);
|
|
2290
2441
|
const context = await resolveOperatorContext(parsed, {
|
|
2291
2442
|
allowMissingConfig: true,
|
|
2292
2443
|
requireApp: true
|
|
@@ -2294,7 +2445,7 @@ async function authCommand(parsed, deps = {}) {
|
|
|
2294
2445
|
const { cfg } = context;
|
|
2295
2446
|
const out = deps.stdout ?? console;
|
|
2296
2447
|
const doFetch = deps.fetch ?? fetch;
|
|
2297
|
-
const email = stringOpt(parsed.options.email) ??
|
|
2448
|
+
const email = stringOpt(parsed.options.email) ?? process15.env.ODLA_USER_EMAIL?.trim();
|
|
2298
2449
|
if (!email) {
|
|
2299
2450
|
throw new Error(
|
|
2300
2451
|
"auth login requires --email <odla-account> or ODLA_USER_EMAIL; confirm the signed-in odla email instead of using git or GitHub identity"
|
|
@@ -2359,9 +2510,7 @@ function bothTenants(cfg) {
|
|
|
2359
2510
|
// src/agent-command.ts
|
|
2360
2511
|
async function agentCommand(parsed, deps = {}) {
|
|
2361
2512
|
const action2 = parsed.positionals[1];
|
|
2362
|
-
if (action2 !== "jobs" && action2 !== "retry")
|
|
2363
|
-
throw new Error(`unknown agent action "${action2 ?? ""}". Try "odla-ai agent jobs --json".`);
|
|
2364
|
-
}
|
|
2513
|
+
if (action2 !== "jobs" && action2 !== "retry") rejectWord(["agent"], action2);
|
|
2365
2514
|
assertArgs(parsed, ["config", "env", "state", "limit", "json", "token"], action2 === "jobs" ? 2 : 3);
|
|
2366
2515
|
if (action2 === "retry" && (parsed.options.state !== void 0 || parsed.options.limit !== void 0)) {
|
|
2367
2516
|
throw new Error('--state and --limit are supported only by "agent jobs"');
|
|
@@ -2536,9 +2685,7 @@ async function appOwnersCommand(parsed, dependencies = {}) {
|
|
|
2536
2685
|
await (sub === "add" ? ownersAdd(email, options) : ownersRemove(email, options));
|
|
2537
2686
|
return;
|
|
2538
2687
|
}
|
|
2539
|
-
|
|
2540
|
-
`unknown app owners subcommand "${sub}". Try "odla-ai app owners list", "odla-ai app owners add <email>", or "odla-ai app owners remove <email>".`
|
|
2541
|
-
);
|
|
2688
|
+
rejectWord(["app", "owners"], sub);
|
|
2542
2689
|
}
|
|
2543
2690
|
|
|
2544
2691
|
// src/app-rename.ts
|
|
@@ -2644,8 +2791,10 @@ async function appCommand(parsed, dependencies = {}) {
|
|
|
2644
2791
|
return;
|
|
2645
2792
|
}
|
|
2646
2793
|
if (sub !== "archive" && sub !== "restore") {
|
|
2647
|
-
|
|
2648
|
-
|
|
2794
|
+
rejectWord(
|
|
2795
|
+
["app"],
|
|
2796
|
+
sub,
|
|
2797
|
+
"Permanent deletion has no CLI: it requires a signed-in owner in Studio."
|
|
2649
2798
|
);
|
|
2650
2799
|
}
|
|
2651
2800
|
assertArgs(parsed, ["config", "token", "email", "yes", "json"], 2);
|
|
@@ -2664,7 +2813,7 @@ async function appCommand(parsed, dependencies = {}) {
|
|
|
2664
2813
|
|
|
2665
2814
|
// src/brand-command.ts
|
|
2666
2815
|
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
2667
|
-
import { dirname as
|
|
2816
|
+
import { dirname as dirname5, resolve as resolve5 } from "path";
|
|
2668
2817
|
|
|
2669
2818
|
// src/brand-design-unpack.ts
|
|
2670
2819
|
import { gunzipSync } from "zlib";
|
|
@@ -2779,7 +2928,7 @@ async function readBundle(source, deps) {
|
|
|
2779
2928
|
async function writeAll(result, outDir) {
|
|
2780
2929
|
for (const file of result.files) {
|
|
2781
2930
|
const target = resolve5(outDir, file.path);
|
|
2782
|
-
await mkdir(
|
|
2931
|
+
await mkdir(dirname5(target), { recursive: true });
|
|
2783
2932
|
await writeFile(target, file.bytes);
|
|
2784
2933
|
}
|
|
2785
2934
|
}
|
|
@@ -2811,7 +2960,8 @@ async function brandCommand(parsed, deps) {
|
|
|
2811
2960
|
await designUnpack(parsed, deps);
|
|
2812
2961
|
return;
|
|
2813
2962
|
}
|
|
2814
|
-
|
|
2963
|
+
if (subject !== "design") rejectWord(["brand"], subject);
|
|
2964
|
+
rejectWord(["brand", "design"], action2, USAGE);
|
|
2815
2965
|
}
|
|
2816
2966
|
|
|
2817
2967
|
// src/calendar-errors.ts
|
|
@@ -3345,7 +3495,7 @@ import {
|
|
|
3345
3495
|
AppsError,
|
|
3346
3496
|
createAppsClient
|
|
3347
3497
|
} from "@odla-ai/apps";
|
|
3348
|
-
import { join as
|
|
3498
|
+
import { join as join6 } from "path";
|
|
3349
3499
|
|
|
3350
3500
|
// src/config-operation-error.ts
|
|
3351
3501
|
var ConfigOperationCommandError = class extends Error {
|
|
@@ -3770,7 +3920,7 @@ async function operationClient(cfg, options, purpose) {
|
|
|
3770
3920
|
platform: cfg.platformUrl,
|
|
3771
3921
|
scope: "app:config:write",
|
|
3772
3922
|
token: options.token,
|
|
3773
|
-
tokenFile:
|
|
3923
|
+
tokenFile: join6(cfg.rootDir, ".odla", "admin-token.local.json"),
|
|
3774
3924
|
rootDir: cfg.rootDir,
|
|
3775
3925
|
email: options.email,
|
|
3776
3926
|
open: options.open,
|
|
@@ -3825,7 +3975,7 @@ function record4(value2) {
|
|
|
3825
3975
|
|
|
3826
3976
|
// src/config-reconcile-command.ts
|
|
3827
3977
|
import { createAppsClient as createAppsClient2, studioAppSettingsPath } from "@odla-ai/apps";
|
|
3828
|
-
import { join as
|
|
3978
|
+
import { join as join7 } from "path";
|
|
3829
3979
|
|
|
3830
3980
|
// src/config-reconcile.ts
|
|
3831
3981
|
import { appServiceIds as appServiceIds2, orderAppServices as orderAppServices2 } from "@odla-ai/apps";
|
|
@@ -4121,7 +4271,7 @@ async function inspectConfig(options) {
|
|
|
4121
4271
|
platform: cfg.platformUrl,
|
|
4122
4272
|
scope: "app:config:read",
|
|
4123
4273
|
token: options.token,
|
|
4124
|
-
tokenFile:
|
|
4274
|
+
tokenFile: join7(cfg.rootDir, ".odla", "admin-token.local.json"),
|
|
4125
4275
|
rootDir: cfg.rootDir,
|
|
4126
4276
|
email: options.email,
|
|
4127
4277
|
open: options.open,
|
|
@@ -4253,13 +4403,13 @@ function quoteArg2(value2) {
|
|
|
4253
4403
|
|
|
4254
4404
|
// src/doctor-checks.ts
|
|
4255
4405
|
import { execFileSync } from "child_process";
|
|
4256
|
-
import { existsSync as
|
|
4257
|
-
import { join as
|
|
4406
|
+
import { existsSync as existsSync7, readFileSync as readFileSync8 } from "fs";
|
|
4407
|
+
import { join as join9, resolve as resolve6 } from "path";
|
|
4258
4408
|
|
|
4259
4409
|
// src/wrangler.ts
|
|
4260
4410
|
import { spawn as spawn2 } from "child_process";
|
|
4261
|
-
import { existsSync as
|
|
4262
|
-
import { join as
|
|
4411
|
+
import { existsSync as existsSync6, readFileSync as readFileSync7 } from "fs";
|
|
4412
|
+
import { join as join8 } from "path";
|
|
4263
4413
|
var defaultRunner = (cmd, args, opts) => new Promise((resolvePromise, reject) => {
|
|
4264
4414
|
const child = spawn2(cmd, args, { cwd: opts?.cwd, stdio: ["pipe", "pipe", "pipe"] });
|
|
4265
4415
|
let stdout = "";
|
|
@@ -4273,8 +4423,8 @@ var defaultRunner = (cmd, args, opts) => new Promise((resolvePromise, reject) =>
|
|
|
4273
4423
|
var WRANGLER_CONFIG_FILES = ["wrangler.jsonc", "wrangler.json", "wrangler.toml"];
|
|
4274
4424
|
function findWranglerConfig(rootDir) {
|
|
4275
4425
|
for (const name of WRANGLER_CONFIG_FILES) {
|
|
4276
|
-
const path =
|
|
4277
|
-
if (
|
|
4426
|
+
const path = join8(rootDir, name);
|
|
4427
|
+
if (existsSync6(path)) return path;
|
|
4278
4428
|
}
|
|
4279
4429
|
return null;
|
|
4280
4430
|
}
|
|
@@ -4434,7 +4584,7 @@ function wranglerWarnings(rootDir) {
|
|
|
4434
4584
|
const dir = resolve6(rootDir, assets.directory);
|
|
4435
4585
|
if (dir === resolve6(rootDir)) {
|
|
4436
4586
|
warnings.push(`${label}assets.directory is the project root \u2014 point it at a dedicated build dir (wrangler dev fails with "spawn EBADF")`);
|
|
4437
|
-
} else if (
|
|
4587
|
+
} else if (existsSync7(join9(dir, "node_modules"))) {
|
|
4438
4588
|
warnings.push(`${label}assets.directory contains node_modules \u2014 wrangler dev's watcher will exhaust file descriptors`);
|
|
4439
4589
|
}
|
|
4440
4590
|
}
|
|
@@ -4470,7 +4620,7 @@ function o11yProjectWarnings(rootDir) {
|
|
|
4470
4620
|
return warnings;
|
|
4471
4621
|
}
|
|
4472
4622
|
const main = typeof config.main === "string" ? resolve6(rootDir, config.main) : null;
|
|
4473
|
-
if (!main || !
|
|
4623
|
+
if (!main || !existsSync7(main)) {
|
|
4474
4624
|
warnings.push("cannot verify o11y Worker instrumentation \u2014 wrangler main is missing or unreadable");
|
|
4475
4625
|
} else {
|
|
4476
4626
|
let source = "";
|
|
@@ -4499,7 +4649,7 @@ function calendarProjectWarnings(rootDir) {
|
|
|
4499
4649
|
}
|
|
4500
4650
|
function readPackageJson(rootDir) {
|
|
4501
4651
|
try {
|
|
4502
|
-
return JSON.parse(readFileSync8(
|
|
4652
|
+
return JSON.parse(readFileSync8(join9(rootDir, "package.json"), "utf8"));
|
|
4503
4653
|
} catch {
|
|
4504
4654
|
return null;
|
|
4505
4655
|
}
|
|
@@ -4793,14 +4943,14 @@ function harnessOption(value2, flag) {
|
|
|
4793
4943
|
}
|
|
4794
4944
|
|
|
4795
4945
|
// src/init.ts
|
|
4796
|
-
import { existsSync as
|
|
4797
|
-
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";
|
|
4798
4948
|
import { appServiceDefinition as appServiceDefinition3, appServiceIds as appServiceIds3 } from "@odla-ai/apps";
|
|
4799
4949
|
function initProject(options) {
|
|
4800
4950
|
const out = options.stdout ?? console;
|
|
4801
4951
|
const rootDir = resolve7(options.rootDir ?? process.cwd());
|
|
4802
4952
|
const configPath = resolve7(rootDir, options.configPath ?? "odla.config.mjs");
|
|
4803
|
-
if (
|
|
4953
|
+
if (existsSync8(configPath) && !options.force) {
|
|
4804
4954
|
throw new Error(`${configPath} already exists. Pass --force to overwrite.`);
|
|
4805
4955
|
}
|
|
4806
4956
|
if (!/^[a-z0-9][a-z0-9-]*$/.test(options.appId)) {
|
|
@@ -4816,9 +4966,9 @@ function initProject(options) {
|
|
|
4816
4966
|
}
|
|
4817
4967
|
}
|
|
4818
4968
|
const aiProvider = options.aiProvider;
|
|
4819
|
-
|
|
4820
|
-
|
|
4821
|
-
|
|
4969
|
+
mkdirSync2(dirname6(configPath), { recursive: true });
|
|
4970
|
+
mkdirSync2(resolve7(rootDir, "src/odla"), { recursive: true });
|
|
4971
|
+
mkdirSync2(resolve7(rootDir, ".odla"), { recursive: true });
|
|
4822
4972
|
writeFileSync2(configPath, configTemplate({ appId: options.appId, name: options.name, envs, services, aiProvider }));
|
|
4823
4973
|
writeIfMissing(resolve7(rootDir, "src/odla/schema.mjs"), schemaTemplate());
|
|
4824
4974
|
writeIfMissing(resolve7(rootDir, "src/odla/rules.mjs"), rulesTemplate());
|
|
@@ -4828,7 +4978,7 @@ function initProject(options) {
|
|
|
4828
4978
|
out.log("updated .gitignore for local odla credentials");
|
|
4829
4979
|
}
|
|
4830
4980
|
function writeIfMissing(path, text4) {
|
|
4831
|
-
if (
|
|
4981
|
+
if (existsSync8(path)) return;
|
|
4832
4982
|
writeFileSync2(path, text4);
|
|
4833
4983
|
}
|
|
4834
4984
|
function configTemplate(input) {
|
|
@@ -5127,9 +5277,9 @@ function printReport(report5, out) {
|
|
|
5127
5277
|
}
|
|
5128
5278
|
|
|
5129
5279
|
// src/skill.ts
|
|
5130
|
-
import { existsSync as
|
|
5131
|
-
import { homedir as
|
|
5132
|
-
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";
|
|
5133
5283
|
import { fileURLToPath } from "url";
|
|
5134
5284
|
|
|
5135
5285
|
// src/skill-adapters.ts
|
|
@@ -5229,7 +5379,7 @@ function installSkill(options = {}) {
|
|
|
5229
5379
|
if (files.length === 0) throw new Error(`no bundled skills found at ${sourceDir}`);
|
|
5230
5380
|
const harnesses = normalizeHarnesses(options.harnesses, options.global === true);
|
|
5231
5381
|
const root = resolve8(options.dir ?? process.cwd());
|
|
5232
|
-
const home = resolve8(options.homeDir ??
|
|
5382
|
+
const home = resolve8(options.homeDir ?? homedir3());
|
|
5233
5383
|
const plans = /* @__PURE__ */ new Map();
|
|
5234
5384
|
const targets = /* @__PURE__ */ new Map();
|
|
5235
5385
|
const rememberTarget = (harness, target) => {
|
|
@@ -5243,48 +5393,48 @@ function installSkill(options = {}) {
|
|
|
5243
5393
|
plans.set(target, { target, content: content2, boundary, managedMerge });
|
|
5244
5394
|
};
|
|
5245
5395
|
const planSkillTree = (targetDir2, boundary = root) => {
|
|
5246
|
-
for (const rel of files) plan(
|
|
5396
|
+
for (const rel of files) plan(join10(targetDir2, rel), readFileSync9(join10(sourceDir, rel), "utf8"), false, boundary);
|
|
5247
5397
|
};
|
|
5248
5398
|
let targetDir;
|
|
5249
5399
|
if (options.global) {
|
|
5250
|
-
const claudeRoot =
|
|
5251
|
-
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");
|
|
5252
5402
|
targetDir = harnesses[0] === "codex" ? codexRoot : claudeRoot;
|
|
5253
5403
|
for (const harness of harnesses) {
|
|
5254
5404
|
const skillRoot = harness === "claude" ? claudeRoot : codexRoot;
|
|
5255
|
-
planSkillTree(skillRoot, harness === "claude" ? home :
|
|
5405
|
+
planSkillTree(skillRoot, harness === "claude" ? home : dirname7(dirname7(codexRoot)));
|
|
5256
5406
|
rememberTarget(harness, skillRoot);
|
|
5257
5407
|
}
|
|
5258
5408
|
} else {
|
|
5259
|
-
const sharedRoot =
|
|
5409
|
+
const sharedRoot = join10(root, ".agents", "skills");
|
|
5260
5410
|
planSkillTree(sharedRoot);
|
|
5261
|
-
const claudeRoot =
|
|
5411
|
+
const claudeRoot = join10(root, ".claude", "skills");
|
|
5262
5412
|
targetDir = harnesses.includes("claude") ? claudeRoot : sharedRoot;
|
|
5263
5413
|
for (const harness of harnesses) rememberTarget(harness, sharedRoot);
|
|
5264
5414
|
if (harnesses.includes("claude")) {
|
|
5265
5415
|
for (const skill of skillNames(files)) {
|
|
5266
|
-
const canonical2 = readFileSync9(
|
|
5267
|
-
plan(
|
|
5416
|
+
const canonical2 = readFileSync9(join10(sourceDir, skill, "SKILL.md"), "utf8");
|
|
5417
|
+
plan(join10(claudeRoot, skill, "SKILL.md"), claudeAdapter(skill, canonical2));
|
|
5268
5418
|
}
|
|
5269
5419
|
rememberTarget("claude", claudeRoot);
|
|
5270
5420
|
}
|
|
5271
5421
|
if (harnesses.includes("cursor")) {
|
|
5272
|
-
const cursorRule =
|
|
5422
|
+
const cursorRule = join10(root, ".cursor", "rules", "odla.mdc");
|
|
5273
5423
|
plan(cursorRule, CURSOR_RULE);
|
|
5274
5424
|
rememberTarget("cursor", cursorRule);
|
|
5275
5425
|
}
|
|
5276
5426
|
if (harnesses.includes("agents")) {
|
|
5277
|
-
const agentsFile =
|
|
5427
|
+
const agentsFile = join10(root, "AGENTS.md");
|
|
5278
5428
|
plan(agentsFile, managedFileContent(agentsFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
|
|
5279
5429
|
rememberTarget("agents", agentsFile);
|
|
5280
5430
|
}
|
|
5281
5431
|
if (harnesses.includes("copilot")) {
|
|
5282
|
-
const copilotFile =
|
|
5432
|
+
const copilotFile = join10(root, ".github", "copilot-instructions.md");
|
|
5283
5433
|
plan(copilotFile, managedFileContent(copilotFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
|
|
5284
5434
|
rememberTarget("copilot", copilotFile);
|
|
5285
5435
|
}
|
|
5286
5436
|
if (harnesses.includes("gemini")) {
|
|
5287
|
-
const geminiFile =
|
|
5437
|
+
const geminiFile = join10(root, "GEMINI.md");
|
|
5288
5438
|
plan(geminiFile, managedFileContent(geminiFile, PROJECT_INSTRUCTIONS, options.force === true, root), true);
|
|
5289
5439
|
rememberTarget("gemini", geminiFile);
|
|
5290
5440
|
}
|
|
@@ -5298,7 +5448,7 @@ function installSkill(options = {}) {
|
|
|
5298
5448
|
conflicts.push(`${file.target} (redirected by symbolic link ${symlink})`);
|
|
5299
5449
|
continue;
|
|
5300
5450
|
}
|
|
5301
|
-
if (!
|
|
5451
|
+
if (!existsSync9(file.target)) {
|
|
5302
5452
|
writtenPaths.add(file.target);
|
|
5303
5453
|
continue;
|
|
5304
5454
|
}
|
|
@@ -5319,8 +5469,8 @@ ${conflicts.map((f) => ` - ${f}`).join("\n")}`
|
|
|
5319
5469
|
);
|
|
5320
5470
|
}
|
|
5321
5471
|
for (const file of plans.values()) {
|
|
5322
|
-
if (!
|
|
5323
|
-
|
|
5472
|
+
if (!existsSync9(file.target) || readFileSync9(file.target, "utf8") !== file.content) {
|
|
5473
|
+
mkdirSync3(dirname7(file.target), { recursive: true });
|
|
5324
5474
|
writeFileSync3(file.target, file.content);
|
|
5325
5475
|
}
|
|
5326
5476
|
}
|
|
@@ -5362,7 +5512,7 @@ function normalizeHarnesses(values, global) {
|
|
|
5362
5512
|
function managedFileContent(path, block2, force, boundary) {
|
|
5363
5513
|
const symlink = symlinkedComponent(boundary, path);
|
|
5364
5514
|
if (symlink) throw new Error(`refusing to manage ${path}: symbolic link component ${symlink}`);
|
|
5365
|
-
if (!
|
|
5515
|
+
if (!existsSync9(path)) return `${block2}
|
|
5366
5516
|
`;
|
|
5367
5517
|
const current = readFileSync9(path, "utf8");
|
|
5368
5518
|
const start = "<!-- odla-ai agent setup:start -->";
|
|
@@ -5391,7 +5541,7 @@ function symlinkedComponent(boundary, target) {
|
|
|
5391
5541
|
}
|
|
5392
5542
|
let current = boundary;
|
|
5393
5543
|
for (const part of rel.split(sep).filter(Boolean)) {
|
|
5394
|
-
current =
|
|
5544
|
+
current = join10(current, part);
|
|
5395
5545
|
try {
|
|
5396
5546
|
if (lstatSync(current).isSymbolicLink()) return current;
|
|
5397
5547
|
} catch (error) {
|
|
@@ -5404,11 +5554,11 @@ function skillNames(files) {
|
|
|
5404
5554
|
return [...new Set(files.filter((file) => /(^|[\\/])SKILL\.md$/.test(file)).map((file) => file.split(/[\\/]/)[0]))].sort();
|
|
5405
5555
|
}
|
|
5406
5556
|
function listFiles(dir) {
|
|
5407
|
-
if (!
|
|
5557
|
+
if (!existsSync9(dir)) return [];
|
|
5408
5558
|
const results = [];
|
|
5409
5559
|
const walk = (current) => {
|
|
5410
5560
|
for (const entry of readdirSync(current, { withFileTypes: true })) {
|
|
5411
|
-
const path =
|
|
5561
|
+
const path = join10(current, entry.name);
|
|
5412
5562
|
if (entry.isDirectory()) walk(path);
|
|
5413
5563
|
else results.push(relative2(dir, path));
|
|
5414
5564
|
}
|
|
@@ -5620,9 +5770,7 @@ async function secretsCommand(parsed, deps) {
|
|
|
5620
5770
|
return;
|
|
5621
5771
|
}
|
|
5622
5772
|
if (sub !== "push") {
|
|
5623
|
-
|
|
5624
|
-
`unknown secrets subcommand "${sub ?? ""}". Try "odla-ai secrets push --env dev", "odla-ai secrets status --env dev", "odla-ai secrets set <name> --env dev --stdin", or "odla-ai secrets set-clerk-key --env dev --stdin".`
|
|
5625
|
-
);
|
|
5773
|
+
rejectWord(["secrets"], sub);
|
|
5626
5774
|
}
|
|
5627
5775
|
assertArgs(parsed, ["config", "env", "dry-run", "yes"], 2);
|
|
5628
5776
|
await secretsPush({
|
|
@@ -5635,7 +5783,7 @@ async function secretsCommand(parsed, deps) {
|
|
|
5635
5783
|
async function projectCommand(command, parsed, deps) {
|
|
5636
5784
|
if (command === "ai") {
|
|
5637
5785
|
const sub = parsed.positionals[1];
|
|
5638
|
-
if (sub !== "models")
|
|
5786
|
+
if (sub !== "models") rejectWord(["ai"], sub);
|
|
5639
5787
|
assertArgs(parsed, ["config", "env", "provider", "json"], 2);
|
|
5640
5788
|
await aiModels({
|
|
5641
5789
|
configPath: stringOpt(parsed.options.config) ?? "odla.config.mjs",
|
|
@@ -5649,9 +5797,7 @@ async function projectCommand(command, parsed, deps) {
|
|
|
5649
5797
|
}
|
|
5650
5798
|
if (command === "config") {
|
|
5651
5799
|
const sub = parsed.positionals[1];
|
|
5652
|
-
if (sub !== "diff" && sub !== "plan" && sub !== "apply")
|
|
5653
|
-
throw new Error(`unknown config subcommand "${sub ?? ""}". Try "odla-ai config diff --json".`);
|
|
5654
|
-
}
|
|
5800
|
+
if (sub !== "diff" && sub !== "plan" && sub !== "apply") rejectWord(["config"], sub);
|
|
5655
5801
|
assertArgs(
|
|
5656
5802
|
parsed,
|
|
5657
5803
|
sub === "apply" ? ["config", "plan", "idempotency-key", "token", "email", "open", "json"] : ["config", "token", "email", "open", "json"],
|
|
@@ -5679,7 +5825,7 @@ async function projectCommand(command, parsed, deps) {
|
|
|
5679
5825
|
if (command === "operations") {
|
|
5680
5826
|
const sub = parsed.positionals[1];
|
|
5681
5827
|
if (sub !== "get" && sub !== "wait") {
|
|
5682
|
-
|
|
5828
|
+
rejectWord(["operations"], sub);
|
|
5683
5829
|
}
|
|
5684
5830
|
assertArgs(
|
|
5685
5831
|
parsed,
|
|
@@ -5753,7 +5899,7 @@ async function projectCommand(command, parsed, deps) {
|
|
|
5753
5899
|
}
|
|
5754
5900
|
if (command === "skill") {
|
|
5755
5901
|
const sub = parsed.positionals[1];
|
|
5756
|
-
if (sub !== "install")
|
|
5902
|
+
if (sub !== "install") rejectWord(["skill"], sub);
|
|
5757
5903
|
install(parsed, 2, deps);
|
|
5758
5904
|
return true;
|
|
5759
5905
|
}
|
|
@@ -5765,7 +5911,7 @@ async function projectCommand(command, parsed, deps) {
|
|
|
5765
5911
|
}
|
|
5766
5912
|
|
|
5767
5913
|
// src/code-connect.ts
|
|
5768
|
-
import { existsSync as
|
|
5914
|
+
import { existsSync as existsSync10 } from "fs";
|
|
5769
5915
|
import { cpus, hostname, totalmem } from "os";
|
|
5770
5916
|
import { resolve as resolve11 } from "path";
|
|
5771
5917
|
|
|
@@ -5776,7 +5922,7 @@ var HARNESS_PROTOCOL_VERSION = 1;
|
|
|
5776
5922
|
import { execFile, spawn as spawn3 } from "child_process";
|
|
5777
5923
|
import { constants } from "fs";
|
|
5778
5924
|
import { access } from "fs/promises";
|
|
5779
|
-
import { delimiter, join as
|
|
5925
|
+
import { delimiter, join as join11 } from "path";
|
|
5780
5926
|
import { getgid, getuid } from "process";
|
|
5781
5927
|
import { mkdir as mkdir2, mkdtemp, realpath, rm, writeFile as writeFile2 } from "fs/promises";
|
|
5782
5928
|
import { tmpdir } from "os";
|
|
@@ -5794,7 +5940,7 @@ function assertPinnedImage(image) {
|
|
|
5794
5940
|
async function commandAvailable(engine) {
|
|
5795
5941
|
for (const directory of (process.env.PATH ?? "").split(delimiter).filter(Boolean)) {
|
|
5796
5942
|
try {
|
|
5797
|
-
await access(
|
|
5943
|
+
await access(join11(directory, engine), constants.X_OK);
|
|
5798
5944
|
return true;
|
|
5799
5945
|
} catch {
|
|
5800
5946
|
}
|
|
@@ -6492,10 +6638,10 @@ import { randomUUID } from "crypto";
|
|
|
6492
6638
|
import { createHash as createHash22, randomUUID as randomUUID2 } from "crypto";
|
|
6493
6639
|
import { createReadStream } from "fs";
|
|
6494
6640
|
import { lstat as lstat22 } from "fs/promises";
|
|
6495
|
-
import { join as
|
|
6641
|
+
import { join as join13 } from "path";
|
|
6496
6642
|
import { mkdir as mkdir3, mkdtemp as mkdtemp3, rm as rm3, writeFile as writeFile3 } from "fs/promises";
|
|
6497
6643
|
import { tmpdir as tmpdir3 } from "os";
|
|
6498
|
-
import { dirname as
|
|
6644
|
+
import { dirname as dirname9, join as join23, resolve as resolve32, sep as sep23 } from "path";
|
|
6499
6645
|
import {
|
|
6500
6646
|
keepRecentExchanges,
|
|
6501
6647
|
runAgent
|
|
@@ -6893,11 +7039,11 @@ function rollup(graph, kind, options = {}) {
|
|
|
6893
7039
|
}
|
|
6894
7040
|
|
|
6895
7041
|
// ../graph/dist/code/index.js
|
|
6896
|
-
function
|
|
7042
|
+
function dirname8(path) {
|
|
6897
7043
|
const at = path.lastIndexOf("/");
|
|
6898
7044
|
return at <= 0 ? "." : path.slice(0, at);
|
|
6899
7045
|
}
|
|
6900
|
-
function
|
|
7046
|
+
function join12(base, specifier) {
|
|
6901
7047
|
const parts = [];
|
|
6902
7048
|
const segments = `${base === "." ? "" : `${base}/`}${specifier}`.split("/");
|
|
6903
7049
|
for (const segment of segments) {
|
|
@@ -6921,7 +7067,7 @@ var BARE_IMPORT = /^\s*import\s*["']([^"']+)["']/gm;
|
|
|
6921
7067
|
var isSourcePath = (path) => SOURCE.test(path);
|
|
6922
7068
|
function resolveImport(fromPath, specifier, known) {
|
|
6923
7069
|
if (!specifier.startsWith(".")) return null;
|
|
6924
|
-
const base =
|
|
7070
|
+
const base = join12(dirname8(fromPath), specifier);
|
|
6925
7071
|
const candidates = [
|
|
6926
7072
|
base,
|
|
6927
7073
|
base.replace(/\.js$/, ".ts"),
|
|
@@ -7725,7 +7871,7 @@ async function inspectArtifacts(workspaceDir, recipe2) {
|
|
|
7725
7871
|
const receipts = [];
|
|
7726
7872
|
for (const artifact of recipe2.expectedArtifacts ?? []) {
|
|
7727
7873
|
try {
|
|
7728
|
-
const path =
|
|
7874
|
+
const path = join13(workspaceDir, artifact.path);
|
|
7729
7875
|
const info = await lstat22(path);
|
|
7730
7876
|
if (!info.isFile() || info.isSymbolicLink()) {
|
|
7731
7877
|
receipts.push({ artifactId: artifact.id, status: "invalid", bytes: null, digest: null });
|
|
@@ -8014,7 +8160,7 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = tmpdir3()) {
|
|
|
8014
8160
|
if (bytes > SOURCE_MAX_BYTES) throw new TypeError("Code source exceeds its byte bound");
|
|
8015
8161
|
const target = resolve32(sourceDir, file.path);
|
|
8016
8162
|
if (!target.startsWith(`${resolve32(sourceDir)}${sep23}`)) throw new TypeError("Code source path escapes its root");
|
|
8017
|
-
await mkdir3(
|
|
8163
|
+
await mkdir3(dirname9(target), { recursive: true });
|
|
8018
8164
|
await writeFile3(target, file.content, { flag: "wx", mode: 420 });
|
|
8019
8165
|
}
|
|
8020
8166
|
for (const reference of snapshot.references ?? []) {
|
|
@@ -8029,7 +8175,7 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = tmpdir3()) {
|
|
|
8029
8175
|
if (bytes > SOURCE_SET_MAX_BYTES) throw new TypeError("Code source set exceeds its byte bound");
|
|
8030
8176
|
const target = resolve32(sourceDir, path);
|
|
8031
8177
|
if (!target.startsWith(`${resolve32(sourceDir)}${sep23}`)) throw new TypeError("Code reference path escapes its root");
|
|
8032
|
-
await mkdir3(
|
|
8178
|
+
await mkdir3(dirname9(target), { recursive: true });
|
|
8033
8179
|
await writeFile3(target, file.content, { flag: "wx", mode: 292 });
|
|
8034
8180
|
}
|
|
8035
8181
|
}
|
|
@@ -8056,7 +8202,7 @@ async function attachCodeRuntimeReferences(workspace, references) {
|
|
|
8056
8202
|
for (const root of [workspace.baselineDir, workspace.workspaceDir]) {
|
|
8057
8203
|
const target = resolve32(root, path);
|
|
8058
8204
|
if (!target.startsWith(`${resolve32(root)}${sep23}`)) throw new TypeError("Code reference path escapes its root");
|
|
8059
|
-
await mkdir3(
|
|
8205
|
+
await mkdir3(dirname9(target), { recursive: true });
|
|
8060
8206
|
await writeFile3(target, file.content, { flag: "wx", mode: 292 });
|
|
8061
8207
|
}
|
|
8062
8208
|
}
|
|
@@ -10119,7 +10265,7 @@ var CODE_BUILD_RECIPES = Object.freeze([{
|
|
|
10119
10265
|
async function codeConnect(options) {
|
|
10120
10266
|
const cwd = options.cwd ?? process.cwd();
|
|
10121
10267
|
const configPath = resolve11(cwd, options.configPath);
|
|
10122
|
-
const cfg =
|
|
10268
|
+
const cfg = existsSync10(configPath) ? await loadProjectConfig(configPath) : null;
|
|
10123
10269
|
const requestedAppId = options.appId?.trim();
|
|
10124
10270
|
if (requestedAppId && !/^[a-z0-9][a-z0-9-]{1,62}$/.test(requestedAppId)) {
|
|
10125
10271
|
throw new Error("--app-id must be a valid odla app id");
|
|
@@ -10314,9 +10460,7 @@ function grantsUrl(cfg, suffix = "") {
|
|
|
10314
10460
|
async function codeGrantCommand(parsed, deps = {}) {
|
|
10315
10461
|
const action2 = parsed.positionals[2];
|
|
10316
10462
|
if (!isAction(action2)) {
|
|
10317
|
-
|
|
10318
|
-
`unknown code grant action "${action2 ?? ""}". Try "odla-ai code grant list --env dev".`
|
|
10319
|
-
);
|
|
10463
|
+
rejectWord(["code", "grant"], action2);
|
|
10320
10464
|
}
|
|
10321
10465
|
const decides = action2 === "approve" || action2 === "revoke";
|
|
10322
10466
|
assertArgs(parsed, ["config", "env", "json", "token", "email", "open"], decides ? 4 : 3);
|
|
@@ -10436,9 +10580,7 @@ function repositoryUrl(cfg, suffix = "") {
|
|
|
10436
10580
|
async function codeRepositoryCommand(parsed, deps = {}) {
|
|
10437
10581
|
const action2 = parsed.positionals[2];
|
|
10438
10582
|
if (!isAction2(action2)) {
|
|
10439
|
-
|
|
10440
|
-
`unknown code repository action "${action2 ?? ""}". Try "odla-ai code repository show --env dev".`
|
|
10441
|
-
);
|
|
10583
|
+
rejectWord(["code", "repository"], action2);
|
|
10442
10584
|
}
|
|
10443
10585
|
assertArgs(parsed, ["config", "env", "repo", "json", "token", "email", "open"], 3);
|
|
10444
10586
|
const cfg = await loadProjectConfig(stringOpt(parsed.options.config) ?? "odla.config.mjs");
|
|
@@ -10513,9 +10655,7 @@ async function codeCommand(parsed, dependencies) {
|
|
|
10513
10655
|
});
|
|
10514
10656
|
}
|
|
10515
10657
|
if (sub !== "connect") {
|
|
10516
|
-
|
|
10517
|
-
`unknown code subcommand "${sub ?? ""}". Try "odla-ai code connect --env dev" or "odla-ai code grant list --env dev".`
|
|
10518
|
-
);
|
|
10658
|
+
rejectWord(["code"], sub);
|
|
10519
10659
|
}
|
|
10520
10660
|
assertArgs(parsed, [
|
|
10521
10661
|
"config",
|
|
@@ -10554,13 +10694,13 @@ async function codeCommand(parsed, dependencies) {
|
|
|
10554
10694
|
}
|
|
10555
10695
|
|
|
10556
10696
|
// src/operator-credentials.ts
|
|
10557
|
-
import
|
|
10697
|
+
import process16 from "process";
|
|
10558
10698
|
function developerTokenStatus(context, parsed, now = Date.now()) {
|
|
10559
10699
|
const cached = readJsonFile(context.cfg.local.tokenFile);
|
|
10560
10700
|
const cacheStatus = !cached?.token ? "missing" : cached.platform !== context.platform.value ? "other-platform" : (cached.expiresAt ?? 0) <= now + 6e4 ? "expired" : "valid";
|
|
10561
10701
|
const source = clean3(
|
|
10562
10702
|
stringOpt(parsed.options.token)
|
|
10563
|
-
) ? "flag" : clean3(
|
|
10703
|
+
) ? "flag" : clean3(process16.env.ODLA_DEV_TOKEN) ? "environment" : cacheStatus === "valid" ? "cache" : "missing";
|
|
10564
10704
|
return {
|
|
10565
10705
|
source,
|
|
10566
10706
|
cacheFile: context.cfg.local.tokenFile,
|
|
@@ -10652,9 +10792,7 @@ async function contextCommand(parsed, deps = {}) {
|
|
|
10652
10792
|
return;
|
|
10653
10793
|
}
|
|
10654
10794
|
if (action2 !== "show") {
|
|
10655
|
-
|
|
10656
|
-
`unknown context action "${action2 ?? ""}". Try show|list|save|remove.`
|
|
10657
|
-
);
|
|
10795
|
+
rejectWord(["context"], action2);
|
|
10658
10796
|
}
|
|
10659
10797
|
const context = await resolveOperatorContext(parsed, {
|
|
10660
10798
|
allowMissingConfig: true,
|
|
@@ -10710,9 +10848,7 @@ async function responseError(response2) {
|
|
|
10710
10848
|
}
|
|
10711
10849
|
async function credentialCommand(parsed, deps = {}) {
|
|
10712
10850
|
const action2 = parsed.positionals[1] ?? "list";
|
|
10713
|
-
if (action2 !== "list" && action2 !== "revoke")
|
|
10714
|
-
throw new Error(`unknown credentials action "${action2}". Try "odla-ai credentials list".`);
|
|
10715
|
-
}
|
|
10851
|
+
if (action2 !== "list" && action2 !== "revoke") rejectWord(["credentials"], action2);
|
|
10716
10852
|
assertArgs(parsed, ["config", "env", "all", "json", "token", "email", "open"], action2 === "revoke" ? 3 : 2);
|
|
10717
10853
|
const cfg = await loadProjectConfig(stringOpt(parsed.options.config) ?? "odla.config.mjs");
|
|
10718
10854
|
const doFetch = deps.fetch ?? fetch;
|
|
@@ -11121,9 +11257,17 @@ Safety:
|
|
|
11121
11257
|
grant, run it once with --request-grant. That flag ignores ODLA_DEV_TOKEN and
|
|
11122
11258
|
the local cache, prints and opens a fresh exact-project owner-review URL, then
|
|
11123
11259
|
continues provisioning with the approved replacement credential.
|
|
11124
|
-
|
|
11125
|
-
|
|
11126
|
-
|
|
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.
|
|
11127
11271
|
Run Code from a GitHub checkout already connected to an app in Studio; an
|
|
11128
11272
|
odla.config.mjs may select the app explicitly but is not required. With an
|
|
11129
11273
|
enrolled code.session device, the Studio repository selection authorizes the
|
|
@@ -11714,7 +11858,7 @@ async function discussCommand(parsed, deps = {}) {
|
|
|
11714
11858
|
assertArgs(parsed, ALLOWED, 3);
|
|
11715
11859
|
const action2 = parsed.positionals[1];
|
|
11716
11860
|
const id2 = parsed.positionals[2];
|
|
11717
|
-
if (!
|
|
11861
|
+
if (!acceptedAfter(["discuss"]).includes(action2 ?? "")) rejectWord(["discuss"], action2);
|
|
11718
11862
|
const ctx = await buildContext(parsed, deps);
|
|
11719
11863
|
switch (action2) {
|
|
11720
11864
|
case "groups":
|
|
@@ -11738,7 +11882,7 @@ async function discussCommand(parsed, deps = {}) {
|
|
|
11738
11882
|
return;
|
|
11739
11883
|
}
|
|
11740
11884
|
default:
|
|
11741
|
-
|
|
11885
|
+
rejectWord(["discuss"], action2);
|
|
11742
11886
|
}
|
|
11743
11887
|
}
|
|
11744
11888
|
|
|
@@ -12458,7 +12602,7 @@ async function pmWatch(ctx, parsed) {
|
|
|
12458
12602
|
}
|
|
12459
12603
|
|
|
12460
12604
|
// src/pm-project-context.ts
|
|
12461
|
-
import { existsSync as
|
|
12605
|
+
import { existsSync as existsSync11, rmSync as rmSync4 } from "fs";
|
|
12462
12606
|
import { resolve as resolve12 } from "path";
|
|
12463
12607
|
var pmProjectContextFile = () => pmContextFile();
|
|
12464
12608
|
function readPmProjectContext(rootDir) {
|
|
@@ -12475,9 +12619,9 @@ function writePmProjectContext(rootDir, value2) {
|
|
|
12475
12619
|
}
|
|
12476
12620
|
function adoptLegacySelection(rootDir) {
|
|
12477
12621
|
const legacy = resolve12(rootDir, ".odla", "pm-project.local.json");
|
|
12478
|
-
if (!
|
|
12622
|
+
if (!existsSync11(legacy)) return;
|
|
12479
12623
|
const previous = readJsonFile(legacy);
|
|
12480
|
-
|
|
12624
|
+
rmSync4(legacy, { force: true });
|
|
12481
12625
|
if (!isSelection(previous)) return;
|
|
12482
12626
|
const selections = readSelections();
|
|
12483
12627
|
if (selections[previous.appId]) return;
|
|
@@ -12648,7 +12792,7 @@ async function pmCommand(parsed, deps = {}) {
|
|
|
12648
12792
|
assertArgs(parsed, COMMON_OPTIONS, 4);
|
|
12649
12793
|
return pmProjectUse(await buildContext2(parsed, deps), requireId2(parsed.positionals[3], action3));
|
|
12650
12794
|
}
|
|
12651
|
-
|
|
12795
|
+
rejectWord(["pm", "project"], action3);
|
|
12652
12796
|
}
|
|
12653
12797
|
if (word === "next") {
|
|
12654
12798
|
assertArgs(parsed, [...COMMON_OPTIONS, "app", "project", "verbose"], 2);
|
|
@@ -12679,10 +12823,10 @@ async function pmCommand(parsed, deps = {}) {
|
|
|
12679
12823
|
return pmHandoff(await buildContext2(parsed, deps), parsed);
|
|
12680
12824
|
}
|
|
12681
12825
|
const entity = ALIASES[word];
|
|
12682
|
-
if (!entity)
|
|
12826
|
+
if (!entity) rejectWord(["pm"], word);
|
|
12683
12827
|
const requestedAction = parsed.positionals[2] ?? "list";
|
|
12684
12828
|
const action2 = canonicalAction(requestedAction);
|
|
12685
|
-
if (!action2)
|
|
12829
|
+
if (!action2) rejectWord(["pm", word], requestedAction);
|
|
12686
12830
|
assertArgs(parsed, allowedOptions(entity, action2), 4);
|
|
12687
12831
|
if ((action2 === "ready" || action2 === "claim" || action2 === "release") && entity !== "task") {
|
|
12688
12832
|
throw new Error(`pm ${action2} is only valid for tasks`);
|
|
@@ -12768,12 +12912,7 @@ async function platformCommand(parsed, deps = {}) {
|
|
|
12768
12912
|
if (action2 === "status") {
|
|
12769
12913
|
return platformStatus(parsed, deps);
|
|
12770
12914
|
}
|
|
12771
|
-
|
|
12772
|
-
`unknown platform action "${[
|
|
12773
|
-
action2,
|
|
12774
|
-
parsed.positionals[2]
|
|
12775
|
-
].filter(Boolean).join(" ")}". Try "odla-ai platform status --json".`
|
|
12776
|
-
);
|
|
12915
|
+
rejectWord(["platform"], action2);
|
|
12777
12916
|
}
|
|
12778
12917
|
async function platformStatus(parsed, deps) {
|
|
12779
12918
|
assertArgs(
|
|
@@ -13110,9 +13249,7 @@ async function o11yCommand(parsed, deps = {}) {
|
|
|
13110
13249
|
);
|
|
13111
13250
|
const action2 = parsed.positionals[1];
|
|
13112
13251
|
if (action2 !== "status") {
|
|
13113
|
-
|
|
13114
|
-
`unknown o11y action "${action2 ?? ""}". Try "odla-ai o11y status --json".`
|
|
13115
|
-
);
|
|
13252
|
+
rejectWord(["o11y"], action2);
|
|
13116
13253
|
}
|
|
13117
13254
|
const minutes = statusMinutes(
|
|
13118
13255
|
numberOpt(parsed.options.minutes, "--minutes") ?? 60
|
|
@@ -13365,9 +13502,7 @@ var OPTIONS = [
|
|
|
13365
13502
|
async function monitorCommand(parsed, deps = {}) {
|
|
13366
13503
|
assertArgs(parsed, OPTIONS, 3);
|
|
13367
13504
|
const action2 = parsed.positionals[1] ?? "status";
|
|
13368
|
-
if (!["
|
|
13369
|
-
throw new Error(`unknown monitor action "${action2}". Try "odla-ai monitor status --json".`);
|
|
13370
|
-
}
|
|
13505
|
+
if (!acceptedAfter(["monitor"]).includes(action2)) rejectWord(["monitor"], action2);
|
|
13371
13506
|
const context = await resolveOperatorContext(parsed, {
|
|
13372
13507
|
allowMissingConfig: action2 !== "plan" && action2 !== "apply",
|
|
13373
13508
|
requireApp: true
|
|
@@ -13516,7 +13651,7 @@ function percent(value2) {
|
|
|
13516
13651
|
// src/provision.ts
|
|
13517
13652
|
import { AppsError as AppsError2, createAppsClient as createAppsClient3, orderAppServices as orderAppServices3, tenantIdFor as tenantIdFor6 } from "@odla-ai/apps";
|
|
13518
13653
|
import { putSecret as putSecret2 } from "@odla-ai/ai";
|
|
13519
|
-
import
|
|
13654
|
+
import process17 from "process";
|
|
13520
13655
|
|
|
13521
13656
|
// src/integration-provision.ts
|
|
13522
13657
|
import { uuidv7 } from "@odla-ai/db";
|
|
@@ -13954,7 +14089,7 @@ async function provision(options) {
|
|
|
13954
14089
|
await provisionIntegrationSeeds(doFetch, cfg.dbEndpoint, tenantId, dbKey, database.integrations, env, out);
|
|
13955
14090
|
}
|
|
13956
14091
|
if (cfg.services.includes("ai") && cfg.ai?.provider && cfg.ai.keyEnv) {
|
|
13957
|
-
const key =
|
|
14092
|
+
const key = process17.env[cfg.ai.keyEnv];
|
|
13958
14093
|
if (key) {
|
|
13959
14094
|
const secretName = cfg.ai.secretName ?? defaultSecretName(cfg.ai.provider);
|
|
13960
14095
|
await putSecret2({ endpoint: cfg.dbEndpoint, token, fetch: doFetch }, tenantId, secretName, key);
|
|
@@ -13996,190 +14131,9 @@ async function provision(options) {
|
|
|
13996
14131
|
|
|
13997
14132
|
// src/record.ts
|
|
13998
14133
|
import { appendFileSync } from "fs";
|
|
13999
|
-
import
|
|
14000
|
-
|
|
14001
|
-
// src/surface.ts
|
|
14002
|
-
var PM_ACTIONS = {
|
|
14003
|
-
list: {},
|
|
14004
|
-
add: {},
|
|
14005
|
-
create: {},
|
|
14006
|
-
get: {},
|
|
14007
|
-
set: {},
|
|
14008
|
-
update: {},
|
|
14009
|
-
status: {},
|
|
14010
|
-
move: {},
|
|
14011
|
-
done: {},
|
|
14012
|
-
comment: {},
|
|
14013
|
-
comments: {},
|
|
14014
|
-
ref: {},
|
|
14015
|
-
rm: {},
|
|
14016
|
-
delete: {}
|
|
14017
|
-
};
|
|
14018
|
-
var PM_TASK_ACTIONS = {
|
|
14019
|
-
...PM_ACTIONS,
|
|
14020
|
-
ready: {},
|
|
14021
|
-
claim: {},
|
|
14022
|
-
release: {}
|
|
14023
|
-
};
|
|
14024
|
-
var PM_ENTITIES = {
|
|
14025
|
-
...Object.fromEntries(
|
|
14026
|
-
["goal", "conformance", "decision", "bug"].map((entity) => [entity, PM_ACTIONS])
|
|
14027
|
-
),
|
|
14028
|
-
task: PM_TASK_ACTIONS,
|
|
14029
|
-
kanban: PM_TASK_ACTIONS
|
|
14030
|
-
};
|
|
14031
|
-
var COMMAND_SURFACE = {
|
|
14032
|
-
agent: { jobs: {}, retry: {} },
|
|
14033
|
-
ai: { models: {} },
|
|
14034
|
-
admin: {
|
|
14035
|
-
ai: {
|
|
14036
|
-
show: {},
|
|
14037
|
-
set: {},
|
|
14038
|
-
credentials: {},
|
|
14039
|
-
models: {},
|
|
14040
|
-
usage: {},
|
|
14041
|
-
audit: {},
|
|
14042
|
-
credential: { set: {} }
|
|
14043
|
-
}
|
|
14044
|
-
},
|
|
14045
|
-
app: {
|
|
14046
|
-
archive: {},
|
|
14047
|
-
restore: {},
|
|
14048
|
-
export: {},
|
|
14049
|
-
import: {},
|
|
14050
|
-
rename: {},
|
|
14051
|
-
"refresh-sandbox": {},
|
|
14052
|
-
"go-live": {},
|
|
14053
|
-
promote: {},
|
|
14054
|
-
owners: { list: {}, add: {}, remove: {} }
|
|
14055
|
-
},
|
|
14056
|
-
auth: { login: {} },
|
|
14057
|
-
brand: { design: { unpack: {} } },
|
|
14058
|
-
bug: { create: {}, list: {}, report: {} },
|
|
14059
|
-
calendar: { status: {}, calendars: {}, connect: {}, disconnect: {} },
|
|
14060
|
-
capabilities: {},
|
|
14061
|
-
code: {
|
|
14062
|
-
connect: {},
|
|
14063
|
-
grant: { request: {}, list: {}, approve: {}, revoke: {} },
|
|
14064
|
-
repository: { show: {}, list: {}, bind: {} }
|
|
14065
|
-
},
|
|
14066
|
-
config: { diff: {}, plan: {}, apply: {} },
|
|
14067
|
-
context: { show: {}, list: {}, save: {}, remove: {} },
|
|
14068
|
-
credentials: { list: {}, revoke: {} },
|
|
14069
|
-
device: { enroll: {}, list: {}, revoke: {} },
|
|
14070
|
-
// `watch`, `read`, `reply`, and `resolve` take a topic id from there on.
|
|
14071
|
-
discuss: {
|
|
14072
|
-
groups: {},
|
|
14073
|
-
list: {},
|
|
14074
|
-
topics: {},
|
|
14075
|
-
read: {},
|
|
14076
|
-
post: {},
|
|
14077
|
-
reply: {},
|
|
14078
|
-
resolve: {},
|
|
14079
|
-
who: {},
|
|
14080
|
-
watch: {}
|
|
14081
|
-
},
|
|
14082
|
-
doctor: {},
|
|
14083
|
-
help: {},
|
|
14084
|
-
init: {},
|
|
14085
|
-
monitor: { plan: {}, apply: {}, run: {}, status: {}, incidents: {}, report: {} },
|
|
14086
|
-
o11y: { status: {} },
|
|
14087
|
-
operations: { get: {}, wait: {} },
|
|
14088
|
-
platform: {
|
|
14089
|
-
status: {}
|
|
14090
|
-
},
|
|
14091
|
-
pm: {
|
|
14092
|
-
...PM_ENTITIES,
|
|
14093
|
-
project: { list: {}, add: {}, create: {}, use: {} },
|
|
14094
|
-
handoff: {},
|
|
14095
|
-
next: {},
|
|
14096
|
-
start: {},
|
|
14097
|
-
watch: {}
|
|
14098
|
-
},
|
|
14099
|
-
provision: {},
|
|
14100
|
-
runbook: {
|
|
14101
|
-
ask: {},
|
|
14102
|
-
search: {},
|
|
14103
|
-
impact: {},
|
|
14104
|
-
list: {},
|
|
14105
|
-
get: {},
|
|
14106
|
-
cat: {},
|
|
14107
|
-
new: {},
|
|
14108
|
-
edit: {},
|
|
14109
|
-
comment: {},
|
|
14110
|
-
import: {},
|
|
14111
|
-
visibility: {},
|
|
14112
|
-
publish: {},
|
|
14113
|
-
archive: {},
|
|
14114
|
-
history: {},
|
|
14115
|
-
revert: {},
|
|
14116
|
-
rm: {},
|
|
14117
|
-
lint: {}
|
|
14118
|
-
},
|
|
14119
|
-
secrets: { push: {}, status: {}, set: {}, "set-clerk-key": {} },
|
|
14120
|
-
security: {
|
|
14121
|
-
plan: {},
|
|
14122
|
-
sources: {},
|
|
14123
|
-
run: {},
|
|
14124
|
-
status: {},
|
|
14125
|
-
report: {},
|
|
14126
|
-
github: { connect: {}, disconnect: {} }
|
|
14127
|
-
},
|
|
14128
|
-
setup: {},
|
|
14129
|
-
skill: { install: {} },
|
|
14130
|
-
smoke: {},
|
|
14131
|
-
version: {},
|
|
14132
|
-
whoami: {}
|
|
14133
|
-
};
|
|
14134
|
-
function acceptedAfter(path) {
|
|
14135
|
-
let node = COMMAND_SURFACE;
|
|
14136
|
-
for (const word of path) {
|
|
14137
|
-
node = node?.[word];
|
|
14138
|
-
if (!node) return [];
|
|
14139
|
-
}
|
|
14140
|
-
return Object.keys(node).sort();
|
|
14141
|
-
}
|
|
14142
|
-
function validateInvocation(words2) {
|
|
14143
|
-
let node = COMMAND_SURFACE;
|
|
14144
|
-
const walked = [];
|
|
14145
|
-
for (const word of words2) {
|
|
14146
|
-
if (Object.keys(node).length === 0) return null;
|
|
14147
|
-
const next = node[word];
|
|
14148
|
-
if (!next) return { validPrefix: walked.join(" "), word, accepted: Object.keys(node).sort() };
|
|
14149
|
-
walked.push(word);
|
|
14150
|
-
node = next;
|
|
14151
|
-
}
|
|
14152
|
-
return null;
|
|
14153
|
-
}
|
|
14154
|
-
function describeProblem(problem) {
|
|
14155
|
-
const where = problem.validPrefix ? `after "${problem.validPrefix}"` : "as a command";
|
|
14156
|
-
return `"${problem.word}" is not accepted ${where} \u2014 try: ${problem.accepted.join(", ")}`;
|
|
14157
|
-
}
|
|
14158
|
-
function invocationPath(words2) {
|
|
14159
|
-
let node = COMMAND_SURFACE;
|
|
14160
|
-
const path = [];
|
|
14161
|
-
for (const word of words2) {
|
|
14162
|
-
const next = node[word];
|
|
14163
|
-
if (!next) break;
|
|
14164
|
-
path.push(word);
|
|
14165
|
-
node = next;
|
|
14166
|
-
if (Object.keys(node).length === 0) break;
|
|
14167
|
-
}
|
|
14168
|
-
return path;
|
|
14169
|
-
}
|
|
14170
|
-
function surfacePaths(node = COMMAND_SURFACE, prefix = []) {
|
|
14171
|
-
const paths2 = [];
|
|
14172
|
-
for (const [word, child] of Object.entries(node)) {
|
|
14173
|
-
const path = [...prefix, word];
|
|
14174
|
-
paths2.push(path);
|
|
14175
|
-
paths2.push(...surfacePaths(child, path));
|
|
14176
|
-
}
|
|
14177
|
-
return paths2;
|
|
14178
|
-
}
|
|
14179
|
-
|
|
14180
|
-
// src/record.ts
|
|
14134
|
+
import process18 from "process";
|
|
14181
14135
|
function recordInvocation(parsed) {
|
|
14182
|
-
const file =
|
|
14136
|
+
const file = process18.env.ODLA_CLI_RECORD;
|
|
14183
14137
|
if (!file) return;
|
|
14184
14138
|
try {
|
|
14185
14139
|
const entry = {
|
|
@@ -14250,9 +14204,9 @@ function parseDeviceTtl(raw) {
|
|
|
14250
14204
|
}
|
|
14251
14205
|
|
|
14252
14206
|
// src/device-command.ts
|
|
14253
|
-
import { chmodSync as
|
|
14254
|
-
import { dirname as
|
|
14255
|
-
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";
|
|
14256
14210
|
async function deviceCommand(parsed, deps) {
|
|
14257
14211
|
assertArgs(parsed, [
|
|
14258
14212
|
"app",
|
|
@@ -14271,6 +14225,7 @@ async function deviceCommand(parsed, deps) {
|
|
|
14271
14225
|
"wait"
|
|
14272
14226
|
], 3);
|
|
14273
14227
|
const action2 = parsed.positionals[1] ?? "";
|
|
14228
|
+
if (!acceptedAfter(["device"]).includes(action2)) rejectWord(["device"], action2);
|
|
14274
14229
|
const out = deps.stdout ?? console;
|
|
14275
14230
|
const doFetch = deps.fetch ?? fetch;
|
|
14276
14231
|
const cfg = await loadProjectConfig(stringOpt(parsed.options.config));
|
|
@@ -14278,7 +14233,7 @@ async function deviceCommand(parsed, deps) {
|
|
|
14278
14233
|
if (action2 === "enroll") return enroll(parsed, deps, cfg, doFetch, out, json);
|
|
14279
14234
|
if (action2 === "list") return list2(parsed, deps, cfg, doFetch, out, json);
|
|
14280
14235
|
if (action2 === "revoke") return revoke(parsed, deps, cfg, doFetch, out, json);
|
|
14281
|
-
|
|
14236
|
+
rejectWord(["device"], action2);
|
|
14282
14237
|
}
|
|
14283
14238
|
async function enroll(parsed, deps, cfg, doFetch, out, json) {
|
|
14284
14239
|
const name = stringOpt(parsed.options.name) ?? defaultDeviceName();
|
|
@@ -14309,7 +14264,7 @@ async function enroll(parsed, deps, cfg, doFetch, out, json) {
|
|
|
14309
14264
|
headers: { authorization: `Bearer ${token}`, "content-type": "application/json" },
|
|
14310
14265
|
body: JSON.stringify({
|
|
14311
14266
|
name,
|
|
14312
|
-
platform:
|
|
14267
|
+
platform: process19.platform,
|
|
14313
14268
|
appIds: apps,
|
|
14314
14269
|
...capabilities ? { capabilities } : {},
|
|
14315
14270
|
...scopes ? { scopes } : {},
|
|
@@ -14321,14 +14276,14 @@ async function enroll(parsed, deps, cfg, doFetch, out, json) {
|
|
|
14321
14276
|
throw new Error(`device enroll failed: ${body.error?.message ?? `registry returned ${response2.status}`} (${response2.status})`);
|
|
14322
14277
|
}
|
|
14323
14278
|
const path = deviceCredentialPath();
|
|
14324
|
-
|
|
14279
|
+
mkdirSync4(dirname10(path), { recursive: true });
|
|
14325
14280
|
writeFileSync4(path, JSON.stringify({
|
|
14326
14281
|
token: body.token,
|
|
14327
14282
|
platform: cfg.platformUrl.replace(/\/$/, ""),
|
|
14328
14283
|
deviceId: body.device.deviceId,
|
|
14329
14284
|
name
|
|
14330
14285
|
}, null, 2));
|
|
14331
|
-
|
|
14286
|
+
chmodSync2(path, 384);
|
|
14332
14287
|
rememberMachineIdentity(cfg.platformUrl.replace(/\/$/, ""), stringOpt(parsed.options.email));
|
|
14333
14288
|
supersedeAdvisory("credential.expiring");
|
|
14334
14289
|
const reach = body.device.appIds.includes(ALL_OWNED_APPS) ? "every app you own, now and later" : body.device.appIds.join(", ");
|
|
@@ -14428,7 +14383,7 @@ async function scopedToken2(parsed, deps, cfg, doFetch, out, label, scope = "app
|
|
|
14428
14383
|
});
|
|
14429
14384
|
}
|
|
14430
14385
|
function defaultDeviceName() {
|
|
14431
|
-
return `${
|
|
14386
|
+
return `${process19.env.HOSTNAME ?? process19.env.HOST ?? "machine"}-${process19.platform}`;
|
|
14432
14387
|
}
|
|
14433
14388
|
|
|
14434
14389
|
// src/runbook-actions.ts
|
|
@@ -14572,7 +14527,7 @@ async function runbookRemove(ctx, slug) {
|
|
|
14572
14527
|
|
|
14573
14528
|
// src/runbook-import.ts
|
|
14574
14529
|
import { readFileSync as readFileSync11, readdirSync as readdirSync2, statSync } from "fs";
|
|
14575
|
-
import { basename as basename2, join as
|
|
14530
|
+
import { basename as basename2, join as join14 } from "path";
|
|
14576
14531
|
function parseRunbook(text4, slug) {
|
|
14577
14532
|
let rest = text4;
|
|
14578
14533
|
const meta = {};
|
|
@@ -14602,7 +14557,7 @@ function readRunbookDir(dir) {
|
|
|
14602
14557
|
if (!files.length) throw new Error(`no .md files in ${dir}`);
|
|
14603
14558
|
return files.map((file) => {
|
|
14604
14559
|
const slug = basename2(file, ".md");
|
|
14605
|
-
const parsed = parseRunbook(readFileSync11(
|
|
14560
|
+
const parsed = parseRunbook(readFileSync11(join14(dir, file), "utf8"), slug);
|
|
14606
14561
|
return { file, slug, ...parsed, words: parsed.body.split(/\s+/).filter(Boolean).length };
|
|
14607
14562
|
});
|
|
14608
14563
|
}
|
|
@@ -14670,8 +14625,8 @@ async function upsert(ctx, r, visibility) {
|
|
|
14670
14625
|
|
|
14671
14626
|
// src/runbook-impact.ts
|
|
14672
14627
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
14673
|
-
import { existsSync as
|
|
14674
|
-
import { join as
|
|
14628
|
+
import { existsSync as existsSync12, readFileSync as readFileSync12 } from "fs";
|
|
14629
|
+
import { join as join15 } from "path";
|
|
14675
14630
|
|
|
14676
14631
|
// src/runbook-impact-scan.ts
|
|
14677
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$]*)/;
|
|
@@ -14840,8 +14795,8 @@ ${body.split("\n").map((line2) => `+${line2}`).join("\n")}
|
|
|
14840
14795
|
}
|
|
14841
14796
|
function manifestLabeller(root) {
|
|
14842
14797
|
return (workspace) => {
|
|
14843
|
-
const manifest =
|
|
14844
|
-
if (!
|
|
14798
|
+
const manifest = join15(root, workspace, "package.json");
|
|
14799
|
+
if (!existsSync12(manifest)) return void 0;
|
|
14845
14800
|
try {
|
|
14846
14801
|
const name = JSON.parse(readFileSync12(manifest, "utf8")).name;
|
|
14847
14802
|
return typeof name === "string" ? name : void 0;
|
|
@@ -14910,7 +14865,7 @@ function report4(ctx, impacts) {
|
|
|
14910
14865
|
async function runbookImpact(ctx, options, deps = {}) {
|
|
14911
14866
|
const cwd = deps.cwd ?? process.cwd();
|
|
14912
14867
|
const runGit = deps.runGit ?? gitRunner(cwd);
|
|
14913
|
-
const read3 = deps.readRepoFile ?? ((path) => readFileSync12(
|
|
14868
|
+
const read3 = deps.readRepoFile ?? ((path) => readFileSync12(join15(cwd, path), "utf8"));
|
|
14914
14869
|
const surfaces = changedSurfaces(collectDiff(runGit, options.base, read3), manifestLabeller(cwd));
|
|
14915
14870
|
if (!surfaces.length) {
|
|
14916
14871
|
return ctx.out.log(
|
|
@@ -15037,12 +14992,12 @@ async function runbookComment(ctx, slug, body) {
|
|
|
15037
14992
|
|
|
15038
14993
|
// src/runbook-editor.ts
|
|
15039
14994
|
import { spawnSync } from "child_process";
|
|
15040
|
-
import { mkdtempSync, readFileSync as readFileSync13, rmSync as
|
|
14995
|
+
import { mkdtempSync, readFileSync as readFileSync13, rmSync as rmSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
15041
14996
|
import { tmpdir as tmpdir4 } from "os";
|
|
15042
|
-
import { join as
|
|
15043
|
-
import
|
|
14997
|
+
import { join as join16 } from "path";
|
|
14998
|
+
import process20 from "process";
|
|
15044
14999
|
var EDITOR_ENV = ["ODLA_EDITOR", "VISUAL", "EDITOR"];
|
|
15045
|
-
function resolveEditor(env =
|
|
15000
|
+
function resolveEditor(env = process20.env) {
|
|
15046
15001
|
for (const name of EDITOR_ENV) {
|
|
15047
15002
|
const value2 = env[name];
|
|
15048
15003
|
if (value2 && value2.trim()) return value2.trim();
|
|
@@ -15056,8 +15011,8 @@ function defaultRun(command, path) {
|
|
|
15056
15011
|
return result.status ?? 0;
|
|
15057
15012
|
}
|
|
15058
15013
|
function editText(initial, slug, deps = {}) {
|
|
15059
|
-
const env = deps.env ??
|
|
15060
|
-
const interactive = deps.interactive ?? (() => Boolean(
|
|
15014
|
+
const env = deps.env ?? process20.env;
|
|
15015
|
+
const interactive = deps.interactive ?? (() => Boolean(process20.stdin.isTTY));
|
|
15061
15016
|
const editor = resolveEditor(env);
|
|
15062
15017
|
if (!editor)
|
|
15063
15018
|
throw new Error(
|
|
@@ -15065,8 +15020,8 @@ function editText(initial, slug, deps = {}) {
|
|
|
15065
15020
|
);
|
|
15066
15021
|
if (!interactive())
|
|
15067
15022
|
throw new Error(`cannot open an editor without a terminal \u2014 pass --file <path> or --body "\u2026" instead`);
|
|
15068
|
-
const dir = mkdtempSync(
|
|
15069
|
-
const file =
|
|
15023
|
+
const dir = mkdtempSync(join16(tmpdir4(), "odla-runbook-"));
|
|
15024
|
+
const file = join16(dir, `${slug}.md`);
|
|
15070
15025
|
try {
|
|
15071
15026
|
writeFileSync5(file, initial, { mode: 384 });
|
|
15072
15027
|
const code = defaultRunOrInjected(deps)(editor, file);
|
|
@@ -15074,7 +15029,7 @@ function editText(initial, slug, deps = {}) {
|
|
|
15074
15029
|
const edited = readFileSync13(file, "utf8");
|
|
15075
15030
|
return edited === initial ? null : edited;
|
|
15076
15031
|
} finally {
|
|
15077
|
-
|
|
15032
|
+
rmSync5(dir, { recursive: true, force: true });
|
|
15078
15033
|
}
|
|
15079
15034
|
}
|
|
15080
15035
|
var defaultRunOrInjected = (deps) => deps.run ?? defaultRun;
|
|
@@ -15176,6 +15131,7 @@ async function buildContext3(parsed, deps, action2) {
|
|
|
15176
15131
|
async function runbookCommand(parsed, deps = {}) {
|
|
15177
15132
|
const action2 = parsed.positionals[1] ?? "list";
|
|
15178
15133
|
assertArgs(parsed, ALLOWED2, action2 === "ask" || action2 === "search" ? 64 : 4);
|
|
15134
|
+
if (!acceptedAfter(["runbook"]).includes(action2)) rejectWord(["runbook"], action2);
|
|
15179
15135
|
const ctx = await buildContext3(parsed, deps, action2);
|
|
15180
15136
|
const slug = parsed.positionals[2];
|
|
15181
15137
|
switch (action2) {
|
|
@@ -15265,7 +15221,7 @@ async function runbookCommand(parsed, deps = {}) {
|
|
|
15265
15221
|
case "rm":
|
|
15266
15222
|
return runbookRemove(ctx, requireSlug(slug, "rm"));
|
|
15267
15223
|
default:
|
|
15268
|
-
|
|
15224
|
+
rejectWord(["runbook"], action2);
|
|
15269
15225
|
}
|
|
15270
15226
|
}
|
|
15271
15227
|
|
|
@@ -15886,9 +15842,7 @@ async function securityCommand(parsed, dependencies) {
|
|
|
15886
15842
|
else printHostedReport(context.stdout, report5);
|
|
15887
15843
|
return;
|
|
15888
15844
|
}
|
|
15889
|
-
if (sub !== "run")
|
|
15890
|
-
throw new Error('unknown security command. Try "odla-ai security plan", "security sources", or "security run".');
|
|
15891
|
-
}
|
|
15845
|
+
if (sub !== "run") rejectWord(["security"], sub);
|
|
15892
15846
|
const sourceId = stringOpt(parsed.options.source);
|
|
15893
15847
|
if (sourceId) await runSourceSecurityCommand(parsed, dependencies, sourceId);
|
|
15894
15848
|
else await runLocalSecurityCommand(parsed, dependencies);
|
|
@@ -15905,9 +15859,7 @@ async function githubSecurityCommand(parsed, dependencies) {
|
|
|
15905
15859
|
stringOpt(parsed.options.env)
|
|
15906
15860
|
);
|
|
15907
15861
|
}
|
|
15908
|
-
if (action2 !== "connect")
|
|
15909
|
-
throw new Error('unknown security github command. Try "odla-ai security github connect".');
|
|
15910
|
-
}
|
|
15862
|
+
if (action2 !== "connect") rejectWord(["security", "github"], action2);
|
|
15911
15863
|
assertArgs(parsed, ["config", "env", "platform", "repo", "email", "open"], 3);
|
|
15912
15864
|
await requireStudioHuman(
|
|
15913
15865
|
stringOpt(parsed.options.config) ?? "odla.config.mjs",
|
|
@@ -15970,6 +15922,11 @@ async function runCli(argv = process.argv.slice(2), dependencies = {}) {
|
|
|
15970
15922
|
throw error;
|
|
15971
15923
|
} finally {
|
|
15972
15924
|
renderAdvisories(out, advisories);
|
|
15925
|
+
try {
|
|
15926
|
+
const behind = updateNotice();
|
|
15927
|
+
if (behind) out.error(behind);
|
|
15928
|
+
} catch {
|
|
15929
|
+
}
|
|
15973
15930
|
}
|
|
15974
15931
|
}
|
|
15975
15932
|
async function dispatchCli(argv, dependencies) {
|
|
@@ -16050,6 +16007,7 @@ async function dispatchCli(argv, dependencies) {
|
|
|
16050
16007
|
}
|
|
16051
16008
|
if (command === "bug") {
|
|
16052
16009
|
const action2 = parsed.positionals[1] ?? "list";
|
|
16010
|
+
if (!acceptedAfter(["bug"]).includes(action2)) rejectWord(["bug"], action2);
|
|
16053
16011
|
const canonical2 = action2 === "report" || action2 === "create" ? "add" : action2;
|
|
16054
16012
|
await pmCommand({
|
|
16055
16013
|
...parsed,
|
|
@@ -16078,7 +16036,7 @@ async function dispatchCli(argv, dependencies) {
|
|
|
16078
16036
|
return;
|
|
16079
16037
|
}
|
|
16080
16038
|
if (await projectCommand(command, parsed, runtime)) return;
|
|
16081
|
-
|
|
16039
|
+
rejectWord([], command);
|
|
16082
16040
|
}
|
|
16083
16041
|
async function provisionCommand(parsed, dependencies) {
|
|
16084
16042
|
assertArgs(parsed, [
|
|
@@ -16122,7 +16080,7 @@ async function provisionCommand(parsed, dependencies) {
|
|
|
16122
16080
|
async function calendarCommand(parsed, dependencies) {
|
|
16123
16081
|
const sub = parsed.positionals[1];
|
|
16124
16082
|
if (sub !== "status" && sub !== "calendars" && sub !== "connect" && sub !== "disconnect") {
|
|
16125
|
-
|
|
16083
|
+
rejectWord(["calendar"], sub);
|
|
16126
16084
|
}
|
|
16127
16085
|
assertArgs(parsed, ["config", "env", "json", "token", "email", "open", "yes"], 2);
|
|
16128
16086
|
if (sub !== "status" && sub !== "calendars" && parsed.options.json !== void 0) throw new Error(`--json is supported only by calendar status/calendars`);
|
|
@@ -16149,6 +16107,12 @@ export {
|
|
|
16149
16107
|
getScopedPlatformToken,
|
|
16150
16108
|
SYSTEM_AI_PURPOSES,
|
|
16151
16109
|
adminAi,
|
|
16110
|
+
COMMAND_SURFACE,
|
|
16111
|
+
acceptedAfter,
|
|
16112
|
+
validateInvocation,
|
|
16113
|
+
describeProblem,
|
|
16114
|
+
invocationPath,
|
|
16115
|
+
surfacePaths,
|
|
16152
16116
|
calendarServiceConfig,
|
|
16153
16117
|
calendarBookingPageUrl,
|
|
16154
16118
|
GOOGLE_CALENDAR_EVENTS_SCOPE,
|
|
@@ -16186,12 +16150,6 @@ export {
|
|
|
16186
16150
|
monitoringWireConfig,
|
|
16187
16151
|
monitorCommand,
|
|
16188
16152
|
provision,
|
|
16189
|
-
COMMAND_SURFACE,
|
|
16190
|
-
acceptedAfter,
|
|
16191
|
-
validateInvocation,
|
|
16192
|
-
describeProblem,
|
|
16193
|
-
invocationPath,
|
|
16194
|
-
surfacePaths,
|
|
16195
16153
|
runHostedSecurity,
|
|
16196
16154
|
getHostedSecurityIntent,
|
|
16197
16155
|
getHostedSecurityPlan,
|
|
@@ -16203,4 +16161,4 @@ export {
|
|
|
16203
16161
|
isTerminalHostedSecurityStatus,
|
|
16204
16162
|
runCli
|
|
16205
16163
|
};
|
|
16206
|
-
//# sourceMappingURL=chunk-
|
|
16164
|
+
//# sourceMappingURL=chunk-JHB6352A.js.map
|