@odla-ai/cli 0.10.2 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -4
- package/dist/bin.cjs +58 -23
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-MWVKOIGR.js → chunk-DC4MIB4X.js} +59 -24
- package/dist/chunk-DC4MIB4X.js.map +1 -0
- package/dist/index.cjs +58 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -10
- package/dist/index.d.ts +10 -10
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-MWVKOIGR.js.map +0 -1
package/README.md
CHANGED
|
@@ -110,10 +110,11 @@ shown-once credential.
|
|
|
110
110
|
`--email <account>` or `ODLA_USER_EMAIL`; the matching existing account must
|
|
111
111
|
sign in, review the exact code, and approve it. Opening the URL alone does
|
|
112
112
|
not claim the request.
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
113
|
+
When the code is displayed it opens the approval page in your browser —
|
|
114
|
+
including from scripted and agent-driven shells; only CI, SSH sessions, and
|
|
115
|
+
display-less hosts skip the launch. Pass `--open` to force it anyway, or
|
|
116
|
+
`--no-open` to suppress it. Browser launch is best-effort; the printed URL
|
|
117
|
+
and code always remain the fallback.
|
|
117
118
|
2. Creates the platform app if needed.
|
|
118
119
|
3. Enables configured services in every configured environment. Calendar
|
|
119
120
|
config is normalized per env and fixed to Google read-only access.
|
package/dist/bin.cjs
CHANGED
|
@@ -237,12 +237,16 @@ function handshakeEmail(value, cached) {
|
|
|
237
237
|
}
|
|
238
238
|
return email;
|
|
239
239
|
}
|
|
240
|
-
function approvalBrowser(options) {
|
|
240
|
+
function approvalBrowser(options, host = {}) {
|
|
241
241
|
if (options.open === true) return { open: true, mode: "forced" };
|
|
242
242
|
if (options.open === false) return { open: false, reason: "disabled by --no-open" };
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
if (
|
|
243
|
+
const env = host.env ?? import_node_process2.default.env;
|
|
244
|
+
if (env.CI) return { open: false, reason: "CI environment" };
|
|
245
|
+
if (env.SSH_CONNECTION || env.SSH_TTY) return { open: false, reason: "SSH session; pass --open to force" };
|
|
246
|
+
const platform = host.platform ?? import_node_process2.default.platform;
|
|
247
|
+
if (platform !== "darwin" && platform !== "win32" && !env.DISPLAY && !env.WAYLAND_DISPLAY) {
|
|
248
|
+
return { open: false, reason: "no graphical display; pass --open to force" };
|
|
249
|
+
}
|
|
246
250
|
return { open: true, mode: "auto" };
|
|
247
251
|
}
|
|
248
252
|
function handshakeUrl(platformUrl, userCode) {
|
|
@@ -339,8 +343,9 @@ async function scopedToken(platform, scope, options, doFetch, out) {
|
|
|
339
343
|
onCode: async ({ userCode, expiresIn, verificationUriComplete }) => {
|
|
340
344
|
const approvalUrl = verificationUriComplete ?? handshakeUrl(audience, userCode);
|
|
341
345
|
out.log(`Review, then approve scoped request ${userCode} at ${approvalUrl} within ${Math.floor(expiresIn / 60)}m.`);
|
|
342
|
-
|
|
343
|
-
|
|
346
|
+
if (approvalBrowser({ open: options.open }).open) {
|
|
347
|
+
await (options.openApprovalUrl ?? openUrl)(approvalUrl).catch(() => void 0);
|
|
348
|
+
}
|
|
344
349
|
}
|
|
345
350
|
});
|
|
346
351
|
const tokens = cache?.platform === audience ? { ...cache.tokens ?? {} } : {};
|
|
@@ -1084,6 +1089,28 @@ function unique(values) {
|
|
|
1084
1089
|
return [...new Set(values.filter(Boolean))];
|
|
1085
1090
|
}
|
|
1086
1091
|
|
|
1092
|
+
// src/calendar-errors.ts
|
|
1093
|
+
var PLATFORM_NOT_READY_CODES = /* @__PURE__ */ new Set([
|
|
1094
|
+
"calendar_google_oauth_not_configured",
|
|
1095
|
+
"calendar_token_vault_not_configured",
|
|
1096
|
+
"calendar_db_ingress_not_configured"
|
|
1097
|
+
]);
|
|
1098
|
+
var CalendarRequestError = class extends Error {
|
|
1099
|
+
/** HTTP status returned by the registry. */
|
|
1100
|
+
status;
|
|
1101
|
+
/** Machine-readable `error.code` from the response body, when present. */
|
|
1102
|
+
code;
|
|
1103
|
+
constructor(message, status, code) {
|
|
1104
|
+
super(message);
|
|
1105
|
+
this.name = "CalendarRequestError";
|
|
1106
|
+
this.status = status;
|
|
1107
|
+
if (code !== void 0) this.code = code;
|
|
1108
|
+
}
|
|
1109
|
+
};
|
|
1110
|
+
function isCalendarPlatformNotReady(error) {
|
|
1111
|
+
return error instanceof CalendarRequestError && error.code !== void 0 && PLATFORM_NOT_READY_CODES.has(error.code);
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1087
1114
|
// src/redact.ts
|
|
1088
1115
|
var REPLACEMENTS = [
|
|
1089
1116
|
[/odla_(sk|dev)_[A-Za-z0-9._-]+/g, "odla_$1_[redacted]"],
|
|
@@ -1249,7 +1276,8 @@ async function calendarJson(ctx, suffix, init) {
|
|
|
1249
1276
|
if (!response.ok) {
|
|
1250
1277
|
const code = textField(body.error?.code, 128);
|
|
1251
1278
|
const message = textField(body.error?.message, 500);
|
|
1252
|
-
|
|
1279
|
+
const detail = `${code ? ` ${code}` : ""}${message ? `: ${message}` : ""}`;
|
|
1280
|
+
throw new CalendarRequestError(redactSecrets(`calendar request failed (${response.status})${detail}`), response.status, code);
|
|
1253
1281
|
}
|
|
1254
1282
|
return body;
|
|
1255
1283
|
}
|
|
@@ -1389,7 +1417,6 @@ async function lifecycleContext(options) {
|
|
|
1389
1417
|
token: options.token,
|
|
1390
1418
|
email: options.email,
|
|
1391
1419
|
open: options.open,
|
|
1392
|
-
interactive: options.interactive,
|
|
1393
1420
|
openApprovalUrl: options.openConsentUrl
|
|
1394
1421
|
},
|
|
1395
1422
|
doFetch,
|
|
@@ -1398,7 +1425,7 @@ async function lifecycleContext(options) {
|
|
|
1398
1425
|
return { cfg, ctx: { platform: cfg.platformUrl, appId: cfg.app.id, env, token, fetch: doFetch }, out };
|
|
1399
1426
|
}
|
|
1400
1427
|
function connectionOptions(options, out) {
|
|
1401
|
-
const browser = approvalBrowser({
|
|
1428
|
+
const browser = approvalBrowser({ open: options.open });
|
|
1402
1429
|
return {
|
|
1403
1430
|
out,
|
|
1404
1431
|
open: browser.open,
|
|
@@ -1859,8 +1886,9 @@ Safety:
|
|
|
1859
1886
|
Provision caches the approved developer token and service credentials under
|
|
1860
1887
|
.odla/ with mode 0600, and init adds those paths to .gitignore. Secret push
|
|
1861
1888
|
preflights Wrangler before any shown-once issuance or destructive rotation.
|
|
1862
|
-
Provision opens the approval page automatically
|
|
1863
|
-
|
|
1889
|
+
Provision opens the approval page in your browser automatically whenever the
|
|
1890
|
+
machine can show one, including agent-driven runs; only CI, SSH, and
|
|
1891
|
+
display-less hosts skip it. Use --open to force or --no-open to suppress.
|
|
1864
1892
|
A fresh device handshake requires --email <odla-account> or ODLA_USER_EMAIL.
|
|
1865
1893
|
The email is a non-secret identity hint: never provide a password or session
|
|
1866
1894
|
token. The matching account must already exist, be signed in, explicitly
|
|
@@ -2300,17 +2328,6 @@ async function provision(options) {
|
|
|
2300
2328
|
if (cfg.services.includes("calendar")) {
|
|
2301
2329
|
const calendarCtx = { platform: cfg.platformUrl, appId: cfg.app.id, env, token, fetch: doFetch };
|
|
2302
2330
|
await applyCalendarBookingPage(calendarCtx, calendarBookingPageUrl(cfg, env), out);
|
|
2303
|
-
await ensureCalendarConnected(
|
|
2304
|
-
calendarCtx,
|
|
2305
|
-
{
|
|
2306
|
-
open: options.open,
|
|
2307
|
-
interactive: options.interactive,
|
|
2308
|
-
openConsentUrl: options.openApprovalUrl,
|
|
2309
|
-
wait: options.calendarPollWait,
|
|
2310
|
-
pollTimeoutMs: options.calendarPollTimeoutMs,
|
|
2311
|
-
stdout: out
|
|
2312
|
-
}
|
|
2313
|
-
);
|
|
2314
2331
|
}
|
|
2315
2332
|
}
|
|
2316
2333
|
for (const env of cfg.envs) {
|
|
@@ -2374,6 +2391,25 @@ ${env}: credentials are already saved; retry "odla-ai secrets push --env ${env}$
|
|
|
2374
2391
|
writeDevVars(devVarsTarget, credentials, env, o11yDevVars(cfg));
|
|
2375
2392
|
out.log(`dev vars: wrote ${displayPath(devVarsTarget, cfg.rootDir)} for ${env}`);
|
|
2376
2393
|
}
|
|
2394
|
+
if (cfg.services.includes("calendar")) {
|
|
2395
|
+
for (const env of cfg.envs) {
|
|
2396
|
+
try {
|
|
2397
|
+
await ensureCalendarConnected(
|
|
2398
|
+
{ platform: cfg.platformUrl, appId: cfg.app.id, env, token, fetch: doFetch },
|
|
2399
|
+
{
|
|
2400
|
+
open: options.open,
|
|
2401
|
+
openConsentUrl: options.openApprovalUrl,
|
|
2402
|
+
wait: options.calendarPollWait,
|
|
2403
|
+
pollTimeoutMs: options.calendarPollTimeoutMs,
|
|
2404
|
+
stdout: out
|
|
2405
|
+
}
|
|
2406
|
+
);
|
|
2407
|
+
} catch (error) {
|
|
2408
|
+
if (!isCalendarPlatformNotReady(error)) throw error;
|
|
2409
|
+
out.log(`${env}: calendar consent skipped (${error.code}); resume with "odla-ai calendar connect --env ${env}" once the platform is configured`);
|
|
2410
|
+
}
|
|
2411
|
+
}
|
|
2412
|
+
}
|
|
2377
2413
|
}
|
|
2378
2414
|
function serviceRank(service) {
|
|
2379
2415
|
if (service === "db") return 0;
|
|
@@ -3672,7 +3708,6 @@ async function smoke(options) {
|
|
|
3672
3708
|
token: options.token,
|
|
3673
3709
|
email: options.email,
|
|
3674
3710
|
open: options.open,
|
|
3675
|
-
interactive: options.interactive,
|
|
3676
3711
|
openApprovalUrl: options.openApprovalUrl
|
|
3677
3712
|
},
|
|
3678
3713
|
doFetch,
|