@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.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-DCSVQAZ6.js")).runCli()).catch((err) => {
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("a fresh odla handshake requires --email <account> or ODLA_USER_EMAIL");
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 process11 from "process";
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(process11.env.ODLA_DEV_TOKEN) ? "environment" : cacheStatus === "valid" ? "cache" : "missing";
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. The matching account must already exist, be signed in, explicitly
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
- { configPath: cfg.configPath, token: stringOpt(parsed.options.token), email: stringOpt(parsed.options.email), open: false },
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 process12 from "process";
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 = process12.env[cfg.ai.keyEnv];
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 process13 from "process";
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 = process13.env.ODLA_CLI_RECORD;
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 process14 from "process";
11891
+ import process15 from "process";
11655
11892
  var EDITOR_ENV = ["ODLA_EDITOR", "VISUAL", "EDITOR"];
11656
- function resolveEditor(env = process14.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 ?? process14.env;
11671
- const interactive = deps.interactive ?? (() => Boolean(process14.stdin.isTTY));
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-3WVVHH3Y.js.map
13028
+ //# sourceMappingURL=chunk-HCNESRIA.js.map