@odla-ai/cli 0.27.0 → 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 +133 -123
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-DS64H4FY.js → chunk-JWEBGIBR.js} +133 -123
- package/dist/chunk-JWEBGIBR.js.map +1 -0
- package/dist/index.cjs +133 -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-DS64H4FY.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 {
|
|
@@ -312,10 +360,11 @@ async function freshHandshake(ctx, waitMs) {
|
|
|
312
360
|
endpoint: ctx.cfg.platformUrl,
|
|
313
361
|
email: ctx.email,
|
|
314
362
|
label: `${ctx.cfg.app.id} provisioner`,
|
|
363
|
+
projectIds: [ctx.cfg.app.id],
|
|
315
364
|
fetch: ctx.doFetch,
|
|
316
365
|
waitMs,
|
|
317
|
-
onCode: async ({ userCode, deviceCode, expiresIn, interval
|
|
318
|
-
const approvalUrl =
|
|
366
|
+
onCode: async ({ userCode, deviceCode, expiresIn, interval }) => {
|
|
367
|
+
const approvalUrl = handshakeUrl(ctx.audience, userCode);
|
|
319
368
|
started = {
|
|
320
369
|
platform: ctx.audience,
|
|
321
370
|
email: ctx.email,
|
|
@@ -326,15 +375,12 @@ async function freshHandshake(ctx, waitMs) {
|
|
|
326
375
|
expiresAt: Date.now() + expiresIn * 1e3
|
|
327
376
|
};
|
|
328
377
|
writePendingHandshake(ctx.pendingFile, started);
|
|
329
|
-
|
|
378
|
+
await presentHandshakeApproval(ctx.out, {
|
|
330
379
|
userCode,
|
|
331
380
|
approvalUrl,
|
|
332
381
|
minutesLeft: Math.floor(expiresIn / 60),
|
|
333
|
-
purpose: `sign this terminal in as ${ctx.email}
|
|
334
|
-
|
|
335
|
-
browserSkipped: ctx.browser.reason
|
|
336
|
-
});
|
|
337
|
-
await launchApproval(ctx, approvalUrl);
|
|
382
|
+
purpose: `sign this terminal in as ${ctx.email}`
|
|
383
|
+
}, ctx.options);
|
|
338
384
|
stopReminder = approvalReminder(ctx.out, started);
|
|
339
385
|
}
|
|
340
386
|
});
|
|
@@ -349,18 +395,6 @@ async function freshHandshake(ctx, waitMs) {
|
|
|
349
395
|
stopReminder?.();
|
|
350
396
|
}
|
|
351
397
|
}
|
|
352
|
-
async function launchApproval(ctx, approvalUrl) {
|
|
353
|
-
if (ctx.browser.open) {
|
|
354
|
-
try {
|
|
355
|
-
await (ctx.options.openApprovalUrl ?? openUrl)(approvalUrl);
|
|
356
|
-
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`);
|
|
357
|
-
} catch (err) {
|
|
358
|
-
ctx.out.error(`auth: could not open browser (${err instanceof Error ? err.message : String(err)})`);
|
|
359
|
-
}
|
|
360
|
-
} else if (ctx.browser.reason) {
|
|
361
|
-
ctx.out.error(`auth: browser launch skipped (${ctx.browser.reason}) \u2014 show the human the URL above`);
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
398
|
function stillPending(pending, email) {
|
|
365
399
|
return new OdlaError(
|
|
366
400
|
"handshake_pending",
|
|
@@ -369,30 +403,12 @@ function stillPending(pending, email) {
|
|
|
369
403
|
);
|
|
370
404
|
}
|
|
371
405
|
function handshakeEmail(value2, cached) {
|
|
372
|
-
const email = (value2 ??
|
|
406
|
+
const email = (value2 ?? process5.env.ODLA_USER_EMAIL ?? cached ?? "").trim().toLowerCase();
|
|
373
407
|
if (email.length > 254 || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
|
374
408
|
throw new Error("a fresh odla handshake requires --email <account> or ODLA_USER_EMAIL");
|
|
375
409
|
}
|
|
376
410
|
return email;
|
|
377
411
|
}
|
|
378
|
-
function approvalBrowser(options, host = {}) {
|
|
379
|
-
if (options.open === true) return { open: true, mode: "forced" };
|
|
380
|
-
if (options.open === false) return { open: false, reason: "disabled by --no-open" };
|
|
381
|
-
const env = host.env ?? process4.env;
|
|
382
|
-
if (env.VITEST || env.NODE_ENV === "test") return { open: false, reason: "test environment" };
|
|
383
|
-
if (env.CI) return { open: false, reason: "CI environment" };
|
|
384
|
-
if (env.SSH_CONNECTION || env.SSH_TTY) return { open: false, reason: "SSH session; pass --open to force" };
|
|
385
|
-
const platform = host.platform ?? process4.platform;
|
|
386
|
-
if (platform !== "darwin" && platform !== "win32" && !env.DISPLAY && !env.WAYLAND_DISPLAY) {
|
|
387
|
-
return { open: false, reason: "no graphical display; pass --open to force" };
|
|
388
|
-
}
|
|
389
|
-
return { open: true, mode: "auto" };
|
|
390
|
-
}
|
|
391
|
-
function handshakeUrl(platformUrl, userCode) {
|
|
392
|
-
const url = new URL("/studio", platformUrl);
|
|
393
|
-
url.searchParams.set("code", userCode);
|
|
394
|
-
return url.toString();
|
|
395
|
-
}
|
|
396
412
|
function platformAudience(value2) {
|
|
397
413
|
let url;
|
|
398
414
|
try {
|
|
@@ -417,7 +433,7 @@ async function getScopedPlatformToken(options) {
|
|
|
417
433
|
async function resolveAdminPlatformToken(options) {
|
|
418
434
|
const audience = platformAudience(options.platform);
|
|
419
435
|
if (options.token) return options.token;
|
|
420
|
-
const fromEnv =
|
|
436
|
+
const fromEnv = process6.env.ODLA_ADMIN_TOKEN;
|
|
421
437
|
if (fromEnv) return audienceBoundEnvToken(fromEnv, audience);
|
|
422
438
|
return scopedToken(
|
|
423
439
|
audience,
|
|
@@ -429,7 +445,7 @@ async function resolveAdminPlatformToken(options) {
|
|
|
429
445
|
}
|
|
430
446
|
function audienceBoundEnvToken(token, platform) {
|
|
431
447
|
const audience = platformAudience(platform);
|
|
432
|
-
const declared =
|
|
448
|
+
const declared = process6.env.ODLA_ADMIN_TOKEN_AUDIENCE;
|
|
433
449
|
if (declared) {
|
|
434
450
|
if (platformAudience(declared) !== audience) throw new Error("ODLA_ADMIN_TOKEN_AUDIENCE does not match the configured platform");
|
|
435
451
|
} else if (audience !== "https://odla.ai") {
|
|
@@ -454,7 +470,7 @@ var SCOPE_PURPOSE = {
|
|
|
454
470
|
};
|
|
455
471
|
async function scopedToken(platform, scope, options, doFetch, out) {
|
|
456
472
|
const audience = platformAudience(platform);
|
|
457
|
-
const rootDir = options.rootDir ??
|
|
473
|
+
const rootDir = options.rootDir ?? process6.cwd();
|
|
458
474
|
const tokenFile = options.tokenFile ?? join2(rootDir, ".odla/admin-token.local.json");
|
|
459
475
|
const cache = options.cache === false ? null : readJsonFile(tokenFile);
|
|
460
476
|
const cached = cache?.platform === audience ? cache.tokens?.[scope] : void 0;
|
|
@@ -469,21 +485,14 @@ async function scopedToken(platform, scope, options, doFetch, out) {
|
|
|
469
485
|
label: options.label ?? `odla CLI (${scope})`,
|
|
470
486
|
scopes: [scope],
|
|
471
487
|
fetch: doFetch,
|
|
472
|
-
onCode: async ({ userCode, expiresIn
|
|
473
|
-
|
|
474
|
-
const browser = approvalBrowser({ open: options.open });
|
|
475
|
-
printApproval(out, {
|
|
488
|
+
onCode: async ({ userCode, expiresIn }) => {
|
|
489
|
+
await presentHandshakeApproval(out, {
|
|
476
490
|
userCode,
|
|
477
|
-
approvalUrl,
|
|
491
|
+
approvalUrl: handshakeUrl(audience, userCode),
|
|
478
492
|
minutesLeft: Math.floor(expiresIn / 60),
|
|
479
493
|
purpose: SCOPE_PURPOSE[scope] ?? `use ${scope}`,
|
|
480
|
-
approver: scope.startsWith("app:") ? "A signed-in app owner" : "A signed-in odla platform admin"
|
|
481
|
-
|
|
482
|
-
browserSkipped: browser.reason
|
|
483
|
-
});
|
|
484
|
-
if (browser.open) {
|
|
485
|
-
await (options.openApprovalUrl ?? openUrl)(approvalUrl).catch(() => void 0);
|
|
486
|
-
}
|
|
494
|
+
approver: scope.startsWith("app:") ? "A signed-in app owner" : "A signed-in odla platform admin"
|
|
495
|
+
}, options);
|
|
487
496
|
}
|
|
488
497
|
});
|
|
489
498
|
if (options.cache !== false) {
|
|
@@ -513,15 +522,15 @@ function requireSystemAiPurpose(value2) {
|
|
|
513
522
|
}
|
|
514
523
|
|
|
515
524
|
// src/admin-ai.ts
|
|
516
|
-
import
|
|
525
|
+
import process8 from "process";
|
|
517
526
|
|
|
518
527
|
// src/secret-input.ts
|
|
519
|
-
import
|
|
528
|
+
import process7 from "process";
|
|
520
529
|
var MAX_BYTES = 64 * 1024;
|
|
521
530
|
async function secretInputValue(options, kind = "credential") {
|
|
522
531
|
if (options.fromEnv && options.stdin) throw new Error("choose exactly one of --from-env or --stdin");
|
|
523
532
|
let value2;
|
|
524
|
-
if (options.fromEnv) value2 =
|
|
533
|
+
if (options.fromEnv) value2 = process7.env[options.fromEnv];
|
|
525
534
|
else if (options.stdin) value2 = await (options.readStdin ?? (() => readSecretStream(kind)))();
|
|
526
535
|
else throw new Error(`${kind} input required: use --from-env <NAME> or --stdin; values are never accepted as arguments`);
|
|
527
536
|
value2 = value2?.replace(/[\r\n]+$/, "");
|
|
@@ -529,7 +538,7 @@ async function secretInputValue(options, kind = "credential") {
|
|
|
529
538
|
if (new TextEncoder().encode(value2).byteLength > MAX_BYTES) throw new Error(`${kind} exceeds 64 KiB`);
|
|
530
539
|
return value2;
|
|
531
540
|
}
|
|
532
|
-
async function readSecretStream(kind, stream =
|
|
541
|
+
async function readSecretStream(kind, stream = process7.stdin) {
|
|
533
542
|
let value2 = "";
|
|
534
543
|
for await (const chunk of stream) {
|
|
535
544
|
value2 += String(chunk);
|
|
@@ -692,7 +701,7 @@ function isRecord2(value2) {
|
|
|
692
701
|
|
|
693
702
|
// src/admin-ai.ts
|
|
694
703
|
async function adminAi(options) {
|
|
695
|
-
const platform = platformAudience(options.platform ??
|
|
704
|
+
const platform = platformAudience(options.platform ?? process8.env.ODLA_PLATFORM ?? "https://odla.ai");
|
|
696
705
|
const doFetch = options.fetch ?? fetch;
|
|
697
706
|
const out = options.stdout ?? console;
|
|
698
707
|
const usageQuery = options.action === "usage" ? adminAiUsageQuery(options) : void 0;
|
|
@@ -7123,7 +7132,7 @@ function record6(value2) {
|
|
|
7123
7132
|
// src/provision.ts
|
|
7124
7133
|
import { createAppsClient as createAppsClient3, orderAppServices as orderAppServices3, tenantIdFor as tenantIdFor4 } from "@odla-ai/apps";
|
|
7125
7134
|
import { putSecret as putSecret2 } from "@odla-ai/ai";
|
|
7126
|
-
import
|
|
7135
|
+
import process9 from "process";
|
|
7127
7136
|
|
|
7128
7137
|
// src/integration-provision.ts
|
|
7129
7138
|
import { uuidv7 } from "@odla-ai/db";
|
|
@@ -7430,7 +7439,7 @@ ${env}: credentials are already saved; retry "odla-ai secrets push --env ${env}$
|
|
|
7430
7439
|
await provisionIntegrationSeeds(doFetch, cfg.dbEndpoint, tenantId, dbKey, database.integrations, env, out);
|
|
7431
7440
|
}
|
|
7432
7441
|
if (cfg.services.includes("ai") && cfg.ai?.provider && cfg.ai.keyEnv) {
|
|
7433
|
-
const key =
|
|
7442
|
+
const key = process9.env[cfg.ai.keyEnv];
|
|
7434
7443
|
if (key) {
|
|
7435
7444
|
const secretName = cfg.ai.secretName ?? defaultSecretName(cfg.ai.provider);
|
|
7436
7445
|
await putSecret2({ endpoint: cfg.dbEndpoint, token, fetch: doFetch }, tenantId, secretName, key);
|
|
@@ -7976,21 +7985,21 @@ function addOption(options, name, value2) {
|
|
|
7976
7985
|
// src/operator-context.ts
|
|
7977
7986
|
import { existsSync as existsSync10 } from "fs";
|
|
7978
7987
|
import { join as join12, resolve as resolve11 } from "path";
|
|
7979
|
-
import
|
|
7988
|
+
import process11 from "process";
|
|
7980
7989
|
|
|
7981
7990
|
// src/operator-profiles.ts
|
|
7982
7991
|
import { existsSync as existsSync9, readFileSync as readFileSync8 } from "fs";
|
|
7983
7992
|
import { homedir as homedir2 } from "os";
|
|
7984
7993
|
import { dirname as dirname7, join as join11, resolve as resolve10 } from "path";
|
|
7985
|
-
import
|
|
7994
|
+
import process10 from "process";
|
|
7986
7995
|
function operatorProfileFile() {
|
|
7987
7996
|
return resolve10(
|
|
7988
|
-
clean(
|
|
7997
|
+
clean(process10.env.ODLA_CONTEXT_FILE) ?? join11(homedir2(), ".odla", "contexts.json")
|
|
7989
7998
|
);
|
|
7990
7999
|
}
|
|
7991
8000
|
function resolveOperatorProfile(parsed) {
|
|
7992
8001
|
const fromFlag = clean(stringOpt(parsed.options.context));
|
|
7993
|
-
const fromEnvironment = clean(
|
|
8002
|
+
const fromEnvironment = clean(process10.env.ODLA_CONTEXT);
|
|
7994
8003
|
const name = fromFlag ?? fromEnvironment ?? null;
|
|
7995
8004
|
const file = operatorProfileFile();
|
|
7996
8005
|
if (!name) {
|
|
@@ -8119,13 +8128,13 @@ async function resolveOperatorContext(parsed, options = {}) {
|
|
|
8119
8128
|
}
|
|
8120
8129
|
const loaded = hasConfig ? await loadProjectConfig(configArgument) : void 0;
|
|
8121
8130
|
const platformFlag = clean2(stringOpt(parsed.options.platform));
|
|
8122
|
-
const platformEnvironment = clean2(
|
|
8131
|
+
const platformEnvironment = clean2(process11.env.ODLA_PLATFORM_URL);
|
|
8123
8132
|
const platformValue = platformAudience(
|
|
8124
8133
|
platformFlag ?? platformEnvironment ?? profile.value?.platform ?? loaded?.platformUrl ?? DEFAULT_PLATFORM2
|
|
8125
8134
|
);
|
|
8126
8135
|
const platformSource = platformFlag ? "flag" : platformEnvironment ? "environment" : profile.value ? "profile" : loaded ? "config" : "default";
|
|
8127
8136
|
const appFlag = clean2(stringOpt(parsed.options.app));
|
|
8128
|
-
const appEnvironment = clean2(
|
|
8137
|
+
const appEnvironment = clean2(process11.env.ODLA_APP_ID);
|
|
8129
8138
|
const appValue = appFlag ?? appEnvironment ?? profile.value?.app ?? loaded?.app.id ?? null;
|
|
8130
8139
|
const appSource = appFlag ? "flag" : appEnvironment ? "environment" : profile.value?.app ? "profile" : loaded ? "config" : "unresolved";
|
|
8131
8140
|
if (appValue) assertOperatorName(appValue, "app");
|
|
@@ -8135,16 +8144,16 @@ async function resolveOperatorContext(parsed, options = {}) {
|
|
|
8135
8144
|
);
|
|
8136
8145
|
}
|
|
8137
8146
|
const envFlag = clean2(stringOpt(parsed.options.env));
|
|
8138
|
-
const envEnvironment = clean2(
|
|
8147
|
+
const envEnvironment = clean2(process11.env.ODLA_ENV);
|
|
8139
8148
|
const environmentValue = envFlag ?? envEnvironment ?? profile.value?.environment ?? options.defaultEnvironment ?? null;
|
|
8140
8149
|
const environmentSource = envFlag ? "flag" : envEnvironment ? "environment" : profile.value?.environment ? "profile" : options.defaultEnvironment ? "default" : "unresolved";
|
|
8141
8150
|
if (environmentValue) {
|
|
8142
8151
|
assertOperatorName(environmentValue, "environment");
|
|
8143
8152
|
}
|
|
8144
|
-
const rootDir = loaded?.rootDir ??
|
|
8153
|
+
const rootDir = loaded?.rootDir ?? process11.cwd();
|
|
8145
8154
|
const profileCredentials = operatorCredentialFiles(profile);
|
|
8146
|
-
const tokenFile = clean2(
|
|
8147
|
-
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;
|
|
8148
8157
|
const cfg = loaded ? {
|
|
8149
8158
|
...loaded,
|
|
8150
8159
|
platformUrl: platformValue,
|
|
@@ -9258,13 +9267,13 @@ async function codeCommand(parsed, dependencies) {
|
|
|
9258
9267
|
}
|
|
9259
9268
|
|
|
9260
9269
|
// src/operator-credentials.ts
|
|
9261
|
-
import
|
|
9270
|
+
import process12 from "process";
|
|
9262
9271
|
function developerTokenStatus(context, parsed, now = Date.now()) {
|
|
9263
9272
|
const cached = readJsonFile(context.cfg.local.tokenFile);
|
|
9264
9273
|
const cacheStatus = !cached?.token ? "missing" : cached.platform !== context.platform.value ? "other-platform" : (cached.expiresAt ?? 0) <= now + 6e4 ? "expired" : "valid";
|
|
9265
9274
|
const source = clean3(
|
|
9266
9275
|
stringOpt(parsed.options.token)
|
|
9267
|
-
) ? "flag" : clean3(
|
|
9276
|
+
) ? "flag" : clean3(process12.env.ODLA_DEV_TOKEN) ? "environment" : cacheStatus === "valid" ? "cache" : "missing";
|
|
9268
9277
|
return {
|
|
9269
9278
|
source,
|
|
9270
9279
|
cacheFile: context.cfg.local.tokenFile,
|
|
@@ -9638,15 +9647,16 @@ Safety:
|
|
|
9638
9647
|
the metadata file. Flags and specific ODLA_* scope variables beat a selected
|
|
9639
9648
|
context, which beats project config. There is no ambient current context.
|
|
9640
9649
|
"context show" reports only provenance and cache state and never authenticates.
|
|
9641
|
-
|
|
9642
|
-
|
|
9643
|
-
|
|
9644
|
-
|
|
9645
|
-
|
|
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
|
|
9646
9655
|
under .odla/, so a command killed mid-wait loses nothing \u2014 rerunning resumes
|
|
9647
9656
|
the same code. Outside an interactive terminal the wait is capped (90s by
|
|
9648
9657
|
default, --wait <seconds> to change); a still-pending handshake then exits
|
|
9649
|
-
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.
|
|
9650
9660
|
A fresh device handshake requires --email <odla-account> or ODLA_USER_EMAIL.
|
|
9651
9661
|
The email is a non-secret identity hint: never provide a password or session
|
|
9652
9662
|
token. The matching account must already exist, be signed in, explicitly
|
|
@@ -11054,9 +11064,9 @@ async function read2(url, headers, doFetch) {
|
|
|
11054
11064
|
|
|
11055
11065
|
// src/record.ts
|
|
11056
11066
|
import { appendFileSync } from "fs";
|
|
11057
|
-
import
|
|
11067
|
+
import process13 from "process";
|
|
11058
11068
|
function recordInvocation(parsed) {
|
|
11059
|
-
const file =
|
|
11069
|
+
const file = process13.env.ODLA_CLI_RECORD;
|
|
11060
11070
|
if (!file) return;
|
|
11061
11071
|
try {
|
|
11062
11072
|
const entry = {
|
|
@@ -11728,9 +11738,9 @@ import { spawnSync } from "child_process";
|
|
|
11728
11738
|
import { mkdtempSync, readFileSync as readFileSync13, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
11729
11739
|
import { tmpdir as tmpdir4 } from "os";
|
|
11730
11740
|
import { join as join15 } from "path";
|
|
11731
|
-
import
|
|
11741
|
+
import process14 from "process";
|
|
11732
11742
|
var EDITOR_ENV = ["ODLA_EDITOR", "VISUAL", "EDITOR"];
|
|
11733
|
-
function resolveEditor(env =
|
|
11743
|
+
function resolveEditor(env = process14.env) {
|
|
11734
11744
|
for (const name of EDITOR_ENV) {
|
|
11735
11745
|
const value2 = env[name];
|
|
11736
11746
|
if (value2 && value2.trim()) return value2.trim();
|
|
@@ -11744,8 +11754,8 @@ function defaultRun(command, path) {
|
|
|
11744
11754
|
return result.status ?? 0;
|
|
11745
11755
|
}
|
|
11746
11756
|
function editText(initial, slug, deps = {}) {
|
|
11747
|
-
const env = deps.env ??
|
|
11748
|
-
const interactive = deps.interactive ?? (() => Boolean(
|
|
11757
|
+
const env = deps.env ?? process14.env;
|
|
11758
|
+
const interactive = deps.interactive ?? (() => Boolean(process14.stdin.isTTY));
|
|
11749
11759
|
const editor = resolveEditor(env);
|
|
11750
11760
|
if (!editor)
|
|
11751
11761
|
throw new Error(
|
|
@@ -12736,4 +12746,4 @@ export {
|
|
|
12736
12746
|
exitCodeFor,
|
|
12737
12747
|
runCli
|
|
12738
12748
|
};
|
|
12739
|
-
//# sourceMappingURL=chunk-
|
|
12749
|
+
//# sourceMappingURL=chunk-JWEBGIBR.js.map
|