@odla-ai/cli 0.27.17 → 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/dist/bin.cjs +295 -171
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-3WVVHH3Y.js → chunk-HCNESRIA.js} +272 -158
- package/dist/chunk-HCNESRIA.js.map +1 -0
- package/dist/{cli-DCSVQAZ6.js → cli-DDOFM47D.js} +2 -2
- package/dist/index.cjs +271 -157
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-3WVVHH3Y.js.map +0 -1
- /package/dist/{cli-DCSVQAZ6.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]);
|
|
@@ -4917,6 +5142,14 @@ and atomically claim a refined Ready task. Record decisions when you make them
|
|
|
4917
5142
|
and file bugs when you notice them. The conventions and the full command set are
|
|
4918
5143
|
in \`.agents/skills/odla/references/pm.md\`.
|
|
4919
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
|
+
|
|
4920
5153
|
The setup runbooks and their references are installed in this repository, pinned
|
|
4921
5154
|
to this CLI version. Use them as your setup context.
|
|
4922
5155
|
|
|
@@ -9103,7 +9336,7 @@ function developerTokenStatus(context, parsed, now = Date.now()) {
|
|
|
9103
9336
|
const cacheStatus = !cached?.token ? "missing" : cached.platform !== context.platform.value ? "other-platform" : (cached.expiresAt ?? 0) <= now + 6e4 ? "expired" : "valid";
|
|
9104
9337
|
const source = clean3(
|
|
9105
9338
|
stringOpt(parsed.options.token)
|
|
9106
|
-
) ? "flag" : clean3(
|
|
9339
|
+
) ? "flag" : clean3(import_node_process11.default.env.ODLA_DEV_TOKEN) ? "environment" : cacheStatus === "valid" ? "cache" : "missing";
|
|
9107
9340
|
return {
|
|
9108
9341
|
source,
|
|
9109
9342
|
cacheFile: context.cfg.local.tokenFile,
|
|
@@ -9114,12 +9347,12 @@ function clean3(value2) {
|
|
|
9114
9347
|
const normalized = value2?.trim();
|
|
9115
9348
|
return normalized || void 0;
|
|
9116
9349
|
}
|
|
9117
|
-
var
|
|
9350
|
+
var import_node_process11;
|
|
9118
9351
|
var init_operator_credentials = __esm({
|
|
9119
9352
|
"src/operator-credentials.ts"() {
|
|
9120
9353
|
"use strict";
|
|
9121
9354
|
init_cjs_shims();
|
|
9122
|
-
|
|
9355
|
+
import_node_process11 = __toESM(require("process"), 1);
|
|
9123
9356
|
init_argv();
|
|
9124
9357
|
init_local();
|
|
9125
9358
|
}
|
|
@@ -9285,6 +9518,7 @@ Start here:
|
|
|
9285
9518
|
step it made wrong.
|
|
9286
9519
|
|
|
9287
9520
|
Usage:
|
|
9521
|
+
odla-ai auth login --app <id> --email <odla-account> [--platform https://odla.ai] [--no-open] [--wait <seconds>] [--json]
|
|
9288
9522
|
odla-ai setup [--dir <project>] [--agent <name>] [--global] [--force]
|
|
9289
9523
|
odla-ai init --app-id <id> --name <name> [--services db,ai,o11y,calendar] [--env dev --env prod] [--ai-provider <byok-provider>]
|
|
9290
9524
|
odla-ai doctor [--config odla.config.mjs]
|
|
@@ -9321,6 +9555,7 @@ Usage:
|
|
|
9321
9555
|
odla-ai pm task release <id> --expected-revision <n> [--mutation-id <id>] [--json]
|
|
9322
9556
|
odla-ai pm decision add --app <id> --title <t> --body <text> [--status <s>] [--mutation-id <id>] [--json]
|
|
9323
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]
|
|
9324
9559
|
odla-ai pm <goal|task|decision|bug> get <id> [--json]
|
|
9325
9560
|
odla-ai pm <goal|task|decision|bug> ref <id> [--json]
|
|
9326
9561
|
odla-ai pm goal set <id> [--title <t>|--status <s>|--proof <text>|--no-proof|--target <pct>|--no-target] [--mutation-id <id>] [--json]
|
|
@@ -9402,6 +9637,9 @@ function printHelp(output = console) {
|
|
|
9402
9637
|
output.log(`odla-ai
|
|
9403
9638
|
${USAGE_SECTION}
|
|
9404
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.
|
|
9405
9643
|
agent Inspect durable agent wakeups and explicitly requeue a
|
|
9406
9644
|
dead-lettered job; JSON output is stable for remote operators.
|
|
9407
9645
|
runbook odla's operational procedures, stored in the database and read at
|
|
@@ -9469,6 +9707,8 @@ Commands:
|
|
|
9469
9707
|
"app". Entities: goal (alias conformance), task (alias kanban),
|
|
9470
9708
|
decision, bug. Status changes and comments post to each item's
|
|
9471
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.
|
|
9472
9712
|
discuss Group discussions (via @odla-ai/chat) for the apps you co-own:
|
|
9473
9713
|
one group per project, topics with replies, @-mentions of people,
|
|
9474
9714
|
agents, PM items, and projects. Built for unattended use \u2014 post a
|
|
@@ -9526,8 +9766,12 @@ Safety:
|
|
|
9526
9766
|
wait for approval, and re-run to collect.
|
|
9527
9767
|
A fresh device handshake requires --email <odla-account> or ODLA_USER_EMAIL.
|
|
9528
9768
|
The email is a non-secret identity hint: never provide a password or session
|
|
9529
|
-
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
|
|
9530
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.
|
|
9531
9775
|
If provision reports that the current agent principal has no live app.manage
|
|
9532
9776
|
grant, run it once with --request-grant. That flag ignores ODLA_DEV_TOKEN and
|
|
9533
9777
|
the local cache, prints and opens a fresh exact-project owner-review URL, then
|
|
@@ -10064,7 +10308,8 @@ async function buildContext(parsed, deps) {
|
|
|
10064
10308
|
configPath: cfg.configPath,
|
|
10065
10309
|
token: stringOpt(parsed.options.token),
|
|
10066
10310
|
email: stringOpt(parsed.options.email),
|
|
10067
|
-
open: false
|
|
10311
|
+
open: parsed.options.open === false ? false : parsed.options.open === true ? true : void 0,
|
|
10312
|
+
openApprovalUrl: deps.openUrl
|
|
10068
10313
|
},
|
|
10069
10314
|
doFetch,
|
|
10070
10315
|
out
|
|
@@ -10144,7 +10389,8 @@ var init_discuss_command = __esm({
|
|
|
10144
10389
|
"jsonl",
|
|
10145
10390
|
"mutation-id",
|
|
10146
10391
|
"platform",
|
|
10147
|
-
"context"
|
|
10392
|
+
"context",
|
|
10393
|
+
"open"
|
|
10148
10394
|
];
|
|
10149
10395
|
}
|
|
10150
10396
|
});
|
|
@@ -10731,7 +10977,13 @@ async function buildContext2(parsed, deps) {
|
|
|
10731
10977
|
const out = deps.stdout ?? console;
|
|
10732
10978
|
const token = await getDeveloperToken(
|
|
10733
10979
|
cfg,
|
|
10734
|
-
{
|
|
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
|
+
},
|
|
10735
10987
|
doFetch,
|
|
10736
10988
|
out
|
|
10737
10989
|
);
|
|
@@ -10829,7 +11081,7 @@ var init_pm_command = __esm({
|
|
|
10829
11081
|
decision: "decision",
|
|
10830
11082
|
bug: "bug"
|
|
10831
11083
|
};
|
|
10832
|
-
COMMON_OPTIONS = ["config", "token", "email", "json", "platform", "context"];
|
|
11084
|
+
COMMON_OPTIONS = ["config", "token", "email", "json", "platform", "context", "open"];
|
|
10833
11085
|
ACTION_OPTIONS = {
|
|
10834
11086
|
list: ["app", "q", "limit", "offset"],
|
|
10835
11087
|
add: ["app", "title", "mutation-id"],
|
|
@@ -11285,7 +11537,8 @@ async function o11yCommand(parsed, deps = {}) {
|
|
|
11285
11537
|
"json",
|
|
11286
11538
|
"app",
|
|
11287
11539
|
"env",
|
|
11288
|
-
"minutes"
|
|
11540
|
+
"minutes",
|
|
11541
|
+
"open"
|
|
11289
11542
|
],
|
|
11290
11543
|
2
|
|
11291
11544
|
);
|
|
@@ -11312,7 +11565,8 @@ async function o11yCommand(parsed, deps = {}) {
|
|
|
11312
11565
|
configPath: cfg.configPath,
|
|
11313
11566
|
token: stringOpt(parsed.options.token),
|
|
11314
11567
|
email: stringOpt(parsed.options.email),
|
|
11315
|
-
open: false
|
|
11568
|
+
open: parsed.options.open === false ? false : parsed.options.open === true ? true : void 0,
|
|
11569
|
+
openApprovalUrl: deps.openUrl
|
|
11316
11570
|
},
|
|
11317
11571
|
doFetch,
|
|
11318
11572
|
out
|
|
@@ -11766,7 +12020,7 @@ ${env}: credentials are already saved; retry "odla-ai secrets push --env ${env}$
|
|
|
11766
12020
|
await provisionIntegrationSeeds(doFetch, cfg.dbEndpoint, tenantId, dbKey, database.integrations, env, out);
|
|
11767
12021
|
}
|
|
11768
12022
|
if (cfg.services.includes("ai") && cfg.ai?.provider && cfg.ai.keyEnv) {
|
|
11769
|
-
const key =
|
|
12023
|
+
const key = import_node_process12.default.env[cfg.ai.keyEnv];
|
|
11770
12024
|
if (key) {
|
|
11771
12025
|
const secretName = cfg.ai.secretName ?? defaultSecretName(cfg.ai.provider);
|
|
11772
12026
|
await (0, import_ai4.putSecret)({ endpoint: cfg.dbEndpoint, token, fetch: doFetch }, tenantId, secretName, key);
|
|
@@ -11805,14 +12059,14 @@ ${env}: credentials are already saved; retry "odla-ai secrets push --env ${env}$
|
|
|
11805
12059
|
}
|
|
11806
12060
|
}
|
|
11807
12061
|
}
|
|
11808
|
-
var import_apps12, import_ai4,
|
|
12062
|
+
var import_apps12, import_ai4, import_node_process12;
|
|
11809
12063
|
var init_provision = __esm({
|
|
11810
12064
|
"src/provision.ts"() {
|
|
11811
12065
|
"use strict";
|
|
11812
12066
|
init_cjs_shims();
|
|
11813
12067
|
import_apps12 = require("@odla-ai/apps");
|
|
11814
12068
|
import_ai4 = require("@odla-ai/ai");
|
|
11815
|
-
|
|
12069
|
+
import_node_process12 = __toESM(require("process"), 1);
|
|
11816
12070
|
init_config();
|
|
11817
12071
|
init_calendar();
|
|
11818
12072
|
init_calendar_errors();
|
|
@@ -11923,7 +12177,9 @@ var init_surface = __esm({
|
|
|
11923
12177
|
promote: {},
|
|
11924
12178
|
owners: { list: {}, add: {}, remove: {} }
|
|
11925
12179
|
},
|
|
12180
|
+
auth: { login: {} },
|
|
11926
12181
|
brand: { design: { unpack: {} } },
|
|
12182
|
+
bug: { create: {}, list: {}, report: {} },
|
|
11927
12183
|
calendar: { status: {}, calendars: {}, connect: {}, disconnect: {} },
|
|
11928
12184
|
capabilities: {},
|
|
11929
12185
|
code: { connect: {} },
|
|
@@ -11995,7 +12251,7 @@ var init_surface = __esm({
|
|
|
11995
12251
|
|
|
11996
12252
|
// src/record.ts
|
|
11997
12253
|
function recordInvocation(parsed) {
|
|
11998
|
-
const file =
|
|
12254
|
+
const file = import_node_process13.default.env.ODLA_CLI_RECORD;
|
|
11999
12255
|
if (!file) return;
|
|
12000
12256
|
try {
|
|
12001
12257
|
const entry = {
|
|
@@ -12008,13 +12264,13 @@ function recordInvocation(parsed) {
|
|
|
12008
12264
|
} catch {
|
|
12009
12265
|
}
|
|
12010
12266
|
}
|
|
12011
|
-
var import_node_fs17,
|
|
12267
|
+
var import_node_fs17, import_node_process13;
|
|
12012
12268
|
var init_record = __esm({
|
|
12013
12269
|
"src/record.ts"() {
|
|
12014
12270
|
"use strict";
|
|
12015
12271
|
init_cjs_shims();
|
|
12016
12272
|
import_node_fs17 = require("fs");
|
|
12017
|
-
|
|
12273
|
+
import_node_process13 = __toESM(require("process"), 1);
|
|
12018
12274
|
init_surface();
|
|
12019
12275
|
}
|
|
12020
12276
|
});
|
|
@@ -12673,7 +12929,7 @@ var init_runbook_search_command = __esm({
|
|
|
12673
12929
|
});
|
|
12674
12930
|
|
|
12675
12931
|
// src/runbook-editor.ts
|
|
12676
|
-
function resolveEditor(env =
|
|
12932
|
+
function resolveEditor(env = import_node_process14.default.env) {
|
|
12677
12933
|
for (const name of EDITOR_ENV) {
|
|
12678
12934
|
const value2 = env[name];
|
|
12679
12935
|
if (value2 && value2.trim()) return value2.trim();
|
|
@@ -12687,8 +12943,8 @@ function defaultRun(command, path) {
|
|
|
12687
12943
|
return result.status ?? 0;
|
|
12688
12944
|
}
|
|
12689
12945
|
function editText(initial, slug, deps = {}) {
|
|
12690
|
-
const env = deps.env ??
|
|
12691
|
-
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));
|
|
12692
12948
|
const editor = resolveEditor(env);
|
|
12693
12949
|
if (!editor)
|
|
12694
12950
|
throw new Error(
|
|
@@ -12708,7 +12964,7 @@ function editText(initial, slug, deps = {}) {
|
|
|
12708
12964
|
(0, import_node_fs21.rmSync)(dir, { recursive: true, force: true });
|
|
12709
12965
|
}
|
|
12710
12966
|
}
|
|
12711
|
-
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;
|
|
12712
12968
|
var init_runbook_editor = __esm({
|
|
12713
12969
|
"src/runbook-editor.ts"() {
|
|
12714
12970
|
"use strict";
|
|
@@ -12717,7 +12973,7 @@ var init_runbook_editor = __esm({
|
|
|
12717
12973
|
import_node_fs21 = require("fs");
|
|
12718
12974
|
import_node_os5 = require("os");
|
|
12719
12975
|
import_node_path19 = require("path");
|
|
12720
|
-
|
|
12976
|
+
import_node_process14 = __toESM(require("process"), 1);
|
|
12721
12977
|
EDITOR_ENV = ["ODLA_EDITOR", "VISUAL", "EDITOR"];
|
|
12722
12978
|
defaultRunOrInjected = (deps) => deps.run ?? defaultRun;
|
|
12723
12979
|
}
|
|
@@ -12745,152 +13001,6 @@ var init_runbook_edit_flow = __esm({
|
|
|
12745
13001
|
}
|
|
12746
13002
|
});
|
|
12747
13003
|
|
|
12748
|
-
// src/whoami-command.ts
|
|
12749
|
-
function principalKind(value2, machine) {
|
|
12750
|
-
return value2 === "human" || value2 === "agent" || value2 === "service" ? value2 : machine ? "service" : "human";
|
|
12751
|
-
}
|
|
12752
|
-
function credentialKind(value2, machine, scopes) {
|
|
12753
|
-
if (value2 === "machine" || value2 === "device" || value2 === "clerk") return value2;
|
|
12754
|
-
if (machine) return "machine";
|
|
12755
|
-
if (scopes.length) return "device";
|
|
12756
|
-
return "unknown";
|
|
12757
|
-
}
|
|
12758
|
-
function managerOf(value2) {
|
|
12759
|
-
if (!value2 || typeof value2 !== "object") return null;
|
|
12760
|
-
const row = value2;
|
|
12761
|
-
const principalId = text(row.principalId);
|
|
12762
|
-
if (!principalId) return null;
|
|
12763
|
-
return {
|
|
12764
|
-
principalId,
|
|
12765
|
-
displayName: text(row.displayName) ?? "Unnamed member",
|
|
12766
|
-
handle: text(row.handle) ?? ""
|
|
12767
|
-
};
|
|
12768
|
-
}
|
|
12769
|
-
function unnamedPrincipal(kind) {
|
|
12770
|
-
if (kind === "agent") return "Unnamed agent";
|
|
12771
|
-
if (kind === "service") return "Unnamed service";
|
|
12772
|
-
return "Unnamed member";
|
|
12773
|
-
}
|
|
12774
|
-
async function fetchIdentity(platformUrl, token, doFetch) {
|
|
12775
|
-
const res = await doFetch(`${platformUrl.replace(/\/$/, "")}/registry/me`, {
|
|
12776
|
-
headers: { authorization: `Bearer ${token}` }
|
|
12777
|
-
});
|
|
12778
|
-
if (!res.ok) throw new Error(`could not resolve identity (HTTP ${res.status})`);
|
|
12779
|
-
const body = await res.json();
|
|
12780
|
-
const developerId = text(body.developerId) ?? "";
|
|
12781
|
-
const machine = body.machine === true;
|
|
12782
|
-
const scopes = Array.isArray(body.scopes) ? body.scopes.map(String) : [];
|
|
12783
|
-
const principalId = text(body.principalId) ?? developerId;
|
|
12784
|
-
const email = text(body.email);
|
|
12785
|
-
const kind = principalKind(body.principalKind, machine);
|
|
12786
|
-
const displayName = text(body.displayName) ?? email ?? unnamedPrincipal(kind);
|
|
12787
|
-
const handle = text(body.handle) ?? "";
|
|
12788
|
-
const credential2 = body.credential && typeof body.credential === "object" ? body.credential : {};
|
|
12789
|
-
return {
|
|
12790
|
-
developerId,
|
|
12791
|
-
principalId,
|
|
12792
|
-
principalKind: kind,
|
|
12793
|
-
displayName,
|
|
12794
|
-
handle,
|
|
12795
|
-
manager: managerOf(body.manager),
|
|
12796
|
-
credential: {
|
|
12797
|
-
id: text(credential2.id),
|
|
12798
|
-
kind: credentialKind(credential2.kind, machine, scopes)
|
|
12799
|
-
},
|
|
12800
|
-
email,
|
|
12801
|
-
admin: body.admin === true,
|
|
12802
|
-
machine,
|
|
12803
|
-
scopes
|
|
12804
|
-
};
|
|
12805
|
-
}
|
|
12806
|
-
function credentialLabel(identity) {
|
|
12807
|
-
if (identity.credential.kind === "machine") return "machine (platform admin secret)";
|
|
12808
|
-
if (identity.credential.kind === "device")
|
|
12809
|
-
return identity.scopes.length ? "device (scoped)" : "device";
|
|
12810
|
-
if (identity.credential.kind === "clerk") return "clerk";
|
|
12811
|
-
return "unknown (legacy server)";
|
|
12812
|
-
}
|
|
12813
|
-
function namedPrincipal(identity) {
|
|
12814
|
-
return identity.handle && identity.handle !== identity.displayName ? `${identity.displayName} (@${identity.handle})` : identity.displayName;
|
|
12815
|
-
}
|
|
12816
|
-
function namedManager(manager) {
|
|
12817
|
-
return manager.handle && manager.handle !== manager.displayName ? `${manager.displayName} (@${manager.handle})` : manager.displayName;
|
|
12818
|
-
}
|
|
12819
|
-
function accountableOwner(identity) {
|
|
12820
|
-
if (identity.principalKind === "agent" && identity.manager) {
|
|
12821
|
-
return namedManager(identity.manager);
|
|
12822
|
-
}
|
|
12823
|
-
if (identity.principalKind === "human" && identity.principalId === identity.developerId) {
|
|
12824
|
-
return namedPrincipal(identity);
|
|
12825
|
-
}
|
|
12826
|
-
return identity.email ?? "Unnamed member";
|
|
12827
|
-
}
|
|
12828
|
-
async function whoamiCommand(parsed, deps = {}) {
|
|
12829
|
-
assertArgs(
|
|
12830
|
-
parsed,
|
|
12831
|
-
["config", "context", "platform", "token", "email", "json"],
|
|
12832
|
-
1
|
|
12833
|
-
);
|
|
12834
|
-
const out = deps.stdout ?? console;
|
|
12835
|
-
const doFetch = deps.fetch ?? fetch;
|
|
12836
|
-
const { cfg } = await resolveOperatorContext(parsed, {
|
|
12837
|
-
allowMissingConfig: true
|
|
12838
|
-
});
|
|
12839
|
-
const token = await getDeveloperToken(
|
|
12840
|
-
cfg,
|
|
12841
|
-
{
|
|
12842
|
-
configPath: cfg.configPath,
|
|
12843
|
-
token: stringOpt(parsed.options.token),
|
|
12844
|
-
email: stringOpt(parsed.options.email),
|
|
12845
|
-
// As above: never suppress the browser for an ordinary sign-in.
|
|
12846
|
-
open: void 0
|
|
12847
|
-
},
|
|
12848
|
-
doFetch,
|
|
12849
|
-
out
|
|
12850
|
-
);
|
|
12851
|
-
const identity = await fetchIdentity(cfg.platformUrl, token, doFetch);
|
|
12852
|
-
if (parsed.options.json === true) {
|
|
12853
|
-
out.log(JSON.stringify(identity, null, 2));
|
|
12854
|
-
return;
|
|
12855
|
-
}
|
|
12856
|
-
out.log(`platform: ${cfg.platformUrl}`);
|
|
12857
|
-
out.log(`principal: ${namedPrincipal(identity)}`);
|
|
12858
|
-
out.log(`principal id: ${identity.principalId}`);
|
|
12859
|
-
out.log(`kind: ${identity.principalKind}`);
|
|
12860
|
-
if (identity.principalKind === "agent")
|
|
12861
|
-
out.log(
|
|
12862
|
-
`manager: ${identity.manager ? namedManager(identity.manager) : "(unknown)"}`
|
|
12863
|
-
);
|
|
12864
|
-
out.log(`owner: ${accountableOwner(identity)}`);
|
|
12865
|
-
out.log(`owner id: ${identity.developerId}`);
|
|
12866
|
-
out.log(`email: ${identity.email ?? "(none)"}`);
|
|
12867
|
-
out.log(`credential: ${credentialLabel(identity)}`);
|
|
12868
|
-
if (identity.credential.id)
|
|
12869
|
-
out.log(`credential id: ${identity.credential.id}`);
|
|
12870
|
-
out.log(`admin: ${identity.admin ? "yes" : "no"}`);
|
|
12871
|
-
if (identity.scopes.length) out.log(`scopes: ${identity.scopes.join(", ")}`);
|
|
12872
|
-
if (!identity.admin) {
|
|
12873
|
-
if (identity.scopes.includes("platform:runbook:write")) {
|
|
12874
|
-
out.log("\nThis exact scope can read and edit all platform runbook content.");
|
|
12875
|
-
out.log("The device token is not ambient admin and cannot change visibility.");
|
|
12876
|
-
} else {
|
|
12877
|
-
out.log("\nYou can read operator-visible platform runbooks but not edit them.");
|
|
12878
|
-
out.log("Use a platform-admin-approved runbook write request to edit.");
|
|
12879
|
-
}
|
|
12880
|
-
}
|
|
12881
|
-
}
|
|
12882
|
-
var text;
|
|
12883
|
-
var init_whoami_command = __esm({
|
|
12884
|
-
"src/whoami-command.ts"() {
|
|
12885
|
-
"use strict";
|
|
12886
|
-
init_cjs_shims();
|
|
12887
|
-
init_argv();
|
|
12888
|
-
init_operator_context();
|
|
12889
|
-
init_token();
|
|
12890
|
-
text = (value2) => typeof value2 === "string" && value2.trim() ? value2.trim() : null;
|
|
12891
|
-
}
|
|
12892
|
-
});
|
|
12893
|
-
|
|
12894
13004
|
// src/runbook-command.ts
|
|
12895
13005
|
function requireSlug(slug, action2) {
|
|
12896
13006
|
if (!slug) throw new Error(`"runbook ${action2}" needs a slug, e.g. "odla-ai runbook ${action2} release"`);
|
|
@@ -13863,6 +13973,10 @@ async function runCli(argv2 = process.argv.slice(2), dependencies = {}) {
|
|
|
13863
13973
|
await whoamiCommand(parsed, runtime);
|
|
13864
13974
|
return;
|
|
13865
13975
|
}
|
|
13976
|
+
if (command === "auth") {
|
|
13977
|
+
await authCommand(parsed, runtime);
|
|
13978
|
+
return;
|
|
13979
|
+
}
|
|
13866
13980
|
if (command === "context") {
|
|
13867
13981
|
await contextCommand(parsed, runtime);
|
|
13868
13982
|
return;
|
|
@@ -13903,6 +14017,15 @@ async function runCli(argv2 = process.argv.slice(2), dependencies = {}) {
|
|
|
13903
14017
|
await pmCommand(parsed, runtime);
|
|
13904
14018
|
return;
|
|
13905
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
|
+
}
|
|
13906
14029
|
if (command === "discuss") {
|
|
13907
14030
|
await discussCommand(parsed, runtime);
|
|
13908
14031
|
return;
|
|
@@ -13989,6 +14112,7 @@ var init_cli = __esm({
|
|
|
13989
14112
|
"use strict";
|
|
13990
14113
|
init_cjs_shims();
|
|
13991
14114
|
init_admin_command();
|
|
14115
|
+
init_auth_command();
|
|
13992
14116
|
init_agent_command();
|
|
13993
14117
|
init_app_lifecycle();
|
|
13994
14118
|
init_brand_command();
|