@odla-ai/cli 0.27.15 → 0.28.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/README.md +5 -1
- package/dist/bin.cjs +396 -190
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-MR5QXX3B.js → chunk-HCNESRIA.js} +357 -169
- package/dist/chunk-HCNESRIA.js.map +1 -0
- package/dist/{cli-2RZFZRRT.js → cli-DDOFM47D.js} +2 -2
- package/dist/index.cjs +363 -174
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -1
- package/dist/index.d.ts +14 -1
- package/dist/index.js +3 -1
- package/dist/runtime/pi-agent.js +15 -314577
- package/package.json +2 -2
- package/dist/chunk-MR5QXX3B.js.map +0 -1
- /package/dist/{cli-2RZFZRRT.js.map → cli-DDOFM47D.js.map} +0 -0
package/dist/bin.cjs
CHANGED
|
@@ -509,7 +509,7 @@ async function getDeveloperToken(cfg, options, doFetch, out, grantRequest = {})
|
|
|
509
509
|
const optionalProjectCapabilities = grantRequest.optionalProjectCapabilities ?? [];
|
|
510
510
|
const grantIntent = { projectIds: [cfg.app.id], optionalProjectCapabilities };
|
|
511
511
|
const cached = readJsonFile(cfg.local.tokenFile);
|
|
512
|
-
if (!grantRequest.forceReview) {
|
|
512
|
+
if (!grantRequest.forceReview && !grantRequest.freshLogin) {
|
|
513
513
|
if (options.token) return options.token;
|
|
514
514
|
if (import_node_process4.default.env.ODLA_DEV_TOKEN) {
|
|
515
515
|
const declared = import_node_process4.default.env.ODLA_DEV_TOKEN_AUDIENCE;
|
|
@@ -526,9 +526,9 @@ async function getDeveloperToken(cfg, options, doFetch, out, grantRequest = {})
|
|
|
526
526
|
}
|
|
527
527
|
} else {
|
|
528
528
|
if (options.token) {
|
|
529
|
-
throw new Error("--request-grant cannot be combined with --token; remove --token so the approved replacement credential can be collected and cached");
|
|
529
|
+
throw new Error(grantRequest.forceReview ? "--request-grant cannot be combined with --token; remove --token so the approved replacement credential can be collected and cached" : "a fresh authorization cannot be combined with --token; remove --token so the approved replacement credential can be collected and cached");
|
|
530
530
|
}
|
|
531
|
-
out.error(`auth: requesting fresh owner review for app.manage on exact project "${cfg.app.id}"`);
|
|
531
|
+
out.error(grantRequest.forceReview ? `auth: requesting fresh owner review for app.manage on exact project "${cfg.app.id}"` : `auth: requesting a fresh agent sign-in for exact project "${cfg.app.id}"`);
|
|
532
532
|
}
|
|
533
533
|
const ctx = {
|
|
534
534
|
cfg,
|
|
@@ -541,6 +541,7 @@ async function getDeveloperToken(cfg, options, doFetch, out, grantRequest = {})
|
|
|
541
541
|
grantIntent
|
|
542
542
|
};
|
|
543
543
|
const waitMs = handshakeWaitMs(options.wait);
|
|
544
|
+
if (grantRequest.freshLogin) clearPendingHandshake(ctx.pendingFile);
|
|
544
545
|
const { token, expiresAt } = await resumePendingHandshake(ctx, waitMs) ?? await freshHandshake(ctx, waitMs);
|
|
545
546
|
clearPendingHandshake(ctx.pendingFile);
|
|
546
547
|
writePrivateJson(cfg.local.tokenFile, {
|
|
@@ -663,8 +664,15 @@ function stillPending(pending, email) {
|
|
|
663
664
|
}
|
|
664
665
|
function handshakeEmail(value2, cached) {
|
|
665
666
|
const email = (value2 ?? import_node_process4.default.env.ODLA_USER_EMAIL ?? cached ?? "").trim().toLowerCase();
|
|
667
|
+
if (/@users\.noreply\.github\.com$/i.test(email)) {
|
|
668
|
+
throw new Error(
|
|
669
|
+
`"${email}" is a GitHub commit identity, not an odla account email; use --email <signed-in-odla-account> or ODLA_USER_EMAIL`
|
|
670
|
+
);
|
|
671
|
+
}
|
|
666
672
|
if (email.length > 254 || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
|
667
|
-
throw new Error(
|
|
673
|
+
throw new Error(
|
|
674
|
+
"a fresh odla handshake requires --email <account> or ODLA_USER_EMAIL; use the signed-in email shown in odla Studio, not git or GitHub identity"
|
|
675
|
+
);
|
|
668
676
|
}
|
|
669
677
|
return email;
|
|
670
678
|
}
|
|
@@ -1958,6 +1966,223 @@ var init_admin_command = __esm({
|
|
|
1958
1966
|
}
|
|
1959
1967
|
});
|
|
1960
1968
|
|
|
1969
|
+
// src/whoami-command.ts
|
|
1970
|
+
function principalKind(value2, machine) {
|
|
1971
|
+
return value2 === "human" || value2 === "agent" || value2 === "service" ? value2 : machine ? "service" : "human";
|
|
1972
|
+
}
|
|
1973
|
+
function credentialKind(value2, machine, scopes) {
|
|
1974
|
+
if (value2 === "machine" || value2 === "device" || value2 === "clerk") return value2;
|
|
1975
|
+
if (machine) return "machine";
|
|
1976
|
+
if (scopes.length) return "device";
|
|
1977
|
+
return "unknown";
|
|
1978
|
+
}
|
|
1979
|
+
function managerOf(value2) {
|
|
1980
|
+
if (!value2 || typeof value2 !== "object") return null;
|
|
1981
|
+
const row = value2;
|
|
1982
|
+
const principalId = text(row.principalId);
|
|
1983
|
+
if (!principalId) return null;
|
|
1984
|
+
return {
|
|
1985
|
+
principalId,
|
|
1986
|
+
displayName: text(row.displayName) ?? "Unnamed member",
|
|
1987
|
+
handle: text(row.handle) ?? ""
|
|
1988
|
+
};
|
|
1989
|
+
}
|
|
1990
|
+
function unnamedPrincipal(kind) {
|
|
1991
|
+
if (kind === "agent") return "Unnamed agent";
|
|
1992
|
+
if (kind === "service") return "Unnamed service";
|
|
1993
|
+
return "Unnamed member";
|
|
1994
|
+
}
|
|
1995
|
+
async function fetchIdentity(platformUrl, token, doFetch) {
|
|
1996
|
+
const res = await doFetch(`${platformUrl.replace(/\/$/, "")}/registry/me`, {
|
|
1997
|
+
headers: { authorization: `Bearer ${token}` }
|
|
1998
|
+
});
|
|
1999
|
+
if (!res.ok) throw new Error(`could not resolve identity (HTTP ${res.status})`);
|
|
2000
|
+
const body = await res.json();
|
|
2001
|
+
const developerId = text(body.developerId) ?? "";
|
|
2002
|
+
const machine = body.machine === true;
|
|
2003
|
+
const scopes = Array.isArray(body.scopes) ? body.scopes.map(String) : [];
|
|
2004
|
+
const principalId = text(body.principalId) ?? developerId;
|
|
2005
|
+
const email = text(body.email);
|
|
2006
|
+
const kind = principalKind(body.principalKind, machine);
|
|
2007
|
+
const displayName = text(body.displayName) ?? email ?? unnamedPrincipal(kind);
|
|
2008
|
+
const handle = text(body.handle) ?? "";
|
|
2009
|
+
const credential2 = body.credential && typeof body.credential === "object" ? body.credential : {};
|
|
2010
|
+
return {
|
|
2011
|
+
developerId,
|
|
2012
|
+
principalId,
|
|
2013
|
+
principalKind: kind,
|
|
2014
|
+
displayName,
|
|
2015
|
+
handle,
|
|
2016
|
+
manager: managerOf(body.manager),
|
|
2017
|
+
credential: {
|
|
2018
|
+
id: text(credential2.id),
|
|
2019
|
+
kind: credentialKind(credential2.kind, machine, scopes)
|
|
2020
|
+
},
|
|
2021
|
+
email,
|
|
2022
|
+
admin: body.admin === true,
|
|
2023
|
+
machine,
|
|
2024
|
+
scopes
|
|
2025
|
+
};
|
|
2026
|
+
}
|
|
2027
|
+
function credentialLabel(identity) {
|
|
2028
|
+
if (identity.credential.kind === "machine") return "machine (platform admin secret)";
|
|
2029
|
+
if (identity.credential.kind === "device")
|
|
2030
|
+
return identity.scopes.length ? "device (scoped)" : "device";
|
|
2031
|
+
if (identity.credential.kind === "clerk") return "clerk";
|
|
2032
|
+
return "unknown (legacy server)";
|
|
2033
|
+
}
|
|
2034
|
+
function namedPrincipal(identity) {
|
|
2035
|
+
return identity.handle && identity.handle !== identity.displayName ? `${identity.displayName} (@${identity.handle})` : identity.displayName;
|
|
2036
|
+
}
|
|
2037
|
+
function namedManager(manager) {
|
|
2038
|
+
return manager.handle && manager.handle !== manager.displayName ? `${manager.displayName} (@${manager.handle})` : manager.displayName;
|
|
2039
|
+
}
|
|
2040
|
+
function accountableOwner(identity) {
|
|
2041
|
+
if (identity.principalKind === "agent" && identity.manager) {
|
|
2042
|
+
return namedManager(identity.manager);
|
|
2043
|
+
}
|
|
2044
|
+
if (identity.principalKind === "human" && identity.principalId === identity.developerId) {
|
|
2045
|
+
return namedPrincipal(identity);
|
|
2046
|
+
}
|
|
2047
|
+
return identity.email ?? "Unnamed member";
|
|
2048
|
+
}
|
|
2049
|
+
async function whoamiCommand(parsed, deps = {}) {
|
|
2050
|
+
assertArgs(
|
|
2051
|
+
parsed,
|
|
2052
|
+
["config", "context", "platform", "token", "email", "json", "open"],
|
|
2053
|
+
1
|
|
2054
|
+
);
|
|
2055
|
+
const out = deps.stdout ?? console;
|
|
2056
|
+
const doFetch = deps.fetch ?? fetch;
|
|
2057
|
+
const { cfg } = await resolveOperatorContext(parsed, {
|
|
2058
|
+
allowMissingConfig: true
|
|
2059
|
+
});
|
|
2060
|
+
const token = await getDeveloperToken(
|
|
2061
|
+
cfg,
|
|
2062
|
+
{
|
|
2063
|
+
configPath: cfg.configPath,
|
|
2064
|
+
token: stringOpt(parsed.options.token),
|
|
2065
|
+
email: stringOpt(parsed.options.email),
|
|
2066
|
+
// As above: never suppress the browser for an ordinary sign-in.
|
|
2067
|
+
open: parsed.options.open === false ? false : parsed.options.open === true ? true : void 0,
|
|
2068
|
+
openApprovalUrl: deps.openUrl
|
|
2069
|
+
},
|
|
2070
|
+
doFetch,
|
|
2071
|
+
out
|
|
2072
|
+
);
|
|
2073
|
+
const identity = await fetchIdentity(cfg.platformUrl, token, doFetch);
|
|
2074
|
+
if (parsed.options.json === true) {
|
|
2075
|
+
out.log(JSON.stringify(identity, null, 2));
|
|
2076
|
+
return;
|
|
2077
|
+
}
|
|
2078
|
+
out.log(`platform: ${cfg.platformUrl}`);
|
|
2079
|
+
out.log(`principal: ${namedPrincipal(identity)}`);
|
|
2080
|
+
out.log(`principal id: ${identity.principalId}`);
|
|
2081
|
+
out.log(`kind: ${identity.principalKind}`);
|
|
2082
|
+
if (identity.principalKind === "agent")
|
|
2083
|
+
out.log(
|
|
2084
|
+
`manager: ${identity.manager ? namedManager(identity.manager) : "(unknown)"}`
|
|
2085
|
+
);
|
|
2086
|
+
out.log(`owner: ${accountableOwner(identity)}`);
|
|
2087
|
+
out.log(`owner id: ${identity.developerId}`);
|
|
2088
|
+
out.log(`email: ${identity.email ?? "(none)"}`);
|
|
2089
|
+
out.log(`credential: ${credentialLabel(identity)}`);
|
|
2090
|
+
if (identity.credential.id)
|
|
2091
|
+
out.log(`credential id: ${identity.credential.id}`);
|
|
2092
|
+
out.log(`admin: ${identity.admin ? "yes" : "no"}`);
|
|
2093
|
+
if (identity.scopes.length) out.log(`scopes: ${identity.scopes.join(", ")}`);
|
|
2094
|
+
if (!identity.admin) {
|
|
2095
|
+
if (identity.scopes.includes("platform:runbook:write")) {
|
|
2096
|
+
out.log("\nThis exact scope can read and edit all platform runbook content.");
|
|
2097
|
+
out.log("The device token is not ambient admin and cannot change visibility.");
|
|
2098
|
+
} else {
|
|
2099
|
+
out.log("\nYou can read operator-visible platform runbooks but not edit them.");
|
|
2100
|
+
out.log("Use a platform-admin-approved runbook write request to edit.");
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2104
|
+
var text;
|
|
2105
|
+
var init_whoami_command = __esm({
|
|
2106
|
+
"src/whoami-command.ts"() {
|
|
2107
|
+
"use strict";
|
|
2108
|
+
init_cjs_shims();
|
|
2109
|
+
init_argv();
|
|
2110
|
+
init_operator_context();
|
|
2111
|
+
init_token();
|
|
2112
|
+
text = (value2) => typeof value2 === "string" && value2.trim() ? value2.trim() : null;
|
|
2113
|
+
}
|
|
2114
|
+
});
|
|
2115
|
+
|
|
2116
|
+
// src/auth-command.ts
|
|
2117
|
+
async function authCommand(parsed, deps = {}) {
|
|
2118
|
+
assertArgs(parsed, [
|
|
2119
|
+
"config",
|
|
2120
|
+
"context",
|
|
2121
|
+
"platform",
|
|
2122
|
+
"app",
|
|
2123
|
+
"email",
|
|
2124
|
+
"open",
|
|
2125
|
+
"wait",
|
|
2126
|
+
"json"
|
|
2127
|
+
], 2);
|
|
2128
|
+
const action2 = parsed.positionals[1] ?? "login";
|
|
2129
|
+
if (action2 !== "login") {
|
|
2130
|
+
throw new Error(`unknown auth action "${action2}". Try "odla-ai auth login --app <id> --email <odla-account>".`);
|
|
2131
|
+
}
|
|
2132
|
+
const context = await resolveOperatorContext(parsed, {
|
|
2133
|
+
allowMissingConfig: true,
|
|
2134
|
+
requireApp: true
|
|
2135
|
+
});
|
|
2136
|
+
const { cfg } = context;
|
|
2137
|
+
const out = deps.stdout ?? console;
|
|
2138
|
+
const doFetch = deps.fetch ?? fetch;
|
|
2139
|
+
const email = stringOpt(parsed.options.email) ?? import_node_process10.default.env.ODLA_USER_EMAIL?.trim();
|
|
2140
|
+
if (!email) {
|
|
2141
|
+
throw new Error(
|
|
2142
|
+
"auth login requires --email <odla-account> or ODLA_USER_EMAIL; confirm the signed-in odla email instead of using git or GitHub identity"
|
|
2143
|
+
);
|
|
2144
|
+
}
|
|
2145
|
+
const token = await getDeveloperToken(
|
|
2146
|
+
cfg,
|
|
2147
|
+
{
|
|
2148
|
+
configPath: cfg.configPath,
|
|
2149
|
+
email,
|
|
2150
|
+
open: parsed.options.open === false ? false : parsed.options.open === true ? true : void 0,
|
|
2151
|
+
wait: numberOpt(parsed.options.wait, "--wait"),
|
|
2152
|
+
openApprovalUrl: deps.openUrl
|
|
2153
|
+
},
|
|
2154
|
+
doFetch,
|
|
2155
|
+
out,
|
|
2156
|
+
{ freshLogin: true }
|
|
2157
|
+
);
|
|
2158
|
+
const identity = await fetchIdentity(cfg.platformUrl, token, doFetch);
|
|
2159
|
+
if (parsed.options.json === true) {
|
|
2160
|
+
out.log(JSON.stringify({
|
|
2161
|
+
principalId: identity.principalId,
|
|
2162
|
+
displayName: identity.displayName,
|
|
2163
|
+
handle: identity.handle,
|
|
2164
|
+
email: identity.email,
|
|
2165
|
+
appId: cfg.app.id
|
|
2166
|
+
}, null, 2));
|
|
2167
|
+
return;
|
|
2168
|
+
}
|
|
2169
|
+
const handle = identity.handle ? ` (@${identity.handle})` : "";
|
|
2170
|
+
out.log(`Authorized ${identity.displayName}${handle} for ${cfg.app.id}.`);
|
|
2171
|
+
out.log(`odla account: ${identity.email ?? "not returned"}`);
|
|
2172
|
+
}
|
|
2173
|
+
var import_node_process10;
|
|
2174
|
+
var init_auth_command = __esm({
|
|
2175
|
+
"src/auth-command.ts"() {
|
|
2176
|
+
"use strict";
|
|
2177
|
+
init_cjs_shims();
|
|
2178
|
+
import_node_process10 = __toESM(require("process"), 1);
|
|
2179
|
+
init_argv();
|
|
2180
|
+
init_operator_context();
|
|
2181
|
+
init_token();
|
|
2182
|
+
init_whoami_command();
|
|
2183
|
+
}
|
|
2184
|
+
});
|
|
2185
|
+
|
|
1961
2186
|
// src/tenant.ts
|
|
1962
2187
|
function resolveEnv(cfg, requested) {
|
|
1963
2188
|
const env = requested ?? (cfg.envs.includes("dev") ? "dev" : cfg.envs[0]);
|
|
@@ -3035,6 +3260,71 @@ var init_capabilities = __esm({
|
|
|
3035
3260
|
}
|
|
3036
3261
|
});
|
|
3037
3262
|
|
|
3263
|
+
// src/ai-models.ts
|
|
3264
|
+
async function aiModels(options = {}) {
|
|
3265
|
+
const cfg = await loadProjectConfig(options.configPath ?? "odla.config.mjs");
|
|
3266
|
+
const env = options.env ?? cfg.envs[0] ?? "dev";
|
|
3267
|
+
if (!cfg.envs.includes(env)) throw new Error(`ai models env "${env}" is not declared in config envs`);
|
|
3268
|
+
const url = new URL(`/registry/apps/${encodeURIComponent(cfg.app.id)}/public-config`, cfg.platformUrl);
|
|
3269
|
+
url.searchParams.set("env", env);
|
|
3270
|
+
const response2 = await (options.fetch ?? fetch)(url);
|
|
3271
|
+
if (!response2.ok) throw new Error(`read app AI models failed (${response2.status}): ${await safeText4(response2)}`);
|
|
3272
|
+
const body = await response2.json();
|
|
3273
|
+
if (!body.ai) throw new Error(`ai is not configured for ${cfg.app.id}/${env}`);
|
|
3274
|
+
const mode = body.ai.mode === "hosted" ? "hosted" : "byok";
|
|
3275
|
+
const defaultModel = typeof body.ai.model === "string" ? body.ai.model : void 0;
|
|
3276
|
+
let models;
|
|
3277
|
+
if (mode === "hosted") {
|
|
3278
|
+
if (body.ai.enabled !== true) throw new Error(`hosted ai is disabled for ${cfg.app.id}/${env}`);
|
|
3279
|
+
if (!Array.isArray(body.ai.models) || !body.ai.models.every(isModelSpec)) {
|
|
3280
|
+
throw new Error("platform returned an invalid hosted AI model catalog");
|
|
3281
|
+
}
|
|
3282
|
+
models = body.ai.models;
|
|
3283
|
+
} else {
|
|
3284
|
+
const provider = typeof body.ai.provider === "string" ? body.ai.provider : cfg.ai?.provider;
|
|
3285
|
+
if (!provider) throw new Error(`BYOK ai has no provider for ${cfg.app.id}/${env}`);
|
|
3286
|
+
models = Object.values(import_ai.DEFAULT_CATALOG).filter((model) => model.provider === provider);
|
|
3287
|
+
}
|
|
3288
|
+
if (options.provider) models = models.filter((model) => model.provider === options.provider);
|
|
3289
|
+
models.sort((a, b) => a.provider.localeCompare(b.provider) || a.id.localeCompare(b.id));
|
|
3290
|
+
const out = options.stdout ?? console;
|
|
3291
|
+
if (options.json) {
|
|
3292
|
+
out.log(JSON.stringify({ appId: cfg.app.id, env, mode, defaultModel: defaultModel ?? null, models }, null, 2));
|
|
3293
|
+
return;
|
|
3294
|
+
}
|
|
3295
|
+
out.log("provider model default capabilities");
|
|
3296
|
+
for (const model of models) {
|
|
3297
|
+
out.log([model.provider, model.id, model.id === defaultModel ? "yes" : "", capabilityList(model)].join(" "));
|
|
3298
|
+
}
|
|
3299
|
+
}
|
|
3300
|
+
function capabilityList(model) {
|
|
3301
|
+
return [
|
|
3302
|
+
model.capabilities.imageIn ? "image" : "",
|
|
3303
|
+
model.capabilities.audioIn ? "audio" : "",
|
|
3304
|
+
model.capabilities.documentIn ? "document" : "",
|
|
3305
|
+
model.capabilities.toolUse ? "tools" : "",
|
|
3306
|
+
model.capabilities.thinking || model.capabilities.effort ? "reasoning" : "",
|
|
3307
|
+
model.capabilities.webSearch || model.superpowers?.webSearch ? "web-search" : ""
|
|
3308
|
+
].filter(Boolean).join(",");
|
|
3309
|
+
}
|
|
3310
|
+
function isModelSpec(value2) {
|
|
3311
|
+
if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) return false;
|
|
3312
|
+
const model = value2;
|
|
3313
|
+
return typeof model.id === "string" && typeof model.nativeId === "string" && (model.provider === "anthropic" || model.provider === "openai" || model.provider === "google") && Boolean(model.capabilities) && typeof model.capabilities === "object";
|
|
3314
|
+
}
|
|
3315
|
+
async function safeText4(response2) {
|
|
3316
|
+
return (await response2.text().catch(() => "request failed")).slice(0, 300);
|
|
3317
|
+
}
|
|
3318
|
+
var import_ai;
|
|
3319
|
+
var init_ai_models = __esm({
|
|
3320
|
+
"src/ai-models.ts"() {
|
|
3321
|
+
"use strict";
|
|
3322
|
+
init_cjs_shims();
|
|
3323
|
+
import_ai = require("@odla-ai/ai");
|
|
3324
|
+
init_config();
|
|
3325
|
+
}
|
|
3326
|
+
});
|
|
3327
|
+
|
|
3038
3328
|
// src/config-operation-error.ts
|
|
3039
3329
|
var ConfigOperationCommandError;
|
|
3040
3330
|
var init_config_operation_error = __esm({
|
|
@@ -3223,7 +3513,7 @@ var init_config_operation_validate = __esm({
|
|
|
3223
3513
|
|
|
3224
3514
|
// src/provision-helpers.ts
|
|
3225
3515
|
function defaultSecretName(provider) {
|
|
3226
|
-
const names =
|
|
3516
|
+
const names = import_ai2.DEFAULT_SECRET_NAMES;
|
|
3227
3517
|
return names[provider] ?? `${provider}_api_key`;
|
|
3228
3518
|
}
|
|
3229
3519
|
async function assertTenantAdminAccess(doFetch, cfg, env, token) {
|
|
@@ -3233,7 +3523,7 @@ async function assertTenantAdminAccess(doFetch, cfg, env, token) {
|
|
|
3233
3523
|
});
|
|
3234
3524
|
if (res.ok || res.status === 404) return;
|
|
3235
3525
|
if (res.status === 403) {
|
|
3236
|
-
const detail = await
|
|
3526
|
+
const detail = await safeText5(res);
|
|
3237
3527
|
const code = errorCode(detail);
|
|
3238
3528
|
if (code === "human_session_required") {
|
|
3239
3529
|
throw new Error(
|
|
@@ -3249,7 +3539,7 @@ async function assertTenantAdminAccess(doFetch, cfg, env, token) {
|
|
|
3249
3539
|
`${env}: this credential lacks live app.manage authority for "${cfg.app.id}" (tenant ${tenantId}) \u2014 nothing was minted or written; run "odla-ai provision --request-grant --email <odla-account>" to open a fresh owner review. If the human account is not an owner, an existing owner must add it in signed-in Studio; an agent token cannot repair ownership`
|
|
3250
3540
|
);
|
|
3251
3541
|
}
|
|
3252
|
-
throw new Error(`${env}: tenant access preflight (${tenantId}) failed: ${res.status} ${await
|
|
3542
|
+
throw new Error(`${env}: tenant access preflight (${tenantId}) failed: ${res.status} ${await safeText5(res)}`);
|
|
3253
3543
|
}
|
|
3254
3544
|
function errorCode(text2) {
|
|
3255
3545
|
try {
|
|
@@ -3265,7 +3555,7 @@ async function postJson(doFetch, url, bearer, body) {
|
|
|
3265
3555
|
headers: { authorization: `Bearer ${bearer}`, "content-type": "application/json" },
|
|
3266
3556
|
body: JSON.stringify(body)
|
|
3267
3557
|
});
|
|
3268
|
-
if (!res.ok) throw new Error(`${new URL(url).pathname} failed: ${res.status} ${await
|
|
3558
|
+
if (!res.ok) throw new Error(`${new URL(url).pathname} failed: ${res.status} ${await safeText5(res)}`);
|
|
3269
3559
|
}
|
|
3270
3560
|
function normalizeClerkConfig(value2) {
|
|
3271
3561
|
if (!value2) return null;
|
|
@@ -3278,19 +3568,19 @@ function normalizeClerkConfig(value2) {
|
|
|
3278
3568
|
const publishableKey = envValue(cfg.publishableKey);
|
|
3279
3569
|
return publishableKey ? { publishableKey, ...cfg.audience ? { audience: cfg.audience } : {}, ...cfg.mode ? { mode: cfg.mode } : {} } : null;
|
|
3280
3570
|
}
|
|
3281
|
-
async function
|
|
3571
|
+
async function safeText5(res) {
|
|
3282
3572
|
try {
|
|
3283
3573
|
return redactSecrets((await res.text()).slice(0, 500));
|
|
3284
3574
|
} catch {
|
|
3285
3575
|
return "";
|
|
3286
3576
|
}
|
|
3287
3577
|
}
|
|
3288
|
-
var
|
|
3578
|
+
var import_ai2, import_apps4;
|
|
3289
3579
|
var init_provision_helpers = __esm({
|
|
3290
3580
|
"src/provision-helpers.ts"() {
|
|
3291
3581
|
"use strict";
|
|
3292
3582
|
init_cjs_shims();
|
|
3293
|
-
|
|
3583
|
+
import_ai2 = require("@odla-ai/ai");
|
|
3294
3584
|
import_apps4 = require("@odla-ai/apps");
|
|
3295
3585
|
init_config();
|
|
3296
3586
|
init_redact();
|
|
@@ -4745,7 +5035,7 @@ async function secretsSet(options) {
|
|
|
4745
5035
|
optionalProjectCapabilities: ["app.manage"]
|
|
4746
5036
|
});
|
|
4747
5037
|
try {
|
|
4748
|
-
await (0,
|
|
5038
|
+
await (0, import_ai3.putSecret)({ endpoint: cfg.dbEndpoint, token, fetch: doFetch }, tenantId, name, value2);
|
|
4749
5039
|
} catch (err) {
|
|
4750
5040
|
throw new Error(scrubValue(err instanceof Error ? err.message : String(err), value2));
|
|
4751
5041
|
}
|
|
@@ -4790,12 +5080,12 @@ async function resolveVaultWrite(options) {
|
|
|
4790
5080
|
const value2 = await secretInputValue(options, "secret");
|
|
4791
5081
|
return { cfg, tenantId: (0, import_apps10.tenantIdFor)(cfg.app.id, options.env), value: value2, doFetch, out };
|
|
4792
5082
|
}
|
|
4793
|
-
var
|
|
5083
|
+
var import_ai3, import_apps10, PROD_ENV_NAMES2;
|
|
4794
5084
|
var init_secrets_set = __esm({
|
|
4795
5085
|
"src/secrets-set.ts"() {
|
|
4796
5086
|
"use strict";
|
|
4797
5087
|
init_cjs_shims();
|
|
4798
|
-
|
|
5088
|
+
import_ai3 = require("@odla-ai/ai");
|
|
4799
5089
|
import_apps10 = require("@odla-ai/apps");
|
|
4800
5090
|
init_config();
|
|
4801
5091
|
init_redact();
|
|
@@ -4852,6 +5142,14 @@ and atomically claim a refined Ready task. Record decisions when you make them
|
|
|
4852
5142
|
and file bugs when you notice them. The conventions and the full command set are
|
|
4853
5143
|
in \`.agents/skills/odla/references/pm.md\`.
|
|
4854
5144
|
|
|
5145
|
+
Use the human's signed-in odla account email for device authorization; never
|
|
5146
|
+
infer it from git config, commit metadata, or GitHub. If authorization is not
|
|
5147
|
+
already active, run
|
|
5148
|
+
\`npx @odla-ai/cli auth login --app <appId> --email <odla-account>\` and open
|
|
5149
|
+
the exact Studio URL it prints. Never file an odla project or product defect in
|
|
5150
|
+
GitHub Issues: run \`npx @odla-ai/cli bug report --app <appId> ...\` so the bug
|
|
5151
|
+
lands in odla PM with the rest of the project's goals, tasks, and decisions.
|
|
5152
|
+
|
|
4855
5153
|
The setup runbooks and their references are installed in this repository, pinned
|
|
4856
5154
|
to this CLI version. Use them as your setup context.
|
|
4857
5155
|
|
|
@@ -5207,7 +5505,7 @@ async function getJson(doFetch, url, bearer) {
|
|
|
5207
5505
|
const res = await doFetch(url, {
|
|
5208
5506
|
headers: bearer ? { authorization: `Bearer ${bearer}` } : void 0
|
|
5209
5507
|
});
|
|
5210
|
-
if (!res.ok) throw new Error(`${new URL(url).pathname} returned ${res.status}: ${await
|
|
5508
|
+
if (!res.ok) throw new Error(`${new URL(url).pathname} returned ${res.status}: ${await safeText6(res)}`);
|
|
5211
5509
|
return res.json();
|
|
5212
5510
|
}
|
|
5213
5511
|
async function postJson2(doFetch, url, bearer, body) {
|
|
@@ -5216,7 +5514,7 @@ async function postJson2(doFetch, url, bearer, body) {
|
|
|
5216
5514
|
headers: { authorization: `Bearer ${bearer}`, "content-type": "application/json" },
|
|
5217
5515
|
body: JSON.stringify(body)
|
|
5218
5516
|
});
|
|
5219
|
-
if (!res.ok) throw new Error(`${new URL(url).pathname} returned ${res.status}: ${await
|
|
5517
|
+
if (!res.ok) throw new Error(`${new URL(url).pathname} returned ${res.status}: ${await safeText6(res)}`);
|
|
5220
5518
|
return res.json();
|
|
5221
5519
|
}
|
|
5222
5520
|
function publicConfigUrl(platformUrl, appId, env) {
|
|
@@ -5224,7 +5522,7 @@ function publicConfigUrl(platformUrl, appId, env) {
|
|
|
5224
5522
|
url.searchParams.set("env", env);
|
|
5225
5523
|
return url.toString();
|
|
5226
5524
|
}
|
|
5227
|
-
async function
|
|
5525
|
+
async function safeText6(res) {
|
|
5228
5526
|
try {
|
|
5229
5527
|
return redactSecrets((await res.text()).slice(0, 500));
|
|
5230
5528
|
} catch {
|
|
@@ -5291,6 +5589,20 @@ async function secretsCommand(parsed, deps) {
|
|
|
5291
5589
|
});
|
|
5292
5590
|
}
|
|
5293
5591
|
async function projectCommand(command, parsed, deps) {
|
|
5592
|
+
if (command === "ai") {
|
|
5593
|
+
const sub = parsed.positionals[1];
|
|
5594
|
+
if (sub !== "models") throw new Error(`unknown ai subcommand "${sub ?? ""}". Try "odla-ai ai models --env dev".`);
|
|
5595
|
+
assertArgs(parsed, ["config", "env", "provider", "json"], 2);
|
|
5596
|
+
await aiModels({
|
|
5597
|
+
configPath: stringOpt(parsed.options.config) ?? "odla.config.mjs",
|
|
5598
|
+
env: stringOpt(parsed.options.env),
|
|
5599
|
+
provider: stringOpt(parsed.options.provider),
|
|
5600
|
+
json: parsed.options.json === true,
|
|
5601
|
+
fetch: deps.fetch,
|
|
5602
|
+
stdout: deps.stdout
|
|
5603
|
+
});
|
|
5604
|
+
return true;
|
|
5605
|
+
}
|
|
5294
5606
|
if (command === "config") {
|
|
5295
5607
|
const sub = parsed.positionals[1];
|
|
5296
5608
|
if (sub !== "diff" && sub !== "plan" && sub !== "apply") {
|
|
@@ -5413,6 +5725,7 @@ var init_cli_project = __esm({
|
|
|
5413
5725
|
init_cjs_shims();
|
|
5414
5726
|
init_argv();
|
|
5415
5727
|
init_capabilities();
|
|
5728
|
+
init_ai_models();
|
|
5416
5729
|
init_config_operation_command();
|
|
5417
5730
|
init_config_reconcile_command();
|
|
5418
5731
|
init_doctor();
|
|
@@ -9023,7 +9336,7 @@ function developerTokenStatus(context, parsed, now = Date.now()) {
|
|
|
9023
9336
|
const cacheStatus = !cached?.token ? "missing" : cached.platform !== context.platform.value ? "other-platform" : (cached.expiresAt ?? 0) <= now + 6e4 ? "expired" : "valid";
|
|
9024
9337
|
const source = clean3(
|
|
9025
9338
|
stringOpt(parsed.options.token)
|
|
9026
|
-
) ? "flag" : clean3(
|
|
9339
|
+
) ? "flag" : clean3(import_node_process11.default.env.ODLA_DEV_TOKEN) ? "environment" : cacheStatus === "valid" ? "cache" : "missing";
|
|
9027
9340
|
return {
|
|
9028
9341
|
source,
|
|
9029
9342
|
cacheFile: context.cfg.local.tokenFile,
|
|
@@ -9034,12 +9347,12 @@ function clean3(value2) {
|
|
|
9034
9347
|
const normalized = value2?.trim();
|
|
9035
9348
|
return normalized || void 0;
|
|
9036
9349
|
}
|
|
9037
|
-
var
|
|
9350
|
+
var import_node_process11;
|
|
9038
9351
|
var init_operator_credentials = __esm({
|
|
9039
9352
|
"src/operator-credentials.ts"() {
|
|
9040
9353
|
"use strict";
|
|
9041
9354
|
init_cjs_shims();
|
|
9042
|
-
|
|
9355
|
+
import_node_process11 = __toESM(require("process"), 1);
|
|
9043
9356
|
init_argv();
|
|
9044
9357
|
init_local();
|
|
9045
9358
|
}
|
|
@@ -9205,9 +9518,11 @@ Start here:
|
|
|
9205
9518
|
step it made wrong.
|
|
9206
9519
|
|
|
9207
9520
|
Usage:
|
|
9521
|
+
odla-ai auth login --app <id> --email <odla-account> [--platform https://odla.ai] [--no-open] [--wait <seconds>] [--json]
|
|
9208
9522
|
odla-ai setup [--dir <project>] [--agent <name>] [--global] [--force]
|
|
9209
9523
|
odla-ai init --app-id <id> --name <name> [--services db,ai,o11y,calendar] [--env dev --env prod] [--ai-provider <byok-provider>]
|
|
9210
9524
|
odla-ai doctor [--config odla.config.mjs]
|
|
9525
|
+
odla-ai ai models [--config odla.config.mjs] [--env dev] [--provider <id>] [--json]
|
|
9211
9526
|
odla-ai config <diff|plan> [--config odla.config.mjs] [--email <odla-account>] [--json]
|
|
9212
9527
|
odla-ai config apply --plan <plan.json> [--idempotency-key <key>] [--email <odla-account>] [--json]
|
|
9213
9528
|
odla-ai operations get <operation-id> [--json]
|
|
@@ -9240,6 +9555,7 @@ Usage:
|
|
|
9240
9555
|
odla-ai pm task release <id> --expected-revision <n> [--mutation-id <id>] [--json]
|
|
9241
9556
|
odla-ai pm decision add --app <id> --title <t> --body <text> [--status <s>] [--mutation-id <id>] [--json]
|
|
9242
9557
|
odla-ai pm bug add --app <id> --title <t> (--description <text>|--body <text>) [--status <s>] [--severity <s>] [--goal <id>] [--assignee <id>] [--decision <id>] [--mutation-id <id>] [--json]
|
|
9558
|
+
odla-ai bug report --app <id> --title <t> (--description <text>|--body <text>) [--severity <s>] [--json]
|
|
9243
9559
|
odla-ai pm <goal|task|decision|bug> get <id> [--json]
|
|
9244
9560
|
odla-ai pm <goal|task|decision|bug> ref <id> [--json]
|
|
9245
9561
|
odla-ai pm goal set <id> [--title <t>|--status <s>|--proof <text>|--no-proof|--target <pct>|--no-target] [--mutation-id <id>] [--json]
|
|
@@ -9321,6 +9637,9 @@ function printHelp(output = console) {
|
|
|
9321
9637
|
output.log(`odla-ai
|
|
9322
9638
|
${USAGE_SECTION}
|
|
9323
9639
|
Commands:
|
|
9640
|
+
auth Start a fresh, exact-project agent authorization in the browser.
|
|
9641
|
+
The email is the signed-in odla account, never git or GitHub
|
|
9642
|
+
identity. The approval screen confirms the agent name first.
|
|
9324
9643
|
agent Inspect durable agent wakeups and explicitly requeue a
|
|
9325
9644
|
dead-lettered job; JSON output is stable for remote operators.
|
|
9326
9645
|
runbook odla's operational procedures, stored in the database and read at
|
|
@@ -9388,6 +9707,8 @@ Commands:
|
|
|
9388
9707
|
"app". Entities: goal (alias conformance), task (alias kanban),
|
|
9389
9708
|
decision, bug. Status changes and comments post to each item's
|
|
9390
9709
|
@odla-ai/chat discussion thread.
|
|
9710
|
+
bug Intent-first alias for PM bugs. "bug report" writes to
|
|
9711
|
+
odla PM; odla product defects do not belong in GitHub Issues.
|
|
9391
9712
|
discuss Group discussions (via @odla-ai/chat) for the apps you co-own:
|
|
9392
9713
|
one group per project, topics with replies, @-mentions of people,
|
|
9393
9714
|
agents, PM items, and projects. Built for unattended use \u2014 post a
|
|
@@ -9445,8 +9766,12 @@ Safety:
|
|
|
9445
9766
|
wait for approval, and re-run to collect.
|
|
9446
9767
|
A fresh device handshake requires --email <odla-account> or ODLA_USER_EMAIL.
|
|
9447
9768
|
The email is a non-secret identity hint: never provide a password or session
|
|
9448
|
-
token.
|
|
9769
|
+
token. It is the email shown by the signed-in odla account \u2014 never infer it
|
|
9770
|
+
from git config, a commit author, or GitHub. The matching account must already exist, be signed in, explicitly
|
|
9449
9771
|
review the exact code, and finish any current request before claiming another.
|
|
9772
|
+
Use "auth login --app <id> --email <odla-account>" when an outside agent needs
|
|
9773
|
+
a deliberate fresh request; it ignores cached credentials and opens the same
|
|
9774
|
+
focused authorization sequence used by every first-time command.
|
|
9450
9775
|
If provision reports that the current agent principal has no live app.manage
|
|
9451
9776
|
grant, run it once with --request-grant. That flag ignores ODLA_DEV_TOKEN and
|
|
9452
9777
|
the local cache, prints and opens a fresh exact-project owner-review URL, then
|
|
@@ -9983,7 +10308,8 @@ async function buildContext(parsed, deps) {
|
|
|
9983
10308
|
configPath: cfg.configPath,
|
|
9984
10309
|
token: stringOpt(parsed.options.token),
|
|
9985
10310
|
email: stringOpt(parsed.options.email),
|
|
9986
|
-
open: false
|
|
10311
|
+
open: parsed.options.open === false ? false : parsed.options.open === true ? true : void 0,
|
|
10312
|
+
openApprovalUrl: deps.openUrl
|
|
9987
10313
|
},
|
|
9988
10314
|
doFetch,
|
|
9989
10315
|
out
|
|
@@ -10063,7 +10389,8 @@ var init_discuss_command = __esm({
|
|
|
10063
10389
|
"jsonl",
|
|
10064
10390
|
"mutation-id",
|
|
10065
10391
|
"platform",
|
|
10066
|
-
"context"
|
|
10392
|
+
"context",
|
|
10393
|
+
"open"
|
|
10067
10394
|
];
|
|
10068
10395
|
}
|
|
10069
10396
|
});
|
|
@@ -10650,7 +10977,13 @@ async function buildContext2(parsed, deps) {
|
|
|
10650
10977
|
const out = deps.stdout ?? console;
|
|
10651
10978
|
const token = await getDeveloperToken(
|
|
10652
10979
|
cfg,
|
|
10653
|
-
{
|
|
10980
|
+
{
|
|
10981
|
+
configPath: cfg.configPath,
|
|
10982
|
+
token: stringOpt(parsed.options.token),
|
|
10983
|
+
email: stringOpt(parsed.options.email),
|
|
10984
|
+
open: parsed.options.open === false ? false : parsed.options.open === true ? true : void 0,
|
|
10985
|
+
openApprovalUrl: deps.openUrl
|
|
10986
|
+
},
|
|
10654
10987
|
doFetch,
|
|
10655
10988
|
out
|
|
10656
10989
|
);
|
|
@@ -10748,7 +11081,7 @@ var init_pm_command = __esm({
|
|
|
10748
11081
|
decision: "decision",
|
|
10749
11082
|
bug: "bug"
|
|
10750
11083
|
};
|
|
10751
|
-
COMMON_OPTIONS = ["config", "token", "email", "json", "platform", "context"];
|
|
11084
|
+
COMMON_OPTIONS = ["config", "token", "email", "json", "platform", "context", "open"];
|
|
10752
11085
|
ACTION_OPTIONS = {
|
|
10753
11086
|
list: ["app", "q", "limit", "offset"],
|
|
10754
11087
|
add: ["app", "title", "mutation-id"],
|
|
@@ -11204,7 +11537,8 @@ async function o11yCommand(parsed, deps = {}) {
|
|
|
11204
11537
|
"json",
|
|
11205
11538
|
"app",
|
|
11206
11539
|
"env",
|
|
11207
|
-
"minutes"
|
|
11540
|
+
"minutes",
|
|
11541
|
+
"open"
|
|
11208
11542
|
],
|
|
11209
11543
|
2
|
|
11210
11544
|
);
|
|
@@ -11231,7 +11565,8 @@ async function o11yCommand(parsed, deps = {}) {
|
|
|
11231
11565
|
configPath: cfg.configPath,
|
|
11232
11566
|
token: stringOpt(parsed.options.token),
|
|
11233
11567
|
email: stringOpt(parsed.options.email),
|
|
11234
|
-
open: false
|
|
11568
|
+
open: parsed.options.open === false ? false : parsed.options.open === true ? true : void 0,
|
|
11569
|
+
openApprovalUrl: deps.openUrl
|
|
11235
11570
|
},
|
|
11236
11571
|
doFetch,
|
|
11237
11572
|
out
|
|
@@ -11468,14 +11803,14 @@ async function mintDbKey(opts, tenantId) {
|
|
|
11468
11803
|
appId: tenantId
|
|
11469
11804
|
})
|
|
11470
11805
|
});
|
|
11471
|
-
if (!created.ok) throw new Error(`db app create (${tenantId}) failed: ${created.status} ${await
|
|
11806
|
+
if (!created.ok) throw new Error(`db app create (${tenantId}) failed: ${created.status} ${await safeText7(created)}`);
|
|
11472
11807
|
res = await opts.fetch(`${opts.cfg.dbEndpoint}/admin/apps/${encodeURIComponent(tenantId)}/keys`, {
|
|
11473
11808
|
method: "POST",
|
|
11474
11809
|
headers,
|
|
11475
11810
|
body: "{}"
|
|
11476
11811
|
});
|
|
11477
11812
|
}
|
|
11478
|
-
if (!res.ok) throw new Error(`db key mint (${tenantId}) failed: ${res.status} ${await
|
|
11813
|
+
if (!res.ok) throw new Error(`db key mint (${tenantId}) failed: ${res.status} ${await safeText7(res)}`);
|
|
11479
11814
|
const body = await res.json();
|
|
11480
11815
|
if (!body.key) throw new Error(`db key mint (${tenantId}) returned no key`);
|
|
11481
11816
|
return body.key;
|
|
@@ -11491,12 +11826,12 @@ async function issueO11yToken(opts) {
|
|
|
11491
11826
|
`o11y token already exists for env "${opts.env}", but its shown-once value is not in the local credentials file; run "odla-ai provision --rotate-o11y-token --push-secrets" to replace it explicitly`
|
|
11492
11827
|
);
|
|
11493
11828
|
}
|
|
11494
|
-
if (!res.ok) throw new Error(`o11y token ${opts.rotateO11y ? "rotation" : "issue"} (${opts.env}) failed: ${res.status} ${await
|
|
11829
|
+
if (!res.ok) throw new Error(`o11y token ${opts.rotateO11y ? "rotation" : "issue"} (${opts.env}) failed: ${res.status} ${await safeText7(res)}`);
|
|
11495
11830
|
const body = await res.json();
|
|
11496
11831
|
if (!body.token) throw new Error(`o11y token ${opts.rotateO11y ? "rotation" : "issue"} (${opts.env}) returned no token`);
|
|
11497
11832
|
return body.token;
|
|
11498
11833
|
}
|
|
11499
|
-
async function
|
|
11834
|
+
async function safeText7(res) {
|
|
11500
11835
|
try {
|
|
11501
11836
|
return redactSecrets((await res.text()).slice(0, 500));
|
|
11502
11837
|
} catch {
|
|
@@ -11685,10 +12020,10 @@ ${env}: credentials are already saved; retry "odla-ai secrets push --env ${env}$
|
|
|
11685
12020
|
await provisionIntegrationSeeds(doFetch, cfg.dbEndpoint, tenantId, dbKey, database.integrations, env, out);
|
|
11686
12021
|
}
|
|
11687
12022
|
if (cfg.services.includes("ai") && cfg.ai?.provider && cfg.ai.keyEnv) {
|
|
11688
|
-
const key =
|
|
12023
|
+
const key = import_node_process12.default.env[cfg.ai.keyEnv];
|
|
11689
12024
|
if (key) {
|
|
11690
12025
|
const secretName = cfg.ai.secretName ?? defaultSecretName(cfg.ai.provider);
|
|
11691
|
-
await (0,
|
|
12026
|
+
await (0, import_ai4.putSecret)({ endpoint: cfg.dbEndpoint, token, fetch: doFetch }, tenantId, secretName, key);
|
|
11692
12027
|
out.log(`${env}: ${cfg.ai.provider} key stored in vault (${secretName})`);
|
|
11693
12028
|
} else {
|
|
11694
12029
|
out.log(`${env}: ${cfg.ai.keyEnv} not set; skipped provider key storage`);
|
|
@@ -11724,14 +12059,14 @@ ${env}: credentials are already saved; retry "odla-ai secrets push --env ${env}$
|
|
|
11724
12059
|
}
|
|
11725
12060
|
}
|
|
11726
12061
|
}
|
|
11727
|
-
var import_apps12,
|
|
12062
|
+
var import_apps12, import_ai4, import_node_process12;
|
|
11728
12063
|
var init_provision = __esm({
|
|
11729
12064
|
"src/provision.ts"() {
|
|
11730
12065
|
"use strict";
|
|
11731
12066
|
init_cjs_shims();
|
|
11732
12067
|
import_apps12 = require("@odla-ai/apps");
|
|
11733
|
-
|
|
11734
|
-
|
|
12068
|
+
import_ai4 = require("@odla-ai/ai");
|
|
12069
|
+
import_node_process12 = __toESM(require("process"), 1);
|
|
11735
12070
|
init_config();
|
|
11736
12071
|
init_calendar();
|
|
11737
12072
|
init_calendar_errors();
|
|
@@ -11819,6 +12154,7 @@ var init_surface = __esm({
|
|
|
11819
12154
|
};
|
|
11820
12155
|
COMMAND_SURFACE = {
|
|
11821
12156
|
agent: { jobs: {}, retry: {} },
|
|
12157
|
+
ai: { models: {} },
|
|
11822
12158
|
admin: {
|
|
11823
12159
|
ai: {
|
|
11824
12160
|
show: {},
|
|
@@ -11841,7 +12177,9 @@ var init_surface = __esm({
|
|
|
11841
12177
|
promote: {},
|
|
11842
12178
|
owners: { list: {}, add: {}, remove: {} }
|
|
11843
12179
|
},
|
|
12180
|
+
auth: { login: {} },
|
|
11844
12181
|
brand: { design: { unpack: {} } },
|
|
12182
|
+
bug: { create: {}, list: {}, report: {} },
|
|
11845
12183
|
calendar: { status: {}, calendars: {}, connect: {}, disconnect: {} },
|
|
11846
12184
|
capabilities: {},
|
|
11847
12185
|
code: { connect: {} },
|
|
@@ -11913,7 +12251,7 @@ var init_surface = __esm({
|
|
|
11913
12251
|
|
|
11914
12252
|
// src/record.ts
|
|
11915
12253
|
function recordInvocation(parsed) {
|
|
11916
|
-
const file =
|
|
12254
|
+
const file = import_node_process13.default.env.ODLA_CLI_RECORD;
|
|
11917
12255
|
if (!file) return;
|
|
11918
12256
|
try {
|
|
11919
12257
|
const entry = {
|
|
@@ -11926,13 +12264,13 @@ function recordInvocation(parsed) {
|
|
|
11926
12264
|
} catch {
|
|
11927
12265
|
}
|
|
11928
12266
|
}
|
|
11929
|
-
var import_node_fs17,
|
|
12267
|
+
var import_node_fs17, import_node_process13;
|
|
11930
12268
|
var init_record = __esm({
|
|
11931
12269
|
"src/record.ts"() {
|
|
11932
12270
|
"use strict";
|
|
11933
12271
|
init_cjs_shims();
|
|
11934
12272
|
import_node_fs17 = require("fs");
|
|
11935
|
-
|
|
12273
|
+
import_node_process13 = __toESM(require("process"), 1);
|
|
11936
12274
|
init_surface();
|
|
11937
12275
|
}
|
|
11938
12276
|
});
|
|
@@ -12591,7 +12929,7 @@ var init_runbook_search_command = __esm({
|
|
|
12591
12929
|
});
|
|
12592
12930
|
|
|
12593
12931
|
// src/runbook-editor.ts
|
|
12594
|
-
function resolveEditor(env =
|
|
12932
|
+
function resolveEditor(env = import_node_process14.default.env) {
|
|
12595
12933
|
for (const name of EDITOR_ENV) {
|
|
12596
12934
|
const value2 = env[name];
|
|
12597
12935
|
if (value2 && value2.trim()) return value2.trim();
|
|
@@ -12605,8 +12943,8 @@ function defaultRun(command, path) {
|
|
|
12605
12943
|
return result.status ?? 0;
|
|
12606
12944
|
}
|
|
12607
12945
|
function editText(initial, slug, deps = {}) {
|
|
12608
|
-
const env = deps.env ??
|
|
12609
|
-
const interactive = deps.interactive ?? (() => Boolean(
|
|
12946
|
+
const env = deps.env ?? import_node_process14.default.env;
|
|
12947
|
+
const interactive = deps.interactive ?? (() => Boolean(import_node_process14.default.stdin.isTTY));
|
|
12610
12948
|
const editor = resolveEditor(env);
|
|
12611
12949
|
if (!editor)
|
|
12612
12950
|
throw new Error(
|
|
@@ -12626,7 +12964,7 @@ function editText(initial, slug, deps = {}) {
|
|
|
12626
12964
|
(0, import_node_fs21.rmSync)(dir, { recursive: true, force: true });
|
|
12627
12965
|
}
|
|
12628
12966
|
}
|
|
12629
|
-
var import_node_child_process8, import_node_fs21, import_node_os5, import_node_path19,
|
|
12967
|
+
var import_node_child_process8, import_node_fs21, import_node_os5, import_node_path19, import_node_process14, EDITOR_ENV, defaultRunOrInjected;
|
|
12630
12968
|
var init_runbook_editor = __esm({
|
|
12631
12969
|
"src/runbook-editor.ts"() {
|
|
12632
12970
|
"use strict";
|
|
@@ -12635,7 +12973,7 @@ var init_runbook_editor = __esm({
|
|
|
12635
12973
|
import_node_fs21 = require("fs");
|
|
12636
12974
|
import_node_os5 = require("os");
|
|
12637
12975
|
import_node_path19 = require("path");
|
|
12638
|
-
|
|
12976
|
+
import_node_process14 = __toESM(require("process"), 1);
|
|
12639
12977
|
EDITOR_ENV = ["ODLA_EDITOR", "VISUAL", "EDITOR"];
|
|
12640
12978
|
defaultRunOrInjected = (deps) => deps.run ?? defaultRun;
|
|
12641
12979
|
}
|
|
@@ -12663,152 +13001,6 @@ var init_runbook_edit_flow = __esm({
|
|
|
12663
13001
|
}
|
|
12664
13002
|
});
|
|
12665
13003
|
|
|
12666
|
-
// src/whoami-command.ts
|
|
12667
|
-
function principalKind(value2, machine) {
|
|
12668
|
-
return value2 === "human" || value2 === "agent" || value2 === "service" ? value2 : machine ? "service" : "human";
|
|
12669
|
-
}
|
|
12670
|
-
function credentialKind(value2, machine, scopes) {
|
|
12671
|
-
if (value2 === "machine" || value2 === "device" || value2 === "clerk") return value2;
|
|
12672
|
-
if (machine) return "machine";
|
|
12673
|
-
if (scopes.length) return "device";
|
|
12674
|
-
return "unknown";
|
|
12675
|
-
}
|
|
12676
|
-
function managerOf(value2) {
|
|
12677
|
-
if (!value2 || typeof value2 !== "object") return null;
|
|
12678
|
-
const row = value2;
|
|
12679
|
-
const principalId = text(row.principalId);
|
|
12680
|
-
if (!principalId) return null;
|
|
12681
|
-
return {
|
|
12682
|
-
principalId,
|
|
12683
|
-
displayName: text(row.displayName) ?? "Unnamed member",
|
|
12684
|
-
handle: text(row.handle) ?? ""
|
|
12685
|
-
};
|
|
12686
|
-
}
|
|
12687
|
-
function unnamedPrincipal(kind) {
|
|
12688
|
-
if (kind === "agent") return "Unnamed agent";
|
|
12689
|
-
if (kind === "service") return "Unnamed service";
|
|
12690
|
-
return "Unnamed member";
|
|
12691
|
-
}
|
|
12692
|
-
async function fetchIdentity(platformUrl, token, doFetch) {
|
|
12693
|
-
const res = await doFetch(`${platformUrl.replace(/\/$/, "")}/registry/me`, {
|
|
12694
|
-
headers: { authorization: `Bearer ${token}` }
|
|
12695
|
-
});
|
|
12696
|
-
if (!res.ok) throw new Error(`could not resolve identity (HTTP ${res.status})`);
|
|
12697
|
-
const body = await res.json();
|
|
12698
|
-
const developerId = text(body.developerId) ?? "";
|
|
12699
|
-
const machine = body.machine === true;
|
|
12700
|
-
const scopes = Array.isArray(body.scopes) ? body.scopes.map(String) : [];
|
|
12701
|
-
const principalId = text(body.principalId) ?? developerId;
|
|
12702
|
-
const email = text(body.email);
|
|
12703
|
-
const kind = principalKind(body.principalKind, machine);
|
|
12704
|
-
const displayName = text(body.displayName) ?? email ?? unnamedPrincipal(kind);
|
|
12705
|
-
const handle = text(body.handle) ?? "";
|
|
12706
|
-
const credential2 = body.credential && typeof body.credential === "object" ? body.credential : {};
|
|
12707
|
-
return {
|
|
12708
|
-
developerId,
|
|
12709
|
-
principalId,
|
|
12710
|
-
principalKind: kind,
|
|
12711
|
-
displayName,
|
|
12712
|
-
handle,
|
|
12713
|
-
manager: managerOf(body.manager),
|
|
12714
|
-
credential: {
|
|
12715
|
-
id: text(credential2.id),
|
|
12716
|
-
kind: credentialKind(credential2.kind, machine, scopes)
|
|
12717
|
-
},
|
|
12718
|
-
email,
|
|
12719
|
-
admin: body.admin === true,
|
|
12720
|
-
machine,
|
|
12721
|
-
scopes
|
|
12722
|
-
};
|
|
12723
|
-
}
|
|
12724
|
-
function credentialLabel(identity) {
|
|
12725
|
-
if (identity.credential.kind === "machine") return "machine (platform admin secret)";
|
|
12726
|
-
if (identity.credential.kind === "device")
|
|
12727
|
-
return identity.scopes.length ? "device (scoped)" : "device";
|
|
12728
|
-
if (identity.credential.kind === "clerk") return "clerk";
|
|
12729
|
-
return "unknown (legacy server)";
|
|
12730
|
-
}
|
|
12731
|
-
function namedPrincipal(identity) {
|
|
12732
|
-
return identity.handle && identity.handle !== identity.displayName ? `${identity.displayName} (@${identity.handle})` : identity.displayName;
|
|
12733
|
-
}
|
|
12734
|
-
function namedManager(manager) {
|
|
12735
|
-
return manager.handle && manager.handle !== manager.displayName ? `${manager.displayName} (@${manager.handle})` : manager.displayName;
|
|
12736
|
-
}
|
|
12737
|
-
function accountableOwner(identity) {
|
|
12738
|
-
if (identity.principalKind === "agent" && identity.manager) {
|
|
12739
|
-
return namedManager(identity.manager);
|
|
12740
|
-
}
|
|
12741
|
-
if (identity.principalKind === "human" && identity.principalId === identity.developerId) {
|
|
12742
|
-
return namedPrincipal(identity);
|
|
12743
|
-
}
|
|
12744
|
-
return identity.email ?? "Unnamed member";
|
|
12745
|
-
}
|
|
12746
|
-
async function whoamiCommand(parsed, deps = {}) {
|
|
12747
|
-
assertArgs(
|
|
12748
|
-
parsed,
|
|
12749
|
-
["config", "context", "platform", "token", "email", "json"],
|
|
12750
|
-
1
|
|
12751
|
-
);
|
|
12752
|
-
const out = deps.stdout ?? console;
|
|
12753
|
-
const doFetch = deps.fetch ?? fetch;
|
|
12754
|
-
const { cfg } = await resolveOperatorContext(parsed, {
|
|
12755
|
-
allowMissingConfig: true
|
|
12756
|
-
});
|
|
12757
|
-
const token = await getDeveloperToken(
|
|
12758
|
-
cfg,
|
|
12759
|
-
{
|
|
12760
|
-
configPath: cfg.configPath,
|
|
12761
|
-
token: stringOpt(parsed.options.token),
|
|
12762
|
-
email: stringOpt(parsed.options.email),
|
|
12763
|
-
// As above: never suppress the browser for an ordinary sign-in.
|
|
12764
|
-
open: void 0
|
|
12765
|
-
},
|
|
12766
|
-
doFetch,
|
|
12767
|
-
out
|
|
12768
|
-
);
|
|
12769
|
-
const identity = await fetchIdentity(cfg.platformUrl, token, doFetch);
|
|
12770
|
-
if (parsed.options.json === true) {
|
|
12771
|
-
out.log(JSON.stringify(identity, null, 2));
|
|
12772
|
-
return;
|
|
12773
|
-
}
|
|
12774
|
-
out.log(`platform: ${cfg.platformUrl}`);
|
|
12775
|
-
out.log(`principal: ${namedPrincipal(identity)}`);
|
|
12776
|
-
out.log(`principal id: ${identity.principalId}`);
|
|
12777
|
-
out.log(`kind: ${identity.principalKind}`);
|
|
12778
|
-
if (identity.principalKind === "agent")
|
|
12779
|
-
out.log(
|
|
12780
|
-
`manager: ${identity.manager ? namedManager(identity.manager) : "(unknown)"}`
|
|
12781
|
-
);
|
|
12782
|
-
out.log(`owner: ${accountableOwner(identity)}`);
|
|
12783
|
-
out.log(`owner id: ${identity.developerId}`);
|
|
12784
|
-
out.log(`email: ${identity.email ?? "(none)"}`);
|
|
12785
|
-
out.log(`credential: ${credentialLabel(identity)}`);
|
|
12786
|
-
if (identity.credential.id)
|
|
12787
|
-
out.log(`credential id: ${identity.credential.id}`);
|
|
12788
|
-
out.log(`admin: ${identity.admin ? "yes" : "no"}`);
|
|
12789
|
-
if (identity.scopes.length) out.log(`scopes: ${identity.scopes.join(", ")}`);
|
|
12790
|
-
if (!identity.admin) {
|
|
12791
|
-
if (identity.scopes.includes("platform:runbook:write")) {
|
|
12792
|
-
out.log("\nThis exact scope can read and edit all platform runbook content.");
|
|
12793
|
-
out.log("The device token is not ambient admin and cannot change visibility.");
|
|
12794
|
-
} else {
|
|
12795
|
-
out.log("\nYou can read operator-visible platform runbooks but not edit them.");
|
|
12796
|
-
out.log("Use a platform-admin-approved runbook write request to edit.");
|
|
12797
|
-
}
|
|
12798
|
-
}
|
|
12799
|
-
}
|
|
12800
|
-
var text;
|
|
12801
|
-
var init_whoami_command = __esm({
|
|
12802
|
-
"src/whoami-command.ts"() {
|
|
12803
|
-
"use strict";
|
|
12804
|
-
init_cjs_shims();
|
|
12805
|
-
init_argv();
|
|
12806
|
-
init_operator_context();
|
|
12807
|
-
init_token();
|
|
12808
|
-
text = (value2) => typeof value2 === "string" && value2.trim() ? value2.trim() : null;
|
|
12809
|
-
}
|
|
12810
|
-
});
|
|
12811
|
-
|
|
12812
13004
|
// src/runbook-command.ts
|
|
12813
13005
|
function requireSlug(slug, action2) {
|
|
12814
13006
|
if (!slug) throw new Error(`"runbook ${action2}" needs a slug, e.g. "odla-ai runbook ${action2} release"`);
|
|
@@ -13781,6 +13973,10 @@ async function runCli(argv2 = process.argv.slice(2), dependencies = {}) {
|
|
|
13781
13973
|
await whoamiCommand(parsed, runtime);
|
|
13782
13974
|
return;
|
|
13783
13975
|
}
|
|
13976
|
+
if (command === "auth") {
|
|
13977
|
+
await authCommand(parsed, runtime);
|
|
13978
|
+
return;
|
|
13979
|
+
}
|
|
13784
13980
|
if (command === "context") {
|
|
13785
13981
|
await contextCommand(parsed, runtime);
|
|
13786
13982
|
return;
|
|
@@ -13821,6 +14017,15 @@ async function runCli(argv2 = process.argv.slice(2), dependencies = {}) {
|
|
|
13821
14017
|
await pmCommand(parsed, runtime);
|
|
13822
14018
|
return;
|
|
13823
14019
|
}
|
|
14020
|
+
if (command === "bug") {
|
|
14021
|
+
const action2 = parsed.positionals[1] ?? "list";
|
|
14022
|
+
const canonical = action2 === "report" || action2 === "create" ? "add" : action2;
|
|
14023
|
+
await pmCommand({
|
|
14024
|
+
...parsed,
|
|
14025
|
+
positionals: ["pm", "bug", canonical, ...parsed.positionals.slice(2)]
|
|
14026
|
+
}, runtime);
|
|
14027
|
+
return;
|
|
14028
|
+
}
|
|
13824
14029
|
if (command === "discuss") {
|
|
13825
14030
|
await discussCommand(parsed, runtime);
|
|
13826
14031
|
return;
|
|
@@ -13907,6 +14112,7 @@ var init_cli = __esm({
|
|
|
13907
14112
|
"use strict";
|
|
13908
14113
|
init_cjs_shims();
|
|
13909
14114
|
init_admin_command();
|
|
14115
|
+
init_auth_command();
|
|
13910
14116
|
init_agent_command();
|
|
13911
14117
|
init_app_lifecycle();
|
|
13912
14118
|
init_brand_command();
|