@odla-ai/cli 0.27.17 → 0.28.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/dist/bin.cjs +297 -172
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-3WVVHH3Y.js → chunk-STJCWZJU.js} +274 -159
- package/dist/chunk-STJCWZJU.js.map +1 -0
- package/dist/{cli-DCSVQAZ6.js → cli-2WZIFXA6.js} +2 -2
- package/dist/index.cjs +273 -158
- 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-2WZIFXA6.js.map} +0 -0
package/dist/bin.js
CHANGED
|
@@ -174,7 +174,7 @@ function absoluteEntryPath(entryPath) {
|
|
|
174
174
|
|
|
175
175
|
// src/bin.ts
|
|
176
176
|
var argv = process.argv.slice(2);
|
|
177
|
-
requireCurrentCliForProvision(argv).then(() => requireCoherentProvisionRuntime(argv)).then(async () => (await import("./cli-
|
|
177
|
+
requireCurrentCliForProvision(argv).then(() => requireCoherentProvisionRuntime(argv)).then(async () => (await import("./cli-2WZIFXA6.js")).runCli()).catch((err) => {
|
|
178
178
|
console.error(redactSecrets(`odla-ai: ${err instanceof Error ? err.message : String(err)}`));
|
|
179
179
|
process.exitCode = exitCodeFor(err);
|
|
180
180
|
});
|
|
@@ -304,7 +304,7 @@ async function getDeveloperToken(cfg, options, doFetch, out, grantRequest = {})
|
|
|
304
304
|
const optionalProjectCapabilities = grantRequest.optionalProjectCapabilities ?? [];
|
|
305
305
|
const grantIntent = { projectIds: [cfg.app.id], optionalProjectCapabilities };
|
|
306
306
|
const cached = readJsonFile(cfg.local.tokenFile);
|
|
307
|
-
if (!grantRequest.forceReview) {
|
|
307
|
+
if (!grantRequest.forceReview && !grantRequest.freshLogin) {
|
|
308
308
|
if (options.token) return options.token;
|
|
309
309
|
if (process5.env.ODLA_DEV_TOKEN) {
|
|
310
310
|
const declared = process5.env.ODLA_DEV_TOKEN_AUDIENCE;
|
|
@@ -321,9 +321,9 @@ async function getDeveloperToken(cfg, options, doFetch, out, grantRequest = {})
|
|
|
321
321
|
}
|
|
322
322
|
} else {
|
|
323
323
|
if (options.token) {
|
|
324
|
-
throw new Error("--request-grant cannot be combined with --token; remove --token so the approved replacement credential can be collected and cached");
|
|
324
|
+
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");
|
|
325
325
|
}
|
|
326
|
-
out.error(`auth: requesting fresh owner review for app.manage on exact project "${cfg.app.id}"`);
|
|
326
|
+
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}"`);
|
|
327
327
|
}
|
|
328
328
|
const ctx = {
|
|
329
329
|
cfg,
|
|
@@ -336,6 +336,7 @@ async function getDeveloperToken(cfg, options, doFetch, out, grantRequest = {})
|
|
|
336
336
|
grantIntent
|
|
337
337
|
};
|
|
338
338
|
const waitMs = handshakeWaitMs(options.wait);
|
|
339
|
+
if (grantRequest.freshLogin) clearPendingHandshake(ctx.pendingFile);
|
|
339
340
|
const { token, expiresAt } = await resumePendingHandshake(ctx, waitMs) ?? await freshHandshake(ctx, waitMs);
|
|
340
341
|
clearPendingHandshake(ctx.pendingFile);
|
|
341
342
|
writePrivateJson(cfg.local.tokenFile, {
|
|
@@ -393,11 +394,12 @@ async function resumePendingHandshake(ctx, waitMs) {
|
|
|
393
394
|
async function freshHandshake(ctx, waitMs) {
|
|
394
395
|
let started;
|
|
395
396
|
let stopReminder;
|
|
397
|
+
const provisioning = ctx.grantIntent.optionalProjectCapabilities.includes("app.manage");
|
|
396
398
|
try {
|
|
397
399
|
return await requestToken({
|
|
398
400
|
endpoint: ctx.cfg.platformUrl,
|
|
399
401
|
email: ctx.email,
|
|
400
|
-
label: `${ctx.cfg.app.id} provisioner`,
|
|
402
|
+
label: `${ctx.cfg.app.id} ${provisioning ? "provisioner" : "agent"}`,
|
|
401
403
|
agentHandle: projectAgentHandle(ctx.cfg.app.id),
|
|
402
404
|
projectIds: [ctx.cfg.app.id],
|
|
403
405
|
optionalProjectCapabilities: ctx.grantIntent.optionalProjectCapabilities,
|
|
@@ -458,8 +460,15 @@ function stillPending(pending, email) {
|
|
|
458
460
|
}
|
|
459
461
|
function handshakeEmail(value2, cached) {
|
|
460
462
|
const email = (value2 ?? process5.env.ODLA_USER_EMAIL ?? cached ?? "").trim().toLowerCase();
|
|
463
|
+
if (/@users\.noreply\.github\.com$/i.test(email)) {
|
|
464
|
+
throw new Error(
|
|
465
|
+
`"${email}" is a GitHub commit identity, not an odla account email; use --email <signed-in-odla-account> or ODLA_USER_EMAIL`
|
|
466
|
+
);
|
|
467
|
+
}
|
|
461
468
|
if (email.length > 254 || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
|
462
|
-
throw new Error(
|
|
469
|
+
throw new Error(
|
|
470
|
+
"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"
|
|
471
|
+
);
|
|
463
472
|
}
|
|
464
473
|
return email;
|
|
465
474
|
}
|
|
@@ -1627,6 +1636,204 @@ async function adminCommand(parsed, deps = {}) {
|
|
|
1627
1636
|
});
|
|
1628
1637
|
}
|
|
1629
1638
|
|
|
1639
|
+
// src/auth-command.ts
|
|
1640
|
+
import process11 from "process";
|
|
1641
|
+
|
|
1642
|
+
// src/whoami-command.ts
|
|
1643
|
+
var text = (value2) => typeof value2 === "string" && value2.trim() ? value2.trim() : null;
|
|
1644
|
+
function principalKind(value2, machine) {
|
|
1645
|
+
return value2 === "human" || value2 === "agent" || value2 === "service" ? value2 : machine ? "service" : "human";
|
|
1646
|
+
}
|
|
1647
|
+
function credentialKind(value2, machine, scopes) {
|
|
1648
|
+
if (value2 === "machine" || value2 === "device" || value2 === "clerk") return value2;
|
|
1649
|
+
if (machine) return "machine";
|
|
1650
|
+
if (scopes.length) return "device";
|
|
1651
|
+
return "unknown";
|
|
1652
|
+
}
|
|
1653
|
+
function managerOf(value2) {
|
|
1654
|
+
if (!value2 || typeof value2 !== "object") return null;
|
|
1655
|
+
const row = value2;
|
|
1656
|
+
const principalId = text(row.principalId);
|
|
1657
|
+
if (!principalId) return null;
|
|
1658
|
+
return {
|
|
1659
|
+
principalId,
|
|
1660
|
+
displayName: text(row.displayName) ?? "Unnamed member",
|
|
1661
|
+
handle: text(row.handle) ?? ""
|
|
1662
|
+
};
|
|
1663
|
+
}
|
|
1664
|
+
function unnamedPrincipal(kind) {
|
|
1665
|
+
if (kind === "agent") return "Unnamed agent";
|
|
1666
|
+
if (kind === "service") return "Unnamed service";
|
|
1667
|
+
return "Unnamed member";
|
|
1668
|
+
}
|
|
1669
|
+
async function fetchIdentity(platformUrl, token, doFetch) {
|
|
1670
|
+
const res = await doFetch(`${platformUrl.replace(/\/$/, "")}/registry/me`, {
|
|
1671
|
+
headers: { authorization: `Bearer ${token}` }
|
|
1672
|
+
});
|
|
1673
|
+
if (!res.ok) throw new Error(`could not resolve identity (HTTP ${res.status})`);
|
|
1674
|
+
const body = await res.json();
|
|
1675
|
+
const developerId = text(body.developerId) ?? "";
|
|
1676
|
+
const machine = body.machine === true;
|
|
1677
|
+
const scopes = Array.isArray(body.scopes) ? body.scopes.map(String) : [];
|
|
1678
|
+
const principalId = text(body.principalId) ?? developerId;
|
|
1679
|
+
const email = text(body.email);
|
|
1680
|
+
const kind = principalKind(body.principalKind, machine);
|
|
1681
|
+
const displayName = text(body.displayName) ?? email ?? unnamedPrincipal(kind);
|
|
1682
|
+
const handle = text(body.handle) ?? "";
|
|
1683
|
+
const credential2 = body.credential && typeof body.credential === "object" ? body.credential : {};
|
|
1684
|
+
return {
|
|
1685
|
+
developerId,
|
|
1686
|
+
principalId,
|
|
1687
|
+
principalKind: kind,
|
|
1688
|
+
displayName,
|
|
1689
|
+
handle,
|
|
1690
|
+
manager: managerOf(body.manager),
|
|
1691
|
+
credential: {
|
|
1692
|
+
id: text(credential2.id),
|
|
1693
|
+
kind: credentialKind(credential2.kind, machine, scopes)
|
|
1694
|
+
},
|
|
1695
|
+
email,
|
|
1696
|
+
admin: body.admin === true,
|
|
1697
|
+
machine,
|
|
1698
|
+
scopes
|
|
1699
|
+
};
|
|
1700
|
+
}
|
|
1701
|
+
function credentialLabel(identity) {
|
|
1702
|
+
if (identity.credential.kind === "machine") return "machine (platform admin secret)";
|
|
1703
|
+
if (identity.credential.kind === "device")
|
|
1704
|
+
return identity.scopes.length ? "device (scoped)" : "device";
|
|
1705
|
+
if (identity.credential.kind === "clerk") return "clerk";
|
|
1706
|
+
return "unknown (legacy server)";
|
|
1707
|
+
}
|
|
1708
|
+
function namedPrincipal(identity) {
|
|
1709
|
+
return identity.handle && identity.handle !== identity.displayName ? `${identity.displayName} (@${identity.handle})` : identity.displayName;
|
|
1710
|
+
}
|
|
1711
|
+
function namedManager(manager) {
|
|
1712
|
+
return manager.handle && manager.handle !== manager.displayName ? `${manager.displayName} (@${manager.handle})` : manager.displayName;
|
|
1713
|
+
}
|
|
1714
|
+
function accountableOwner(identity) {
|
|
1715
|
+
if (identity.principalKind === "agent" && identity.manager) {
|
|
1716
|
+
return namedManager(identity.manager);
|
|
1717
|
+
}
|
|
1718
|
+
if (identity.principalKind === "human" && identity.principalId === identity.developerId) {
|
|
1719
|
+
return namedPrincipal(identity);
|
|
1720
|
+
}
|
|
1721
|
+
return identity.email ?? "Unnamed member";
|
|
1722
|
+
}
|
|
1723
|
+
async function whoamiCommand(parsed, deps = {}) {
|
|
1724
|
+
assertArgs(
|
|
1725
|
+
parsed,
|
|
1726
|
+
["config", "context", "platform", "token", "email", "json", "open"],
|
|
1727
|
+
1
|
|
1728
|
+
);
|
|
1729
|
+
const out = deps.stdout ?? console;
|
|
1730
|
+
const doFetch = deps.fetch ?? fetch;
|
|
1731
|
+
const { cfg } = await resolveOperatorContext(parsed, {
|
|
1732
|
+
allowMissingConfig: true
|
|
1733
|
+
});
|
|
1734
|
+
const token = await getDeveloperToken(
|
|
1735
|
+
cfg,
|
|
1736
|
+
{
|
|
1737
|
+
configPath: cfg.configPath,
|
|
1738
|
+
token: stringOpt(parsed.options.token),
|
|
1739
|
+
email: stringOpt(parsed.options.email),
|
|
1740
|
+
// As above: never suppress the browser for an ordinary sign-in.
|
|
1741
|
+
open: parsed.options.open === false ? false : parsed.options.open === true ? true : void 0,
|
|
1742
|
+
openApprovalUrl: deps.openUrl
|
|
1743
|
+
},
|
|
1744
|
+
doFetch,
|
|
1745
|
+
out
|
|
1746
|
+
);
|
|
1747
|
+
const identity = await fetchIdentity(cfg.platformUrl, token, doFetch);
|
|
1748
|
+
if (parsed.options.json === true) {
|
|
1749
|
+
out.log(JSON.stringify(identity, null, 2));
|
|
1750
|
+
return;
|
|
1751
|
+
}
|
|
1752
|
+
out.log(`platform: ${cfg.platformUrl}`);
|
|
1753
|
+
out.log(`principal: ${namedPrincipal(identity)}`);
|
|
1754
|
+
out.log(`principal id: ${identity.principalId}`);
|
|
1755
|
+
out.log(`kind: ${identity.principalKind}`);
|
|
1756
|
+
if (identity.principalKind === "agent")
|
|
1757
|
+
out.log(
|
|
1758
|
+
`manager: ${identity.manager ? namedManager(identity.manager) : "(unknown)"}`
|
|
1759
|
+
);
|
|
1760
|
+
out.log(`owner: ${accountableOwner(identity)}`);
|
|
1761
|
+
out.log(`owner id: ${identity.developerId}`);
|
|
1762
|
+
out.log(`email: ${identity.email ?? "(none)"}`);
|
|
1763
|
+
out.log(`credential: ${credentialLabel(identity)}`);
|
|
1764
|
+
if (identity.credential.id)
|
|
1765
|
+
out.log(`credential id: ${identity.credential.id}`);
|
|
1766
|
+
out.log(`admin: ${identity.admin ? "yes" : "no"}`);
|
|
1767
|
+
if (identity.scopes.length) out.log(`scopes: ${identity.scopes.join(", ")}`);
|
|
1768
|
+
if (!identity.admin) {
|
|
1769
|
+
if (identity.scopes.includes("platform:runbook:write")) {
|
|
1770
|
+
out.log("\nThis exact scope can read and edit all platform runbook content.");
|
|
1771
|
+
out.log("The device token is not ambient admin and cannot change visibility.");
|
|
1772
|
+
} else {
|
|
1773
|
+
out.log("\nYou can read operator-visible platform runbooks but not edit them.");
|
|
1774
|
+
out.log("Use a platform-admin-approved runbook write request to edit.");
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
// src/auth-command.ts
|
|
1780
|
+
async function authCommand(parsed, deps = {}) {
|
|
1781
|
+
assertArgs(parsed, [
|
|
1782
|
+
"config",
|
|
1783
|
+
"context",
|
|
1784
|
+
"platform",
|
|
1785
|
+
"app",
|
|
1786
|
+
"email",
|
|
1787
|
+
"open",
|
|
1788
|
+
"wait",
|
|
1789
|
+
"json"
|
|
1790
|
+
], 2);
|
|
1791
|
+
const action2 = parsed.positionals[1] ?? "login";
|
|
1792
|
+
if (action2 !== "login") {
|
|
1793
|
+
throw new Error(`unknown auth action "${action2}". Try "odla-ai auth login --app <id> --email <odla-account>".`);
|
|
1794
|
+
}
|
|
1795
|
+
const context = await resolveOperatorContext(parsed, {
|
|
1796
|
+
allowMissingConfig: true,
|
|
1797
|
+
requireApp: true
|
|
1798
|
+
});
|
|
1799
|
+
const { cfg } = context;
|
|
1800
|
+
const out = deps.stdout ?? console;
|
|
1801
|
+
const doFetch = deps.fetch ?? fetch;
|
|
1802
|
+
const email = stringOpt(parsed.options.email) ?? process11.env.ODLA_USER_EMAIL?.trim();
|
|
1803
|
+
if (!email) {
|
|
1804
|
+
throw new Error(
|
|
1805
|
+
"auth login requires --email <odla-account> or ODLA_USER_EMAIL; confirm the signed-in odla email instead of using git or GitHub identity"
|
|
1806
|
+
);
|
|
1807
|
+
}
|
|
1808
|
+
const token = await getDeveloperToken(
|
|
1809
|
+
cfg,
|
|
1810
|
+
{
|
|
1811
|
+
configPath: cfg.configPath,
|
|
1812
|
+
email,
|
|
1813
|
+
open: parsed.options.open === false ? false : parsed.options.open === true ? true : void 0,
|
|
1814
|
+
wait: numberOpt(parsed.options.wait, "--wait"),
|
|
1815
|
+
openApprovalUrl: deps.openUrl
|
|
1816
|
+
},
|
|
1817
|
+
doFetch,
|
|
1818
|
+
out,
|
|
1819
|
+
{ freshLogin: true }
|
|
1820
|
+
);
|
|
1821
|
+
const identity = await fetchIdentity(cfg.platformUrl, token, doFetch);
|
|
1822
|
+
if (parsed.options.json === true) {
|
|
1823
|
+
out.log(JSON.stringify({
|
|
1824
|
+
principalId: identity.principalId,
|
|
1825
|
+
displayName: identity.displayName,
|
|
1826
|
+
handle: identity.handle,
|
|
1827
|
+
email: identity.email,
|
|
1828
|
+
appId: cfg.app.id
|
|
1829
|
+
}, null, 2));
|
|
1830
|
+
return;
|
|
1831
|
+
}
|
|
1832
|
+
const handle = identity.handle ? ` (@${identity.handle})` : "";
|
|
1833
|
+
out.log(`Authorized ${identity.displayName}${handle} for ${cfg.app.id}.`);
|
|
1834
|
+
out.log(`odla account: ${identity.email ?? "not returned"}`);
|
|
1835
|
+
}
|
|
1836
|
+
|
|
1630
1837
|
// src/tenant.ts
|
|
1631
1838
|
import { tenantIdFor } from "@odla-ai/apps";
|
|
1632
1839
|
function resolveEnv(cfg, requested) {
|
|
@@ -4272,6 +4479,14 @@ and atomically claim a refined Ready task. Record decisions when you make them
|
|
|
4272
4479
|
and file bugs when you notice them. The conventions and the full command set are
|
|
4273
4480
|
in \`.agents/skills/odla/references/pm.md\`.
|
|
4274
4481
|
|
|
4482
|
+
Use the human's signed-in odla account email for device authorization; never
|
|
4483
|
+
infer it from git config, commit metadata, or GitHub. If authorization is not
|
|
4484
|
+
already active, run
|
|
4485
|
+
\`npx @odla-ai/cli auth login --app <appId> --email <odla-account>\` and open
|
|
4486
|
+
the exact Studio URL it prints. Never file an odla project or product defect in
|
|
4487
|
+
GitHub Issues: run \`npx @odla-ai/cli bug report --app <appId> ...\` so the bug
|
|
4488
|
+
lands in odla PM with the rest of the project's goals, tasks, and decisions.
|
|
4489
|
+
|
|
4275
4490
|
The setup runbooks and their references are installed in this repository, pinned
|
|
4276
4491
|
to this CLI version. Use them as your setup context.
|
|
4277
4492
|
|
|
@@ -8337,13 +8552,13 @@ async function codeCommand(parsed, dependencies) {
|
|
|
8337
8552
|
}
|
|
8338
8553
|
|
|
8339
8554
|
// src/operator-credentials.ts
|
|
8340
|
-
import
|
|
8555
|
+
import process12 from "process";
|
|
8341
8556
|
function developerTokenStatus(context, parsed, now = Date.now()) {
|
|
8342
8557
|
const cached = readJsonFile(context.cfg.local.tokenFile);
|
|
8343
8558
|
const cacheStatus = !cached?.token ? "missing" : cached.platform !== context.platform.value ? "other-platform" : (cached.expiresAt ?? 0) <= now + 6e4 ? "expired" : "valid";
|
|
8344
8559
|
const source = clean3(
|
|
8345
8560
|
stringOpt(parsed.options.token)
|
|
8346
|
-
) ? "flag" : clean3(
|
|
8561
|
+
) ? "flag" : clean3(process12.env.ODLA_DEV_TOKEN) ? "environment" : cacheStatus === "valid" ? "cache" : "missing";
|
|
8347
8562
|
return {
|
|
8348
8563
|
source,
|
|
8349
8564
|
cacheFile: context.cfg.local.tokenFile,
|
|
@@ -8500,6 +8715,7 @@ Start here:
|
|
|
8500
8715
|
step it made wrong.
|
|
8501
8716
|
|
|
8502
8717
|
Usage:
|
|
8718
|
+
odla-ai auth login --app <id> --email <odla-account> [--platform https://odla.ai] [--no-open] [--wait <seconds>] [--json]
|
|
8503
8719
|
odla-ai setup [--dir <project>] [--agent <name>] [--global] [--force]
|
|
8504
8720
|
odla-ai init --app-id <id> --name <name> [--services db,ai,o11y,calendar] [--env dev --env prod] [--ai-provider <byok-provider>]
|
|
8505
8721
|
odla-ai doctor [--config odla.config.mjs]
|
|
@@ -8536,6 +8752,7 @@ Usage:
|
|
|
8536
8752
|
odla-ai pm task release <id> --expected-revision <n> [--mutation-id <id>] [--json]
|
|
8537
8753
|
odla-ai pm decision add --app <id> --title <t> --body <text> [--status <s>] [--mutation-id <id>] [--json]
|
|
8538
8754
|
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]
|
|
8755
|
+
odla-ai bug report --app <id> --title <t> (--description <text>|--body <text>) [--severity <s>] [--json]
|
|
8539
8756
|
odla-ai pm <goal|task|decision|bug> get <id> [--json]
|
|
8540
8757
|
odla-ai pm <goal|task|decision|bug> ref <id> [--json]
|
|
8541
8758
|
odla-ai pm goal set <id> [--title <t>|--status <s>|--proof <text>|--no-proof|--target <pct>|--no-target] [--mutation-id <id>] [--json]
|
|
@@ -8615,6 +8832,9 @@ function printHelp(output = console) {
|
|
|
8615
8832
|
output.log(`odla-ai
|
|
8616
8833
|
${USAGE_SECTION}
|
|
8617
8834
|
Commands:
|
|
8835
|
+
auth Start a fresh, exact-project agent authorization in the browser.
|
|
8836
|
+
The email is the signed-in odla account, never git or GitHub
|
|
8837
|
+
identity. The approval screen confirms the agent name first.
|
|
8618
8838
|
agent Inspect durable agent wakeups and explicitly requeue a
|
|
8619
8839
|
dead-lettered job; JSON output is stable for remote operators.
|
|
8620
8840
|
runbook odla's operational procedures, stored in the database and read at
|
|
@@ -8682,6 +8902,8 @@ Commands:
|
|
|
8682
8902
|
"app". Entities: goal (alias conformance), task (alias kanban),
|
|
8683
8903
|
decision, bug. Status changes and comments post to each item's
|
|
8684
8904
|
@odla-ai/chat discussion thread.
|
|
8905
|
+
bug Intent-first alias for PM bugs. "bug report" writes to
|
|
8906
|
+
odla PM; odla product defects do not belong in GitHub Issues.
|
|
8685
8907
|
discuss Group discussions (via @odla-ai/chat) for the apps you co-own:
|
|
8686
8908
|
one group per project, topics with replies, @-mentions of people,
|
|
8687
8909
|
agents, PM items, and projects. Built for unattended use \u2014 post a
|
|
@@ -8739,8 +8961,12 @@ Safety:
|
|
|
8739
8961
|
wait for approval, and re-run to collect.
|
|
8740
8962
|
A fresh device handshake requires --email <odla-account> or ODLA_USER_EMAIL.
|
|
8741
8963
|
The email is a non-secret identity hint: never provide a password or session
|
|
8742
|
-
token.
|
|
8964
|
+
token. It is the email shown by the signed-in odla account \u2014 never infer it
|
|
8965
|
+
from git config, a commit author, or GitHub. The matching account must already exist, be signed in, explicitly
|
|
8743
8966
|
review the exact code, and finish any current request before claiming another.
|
|
8967
|
+
Use "auth login --app <id> --email <odla-account>" when an outside agent needs
|
|
8968
|
+
a deliberate fresh request; it ignores cached credentials and opens the same
|
|
8969
|
+
focused authorization sequence used by every first-time command.
|
|
8744
8970
|
If provision reports that the current agent principal has no live app.manage
|
|
8745
8971
|
grant, run it once with --request-grant. That flag ignores ODLA_DEV_TOKEN and
|
|
8746
8972
|
the local cache, prints and opens a fresh exact-project owner-review URL, then
|
|
@@ -9234,7 +9460,8 @@ var ALLOWED = [
|
|
|
9234
9460
|
"jsonl",
|
|
9235
9461
|
"mutation-id",
|
|
9236
9462
|
"platform",
|
|
9237
|
-
"context"
|
|
9463
|
+
"context",
|
|
9464
|
+
"open"
|
|
9238
9465
|
];
|
|
9239
9466
|
function requireId(id, action2) {
|
|
9240
9467
|
if (!id) throw new Error(`"discuss ${action2}" needs a topic id`);
|
|
@@ -9253,7 +9480,8 @@ async function buildContext(parsed, deps) {
|
|
|
9253
9480
|
configPath: cfg.configPath,
|
|
9254
9481
|
token: stringOpt(parsed.options.token),
|
|
9255
9482
|
email: stringOpt(parsed.options.email),
|
|
9256
|
-
open: false
|
|
9483
|
+
open: parsed.options.open === false ? false : parsed.options.open === true ? true : void 0,
|
|
9484
|
+
openApprovalUrl: deps.openUrl
|
|
9257
9485
|
},
|
|
9258
9486
|
doFetch,
|
|
9259
9487
|
out
|
|
@@ -9828,7 +10056,7 @@ var ALIASES = {
|
|
|
9828
10056
|
decision: "decision",
|
|
9829
10057
|
bug: "bug"
|
|
9830
10058
|
};
|
|
9831
|
-
var COMMON_OPTIONS = ["config", "token", "email", "json", "platform", "context"];
|
|
10059
|
+
var COMMON_OPTIONS = ["config", "token", "email", "json", "platform", "context", "open"];
|
|
9832
10060
|
var ACTION_OPTIONS = {
|
|
9833
10061
|
list: ["app", "q", "limit", "offset"],
|
|
9834
10062
|
add: ["app", "title", "mutation-id"],
|
|
@@ -9892,7 +10120,13 @@ async function buildContext2(parsed, deps) {
|
|
|
9892
10120
|
const out = deps.stdout ?? console;
|
|
9893
10121
|
const token = await getDeveloperToken(
|
|
9894
10122
|
cfg,
|
|
9895
|
-
{
|
|
10123
|
+
{
|
|
10124
|
+
configPath: cfg.configPath,
|
|
10125
|
+
token: stringOpt(parsed.options.token),
|
|
10126
|
+
email: stringOpt(parsed.options.email),
|
|
10127
|
+
open: parsed.options.open === false ? false : parsed.options.open === true ? true : void 0,
|
|
10128
|
+
openApprovalUrl: deps.openUrl
|
|
10129
|
+
},
|
|
9896
10130
|
doFetch,
|
|
9897
10131
|
out
|
|
9898
10132
|
);
|
|
@@ -10356,7 +10590,8 @@ async function o11yCommand(parsed, deps = {}) {
|
|
|
10356
10590
|
"json",
|
|
10357
10591
|
"app",
|
|
10358
10592
|
"env",
|
|
10359
|
-
"minutes"
|
|
10593
|
+
"minutes",
|
|
10594
|
+
"open"
|
|
10360
10595
|
],
|
|
10361
10596
|
2
|
|
10362
10597
|
);
|
|
@@ -10383,7 +10618,8 @@ async function o11yCommand(parsed, deps = {}) {
|
|
|
10383
10618
|
configPath: cfg.configPath,
|
|
10384
10619
|
token: stringOpt(parsed.options.token),
|
|
10385
10620
|
email: stringOpt(parsed.options.email),
|
|
10386
|
-
open: false
|
|
10621
|
+
open: parsed.options.open === false ? false : parsed.options.open === true ? true : void 0,
|
|
10622
|
+
openApprovalUrl: deps.openUrl
|
|
10387
10623
|
},
|
|
10388
10624
|
doFetch,
|
|
10389
10625
|
out
|
|
@@ -10494,7 +10730,7 @@ async function read2(url, headers, doFetch) {
|
|
|
10494
10730
|
// src/provision.ts
|
|
10495
10731
|
import { AppsError as AppsError2, createAppsClient as createAppsClient3, orderAppServices as orderAppServices3, tenantIdFor as tenantIdFor5 } from "@odla-ai/apps";
|
|
10496
10732
|
import { putSecret as putSecret2 } from "@odla-ai/ai";
|
|
10497
|
-
import
|
|
10733
|
+
import process13 from "process";
|
|
10498
10734
|
|
|
10499
10735
|
// src/integration-provision.ts
|
|
10500
10736
|
import { uuidv7 } from "@odla-ai/db";
|
|
@@ -10814,7 +11050,7 @@ ${env}: credentials are already saved; retry "odla-ai secrets push --env ${env}$
|
|
|
10814
11050
|
await provisionIntegrationSeeds(doFetch, cfg.dbEndpoint, tenantId, dbKey, database.integrations, env, out);
|
|
10815
11051
|
}
|
|
10816
11052
|
if (cfg.services.includes("ai") && cfg.ai?.provider && cfg.ai.keyEnv) {
|
|
10817
|
-
const key =
|
|
11053
|
+
const key = process13.env[cfg.ai.keyEnv];
|
|
10818
11054
|
if (key) {
|
|
10819
11055
|
const secretName = cfg.ai.secretName ?? defaultSecretName(cfg.ai.provider);
|
|
10820
11056
|
await putSecret2({ endpoint: cfg.dbEndpoint, token, fetch: doFetch }, tenantId, secretName, key);
|
|
@@ -10856,7 +11092,7 @@ ${env}: credentials are already saved; retry "odla-ai secrets push --env ${env}$
|
|
|
10856
11092
|
|
|
10857
11093
|
// src/record.ts
|
|
10858
11094
|
import { appendFileSync } from "fs";
|
|
10859
|
-
import
|
|
11095
|
+
import process14 from "process";
|
|
10860
11096
|
|
|
10861
11097
|
// src/surface.ts
|
|
10862
11098
|
var PM_ACTIONS = {
|
|
@@ -10913,7 +11149,9 @@ var COMMAND_SURFACE = {
|
|
|
10913
11149
|
promote: {},
|
|
10914
11150
|
owners: { list: {}, add: {}, remove: {} }
|
|
10915
11151
|
},
|
|
11152
|
+
auth: { login: {} },
|
|
10916
11153
|
brand: { design: { unpack: {} } },
|
|
11154
|
+
bug: { create: {}, list: {}, report: {} },
|
|
10917
11155
|
calendar: { status: {}, calendars: {}, connect: {}, disconnect: {} },
|
|
10918
11156
|
capabilities: {},
|
|
10919
11157
|
code: { connect: {} },
|
|
@@ -11028,7 +11266,7 @@ function surfacePaths(node = COMMAND_SURFACE, prefix = []) {
|
|
|
11028
11266
|
|
|
11029
11267
|
// src/record.ts
|
|
11030
11268
|
function recordInvocation(parsed) {
|
|
11031
|
-
const file =
|
|
11269
|
+
const file = process14.env.ODLA_CLI_RECORD;
|
|
11032
11270
|
if (!file) return;
|
|
11033
11271
|
try {
|
|
11034
11272
|
const entry = {
|
|
@@ -11651,9 +11889,9 @@ import { spawnSync } from "child_process";
|
|
|
11651
11889
|
import { mkdtempSync, readFileSync as readFileSync12, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
11652
11890
|
import { tmpdir as tmpdir5 } from "os";
|
|
11653
11891
|
import { join as join15 } from "path";
|
|
11654
|
-
import
|
|
11892
|
+
import process15 from "process";
|
|
11655
11893
|
var EDITOR_ENV = ["ODLA_EDITOR", "VISUAL", "EDITOR"];
|
|
11656
|
-
function resolveEditor(env =
|
|
11894
|
+
function resolveEditor(env = process15.env) {
|
|
11657
11895
|
for (const name of EDITOR_ENV) {
|
|
11658
11896
|
const value2 = env[name];
|
|
11659
11897
|
if (value2 && value2.trim()) return value2.trim();
|
|
@@ -11667,8 +11905,8 @@ function defaultRun(command, path) {
|
|
|
11667
11905
|
return result.status ?? 0;
|
|
11668
11906
|
}
|
|
11669
11907
|
function editText(initial, slug, deps = {}) {
|
|
11670
|
-
const env = deps.env ??
|
|
11671
|
-
const interactive = deps.interactive ?? (() => Boolean(
|
|
11908
|
+
const env = deps.env ?? process15.env;
|
|
11909
|
+
const interactive = deps.interactive ?? (() => Boolean(process15.stdin.isTTY));
|
|
11672
11910
|
const editor = resolveEditor(env);
|
|
11673
11911
|
if (!editor)
|
|
11674
11912
|
throw new Error(
|
|
@@ -11704,142 +11942,6 @@ async function editRunbook(ctx, slug, deps = {}) {
|
|
|
11704
11942
|
return body === null ? null : { body, expectedVersion: found.version };
|
|
11705
11943
|
}
|
|
11706
11944
|
|
|
11707
|
-
// src/whoami-command.ts
|
|
11708
|
-
var text = (value2) => typeof value2 === "string" && value2.trim() ? value2.trim() : null;
|
|
11709
|
-
function principalKind(value2, machine) {
|
|
11710
|
-
return value2 === "human" || value2 === "agent" || value2 === "service" ? value2 : machine ? "service" : "human";
|
|
11711
|
-
}
|
|
11712
|
-
function credentialKind(value2, machine, scopes) {
|
|
11713
|
-
if (value2 === "machine" || value2 === "device" || value2 === "clerk") return value2;
|
|
11714
|
-
if (machine) return "machine";
|
|
11715
|
-
if (scopes.length) return "device";
|
|
11716
|
-
return "unknown";
|
|
11717
|
-
}
|
|
11718
|
-
function managerOf(value2) {
|
|
11719
|
-
if (!value2 || typeof value2 !== "object") return null;
|
|
11720
|
-
const row = value2;
|
|
11721
|
-
const principalId = text(row.principalId);
|
|
11722
|
-
if (!principalId) return null;
|
|
11723
|
-
return {
|
|
11724
|
-
principalId,
|
|
11725
|
-
displayName: text(row.displayName) ?? "Unnamed member",
|
|
11726
|
-
handle: text(row.handle) ?? ""
|
|
11727
|
-
};
|
|
11728
|
-
}
|
|
11729
|
-
function unnamedPrincipal(kind) {
|
|
11730
|
-
if (kind === "agent") return "Unnamed agent";
|
|
11731
|
-
if (kind === "service") return "Unnamed service";
|
|
11732
|
-
return "Unnamed member";
|
|
11733
|
-
}
|
|
11734
|
-
async function fetchIdentity(platformUrl, token, doFetch) {
|
|
11735
|
-
const res = await doFetch(`${platformUrl.replace(/\/$/, "")}/registry/me`, {
|
|
11736
|
-
headers: { authorization: `Bearer ${token}` }
|
|
11737
|
-
});
|
|
11738
|
-
if (!res.ok) throw new Error(`could not resolve identity (HTTP ${res.status})`);
|
|
11739
|
-
const body = await res.json();
|
|
11740
|
-
const developerId = text(body.developerId) ?? "";
|
|
11741
|
-
const machine = body.machine === true;
|
|
11742
|
-
const scopes = Array.isArray(body.scopes) ? body.scopes.map(String) : [];
|
|
11743
|
-
const principalId = text(body.principalId) ?? developerId;
|
|
11744
|
-
const email = text(body.email);
|
|
11745
|
-
const kind = principalKind(body.principalKind, machine);
|
|
11746
|
-
const displayName = text(body.displayName) ?? email ?? unnamedPrincipal(kind);
|
|
11747
|
-
const handle = text(body.handle) ?? "";
|
|
11748
|
-
const credential2 = body.credential && typeof body.credential === "object" ? body.credential : {};
|
|
11749
|
-
return {
|
|
11750
|
-
developerId,
|
|
11751
|
-
principalId,
|
|
11752
|
-
principalKind: kind,
|
|
11753
|
-
displayName,
|
|
11754
|
-
handle,
|
|
11755
|
-
manager: managerOf(body.manager),
|
|
11756
|
-
credential: {
|
|
11757
|
-
id: text(credential2.id),
|
|
11758
|
-
kind: credentialKind(credential2.kind, machine, scopes)
|
|
11759
|
-
},
|
|
11760
|
-
email,
|
|
11761
|
-
admin: body.admin === true,
|
|
11762
|
-
machine,
|
|
11763
|
-
scopes
|
|
11764
|
-
};
|
|
11765
|
-
}
|
|
11766
|
-
function credentialLabel(identity) {
|
|
11767
|
-
if (identity.credential.kind === "machine") return "machine (platform admin secret)";
|
|
11768
|
-
if (identity.credential.kind === "device")
|
|
11769
|
-
return identity.scopes.length ? "device (scoped)" : "device";
|
|
11770
|
-
if (identity.credential.kind === "clerk") return "clerk";
|
|
11771
|
-
return "unknown (legacy server)";
|
|
11772
|
-
}
|
|
11773
|
-
function namedPrincipal(identity) {
|
|
11774
|
-
return identity.handle && identity.handle !== identity.displayName ? `${identity.displayName} (@${identity.handle})` : identity.displayName;
|
|
11775
|
-
}
|
|
11776
|
-
function namedManager(manager) {
|
|
11777
|
-
return manager.handle && manager.handle !== manager.displayName ? `${manager.displayName} (@${manager.handle})` : manager.displayName;
|
|
11778
|
-
}
|
|
11779
|
-
function accountableOwner(identity) {
|
|
11780
|
-
if (identity.principalKind === "agent" && identity.manager) {
|
|
11781
|
-
return namedManager(identity.manager);
|
|
11782
|
-
}
|
|
11783
|
-
if (identity.principalKind === "human" && identity.principalId === identity.developerId) {
|
|
11784
|
-
return namedPrincipal(identity);
|
|
11785
|
-
}
|
|
11786
|
-
return identity.email ?? "Unnamed member";
|
|
11787
|
-
}
|
|
11788
|
-
async function whoamiCommand(parsed, deps = {}) {
|
|
11789
|
-
assertArgs(
|
|
11790
|
-
parsed,
|
|
11791
|
-
["config", "context", "platform", "token", "email", "json"],
|
|
11792
|
-
1
|
|
11793
|
-
);
|
|
11794
|
-
const out = deps.stdout ?? console;
|
|
11795
|
-
const doFetch = deps.fetch ?? fetch;
|
|
11796
|
-
const { cfg } = await resolveOperatorContext(parsed, {
|
|
11797
|
-
allowMissingConfig: true
|
|
11798
|
-
});
|
|
11799
|
-
const token = await getDeveloperToken(
|
|
11800
|
-
cfg,
|
|
11801
|
-
{
|
|
11802
|
-
configPath: cfg.configPath,
|
|
11803
|
-
token: stringOpt(parsed.options.token),
|
|
11804
|
-
email: stringOpt(parsed.options.email),
|
|
11805
|
-
// As above: never suppress the browser for an ordinary sign-in.
|
|
11806
|
-
open: void 0
|
|
11807
|
-
},
|
|
11808
|
-
doFetch,
|
|
11809
|
-
out
|
|
11810
|
-
);
|
|
11811
|
-
const identity = await fetchIdentity(cfg.platformUrl, token, doFetch);
|
|
11812
|
-
if (parsed.options.json === true) {
|
|
11813
|
-
out.log(JSON.stringify(identity, null, 2));
|
|
11814
|
-
return;
|
|
11815
|
-
}
|
|
11816
|
-
out.log(`platform: ${cfg.platformUrl}`);
|
|
11817
|
-
out.log(`principal: ${namedPrincipal(identity)}`);
|
|
11818
|
-
out.log(`principal id: ${identity.principalId}`);
|
|
11819
|
-
out.log(`kind: ${identity.principalKind}`);
|
|
11820
|
-
if (identity.principalKind === "agent")
|
|
11821
|
-
out.log(
|
|
11822
|
-
`manager: ${identity.manager ? namedManager(identity.manager) : "(unknown)"}`
|
|
11823
|
-
);
|
|
11824
|
-
out.log(`owner: ${accountableOwner(identity)}`);
|
|
11825
|
-
out.log(`owner id: ${identity.developerId}`);
|
|
11826
|
-
out.log(`email: ${identity.email ?? "(none)"}`);
|
|
11827
|
-
out.log(`credential: ${credentialLabel(identity)}`);
|
|
11828
|
-
if (identity.credential.id)
|
|
11829
|
-
out.log(`credential id: ${identity.credential.id}`);
|
|
11830
|
-
out.log(`admin: ${identity.admin ? "yes" : "no"}`);
|
|
11831
|
-
if (identity.scopes.length) out.log(`scopes: ${identity.scopes.join(", ")}`);
|
|
11832
|
-
if (!identity.admin) {
|
|
11833
|
-
if (identity.scopes.includes("platform:runbook:write")) {
|
|
11834
|
-
out.log("\nThis exact scope can read and edit all platform runbook content.");
|
|
11835
|
-
out.log("The device token is not ambient admin and cannot change visibility.");
|
|
11836
|
-
} else {
|
|
11837
|
-
out.log("\nYou can read operator-visible platform runbooks but not edit them.");
|
|
11838
|
-
out.log("Use a platform-admin-approved runbook write request to edit.");
|
|
11839
|
-
}
|
|
11840
|
-
}
|
|
11841
|
-
}
|
|
11842
|
-
|
|
11843
11945
|
// src/runbook-command.ts
|
|
11844
11946
|
var ALLOWED2 = [
|
|
11845
11947
|
"config",
|
|
@@ -12731,6 +12833,10 @@ async function runCli(argv = process.argv.slice(2), dependencies = {}) {
|
|
|
12731
12833
|
await whoamiCommand(parsed, runtime);
|
|
12732
12834
|
return;
|
|
12733
12835
|
}
|
|
12836
|
+
if (command === "auth") {
|
|
12837
|
+
await authCommand(parsed, runtime);
|
|
12838
|
+
return;
|
|
12839
|
+
}
|
|
12734
12840
|
if (command === "context") {
|
|
12735
12841
|
await contextCommand(parsed, runtime);
|
|
12736
12842
|
return;
|
|
@@ -12771,6 +12877,15 @@ async function runCli(argv = process.argv.slice(2), dependencies = {}) {
|
|
|
12771
12877
|
await pmCommand(parsed, runtime);
|
|
12772
12878
|
return;
|
|
12773
12879
|
}
|
|
12880
|
+
if (command === "bug") {
|
|
12881
|
+
const action2 = parsed.positionals[1] ?? "list";
|
|
12882
|
+
const canonical = action2 === "report" || action2 === "create" ? "add" : action2;
|
|
12883
|
+
await pmCommand({
|
|
12884
|
+
...parsed,
|
|
12885
|
+
positionals: ["pm", "bug", canonical, ...parsed.positionals.slice(2)]
|
|
12886
|
+
}, runtime);
|
|
12887
|
+
return;
|
|
12888
|
+
}
|
|
12774
12889
|
if (command === "discuss") {
|
|
12775
12890
|
await discussCommand(parsed, runtime);
|
|
12776
12891
|
return;
|
|
@@ -12911,4 +13026,4 @@ export {
|
|
|
12911
13026
|
isTerminalHostedSecurityStatus,
|
|
12912
13027
|
runCli
|
|
12913
13028
|
};
|
|
12914
|
-
//# sourceMappingURL=chunk-
|
|
13029
|
+
//# sourceMappingURL=chunk-STJCWZJU.js.map
|