@odla-ai/cli 0.27.1 → 0.27.2
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/README.md +6 -6
- package/REQUIREMENTS.md +6 -4
- package/dist/bin.cjs +132 -123
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-4JWDJN3P.js → chunk-JWEBGIBR.js} +132 -123
- package/dist/chunk-JWEBGIBR.js.map +1 -0
- package/dist/index.cjs +132 -123
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -6
- package/dist/index.d.ts +6 -6
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/skills/odla/SKILL.md +10 -8
- package/skills/odla-migrate/references/phase-2-db.md +3 -1
- package/skills/odla-migrate/references/troubleshooting.md +4 -3
- package/dist/chunk-4JWDJN3P.js.map +0 -1
package/dist/bin.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// src/admin-ai-auth.ts
|
|
4
4
|
import { existsSync as existsSync2 } from "fs";
|
|
5
5
|
import { join as join2 } from "path";
|
|
6
|
-
import
|
|
6
|
+
import process6 from "process";
|
|
7
7
|
import { requestToken as requestToken2 } from "@odla-ai/db";
|
|
8
8
|
|
|
9
9
|
// src/local.ts
|
|
@@ -132,36 +132,16 @@ function displayPath(path, rootDir = process.cwd()) {
|
|
|
132
132
|
return rel && !rel.startsWith("..") ? rel : path;
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
// src/
|
|
136
|
-
import
|
|
137
|
-
import process2 from "process";
|
|
138
|
-
async function openUrl(url, options = {}) {
|
|
139
|
-
const command = openerFor(options.platform ?? process2.platform);
|
|
140
|
-
const doSpawn = options.spawnImpl ?? spawn;
|
|
141
|
-
await new Promise((resolve13, reject) => {
|
|
142
|
-
const child = doSpawn(command.cmd, [...command.args, url], {
|
|
143
|
-
stdio: "ignore",
|
|
144
|
-
detached: true
|
|
145
|
-
});
|
|
146
|
-
child.once("error", reject);
|
|
147
|
-
child.once("spawn", () => {
|
|
148
|
-
child.unref();
|
|
149
|
-
resolve13();
|
|
150
|
-
});
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
function openerFor(platform) {
|
|
154
|
-
if (platform === "darwin") return { cmd: "open", args: [] };
|
|
155
|
-
if (platform === "win32") return { cmd: "cmd", args: ["/c", "start", ""] };
|
|
156
|
-
return { cmd: "xdg-open", args: [] };
|
|
157
|
-
}
|
|
135
|
+
// src/handshake-approval.ts
|
|
136
|
+
import process3 from "process";
|
|
158
137
|
|
|
159
138
|
// src/approval-prompt.ts
|
|
160
139
|
function approvalLines(prompt) {
|
|
161
140
|
const lines = [""];
|
|
162
141
|
lines.push(
|
|
163
|
-
`\u23F8
|
|
142
|
+
`\u23F8 AUTHORIZATION REQUIRED${prompt.purpose ? ` to ${prompt.purpose}` : ""} \u2014 code ${prompt.userCode}, ${prompt.minutesLeft}m left.`
|
|
164
143
|
);
|
|
144
|
+
lines.push(" OPEN THIS URL IN THE APPROVER'S SIGNED-IN BROWSER NOW:");
|
|
165
145
|
lines.push("");
|
|
166
146
|
lines.push(` ${prompt.approvalUrl}`);
|
|
167
147
|
lines.push("");
|
|
@@ -175,8 +155,9 @@ function approvalLines(prompt) {
|
|
|
175
155
|
lines.push(` No browser was opened (${prompt.browserSkipped}).`);
|
|
176
156
|
}
|
|
177
157
|
lines.push("");
|
|
178
|
-
lines.push(" AGENTS:
|
|
179
|
-
lines.push("
|
|
158
|
+
lines.push(" AGENTS: use browser control to open the URL above now; do not wait silently.");
|
|
159
|
+
lines.push(" If browser control is unavailable, give the exact URL to the human verbatim.");
|
|
160
|
+
lines.push(" You cannot approve it yourself, retry it away, or start a substitute handshake.");
|
|
180
161
|
lines.push("");
|
|
181
162
|
return lines;
|
|
182
163
|
}
|
|
@@ -190,14 +171,73 @@ function reminderLines(prompt) {
|
|
|
190
171
|
];
|
|
191
172
|
}
|
|
192
173
|
|
|
174
|
+
// src/open.ts
|
|
175
|
+
import { spawn } from "child_process";
|
|
176
|
+
import process2 from "process";
|
|
177
|
+
async function openUrl(url, options = {}) {
|
|
178
|
+
const command = openerFor(options.platform ?? process2.platform);
|
|
179
|
+
const doSpawn = options.spawnImpl ?? spawn;
|
|
180
|
+
await new Promise((resolve13, reject) => {
|
|
181
|
+
const child = doSpawn(command.cmd, [...command.args, url], {
|
|
182
|
+
stdio: "ignore",
|
|
183
|
+
detached: true
|
|
184
|
+
});
|
|
185
|
+
child.once("error", reject);
|
|
186
|
+
child.once("spawn", () => {
|
|
187
|
+
child.unref();
|
|
188
|
+
resolve13();
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
function openerFor(platform) {
|
|
193
|
+
if (platform === "darwin") return { cmd: "open", args: [] };
|
|
194
|
+
if (platform === "win32") return { cmd: "cmd", args: ["/c", "start", ""] };
|
|
195
|
+
return { cmd: "xdg-open", args: [] };
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// src/handshake-approval.ts
|
|
199
|
+
function approvalBrowser(options, host = {}) {
|
|
200
|
+
if (options.open === false) return { open: false, reason: "disabled by --no-open" };
|
|
201
|
+
if (options.open === true) return { open: true, mode: "forced" };
|
|
202
|
+
const env = host.env ?? process3.env;
|
|
203
|
+
if (env.VITEST || env.NODE_ENV === "test") {
|
|
204
|
+
return { open: false, reason: "test environment" };
|
|
205
|
+
}
|
|
206
|
+
return { open: true, mode: "auto" };
|
|
207
|
+
}
|
|
208
|
+
async function presentHandshakeApproval(out, prompt, options) {
|
|
209
|
+
const browser = approvalBrowser(options);
|
|
210
|
+
printApproval(out, {
|
|
211
|
+
...prompt,
|
|
212
|
+
browserAttempted: browser.open,
|
|
213
|
+
browserSkipped: browser.reason
|
|
214
|
+
});
|
|
215
|
+
if (!browser.open) {
|
|
216
|
+
out.error(`auth: browser launch skipped (${browser.reason}) \u2014 open the URL above manually`);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
try {
|
|
220
|
+
await (options.openApprovalUrl ?? openUrl)(prompt.approvalUrl);
|
|
221
|
+
out.error("auth: browser open requested for the approval URL above");
|
|
222
|
+
} catch (error) {
|
|
223
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
224
|
+
out.error(`auth: browser did not open (${reason}) \u2014 OPEN THE URL ABOVE NOW`);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
function handshakeUrl(platformUrl, userCode) {
|
|
228
|
+
const url = new URL("/studio", platformUrl);
|
|
229
|
+
url.searchParams.set("code", userCode);
|
|
230
|
+
return url.toString();
|
|
231
|
+
}
|
|
232
|
+
|
|
193
233
|
// src/token.ts
|
|
194
234
|
import { collectToken, OdlaError, requestToken } from "@odla-ai/db";
|
|
195
|
-
import
|
|
235
|
+
import process5 from "process";
|
|
196
236
|
|
|
197
237
|
// src/handshake-state.ts
|
|
198
238
|
import { rmSync } from "fs";
|
|
199
239
|
import { dirname as dirname2, join } from "path";
|
|
200
|
-
import
|
|
240
|
+
import process4 from "process";
|
|
201
241
|
function handshakeFile(cfg) {
|
|
202
242
|
return join(dirname2(cfg.local.tokenFile), "handshake.local.json");
|
|
203
243
|
}
|
|
@@ -205,9 +245,13 @@ var RESUME_MARGIN_MS = 5e3;
|
|
|
205
245
|
function readPendingHandshake(path, platform, email) {
|
|
206
246
|
const pending = readJsonFile(path);
|
|
207
247
|
if (!pending || pending.platform !== platform || pending.email !== email) return null;
|
|
208
|
-
if (typeof pending.userCode !== "string" || typeof pending.deviceCode !== "string"
|
|
248
|
+
if (typeof pending.userCode !== "string" || typeof pending.deviceCode !== "string") return null;
|
|
209
249
|
if (typeof pending.expiresAt !== "number" || pending.expiresAt <= Date.now() + RESUME_MARGIN_MS) return null;
|
|
210
|
-
return {
|
|
250
|
+
return {
|
|
251
|
+
interval: typeof pending.interval === "number" ? pending.interval : 3,
|
|
252
|
+
...pending,
|
|
253
|
+
approvalUrl: handshakeUrl(platform, pending.userCode)
|
|
254
|
+
};
|
|
211
255
|
}
|
|
212
256
|
function writePendingHandshake(path, pending) {
|
|
213
257
|
writePrivateJson(path, pending);
|
|
@@ -233,7 +277,7 @@ function approvalReminder(out, pending, periodMs = 3e4) {
|
|
|
233
277
|
timer.unref?.();
|
|
234
278
|
return () => clearInterval(timer);
|
|
235
279
|
}
|
|
236
|
-
function handshakeWaitMs(waitSeconds, interactive =
|
|
280
|
+
function handshakeWaitMs(waitSeconds, interactive = process4.stdout.isTTY === true) {
|
|
237
281
|
if (waitSeconds !== void 0) return waitSeconds * 1e3;
|
|
238
282
|
return interactive ? void 0 : 9e4;
|
|
239
283
|
}
|
|
@@ -242,14 +286,14 @@ function handshakeWaitMs(waitSeconds, interactive = process3.stdout.isTTY === tr
|
|
|
242
286
|
async function getDeveloperToken(cfg, options, doFetch, out) {
|
|
243
287
|
if (options.token) return options.token;
|
|
244
288
|
const audience = platformAudience(cfg.platformUrl);
|
|
245
|
-
if (
|
|
246
|
-
const declared =
|
|
289
|
+
if (process5.env.ODLA_DEV_TOKEN) {
|
|
290
|
+
const declared = process5.env.ODLA_DEV_TOKEN_AUDIENCE;
|
|
247
291
|
if (declared) {
|
|
248
292
|
if (platformAudience(declared) !== audience) throw new Error("ODLA_DEV_TOKEN_AUDIENCE does not match the configured platform");
|
|
249
293
|
} else if (audience !== "https://odla.ai") {
|
|
250
294
|
throw new Error("ODLA_DEV_TOKEN_AUDIENCE is required for a non-default platform");
|
|
251
295
|
}
|
|
252
|
-
return
|
|
296
|
+
return process5.env.ODLA_DEV_TOKEN;
|
|
253
297
|
}
|
|
254
298
|
const cached = readJsonFile(cfg.local.tokenFile);
|
|
255
299
|
if (cached?.token && cached.platform === audience && (cached.expiresAt ?? 0) > Date.now() + 6e4) {
|
|
@@ -262,7 +306,6 @@ async function getDeveloperToken(cfg, options, doFetch, out) {
|
|
|
262
306
|
doFetch,
|
|
263
307
|
out,
|
|
264
308
|
audience,
|
|
265
|
-
browser: approvalBrowser(options),
|
|
266
309
|
email: handshakeEmail(options.email, cached?.platform === audience ? cached.email : void 0),
|
|
267
310
|
pendingFile: handshakeFile(cfg)
|
|
268
311
|
};
|
|
@@ -278,7 +321,12 @@ async function resumePendingHandshake(ctx, waitMs) {
|
|
|
278
321
|
if (!pending) return null;
|
|
279
322
|
ctx.out.error("");
|
|
280
323
|
ctx.out.error(`auth: resuming pending handshake \u2014 ${approvalHint(pending)}`);
|
|
281
|
-
await
|
|
324
|
+
await presentHandshakeApproval(ctx.out, {
|
|
325
|
+
userCode: pending.userCode,
|
|
326
|
+
approvalUrl: pending.approvalUrl,
|
|
327
|
+
minutesLeft: Math.max(1, Math.floor((pending.expiresAt - Date.now()) / 6e4)),
|
|
328
|
+
purpose: `sign this terminal in as ${ctx.email}`
|
|
329
|
+
}, ctx.options);
|
|
282
330
|
ctx.out.error("");
|
|
283
331
|
const stopReminder = approvalReminder(ctx.out, pending);
|
|
284
332
|
try {
|
|
@@ -315,8 +363,8 @@ async function freshHandshake(ctx, waitMs) {
|
|
|
315
363
|
projectIds: [ctx.cfg.app.id],
|
|
316
364
|
fetch: ctx.doFetch,
|
|
317
365
|
waitMs,
|
|
318
|
-
onCode: async ({ userCode, deviceCode, expiresIn, interval
|
|
319
|
-
const approvalUrl =
|
|
366
|
+
onCode: async ({ userCode, deviceCode, expiresIn, interval }) => {
|
|
367
|
+
const approvalUrl = handshakeUrl(ctx.audience, userCode);
|
|
320
368
|
started = {
|
|
321
369
|
platform: ctx.audience,
|
|
322
370
|
email: ctx.email,
|
|
@@ -327,15 +375,12 @@ async function freshHandshake(ctx, waitMs) {
|
|
|
327
375
|
expiresAt: Date.now() + expiresIn * 1e3
|
|
328
376
|
};
|
|
329
377
|
writePendingHandshake(ctx.pendingFile, started);
|
|
330
|
-
|
|
378
|
+
await presentHandshakeApproval(ctx.out, {
|
|
331
379
|
userCode,
|
|
332
380
|
approvalUrl,
|
|
333
381
|
minutesLeft: Math.floor(expiresIn / 60),
|
|
334
|
-
purpose: `sign this terminal in as ${ctx.email}
|
|
335
|
-
|
|
336
|
-
browserSkipped: ctx.browser.reason
|
|
337
|
-
});
|
|
338
|
-
await launchApproval(ctx, approvalUrl);
|
|
382
|
+
purpose: `sign this terminal in as ${ctx.email}`
|
|
383
|
+
}, ctx.options);
|
|
339
384
|
stopReminder = approvalReminder(ctx.out, started);
|
|
340
385
|
}
|
|
341
386
|
});
|
|
@@ -350,18 +395,6 @@ async function freshHandshake(ctx, waitMs) {
|
|
|
350
395
|
stopReminder?.();
|
|
351
396
|
}
|
|
352
397
|
}
|
|
353
|
-
async function launchApproval(ctx, approvalUrl) {
|
|
354
|
-
if (ctx.browser.open) {
|
|
355
|
-
try {
|
|
356
|
-
await (ctx.options.openApprovalUrl ?? openUrl)(approvalUrl);
|
|
357
|
-
ctx.out.error(`auth: asked the OS to open a browser${ctx.browser.mode === "auto" ? " (auto)" : ""} \u2014 if no tab appeared, use the URL above`);
|
|
358
|
-
} catch (err) {
|
|
359
|
-
ctx.out.error(`auth: could not open browser (${err instanceof Error ? err.message : String(err)})`);
|
|
360
|
-
}
|
|
361
|
-
} else if (ctx.browser.reason) {
|
|
362
|
-
ctx.out.error(`auth: browser launch skipped (${ctx.browser.reason}) \u2014 show the human the URL above`);
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
398
|
function stillPending(pending, email) {
|
|
366
399
|
return new OdlaError(
|
|
367
400
|
"handshake_pending",
|
|
@@ -370,30 +403,12 @@ function stillPending(pending, email) {
|
|
|
370
403
|
);
|
|
371
404
|
}
|
|
372
405
|
function handshakeEmail(value2, cached) {
|
|
373
|
-
const email = (value2 ??
|
|
406
|
+
const email = (value2 ?? process5.env.ODLA_USER_EMAIL ?? cached ?? "").trim().toLowerCase();
|
|
374
407
|
if (email.length > 254 || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
|
375
408
|
throw new Error("a fresh odla handshake requires --email <account> or ODLA_USER_EMAIL");
|
|
376
409
|
}
|
|
377
410
|
return email;
|
|
378
411
|
}
|
|
379
|
-
function approvalBrowser(options, host = {}) {
|
|
380
|
-
if (options.open === true) return { open: true, mode: "forced" };
|
|
381
|
-
if (options.open === false) return { open: false, reason: "disabled by --no-open" };
|
|
382
|
-
const env = host.env ?? process4.env;
|
|
383
|
-
if (env.VITEST || env.NODE_ENV === "test") return { open: false, reason: "test environment" };
|
|
384
|
-
if (env.CI) return { open: false, reason: "CI environment" };
|
|
385
|
-
if (env.SSH_CONNECTION || env.SSH_TTY) return { open: false, reason: "SSH session; pass --open to force" };
|
|
386
|
-
const platform = host.platform ?? process4.platform;
|
|
387
|
-
if (platform !== "darwin" && platform !== "win32" && !env.DISPLAY && !env.WAYLAND_DISPLAY) {
|
|
388
|
-
return { open: false, reason: "no graphical display; pass --open to force" };
|
|
389
|
-
}
|
|
390
|
-
return { open: true, mode: "auto" };
|
|
391
|
-
}
|
|
392
|
-
function handshakeUrl(platformUrl, userCode) {
|
|
393
|
-
const url = new URL("/studio", platformUrl);
|
|
394
|
-
url.searchParams.set("code", userCode);
|
|
395
|
-
return url.toString();
|
|
396
|
-
}
|
|
397
412
|
function platformAudience(value2) {
|
|
398
413
|
let url;
|
|
399
414
|
try {
|
|
@@ -418,7 +433,7 @@ async function getScopedPlatformToken(options) {
|
|
|
418
433
|
async function resolveAdminPlatformToken(options) {
|
|
419
434
|
const audience = platformAudience(options.platform);
|
|
420
435
|
if (options.token) return options.token;
|
|
421
|
-
const fromEnv =
|
|
436
|
+
const fromEnv = process6.env.ODLA_ADMIN_TOKEN;
|
|
422
437
|
if (fromEnv) return audienceBoundEnvToken(fromEnv, audience);
|
|
423
438
|
return scopedToken(
|
|
424
439
|
audience,
|
|
@@ -430,7 +445,7 @@ async function resolveAdminPlatformToken(options) {
|
|
|
430
445
|
}
|
|
431
446
|
function audienceBoundEnvToken(token, platform) {
|
|
432
447
|
const audience = platformAudience(platform);
|
|
433
|
-
const declared =
|
|
448
|
+
const declared = process6.env.ODLA_ADMIN_TOKEN_AUDIENCE;
|
|
434
449
|
if (declared) {
|
|
435
450
|
if (platformAudience(declared) !== audience) throw new Error("ODLA_ADMIN_TOKEN_AUDIENCE does not match the configured platform");
|
|
436
451
|
} else if (audience !== "https://odla.ai") {
|
|
@@ -455,7 +470,7 @@ var SCOPE_PURPOSE = {
|
|
|
455
470
|
};
|
|
456
471
|
async function scopedToken(platform, scope, options, doFetch, out) {
|
|
457
472
|
const audience = platformAudience(platform);
|
|
458
|
-
const rootDir = options.rootDir ??
|
|
473
|
+
const rootDir = options.rootDir ?? process6.cwd();
|
|
459
474
|
const tokenFile = options.tokenFile ?? join2(rootDir, ".odla/admin-token.local.json");
|
|
460
475
|
const cache = options.cache === false ? null : readJsonFile(tokenFile);
|
|
461
476
|
const cached = cache?.platform === audience ? cache.tokens?.[scope] : void 0;
|
|
@@ -470,21 +485,14 @@ async function scopedToken(platform, scope, options, doFetch, out) {
|
|
|
470
485
|
label: options.label ?? `odla CLI (${scope})`,
|
|
471
486
|
scopes: [scope],
|
|
472
487
|
fetch: doFetch,
|
|
473
|
-
onCode: async ({ userCode, expiresIn
|
|
474
|
-
|
|
475
|
-
const browser = approvalBrowser({ open: options.open });
|
|
476
|
-
printApproval(out, {
|
|
488
|
+
onCode: async ({ userCode, expiresIn }) => {
|
|
489
|
+
await presentHandshakeApproval(out, {
|
|
477
490
|
userCode,
|
|
478
|
-
approvalUrl,
|
|
491
|
+
approvalUrl: handshakeUrl(audience, userCode),
|
|
479
492
|
minutesLeft: Math.floor(expiresIn / 60),
|
|
480
493
|
purpose: SCOPE_PURPOSE[scope] ?? `use ${scope}`,
|
|
481
|
-
approver: scope.startsWith("app:") ? "A signed-in app owner" : "A signed-in odla platform admin"
|
|
482
|
-
|
|
483
|
-
browserSkipped: browser.reason
|
|
484
|
-
});
|
|
485
|
-
if (browser.open) {
|
|
486
|
-
await (options.openApprovalUrl ?? openUrl)(approvalUrl).catch(() => void 0);
|
|
487
|
-
}
|
|
494
|
+
approver: scope.startsWith("app:") ? "A signed-in app owner" : "A signed-in odla platform admin"
|
|
495
|
+
}, options);
|
|
488
496
|
}
|
|
489
497
|
});
|
|
490
498
|
if (options.cache !== false) {
|
|
@@ -514,15 +522,15 @@ function requireSystemAiPurpose(value2) {
|
|
|
514
522
|
}
|
|
515
523
|
|
|
516
524
|
// src/admin-ai.ts
|
|
517
|
-
import
|
|
525
|
+
import process8 from "process";
|
|
518
526
|
|
|
519
527
|
// src/secret-input.ts
|
|
520
|
-
import
|
|
528
|
+
import process7 from "process";
|
|
521
529
|
var MAX_BYTES = 64 * 1024;
|
|
522
530
|
async function secretInputValue(options, kind = "credential") {
|
|
523
531
|
if (options.fromEnv && options.stdin) throw new Error("choose exactly one of --from-env or --stdin");
|
|
524
532
|
let value2;
|
|
525
|
-
if (options.fromEnv) value2 =
|
|
533
|
+
if (options.fromEnv) value2 = process7.env[options.fromEnv];
|
|
526
534
|
else if (options.stdin) value2 = await (options.readStdin ?? (() => readSecretStream(kind)))();
|
|
527
535
|
else throw new Error(`${kind} input required: use --from-env <NAME> or --stdin; values are never accepted as arguments`);
|
|
528
536
|
value2 = value2?.replace(/[\r\n]+$/, "");
|
|
@@ -530,7 +538,7 @@ async function secretInputValue(options, kind = "credential") {
|
|
|
530
538
|
if (new TextEncoder().encode(value2).byteLength > MAX_BYTES) throw new Error(`${kind} exceeds 64 KiB`);
|
|
531
539
|
return value2;
|
|
532
540
|
}
|
|
533
|
-
async function readSecretStream(kind, stream =
|
|
541
|
+
async function readSecretStream(kind, stream = process7.stdin) {
|
|
534
542
|
let value2 = "";
|
|
535
543
|
for await (const chunk of stream) {
|
|
536
544
|
value2 += String(chunk);
|
|
@@ -693,7 +701,7 @@ function isRecord2(value2) {
|
|
|
693
701
|
|
|
694
702
|
// src/admin-ai.ts
|
|
695
703
|
async function adminAi(options) {
|
|
696
|
-
const platform = platformAudience(options.platform ??
|
|
704
|
+
const platform = platformAudience(options.platform ?? process8.env.ODLA_PLATFORM ?? "https://odla.ai");
|
|
697
705
|
const doFetch = options.fetch ?? fetch;
|
|
698
706
|
const out = options.stdout ?? console;
|
|
699
707
|
const usageQuery = options.action === "usage" ? adminAiUsageQuery(options) : void 0;
|
|
@@ -7124,7 +7132,7 @@ function record6(value2) {
|
|
|
7124
7132
|
// src/provision.ts
|
|
7125
7133
|
import { createAppsClient as createAppsClient3, orderAppServices as orderAppServices3, tenantIdFor as tenantIdFor4 } from "@odla-ai/apps";
|
|
7126
7134
|
import { putSecret as putSecret2 } from "@odla-ai/ai";
|
|
7127
|
-
import
|
|
7135
|
+
import process9 from "process";
|
|
7128
7136
|
|
|
7129
7137
|
// src/integration-provision.ts
|
|
7130
7138
|
import { uuidv7 } from "@odla-ai/db";
|
|
@@ -7431,7 +7439,7 @@ ${env}: credentials are already saved; retry "odla-ai secrets push --env ${env}$
|
|
|
7431
7439
|
await provisionIntegrationSeeds(doFetch, cfg.dbEndpoint, tenantId, dbKey, database.integrations, env, out);
|
|
7432
7440
|
}
|
|
7433
7441
|
if (cfg.services.includes("ai") && cfg.ai?.provider && cfg.ai.keyEnv) {
|
|
7434
|
-
const key =
|
|
7442
|
+
const key = process9.env[cfg.ai.keyEnv];
|
|
7435
7443
|
if (key) {
|
|
7436
7444
|
const secretName = cfg.ai.secretName ?? defaultSecretName(cfg.ai.provider);
|
|
7437
7445
|
await putSecret2({ endpoint: cfg.dbEndpoint, token, fetch: doFetch }, tenantId, secretName, key);
|
|
@@ -7977,21 +7985,21 @@ function addOption(options, name, value2) {
|
|
|
7977
7985
|
// src/operator-context.ts
|
|
7978
7986
|
import { existsSync as existsSync10 } from "fs";
|
|
7979
7987
|
import { join as join12, resolve as resolve11 } from "path";
|
|
7980
|
-
import
|
|
7988
|
+
import process11 from "process";
|
|
7981
7989
|
|
|
7982
7990
|
// src/operator-profiles.ts
|
|
7983
7991
|
import { existsSync as existsSync9, readFileSync as readFileSync8 } from "fs";
|
|
7984
7992
|
import { homedir as homedir2 } from "os";
|
|
7985
7993
|
import { dirname as dirname7, join as join11, resolve as resolve10 } from "path";
|
|
7986
|
-
import
|
|
7994
|
+
import process10 from "process";
|
|
7987
7995
|
function operatorProfileFile() {
|
|
7988
7996
|
return resolve10(
|
|
7989
|
-
clean(
|
|
7997
|
+
clean(process10.env.ODLA_CONTEXT_FILE) ?? join11(homedir2(), ".odla", "contexts.json")
|
|
7990
7998
|
);
|
|
7991
7999
|
}
|
|
7992
8000
|
function resolveOperatorProfile(parsed) {
|
|
7993
8001
|
const fromFlag = clean(stringOpt(parsed.options.context));
|
|
7994
|
-
const fromEnvironment = clean(
|
|
8002
|
+
const fromEnvironment = clean(process10.env.ODLA_CONTEXT);
|
|
7995
8003
|
const name = fromFlag ?? fromEnvironment ?? null;
|
|
7996
8004
|
const file = operatorProfileFile();
|
|
7997
8005
|
if (!name) {
|
|
@@ -8120,13 +8128,13 @@ async function resolveOperatorContext(parsed, options = {}) {
|
|
|
8120
8128
|
}
|
|
8121
8129
|
const loaded = hasConfig ? await loadProjectConfig(configArgument) : void 0;
|
|
8122
8130
|
const platformFlag = clean2(stringOpt(parsed.options.platform));
|
|
8123
|
-
const platformEnvironment = clean2(
|
|
8131
|
+
const platformEnvironment = clean2(process11.env.ODLA_PLATFORM_URL);
|
|
8124
8132
|
const platformValue = platformAudience(
|
|
8125
8133
|
platformFlag ?? platformEnvironment ?? profile.value?.platform ?? loaded?.platformUrl ?? DEFAULT_PLATFORM2
|
|
8126
8134
|
);
|
|
8127
8135
|
const platformSource = platformFlag ? "flag" : platformEnvironment ? "environment" : profile.value ? "profile" : loaded ? "config" : "default";
|
|
8128
8136
|
const appFlag = clean2(stringOpt(parsed.options.app));
|
|
8129
|
-
const appEnvironment = clean2(
|
|
8137
|
+
const appEnvironment = clean2(process11.env.ODLA_APP_ID);
|
|
8130
8138
|
const appValue = appFlag ?? appEnvironment ?? profile.value?.app ?? loaded?.app.id ?? null;
|
|
8131
8139
|
const appSource = appFlag ? "flag" : appEnvironment ? "environment" : profile.value?.app ? "profile" : loaded ? "config" : "unresolved";
|
|
8132
8140
|
if (appValue) assertOperatorName(appValue, "app");
|
|
@@ -8136,16 +8144,16 @@ async function resolveOperatorContext(parsed, options = {}) {
|
|
|
8136
8144
|
);
|
|
8137
8145
|
}
|
|
8138
8146
|
const envFlag = clean2(stringOpt(parsed.options.env));
|
|
8139
|
-
const envEnvironment = clean2(
|
|
8147
|
+
const envEnvironment = clean2(process11.env.ODLA_ENV);
|
|
8140
8148
|
const environmentValue = envFlag ?? envEnvironment ?? profile.value?.environment ?? options.defaultEnvironment ?? null;
|
|
8141
8149
|
const environmentSource = envFlag ? "flag" : envEnvironment ? "environment" : profile.value?.environment ? "profile" : options.defaultEnvironment ? "default" : "unresolved";
|
|
8142
8150
|
if (environmentValue) {
|
|
8143
8151
|
assertOperatorName(environmentValue, "environment");
|
|
8144
8152
|
}
|
|
8145
|
-
const rootDir = loaded?.rootDir ??
|
|
8153
|
+
const rootDir = loaded?.rootDir ?? process11.cwd();
|
|
8146
8154
|
const profileCredentials = operatorCredentialFiles(profile);
|
|
8147
|
-
const tokenFile = clean2(
|
|
8148
|
-
const scopedTokenFile = clean2(
|
|
8155
|
+
const tokenFile = clean2(process11.env.ODLA_DEV_TOKEN_FILE) ? resolve11(process11.env.ODLA_DEV_TOKEN_FILE) : profile.name ? profileCredentials.developer : loaded?.local.tokenFile ?? profileCredentials.developer;
|
|
8156
|
+
const scopedTokenFile = clean2(process11.env.ODLA_ADMIN_TOKEN_FILE) ? resolve11(process11.env.ODLA_ADMIN_TOKEN_FILE) : profile.name ? profileCredentials.scoped : loaded ? join12(loaded.rootDir, ".odla", "admin-token.local.json") : profileCredentials.scoped;
|
|
8149
8157
|
const cfg = loaded ? {
|
|
8150
8158
|
...loaded,
|
|
8151
8159
|
platformUrl: platformValue,
|
|
@@ -9259,13 +9267,13 @@ async function codeCommand(parsed, dependencies) {
|
|
|
9259
9267
|
}
|
|
9260
9268
|
|
|
9261
9269
|
// src/operator-credentials.ts
|
|
9262
|
-
import
|
|
9270
|
+
import process12 from "process";
|
|
9263
9271
|
function developerTokenStatus(context, parsed, now = Date.now()) {
|
|
9264
9272
|
const cached = readJsonFile(context.cfg.local.tokenFile);
|
|
9265
9273
|
const cacheStatus = !cached?.token ? "missing" : cached.platform !== context.platform.value ? "other-platform" : (cached.expiresAt ?? 0) <= now + 6e4 ? "expired" : "valid";
|
|
9266
9274
|
const source = clean3(
|
|
9267
9275
|
stringOpt(parsed.options.token)
|
|
9268
|
-
) ? "flag" : clean3(
|
|
9276
|
+
) ? "flag" : clean3(process12.env.ODLA_DEV_TOKEN) ? "environment" : cacheStatus === "valid" ? "cache" : "missing";
|
|
9269
9277
|
return {
|
|
9270
9278
|
source,
|
|
9271
9279
|
cacheFile: context.cfg.local.tokenFile,
|
|
@@ -9639,15 +9647,16 @@ Safety:
|
|
|
9639
9647
|
the metadata file. Flags and specific ODLA_* scope variables beat a selected
|
|
9640
9648
|
context, which beats project config. There is no ambient current context.
|
|
9641
9649
|
"context show" reports only provenance and cache state and never authenticates.
|
|
9642
|
-
|
|
9643
|
-
|
|
9644
|
-
|
|
9645
|
-
|
|
9646
|
-
|
|
9650
|
+
Every real CLI handshake prints one canonical /studio?code= approval URL and
|
|
9651
|
+
attempts to open it \u2014 including in CI, SSH, display-less, and agent-driven
|
|
9652
|
+
shells. Only --no-open suppresses the attempt. Browser launch is best-effort:
|
|
9653
|
+
agents with browser control must open that exact URL immediately; otherwise
|
|
9654
|
+
they must give it to the human verbatim. A started handshake is persisted
|
|
9647
9655
|
under .odla/, so a command killed mid-wait loses nothing \u2014 rerunning resumes
|
|
9648
9656
|
the same code. Outside an interactive terminal the wait is capped (90s by
|
|
9649
9657
|
default, --wait <seconds> to change); a still-pending handshake then exits
|
|
9650
|
-
with code 75:
|
|
9658
|
+
with code 75: open the same URL (or relay it if browser control is unavailable),
|
|
9659
|
+
wait for approval, and re-run to collect.
|
|
9651
9660
|
A fresh device handshake requires --email <odla-account> or ODLA_USER_EMAIL.
|
|
9652
9661
|
The email is a non-secret identity hint: never provide a password or session
|
|
9653
9662
|
token. The matching account must already exist, be signed in, explicitly
|
|
@@ -11055,9 +11064,9 @@ async function read2(url, headers, doFetch) {
|
|
|
11055
11064
|
|
|
11056
11065
|
// src/record.ts
|
|
11057
11066
|
import { appendFileSync } from "fs";
|
|
11058
|
-
import
|
|
11067
|
+
import process13 from "process";
|
|
11059
11068
|
function recordInvocation(parsed) {
|
|
11060
|
-
const file =
|
|
11069
|
+
const file = process13.env.ODLA_CLI_RECORD;
|
|
11061
11070
|
if (!file) return;
|
|
11062
11071
|
try {
|
|
11063
11072
|
const entry = {
|
|
@@ -11729,9 +11738,9 @@ import { spawnSync } from "child_process";
|
|
|
11729
11738
|
import { mkdtempSync, readFileSync as readFileSync13, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
11730
11739
|
import { tmpdir as tmpdir4 } from "os";
|
|
11731
11740
|
import { join as join15 } from "path";
|
|
11732
|
-
import
|
|
11741
|
+
import process14 from "process";
|
|
11733
11742
|
var EDITOR_ENV = ["ODLA_EDITOR", "VISUAL", "EDITOR"];
|
|
11734
|
-
function resolveEditor(env =
|
|
11743
|
+
function resolveEditor(env = process14.env) {
|
|
11735
11744
|
for (const name of EDITOR_ENV) {
|
|
11736
11745
|
const value2 = env[name];
|
|
11737
11746
|
if (value2 && value2.trim()) return value2.trim();
|
|
@@ -11745,8 +11754,8 @@ function defaultRun(command, path) {
|
|
|
11745
11754
|
return result.status ?? 0;
|
|
11746
11755
|
}
|
|
11747
11756
|
function editText(initial, slug, deps = {}) {
|
|
11748
|
-
const env = deps.env ??
|
|
11749
|
-
const interactive = deps.interactive ?? (() => Boolean(
|
|
11757
|
+
const env = deps.env ?? process14.env;
|
|
11758
|
+
const interactive = deps.interactive ?? (() => Boolean(process14.stdin.isTTY));
|
|
11750
11759
|
const editor = resolveEditor(env);
|
|
11751
11760
|
if (!editor)
|
|
11752
11761
|
throw new Error(
|
|
@@ -12737,4 +12746,4 @@ export {
|
|
|
12737
12746
|
exitCodeFor,
|
|
12738
12747
|
runCli
|
|
12739
12748
|
};
|
|
12740
|
-
//# sourceMappingURL=chunk-
|
|
12749
|
+
//# sourceMappingURL=chunk-JWEBGIBR.js.map
|