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