@zleap-ai/sag-cli 0.3.0 → 0.4.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/cli.js CHANGED
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
- import { confirm, password, select } from "@inquirer/prompts";
4
+ import { confirm, input, select } from "@inquirer/prompts";
5
5
 
6
6
  // package.json
7
7
  var package_default = {
8
8
  name: "@zleap-ai/sag-cli",
9
- version: "0.3.0",
9
+ version: "0.4.1",
10
10
  description: "Command-line client and diagnostics for SAG knowledge bases",
11
11
  type: "module",
12
12
  bin: {
@@ -177,7 +177,7 @@ var claudeEntrySchema = z.object({
177
177
  function invalidSpec() {
178
178
  return new CliError(
179
179
  "INVALID_ARGUMENT",
180
- "Claude Code v0.2 supports Docker stdio MCP connections only",
180
+ "The Claude Code adapter supports Docker stdio MCP connections only",
181
181
  {
182
182
  exitCode: exitCodes.invalidArgument
183
183
  }
@@ -206,6 +206,12 @@ var ClaudeCodeAdapter = class {
206
206
  }
207
207
  runner;
208
208
  id = "claude-code";
209
+ capabilities = {
210
+ transports: ["stdio"],
211
+ scopes: ["user"],
212
+ structuredRead: true,
213
+ projectApproval: false
214
+ };
209
215
  readConfig;
210
216
  async detect() {
211
217
  try {
@@ -294,7 +300,7 @@ var CODEX_STATUS_FIELDS = /* @__PURE__ */ new Set(["name", "disabled_reason", "a
294
300
  function invalidSpec2() {
295
301
  return new CliError(
296
302
  "INVALID_ARGUMENT",
297
- "Codex v0.2 supports Docker stdio MCP connections only",
303
+ "The Codex adapter supports Docker stdio MCP connections only",
298
304
  {
299
305
  exitCode: exitCodes.invalidArgument
300
306
  }
@@ -318,6 +324,12 @@ var CodexAdapter = class {
318
324
  }
319
325
  runner;
320
326
  id = "codex";
327
+ capabilities = {
328
+ transports: ["stdio"],
329
+ scopes: ["user"],
330
+ structuredRead: true,
331
+ projectApproval: false
332
+ };
321
333
  async detect() {
322
334
  try {
323
335
  const result = await this.runner.run({
@@ -534,9 +546,9 @@ var ManagedConnectionStore = class {
534
546
  await this.#saveUnlocked(state);
535
547
  });
536
548
  }
537
- async list(input) {
549
+ async list(input2) {
538
550
  return Object.values((await this.load()).connections).filter(
539
- (connection) => (!input?.agent || connection.agent === input.agent) && (!input?.scope || connection.scope === input.scope)
551
+ (connection) => (!input2?.agent || connection.agent === input2.agent) && (!input2?.scope || connection.scope === input2.scope)
540
552
  );
541
553
  }
542
554
  };
@@ -558,12 +570,12 @@ function validateProfileName(name) {
558
570
  }
559
571
  return name;
560
572
  }
561
- function normalizeOrigin(input) {
573
+ function normalizeOrigin(input2) {
562
574
  let url;
563
575
  try {
564
- url = new URL(input);
576
+ url = new URL(input2);
565
577
  } catch (cause) {
566
- throw new CliError("INVALID_ARGUMENT", `Invalid SAG URL: ${input}`, {
578
+ throw new CliError("INVALID_ARGUMENT", `Invalid SAG URL: ${input2}`, {
567
579
  exitCode: exitCodes.invalidArgument,
568
580
  cause,
569
581
  hint: "Use an absolute HTTP or HTTPS URL such as http://localhost:8000."
@@ -1090,16 +1102,6 @@ var McpVerifier = class {
1090
1102
  }
1091
1103
  cliVersion;
1092
1104
  async verify(spec, options) {
1093
- if (spec.transport !== "stdio") {
1094
- throw new CliError(
1095
- "INVALID_ARGUMENT",
1096
- "HTTP MCP is not supported in SAG CLI v0.2",
1097
- {
1098
- exitCode: exitCodes.invalidArgument,
1099
- hint: "Use a local Docker stdio connection."
1100
- }
1101
- );
1102
- }
1103
1105
  const client = new Client(
1104
1106
  {
1105
1107
  name: "@zleap-ai/sag-cli",
@@ -1191,12 +1193,12 @@ var McpVerifier = class {
1191
1193
  // src/process/runner.ts
1192
1194
  import { spawn } from "child_process";
1193
1195
  var NodeProcessRunner = class {
1194
- async run(input) {
1196
+ async run(input2) {
1195
1197
  return new Promise((resolve, reject) => {
1196
- const child = spawn(input.command, input.args, {
1198
+ const child = spawn(input2.command, input2.args, {
1197
1199
  shell: false,
1198
- env: input.env ?? process.env,
1199
- stdio: [input.stdin === "pipe" ? "pipe" : "ignore", "pipe", "pipe"]
1200
+ env: input2.env ?? process.env,
1201
+ stdio: [input2.stdin === "pipe" ? "pipe" : "ignore", "pipe", "pipe"]
1200
1202
  });
1201
1203
  const childStdout = child.stdout;
1202
1204
  const childStderr = child.stderr;
@@ -1210,7 +1212,7 @@ var NodeProcessRunner = class {
1210
1212
  forceKill = setTimeout(() => {
1211
1213
  child.kill("SIGKILL");
1212
1214
  }, 250);
1213
- }, input.timeoutMs);
1215
+ }, input2.timeoutMs);
1214
1216
  childStdout?.setEncoding("utf8");
1215
1217
  childStderr?.setEncoding("utf8");
1216
1218
  childStdout?.on("data", (chunk) => {
@@ -1223,10 +1225,10 @@ var NodeProcessRunner = class {
1223
1225
  clearTimeout(timeout);
1224
1226
  if (forceKill) clearTimeout(forceKill);
1225
1227
  reject(
1226
- new CliError("DEPENDENCY_MISSING", `Unable to start ${input.command}`, {
1228
+ new CliError("DEPENDENCY_MISSING", `Unable to start ${input2.command}`, {
1227
1229
  exitCode: exitCodes.dependencyMissing,
1228
1230
  cause,
1229
- hint: `Install ${input.command} and ensure it is available on PATH.`
1231
+ hint: `Install ${input2.command} and ensure it is available on PATH.`
1230
1232
  })
1231
1233
  );
1232
1234
  });
@@ -1235,9 +1237,9 @@ var NodeProcessRunner = class {
1235
1237
  if (forceKill) clearTimeout(forceKill);
1236
1238
  if (timedOut) {
1237
1239
  reject(
1238
- new CliError("DEPENDENCY_MISSING", `${input.command} timed out`, {
1240
+ new CliError("DEPENDENCY_MISSING", `${input2.command} timed out`, {
1239
1241
  exitCode: exitCodes.dependencyMissing,
1240
- hint: `Check that ${input.command} is responsive and try again.`
1242
+ hint: `Check that ${input2.command} is responsive and try again.`
1241
1243
  })
1242
1244
  );
1243
1245
  return;
@@ -1274,6 +1276,10 @@ var userSchema = z6.object({
1274
1276
  name: z6.string(),
1275
1277
  created_at: dateTime.optional()
1276
1278
  }).passthrough();
1279
+ var loginResponseSchema = z6.object({
1280
+ access_token: z6.string().min(1),
1281
+ user: userSchema
1282
+ }).passthrough();
1277
1283
  var sourceSchema = z6.object({
1278
1284
  id: z6.string().min(1),
1279
1285
  name: z6.string(),
@@ -1394,6 +1400,7 @@ var entityContextSchema = z6.object({
1394
1400
  }).passthrough();
1395
1401
 
1396
1402
  // src/api/client.ts
1403
+ var searchTimeoutMs = 15e3;
1397
1404
  var SagClient = class {
1398
1405
  #origin;
1399
1406
  #token;
@@ -1423,6 +1430,14 @@ var SagClient = class {
1423
1430
  me() {
1424
1431
  return this.#request("/api/v1/auth/me", userSchema);
1425
1432
  }
1433
+ async login(name) {
1434
+ const response = await this.#request("/api/v1/auth/login", loginResponseSchema, {
1435
+ authenticated: false,
1436
+ method: "POST",
1437
+ body: { name }
1438
+ });
1439
+ return { accessToken: response.access_token, user: response.user };
1440
+ }
1426
1441
  mcpDescriptor() {
1427
1442
  return this.#request("/api/v1/system/mcp", mcpDescriptorSchema);
1428
1443
  }
@@ -1447,10 +1462,11 @@ var SagClient = class {
1447
1462
  documentSchema
1448
1463
  );
1449
1464
  }
1450
- search(input) {
1465
+ search(input2) {
1451
1466
  return this.#request("/api/v1/search", searchResponseSchema, {
1452
1467
  method: "POST",
1453
- body: input
1468
+ body: input2,
1469
+ timeoutMs: searchTimeoutMs
1454
1470
  });
1455
1471
  }
1456
1472
  outline(sourceId, documentId) {
@@ -1513,7 +1529,7 @@ var SagClient = class {
1513
1529
  method: options.method ?? "GET",
1514
1530
  headers,
1515
1531
  ...options.body !== void 0 ? { body: JSON.stringify(options.body) } : {},
1516
- signal: AbortSignal.timeout(this.#timeoutMs)
1532
+ signal: AbortSignal.timeout(options.timeoutMs ?? this.#timeoutMs)
1517
1533
  });
1518
1534
  } catch (cause) {
1519
1535
  throw new CliError("NETWORK_UNREACHABLE", `Cannot reach SAG at ${this.#origin}`, {
@@ -1603,81 +1619,98 @@ function hostFailure3(message, cause, hint) {
1603
1619
  hint: hint ?? "Inspect the Agent MCP configuration and run the command again."
1604
1620
  });
1605
1621
  }
1606
- function determinePlan(input) {
1622
+ function determinePlan(input2) {
1607
1623
  let action;
1608
- if (!input.hostFingerprint || !input.hostSpecFingerprint) {
1609
- if (input.managed) {
1624
+ if (!input2.hostFingerprint || !input2.hostSpecFingerprint) {
1625
+ if (input2.managed) {
1610
1626
  throw conflict("Managed state exists but the Agent MCP entry is missing");
1611
1627
  }
1612
1628
  action = "create";
1613
- } else if (!input.managed) {
1629
+ } else if (!input2.managed) {
1614
1630
  throw conflict("The Agent MCP name is already owned by the user");
1615
- } else if (input.managed.fingerprint !== input.hostFingerprint) {
1631
+ } else if (input2.managed.fingerprint !== input2.hostFingerprint) {
1616
1632
  throw conflict("The managed Agent MCP entry was changed outside SAG CLI");
1617
- } else if (input.hostSpecFingerprint === input.desiredFingerprint) {
1633
+ } else if (input2.hostSpecFingerprint === input2.desiredFingerprint) {
1618
1634
  action = "unchanged";
1619
1635
  } else {
1620
1636
  action = "update";
1621
1637
  }
1622
1638
  return {
1623
1639
  action,
1624
- agent: input.agent,
1625
- serverName: input.serverName,
1626
- scope: input.scope,
1640
+ agent: input2.agent,
1641
+ serverName: input2.serverName,
1642
+ scope: input2.scope,
1627
1643
  provider: "docker-stdio",
1628
- target: input.target.name,
1629
- sourceId: input.sourceId ?? null,
1630
- ...input.hostSpecFingerprint ? { currentFingerprint: input.hostSpecFingerprint } : {},
1631
- desiredFingerprint: input.desiredFingerprint
1644
+ target: input2.target.name,
1645
+ sourceId: input2.sourceId ?? null,
1646
+ ...input2.hostSpecFingerprint ? { currentFingerprint: input2.hostSpecFingerprint } : {},
1647
+ desiredFingerprint: input2.desiredFingerprint
1632
1648
  };
1633
1649
  }
1634
- function managedConnection(input, resolved, fingerprint, existing) {
1650
+ function managedConnection(input2, resolved, fingerprint, existing) {
1635
1651
  return {
1636
- agent: input.agent,
1652
+ agent: input2.agent,
1637
1653
  provider: "docker-stdio",
1638
- profile: input.profile,
1639
- serverName: input.serverName,
1654
+ profile: input2.profile,
1655
+ serverName: input2.serverName,
1640
1656
  docker: {
1641
1657
  ...resolved.target.composeProject ? { composeProject: resolved.target.composeProject } : {},
1642
1658
  ...resolved.target.composeService ? { composeService: resolved.target.composeService } : {},
1643
1659
  containerName: resolved.target.name
1644
1660
  },
1645
- sourceId: input.sourceId ?? null,
1646
- scope: input.scope,
1661
+ sourceId: input2.sourceId ?? null,
1662
+ scope: input2.scope,
1647
1663
  fingerprint,
1648
- createdAt: existing?.createdAt ?? input.now().toISOString()
1664
+ createdAt: existing?.createdAt ?? input2.now().toISOString()
1649
1665
  };
1650
1666
  }
1651
- async function rollbackHost(input) {
1652
- if (input.previousSpec) {
1667
+ async function rollbackHost(input2) {
1668
+ if (input2.previousSpec) {
1653
1669
  try {
1654
- await input.adapter.remove(input.serverName, input.scope);
1670
+ await input2.adapter.remove(input2.serverName, input2.scope);
1655
1671
  } catch {
1656
1672
  }
1657
- await input.adapter.add(input.serverName, input.scope, input.previousSpec);
1658
- const restored = await input.adapter.read(input.serverName, input.scope);
1659
- if (!restored || restored.fingerprint !== (input.previousFingerprint ?? fingerprintConnection(input.previousSpec))) {
1673
+ await input2.adapter.add(input2.serverName, input2.scope, input2.previousSpec);
1674
+ const restored = await input2.adapter.read(input2.serverName, input2.scope);
1675
+ if (!restored || restored.fingerprint !== (input2.previousFingerprint ?? fingerprintConnection(input2.previousSpec))) {
1660
1676
  throw new Error("Previous Agent MCP entry was not restored");
1661
1677
  }
1662
1678
  return;
1663
1679
  }
1664
- await input.adapter.remove(input.serverName, input.scope);
1680
+ await input2.adapter.remove(input2.serverName, input2.scope);
1665
1681
  }
1666
- async function connectAgent(input) {
1667
- validateServerName(input.serverName);
1668
- const resolved = await input.resolveConnection();
1669
- await input.verifier.verify(resolved.spec, { timeoutMs: input.timeoutMs });
1670
- await input.adapter.detect();
1671
- const host = await input.adapter.read(input.serverName, input.scope);
1672
- const key = connectionKey(input.agent, input.scope, input.serverName);
1673
- const managed = await input.state.get(key);
1682
+ async function connectAgent(input2) {
1683
+ validateServerName(input2.serverName);
1684
+ const resolved = await input2.resolveConnection();
1685
+ if (!input2.adapter.capabilities.transports.includes(resolved.spec.transport)) {
1686
+ throw new CliError(
1687
+ "INVALID_ARGUMENT",
1688
+ `${input2.agent} adapter does not support ${resolved.spec.transport} MCP connections`,
1689
+ {
1690
+ exitCode: exitCodes.invalidArgument,
1691
+ hint: "Choose a provider supported by the installed Agent adapter."
1692
+ }
1693
+ );
1694
+ }
1695
+ if (!input2.adapter.capabilities.scopes.includes(input2.scope)) {
1696
+ throw new CliError(
1697
+ "INVALID_ARGUMENT",
1698
+ `${input2.agent} adapter does not support ${input2.scope} scope`,
1699
+ { exitCode: exitCodes.invalidArgument }
1700
+ );
1701
+ }
1702
+ await input2.verifier.verify(resolved.spec, { timeoutMs: input2.timeoutMs });
1703
+ await input2.adapter.detect();
1704
+ const host = await input2.adapter.read(input2.serverName, input2.scope);
1705
+ const key = connectionKey(input2.agent, input2.scope, input2.serverName);
1706
+ const managed = await input2.state.get(key);
1674
1707
  const desiredFingerprint = fingerprintConnection(resolved.spec);
1675
1708
  const plan = determinePlan({
1676
- agent: input.agent,
1677
- serverName: input.serverName,
1678
- scope: input.scope,
1709
+ agent: input2.agent,
1710
+ serverName: input2.serverName,
1711
+ scope: input2.scope,
1679
1712
  target: resolved.target,
1680
- ...input.sourceId ? { sourceId: input.sourceId } : {},
1713
+ ...input2.sourceId ? { sourceId: input2.sourceId } : {},
1681
1714
  desiredFingerprint,
1682
1715
  ...host ? { hostFingerprint: host.fingerprint } : {},
1683
1716
  ...host ? { hostSpecFingerprint: fingerprintConnection(host.spec) } : {},
@@ -1685,9 +1718,9 @@ async function connectAgent(input) {
1685
1718
  });
1686
1719
  if (plan.action === "unchanged") {
1687
1720
  return {
1688
- agent: input.agent,
1689
- serverName: input.serverName,
1690
- scope: input.scope,
1721
+ agent: input2.agent,
1722
+ serverName: input2.serverName,
1723
+ scope: input2.scope,
1691
1724
  action: "unchanged",
1692
1725
  changed: false,
1693
1726
  verified: true,
@@ -1695,11 +1728,11 @@ async function connectAgent(input) {
1695
1728
  plan
1696
1729
  };
1697
1730
  }
1698
- if (input.dryRun) {
1731
+ if (input2.dryRun) {
1699
1732
  return {
1700
- agent: input.agent,
1701
- serverName: input.serverName,
1702
- scope: input.scope,
1733
+ agent: input2.agent,
1734
+ serverName: input2.serverName,
1735
+ scope: input2.scope,
1703
1736
  action: plan.action,
1704
1737
  changed: false,
1705
1738
  verified: true,
@@ -1707,13 +1740,13 @@ async function connectAgent(input) {
1707
1740
  plan
1708
1741
  };
1709
1742
  }
1710
- if (!input.yes && !await input.confirm(
1711
- `${plan.action === "create" ? "Add" : "Update"} ${input.serverName} in ${input.agent}?`
1743
+ if (!input2.yes && !await input2.confirm(
1744
+ `${plan.action === "create" ? "Add" : "Update"} ${input2.serverName} in ${input2.agent}?`
1712
1745
  )) {
1713
1746
  return {
1714
- agent: input.agent,
1715
- serverName: input.serverName,
1716
- scope: input.scope,
1747
+ agent: input2.agent,
1748
+ serverName: input2.serverName,
1749
+ scope: input2.scope,
1717
1750
  action: "cancelled",
1718
1751
  changed: false,
1719
1752
  verified: true,
@@ -1723,23 +1756,23 @@ async function connectAgent(input) {
1723
1756
  }
1724
1757
  try {
1725
1758
  if (plan.action === "update") {
1726
- await input.adapter.remove(input.serverName, input.scope);
1759
+ await input2.adapter.remove(input2.serverName, input2.scope);
1727
1760
  }
1728
- await input.adapter.add(input.serverName, input.scope, resolved.spec);
1729
- const readBack = await input.adapter.read(input.serverName, input.scope);
1761
+ await input2.adapter.add(input2.serverName, input2.scope, resolved.spec);
1762
+ const readBack = await input2.adapter.read(input2.serverName, input2.scope);
1730
1763
  if (!readBack || fingerprintConnection(readBack.spec) !== desiredFingerprint) {
1731
1764
  throw hostFailure3("Agent MCP read-back did not match the requested connection");
1732
1765
  }
1733
- await input.verifier.verify(readBack.spec, { timeoutMs: input.timeoutMs });
1734
- await input.state.set(
1735
- managedConnection(input, resolved, readBack.fingerprint, managed)
1766
+ await input2.verifier.verify(readBack.spec, { timeoutMs: input2.timeoutMs });
1767
+ await input2.state.set(
1768
+ managedConnection(input2, resolved, readBack.fingerprint, managed)
1736
1769
  );
1737
1770
  } catch (cause) {
1738
1771
  try {
1739
1772
  await rollbackHost({
1740
- adapter: input.adapter,
1741
- serverName: input.serverName,
1742
- scope: input.scope,
1773
+ adapter: input2.adapter,
1774
+ serverName: input2.serverName,
1775
+ scope: input2.scope,
1743
1776
  ...host ? { previousSpec: host.spec } : {},
1744
1777
  ...host ? { previousFingerprint: host.fingerprint } : {}
1745
1778
  });
@@ -1747,7 +1780,7 @@ async function connectAgent(input) {
1747
1780
  throw hostFailure3(
1748
1781
  "Agent MCP configuration failed and rollback also failed",
1749
1782
  cause,
1750
- `Inspect the ${input.agent} MCP entry \`${input.serverName}\` before retrying.`
1783
+ `Inspect the ${input2.agent} MCP entry \`${input2.serverName}\` before retrying.`
1751
1784
  );
1752
1785
  }
1753
1786
  throw hostFailure3(
@@ -1756,9 +1789,9 @@ async function connectAgent(input) {
1756
1789
  );
1757
1790
  }
1758
1791
  return {
1759
- agent: input.agent,
1760
- serverName: input.serverName,
1761
- scope: input.scope,
1792
+ agent: input2.agent,
1793
+ serverName: input2.serverName,
1794
+ scope: input2.scope,
1762
1795
  action: plan.action,
1763
1796
  changed: true,
1764
1797
  verified: true,
@@ -1766,15 +1799,15 @@ async function connectAgent(input) {
1766
1799
  plan
1767
1800
  };
1768
1801
  }
1769
- async function disconnectAgent(input) {
1770
- validateServerName(input.serverName);
1771
- await input.adapter.detect();
1772
- const key = connectionKey(input.agent, input.scope, input.serverName);
1773
- const managed = await input.state.get(key);
1802
+ async function disconnectAgent(input2) {
1803
+ validateServerName(input2.serverName);
1804
+ await input2.adapter.detect();
1805
+ const key = connectionKey(input2.agent, input2.scope, input2.serverName);
1806
+ const managed = await input2.state.get(key);
1774
1807
  if (!managed) {
1775
1808
  throw conflict("The Agent MCP entry is not managed by SAG CLI");
1776
1809
  }
1777
- const host = await input.adapter.read(input.serverName, input.scope);
1810
+ const host = await input2.adapter.read(input2.serverName, input2.scope);
1778
1811
  if (!host) {
1779
1812
  throw conflict("Managed state exists but the Agent MCP entry is missing");
1780
1813
  }
@@ -1783,20 +1816,20 @@ async function disconnectAgent(input) {
1783
1816
  }
1784
1817
  const plan = {
1785
1818
  action: "unchanged",
1786
- agent: input.agent,
1787
- serverName: input.serverName,
1788
- scope: input.scope,
1819
+ agent: input2.agent,
1820
+ serverName: input2.serverName,
1821
+ scope: input2.scope,
1789
1822
  provider: "docker-stdio",
1790
1823
  target: managed.docker.containerName,
1791
1824
  sourceId: managed.sourceId,
1792
1825
  currentFingerprint: host.fingerprint,
1793
1826
  desiredFingerprint: host.fingerprint
1794
1827
  };
1795
- if (!input.yes && !await input.confirm(`Remove ${input.serverName} from ${input.agent}?`)) {
1828
+ if (!input2.yes && !await input2.confirm(`Remove ${input2.serverName} from ${input2.agent}?`)) {
1796
1829
  return {
1797
- agent: input.agent,
1798
- serverName: input.serverName,
1799
- scope: input.scope,
1830
+ agent: input2.agent,
1831
+ serverName: input2.serverName,
1832
+ scope: input2.scope,
1800
1833
  action: "cancelled",
1801
1834
  changed: false,
1802
1835
  verified: true,
@@ -1804,12 +1837,12 @@ async function disconnectAgent(input) {
1804
1837
  plan
1805
1838
  };
1806
1839
  }
1807
- await input.adapter.remove(input.serverName, input.scope);
1840
+ await input2.adapter.remove(input2.serverName, input2.scope);
1808
1841
  try {
1809
- await input.state.delete(key);
1842
+ await input2.state.delete(key);
1810
1843
  } catch (cause) {
1811
1844
  try {
1812
- await input.adapter.add(input.serverName, input.scope, host.spec);
1845
+ await input2.adapter.add(input2.serverName, input2.scope, host.spec);
1813
1846
  } catch {
1814
1847
  throw hostFailure3(
1815
1848
  "Managed state cleanup failed and the Agent entry could not be restored",
@@ -1822,9 +1855,9 @@ async function disconnectAgent(input) {
1822
1855
  );
1823
1856
  }
1824
1857
  return {
1825
- agent: input.agent,
1826
- serverName: input.serverName,
1827
- scope: input.scope,
1858
+ agent: input2.agent,
1859
+ serverName: input2.serverName,
1860
+ scope: input2.scope,
1828
1861
  action: "disconnect",
1829
1862
  changed: true,
1830
1863
  verified: true,
@@ -1837,21 +1870,21 @@ async function disconnectAgent(input) {
1837
1870
  var SOURCE_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/u;
1838
1871
  var DockerStdioProvider = class {
1839
1872
  id = "docker-stdio";
1840
- async inspect(input) {
1873
+ async inspect(input2) {
1841
1874
  return {
1842
1875
  provider: this.id,
1843
1876
  ready: true,
1844
- target: input.target.name
1877
+ target: input2.target.name
1845
1878
  };
1846
1879
  }
1847
- async createSpec(input) {
1848
- if (input.sourceId && !SOURCE_ID_PATTERN.test(input.sourceId)) {
1880
+ async createSpec(input2) {
1881
+ if (input2.sourceId && !SOURCE_ID_PATTERN.test(input2.sourceId)) {
1849
1882
  throw new CliError("INVALID_ARGUMENT", "Invalid SAG source id", {
1850
1883
  exitCode: exitCodes.invalidArgument,
1851
1884
  hint: "Use 1-128 letters, numbers, underscores, or dashes."
1852
1885
  });
1853
1886
  }
1854
- const sourceArguments = input.sourceId ? ["-e", `SAG_MCP_SOURCE_ID=${input.sourceId}`] : [];
1887
+ const sourceArguments = input2.sourceId ? ["-e", `SAG_MCP_SOURCE_ID=${input2.sourceId}`] : [];
1855
1888
  return {
1856
1889
  transport: "stdio",
1857
1890
  command: "docker",
@@ -1859,7 +1892,7 @@ var DockerStdioProvider = class {
1859
1892
  "exec",
1860
1893
  "-i",
1861
1894
  ...sourceArguments,
1862
- input.target.name,
1895
+ input2.target.name,
1863
1896
  "python",
1864
1897
  "-m",
1865
1898
  "sag_api.mcp.server"
@@ -1909,13 +1942,13 @@ async function validateTarget(docker, container) {
1909
1942
  }
1910
1943
  return toTarget(inspection);
1911
1944
  }
1912
- async function discoverSagContainer(input) {
1913
- await input.docker.version();
1914
- if (input.container) {
1915
- validateContainer(input.container);
1916
- return validateTarget(input.docker, input.container);
1945
+ async function discoverSagContainer(input2) {
1946
+ await input2.docker.version();
1947
+ if (input2.container) {
1948
+ validateContainer(input2.container);
1949
+ return validateTarget(input2.docker, input2.container);
1917
1950
  }
1918
- const containers = await input.docker.listRunningContainers();
1951
+ const containers = await input2.docker.listRunningContainers();
1919
1952
  const apiCandidates = containers.filter(
1920
1953
  (container) => container.labels["com.docker.compose.service"] === "api"
1921
1954
  );
@@ -1930,7 +1963,7 @@ async function discoverSagContainer(input) {
1930
1963
  let preferredFailure;
1931
1964
  for (const candidate of apiCandidates) {
1932
1965
  try {
1933
- targets.push(await validateTarget(input.docker, candidate.id));
1966
+ targets.push(await validateTarget(input2.docker, candidate.id));
1934
1967
  } catch (error) {
1935
1968
  firstFailure ??= error;
1936
1969
  if (candidate.labels["com.docker.compose.project"] === "sag") {
@@ -1946,13 +1979,13 @@ async function discoverSagContainer(input) {
1946
1979
  if (rankedTargets.length === 1) {
1947
1980
  return rankedTargets[0];
1948
1981
  }
1949
- if (!input.interactive || !input.select) {
1982
+ if (!input2.interactive || !input2.select) {
1950
1983
  throw new CliError("CONFIG_CONFLICT", "Multiple SAG API containers were found", {
1951
1984
  exitCode: exitCodes.configConflict,
1952
1985
  hint: "Pass `--container <name-or-id>` to select one."
1953
1986
  });
1954
1987
  }
1955
- const selectedId = await input.select(rankedTargets);
1988
+ const selectedId = await input2.select(rankedTargets);
1956
1989
  const selected = rankedTargets.find(
1957
1990
  (target) => target.id === selectedId || target.name === selectedId
1958
1991
  );
@@ -1965,34 +1998,34 @@ async function discoverSagContainer(input) {
1965
1998
  }
1966
1999
 
1967
2000
  // src/commands/agent.ts
1968
- async function connectLocalAgent(runtime2, input) {
2001
+ async function connectLocalAgent(runtime2, input2) {
1969
2002
  return connectAgent({
1970
- agent: input.agent,
1971
- serverName: input.serverName,
2003
+ agent: input2.agent,
2004
+ serverName: input2.serverName,
1972
2005
  scope: "user",
1973
- profile: input.profile,
1974
- ...input.sourceId ? { sourceId: input.sourceId } : {},
1975
- timeoutMs: input.timeoutMs,
1976
- dryRun: input.dryRun,
1977
- yes: input.yes,
2006
+ profile: input2.profile,
2007
+ ...input2.sourceId ? { sourceId: input2.sourceId } : {},
2008
+ timeoutMs: input2.timeoutMs,
2009
+ dryRun: input2.dryRun,
2010
+ yes: input2.yes,
1978
2011
  resolveConnection: async () => {
1979
2012
  const target = await discoverSagContainer({
1980
2013
  docker: runtime2.docker,
1981
- ...input.container ? { container: input.container } : {},
2014
+ ...input2.container ? { container: input2.container } : {},
1982
2015
  interactive: runtime2.interactive,
1983
2016
  select: runtime2.selectContainer
1984
2017
  });
1985
2018
  const spec = await new DockerStdioProvider().createSpec({
1986
2019
  target,
1987
- ...input.sourceId ? { sourceId: input.sourceId } : {}
2020
+ ...input2.sourceId ? { sourceId: input2.sourceId } : {}
1988
2021
  });
1989
2022
  return { target, spec };
1990
2023
  },
1991
2024
  verifier: runtime2.verifier,
1992
- adapter: runtime2.adapters[input.agent],
2025
+ adapter: runtime2.adapters[input2.agent],
1993
2026
  state: runtime2.state,
1994
- confirm: input.confirm,
1995
- now: input.now
2027
+ confirm: input2.confirm,
2028
+ now: input2.now
1996
2029
  });
1997
2030
  }
1998
2031
  async function agentStatus(runtime2, agent, serverName) {
@@ -2071,10 +2104,10 @@ async function agentStatus(runtime2, agent, serverName) {
2071
2104
  };
2072
2105
  }
2073
2106
  }
2074
- async function statusLocalAgents(runtime2, input) {
2075
- const agents = input.agent ? [input.agent] : ["codex", "claude-code"];
2107
+ async function statusLocalAgents(runtime2, input2) {
2108
+ const agents = input2.agent ? [input2.agent] : ["codex", "claude-code"];
2076
2109
  return Promise.all(
2077
- agents.map((agent) => agentStatus(runtime2, agent, input.serverName))
2110
+ agents.map((agent) => agentStatus(runtime2, agent, input2.serverName))
2078
2111
  );
2079
2112
  }
2080
2113
  async function listAgentHosts(runtime2) {
@@ -2095,58 +2128,69 @@ async function listAgentHosts(runtime2) {
2095
2128
  })
2096
2129
  );
2097
2130
  }
2098
- async function disconnectLocalAgent(runtime2, input) {
2131
+ async function disconnectLocalAgent(runtime2, input2) {
2099
2132
  return disconnectAgent({
2100
- agent: input.agent,
2101
- serverName: input.serverName,
2133
+ agent: input2.agent,
2134
+ serverName: input2.serverName,
2102
2135
  scope: "user",
2103
- adapter: runtime2.adapters[input.agent],
2136
+ adapter: runtime2.adapters[input2.agent],
2104
2137
  state: runtime2.state,
2105
- yes: input.yes,
2106
- confirm: input.confirm
2138
+ yes: input2.yes,
2139
+ confirm: input2.confirm
2107
2140
  });
2108
2141
  }
2109
2142
 
2110
2143
  // src/commands/auth.ts
2111
- async function login(input) {
2112
- const token = input.environmentToken?.trim() || await input.promptToken();
2113
- if (!token.trim()) {
2114
- throw new CliError("AUTH_REQUIRED", "A SAG token is required", {
2115
- exitCode: exitCodes.authRequired,
2116
- hint: "Paste a token when prompted or set SAG_TOKEN."
2144
+ async function login(input2) {
2145
+ const environmentToken = input2.environmentToken?.trim();
2146
+ if (environmentToken) {
2147
+ const user2 = await input2.validate(environmentToken);
2148
+ await input2.store.set(input2.credentialRef, environmentToken);
2149
+ return {
2150
+ authenticated: true,
2151
+ credentialRef: input2.credentialRef,
2152
+ credentialStore: input2.store.kind,
2153
+ user: user2
2154
+ };
2155
+ }
2156
+ const name = (input2.name ?? await input2.promptName()).trim();
2157
+ if (!name) {
2158
+ throw new CliError("INVALID_ARGUMENT", "A SAG user name is required", {
2159
+ exitCode: exitCodes.invalidArgument,
2160
+ hint: "Pass `--name <name>` or enter a name when prompted."
2117
2161
  });
2118
2162
  }
2119
- const user = await input.validate(token);
2120
- await input.store.set(input.credentialRef, token);
2163
+ const { accessToken, user } = await input2.authenticate(name);
2164
+ await input2.store.set(input2.credentialRef, accessToken);
2121
2165
  return {
2122
2166
  authenticated: true,
2123
- credentialRef: input.credentialRef,
2124
- credentialStore: input.store.kind,
2167
+ credentialRef: input2.credentialRef,
2168
+ credentialStore: input2.store.kind,
2125
2169
  user
2126
2170
  };
2127
2171
  }
2128
- async function status(input) {
2129
- const token = input.environmentToken?.trim() || await input.store.get(input.credentialRef);
2172
+ async function status(input2) {
2173
+ const token = input2.environmentToken?.trim() || await input2.store.get(input2.credentialRef);
2130
2174
  if (!token) {
2131
2175
  return {
2132
2176
  authenticated: false,
2133
- credentialRef: input.credentialRef,
2134
- credentialStore: input.store.kind
2177
+ credentialRef: input2.credentialRef,
2178
+ credentialStore: input2.store.kind
2135
2179
  };
2136
2180
  }
2137
- const user = await input.validate(token);
2181
+ const user = await input2.validate(token);
2138
2182
  return {
2139
2183
  authenticated: true,
2140
- credentialRef: input.credentialRef,
2141
- credentialStore: input.store.kind,
2184
+ credentialRef: input2.credentialRef,
2185
+ credentialStore: input2.store.kind,
2142
2186
  user
2143
2187
  };
2144
2188
  }
2145
- async function logout(input) {
2146
- await input.store.delete(input.credentialRef);
2189
+ async function logout(input2) {
2190
+ await input2.store.delete(input2.credentialRef);
2147
2191
  return {
2148
2192
  authenticated: false,
2149
- credentialRef: input.credentialRef
2193
+ credentialRef: input2.credentialRef
2150
2194
  };
2151
2195
  }
2152
2196
 
@@ -2246,24 +2290,24 @@ async function getEntityContext(client, sourceId, name) {
2246
2290
  }
2247
2291
 
2248
2292
  // src/commands/mcp.ts
2249
- async function testLocalMcp(runtime2, input) {
2293
+ async function testLocalMcp(runtime2, input2) {
2250
2294
  const container = await discoverSagContainer({
2251
2295
  docker: runtime2.docker,
2252
- ...input.container ? { container: input.container } : {},
2296
+ ...input2.container ? { container: input2.container } : {},
2253
2297
  interactive: runtime2.interactive,
2254
2298
  select: runtime2.selectContainer
2255
2299
  });
2256
2300
  const spec = await new DockerStdioProvider().createSpec({
2257
2301
  target: container,
2258
- ...input.sourceId ? { sourceId: input.sourceId } : {}
2302
+ ...input2.sourceId ? { sourceId: input2.sourceId } : {}
2259
2303
  });
2260
2304
  const verification = await runtime2.verifier.verify(spec, {
2261
- timeoutMs: input.timeoutMs
2305
+ timeoutMs: input2.timeoutMs
2262
2306
  });
2263
2307
  return {
2264
2308
  provider: "docker-stdio",
2265
2309
  container,
2266
- sourceId: input.sourceId ?? null,
2310
+ sourceId: input2.sourceId ?? null,
2267
2311
  ...verification
2268
2312
  };
2269
2313
  }
@@ -2275,9 +2319,9 @@ function skipped(name, detail) {
2275
2319
  detail
2276
2320
  };
2277
2321
  }
2278
- async function localMcpDoctorChecks(runtime2, input) {
2322
+ async function localMcpDoctorChecks(runtime2, input2) {
2279
2323
  try {
2280
- const report = await testLocalMcp(runtime2, input);
2324
+ const report = await testLocalMcp(runtime2, input2);
2281
2325
  return [
2282
2326
  {
2283
2327
  name: "docker",
@@ -2474,16 +2518,16 @@ function directCredentialRef(url) {
2474
2518
  const fingerprint = createHash2("sha256").update(url).digest("hex").slice(0, 16);
2475
2519
  return `sag-cli/direct-${fingerprint}`;
2476
2520
  }
2477
- async function createRuntimeContext(input) {
2478
- const config = await input.configStore.load();
2479
- const connection = resolveConnection(input.options, input.environment, config);
2521
+ async function createRuntimeContext(input2) {
2522
+ const config = await input2.configStore.load();
2523
+ const connection = resolveConnection(input2.options, input2.environment, config);
2480
2524
  const credentialRef = connection.credentialRef ?? directCredentialRef(connection.url);
2481
- const token = connection.environmentToken ?? await input.credentialStore.get(credentialRef) ?? void 0;
2525
+ const token = connection.environmentToken ?? await input2.credentialStore.get(credentialRef) ?? void 0;
2482
2526
  return {
2483
2527
  connection,
2484
2528
  credentialRef,
2485
2529
  ...token ? { token } : {},
2486
- client: input.createClient({
2530
+ client: input2.createClient({
2487
2531
  origin: connection.url,
2488
2532
  ...token ? { token } : {}
2489
2533
  })
@@ -2587,7 +2631,7 @@ function documentDetail(total, statuses) {
2587
2631
  const parts = displayOrder.filter((status2) => statuses[status2] > 0).map((status2) => `${statuses[status2]} ${status2}`);
2588
2632
  return `${total} ${total === 1 ? "document" : "documents"}${parts.length ? `: ${parts.join(", ")}` : ""}`;
2589
2633
  }
2590
- async function runDoctor(client, input, localMcpChecks) {
2634
+ async function runDoctor(client, input2, localMcpChecks) {
2591
2635
  const checks = [];
2592
2636
  let sagVersion;
2593
2637
  let authenticated = false;
@@ -2702,8 +2746,8 @@ async function runDoctor(client, input, localMcpChecks) {
2702
2746
  }
2703
2747
  return {
2704
2748
  status: overallStatus(checks),
2705
- url: input.url,
2706
- ...input.profileName ? { profileName: input.profileName } : {},
2749
+ url: input2.url,
2750
+ ...input2.profileName ? { profileName: input2.profileName } : {},
2707
2751
  ...sagVersion ? { sagVersion } : {},
2708
2752
  checks,
2709
2753
  summary: {
@@ -3010,12 +3054,14 @@ function addProfileCommands(program, dependencies2) {
3010
3054
  }
3011
3055
  function addAuthCommands(program, dependencies2) {
3012
3056
  const auth = program.command("auth").description("Manage SAG credentials");
3013
- auth.command("login").action(async () => {
3057
+ auth.command("login").option("--name <name>", "SAG user name for the existing local login").action(async (options) => {
3014
3058
  const context = await runtime(program, dependencies2);
3015
3059
  const result = await login({
3016
3060
  credentialRef: context.credentialRef,
3017
3061
  ...context.connection.environmentToken ? { environmentToken: context.connection.environmentToken } : {},
3018
- promptToken: dependencies2.promptToken,
3062
+ ...options.name ? { name: options.name } : {},
3063
+ promptName: dependencies2.promptName,
3064
+ authenticate: (name) => dependencies2.createClient({ origin: context.connection.url }).login(name),
3019
3065
  validate: async (token) => dependencies2.createClient({ origin: context.connection.url, token }).me(),
3020
3066
  store: dependencies2.credentialStore
3021
3067
  });
@@ -3284,10 +3330,7 @@ var dependencies = {
3284
3330
  configStore: new ConfigStore(defaultConfigPath()),
3285
3331
  credentialStore: credentialSelection.store,
3286
3332
  createClient: defaultClientFactory,
3287
- promptToken: () => password({
3288
- message: "SAG Token",
3289
- mask: "*"
3290
- }),
3333
+ promptName: () => input({ message: "SAG user name" }),
3291
3334
  confirm: (message) => confirm({ message, default: false }),
3292
3335
  writeStdout: (value) => process.stdout.write(value),
3293
3336
  writeStderr: (value) => process.stderr.write(value),