@vm0/cli 9.119.2 → 9.120.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/zero.js CHANGED
@@ -18,15 +18,11 @@ import {
18
18
  createPhoneCall,
19
19
  createSkill,
20
20
  createZeroAgent,
21
- createZeroComputerConnector,
22
- createZeroConnectorSession,
23
21
  createZeroRun,
24
22
  decodeCliTokenPayload,
25
23
  decodeZeroTokenPayload,
26
24
  deleteSkill,
27
25
  deleteZeroAgent,
28
- deleteZeroComputerConnector,
29
- deleteZeroConnector,
30
26
  deleteZeroOrg,
31
27
  deleteZeroOrgModelProvider,
32
28
  deleteZeroOrgSecret,
@@ -44,7 +40,6 @@ import {
44
40
  getActiveOrg,
45
41
  getApiUrl,
46
42
  getAuthMethodsForType,
47
- getBaseUrl,
48
43
  getComputerUseHost,
49
44
  getConnectorDerivedNames,
50
45
  getConnectorEnvironmentMapping,
@@ -65,7 +60,6 @@ import {
65
60
  getZeroAgentInstructions,
66
61
  getZeroAgentUserConnectors,
67
62
  getZeroConnector,
68
- getZeroConnectorSession,
69
63
  getZeroOrg,
70
64
  getZeroOrgMembers,
71
65
  getZeroRun,
@@ -110,6 +104,7 @@ import {
110
104
  resolveFirewallPolicies,
111
105
  resolveZeroScheduleByAgent,
112
106
  saveConfig,
107
+ searchConnectors,
113
108
  searchZeroLogs,
114
109
  sendChatMessage,
115
110
  sendSlackMessage,
@@ -131,7 +126,7 @@ import {
131
126
  upsertZeroOrgModelProvider,
132
127
  withErrorHandler,
133
128
  zeroAgentCustomSkillNameSchema
134
- } from "./chunk-PS4P6JB6.js";
129
+ } from "./chunk-CVXG5BRK.js";
135
130
  import {
136
131
  __toESM,
137
132
  init_esm_shims
@@ -1564,388 +1559,63 @@ Notes:
1564
1559
  // src/commands/zero/connector/index.ts
1565
1560
  init_esm_shims();
1566
1561
 
1567
- // src/commands/zero/connector/connect.ts
1562
+ // src/commands/zero/connector/list.ts
1568
1563
  init_esm_shims();
1569
1564
 
1570
- // src/lib/computer/start-services.ts
1565
+ // src/commands/zero/connector/agent-context.ts
1571
1566
  init_esm_shims();
1572
- import { spawn } from "child_process";
1573
- import { access, constants } from "fs/promises";
1574
- import { createServer } from "net";
1575
- import { homedir } from "os";
1576
- import { join } from "path";
1577
-
1578
- // src/lib/computer/ngrok.ts
1579
- init_esm_shims();
1580
- async function loadNgrok() {
1581
- try {
1582
- const mod = await import("@ngrok/ngrok");
1583
- return mod.default;
1584
- } catch (cause) {
1585
- throw new Error(
1586
- "Failed to load ngrok tunnel module. This may be caused by a system library (GLIBC) incompatibility. See: https://github.com/vm0-ai/vm0/issues/6825",
1587
- { cause }
1588
- );
1589
- }
1590
- }
1591
- async function startNgrokTunnels(ngrokToken, endpointPrefix, webdavPort, cdpPort) {
1592
- const ngrok = await loadNgrok();
1593
- await ngrok.forward({
1594
- addr: `localhost:${webdavPort}`,
1595
- authtoken: ngrokToken,
1596
- domain: `webdav.${endpointPrefix}.internal`
1597
- });
1598
- await ngrok.forward({
1599
- addr: `localhost:${cdpPort}`,
1600
- authtoken: ngrokToken,
1601
- domain: `chrome.${endpointPrefix}.internal`
1602
- });
1603
- }
1604
- async function stopNgrokTunnels() {
1605
- const ngrok = await loadNgrok();
1606
- await ngrok.kill();
1567
+ async function resolveAgentContext(flagAgentId) {
1568
+ const agentId = flagAgentId ?? process.env.ZERO_AGENT_ID;
1569
+ if (!agentId) return null;
1570
+ const [agent, enabledTypes] = await Promise.all([
1571
+ getZeroAgent(agentId),
1572
+ getZeroAgentUserConnectors(agentId)
1573
+ ]);
1574
+ return {
1575
+ agentId: agent.agentId,
1576
+ displayName: agent.displayName ?? agent.agentId,
1577
+ authorizedTypes: new Set(enabledTypes)
1578
+ };
1607
1579
  }
1608
1580
 
1609
- // src/lib/computer/start-services.ts
1610
- var CHROME_CANDIDATES = [
1611
- // macOS absolute paths
1612
- "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
1613
- "/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary",
1614
- "/Applications/Chromium.app/Contents/MacOS/Chromium",
1615
- // Linux / PATH-based
1616
- "google-chrome",
1617
- "google-chrome-stable",
1618
- "chromium-browser",
1619
- "chromium",
1620
- "chrome"
1621
- ];
1622
- async function getRandomPort() {
1623
- return new Promise((resolve, reject) => {
1624
- const server = createServer();
1625
- server.listen(0, "127.0.0.1", () => {
1626
- const { port } = server.address();
1627
- server.close(() => {
1628
- return resolve(port);
1629
- });
1630
- });
1631
- server.on("error", reject);
1632
- });
1633
- }
1634
- async function findBinary(...candidates) {
1635
- for (const candidate of candidates) {
1636
- if (candidate.startsWith("/")) {
1637
- try {
1638
- await access(candidate, constants.X_OK);
1639
- return candidate;
1640
- } catch {
1641
- }
1642
- } else {
1643
- const found = await new Promise((resolve) => {
1644
- const child = spawn("which", [candidate]);
1645
- child.on("close", (code) => {
1646
- return resolve(code === 0);
1647
- });
1648
- });
1649
- if (found) return candidate;
1650
- }
1651
- }
1652
- return null;
1653
- }
1654
- async function checkComputerDependencies() {
1655
- const wsgidavBinary = await findBinary("wsgidav");
1656
- if (!wsgidavBinary) {
1657
- throw new Error(
1658
- "wsgidav not found\n\nInstall with: pip install wsgidav[cheroot]"
1659
- );
1660
- }
1661
- const chromeBinary = await findBinary(...CHROME_CANDIDATES);
1662
- if (!chromeBinary) {
1663
- throw new Error("Chrome not found\n\nInstall Google Chrome or Chromium");
1664
- }
1581
+ // src/commands/zero/connector/list.ts
1582
+ function renderIdentity(connector) {
1583
+ if (connector.externalUsername) return `@${connector.externalUsername}`;
1584
+ if (connector.externalEmail) return connector.externalEmail;
1585
+ return "-";
1665
1586
  }
1666
- async function startComputerServices(credentials) {
1667
- console.log(source_default.cyan("Starting computer connector services..."));
1668
- const wsgidavBinary = await findBinary("wsgidav");
1669
- if (!wsgidavBinary) {
1670
- throw new Error(
1671
- "wsgidav not found\n\nInstall with: pip install wsgidav[cheroot]"
1672
- );
1587
+ function renderConnectedAsCell(connector) {
1588
+ if (!connector) return source_default.dim("(not connected)");
1589
+ const identity = renderIdentity(connector);
1590
+ if (connector.needsReconnect) {
1591
+ return source_default.yellow(`${identity} (reconnect needed)`);
1673
1592
  }
1674
- const chromeBinary = await findBinary(...CHROME_CANDIDATES);
1675
- if (!chromeBinary) {
1676
- throw new Error("Chrome not found\n\nInstall Google Chrome or Chromium");
1677
- }
1678
- const webdavPort = await getRandomPort();
1679
- const cdpPort = await getRandomPort();
1680
- const downloadsPath = join(homedir(), "Downloads");
1681
- const wsgidav = spawn(
1682
- wsgidavBinary,
1683
- [
1684
- "--host=127.0.0.1",
1685
- `--port=${webdavPort}`,
1686
- `--root=${downloadsPath}`,
1687
- "--auth=anonymous",
1688
- "--no-config"
1689
- ],
1690
- { stdio: ["ignore", "pipe", "pipe"] }
1691
- );
1692
- wsgidav.stdout?.on("data", (data) => {
1693
- return process.stdout.write(data);
1694
- });
1695
- wsgidav.stderr?.on("data", (data) => {
1696
- return process.stderr.write(data);
1697
- });
1698
- console.log(source_default.green("\u2713 WebDAV server started"));
1699
- const chrome = spawn(
1700
- chromeBinary,
1701
- [
1702
- `--remote-debugging-port=${cdpPort}`,
1703
- "--remote-debugging-address=127.0.0.1",
1704
- "--headless=new",
1705
- "--no-sandbox",
1706
- "--disable-gpu"
1707
- ],
1708
- { stdio: ["ignore", "pipe", "pipe"] }
1709
- );
1710
- chrome.stdout?.on("data", (data) => {
1711
- return process.stdout.write(data);
1712
- });
1713
- chrome.stderr?.on("data", (data) => {
1714
- return process.stderr.write(data);
1715
- });
1716
- console.log(source_default.green("\u2713 Chrome started"));
1717
- try {
1718
- await startNgrokTunnels(
1719
- credentials.ngrokToken,
1720
- credentials.endpointPrefix,
1721
- webdavPort,
1722
- cdpPort
1723
- );
1724
- console.log(
1725
- source_default.green(
1726
- `\u2713 ngrok tunnels: webdav.${credentials.domain}, chrome.${credentials.domain}`
1727
- )
1728
- );
1729
- console.log();
1730
- console.log(source_default.green("\u2713 Computer connector active"));
1731
- console.log(` WebDAV: ~/Downloads \u2192 webdav.${credentials.domain}`);
1732
- console.log(
1733
- ` Chrome CDP: port ${cdpPort} \u2192 chrome.${credentials.domain}`
1734
- );
1735
- console.log();
1736
- console.log(source_default.dim("Press ^C twice to disconnect"));
1737
- console.log();
1738
- let sigintCount = 0;
1739
- await new Promise((resolve) => {
1740
- const keepAlive = setInterval(() => {
1741
- }, 6e4);
1742
- const done = () => {
1743
- clearInterval(keepAlive);
1744
- process.removeListener("SIGINT", onSigint);
1745
- resolve();
1746
- };
1747
- const onSigint = () => {
1748
- sigintCount++;
1749
- if (sigintCount === 1) {
1750
- console.log(source_default.dim("\nPress ^C again to disconnect and exit..."));
1751
- } else {
1752
- done();
1753
- }
1754
- };
1755
- process.on("SIGINT", onSigint);
1756
- process.once("SIGTERM", done);
1757
- });
1758
- } finally {
1759
- console.log();
1760
- console.log(source_default.cyan("Stopping services..."));
1761
- wsgidav.kill("SIGTERM");
1762
- chrome.kill("SIGTERM");
1763
- await stopNgrokTunnels();
1764
- console.log(source_default.green("\u2713 Services stopped"));
1593
+ const scopeMismatch = connector.authMethod === "oauth" && !hasRequiredScopes(connector.type, connector.oauthScopes);
1594
+ if (scopeMismatch) {
1595
+ return source_default.yellow(`${identity} (permissions update available)`);
1765
1596
  }
1597
+ return identity;
1766
1598
  }
1767
-
1768
- // src/commands/zero/connector/connect.ts
1769
- function delay(ms) {
1770
- return new Promise((resolve) => {
1771
- return setTimeout(resolve, ms);
1772
- });
1599
+ var ANSI_PATTERN = /\u001b\[[0-9;]*m/g;
1600
+ function stripAnsi(s) {
1601
+ return s.replace(ANSI_PATTERN, "");
1773
1602
  }
1774
- function renderHelpText(text) {
1775
- return text.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_m, label, url) => {
1776
- return `${label} (${source_default.cyan(url)})`;
1777
- }).replace(/\*\*([^*]+)\*\*/g, (_m, content) => {
1778
- return source_default.bold(content);
1779
- }).replace(/^> (.+)$/gm, (_m, content) => {
1780
- return source_default.yellow(` ${content}`);
1781
- });
1603
+ function padEndAnsi(s, width) {
1604
+ const visible = stripAnsi(s).length;
1605
+ return s + " ".repeat(Math.max(0, width - visible));
1782
1606
  }
1783
- async function connectViaApiToken(connectorType, tokenValue) {
1784
- const config = CONNECTOR_TYPES[connectorType];
1785
- const apiTokenConfig = config.authMethods["api-token"];
1786
- if (!apiTokenConfig) {
1787
- throw new Error(
1788
- `${config.label} does not support API token authentication`
1789
- );
1790
- }
1791
- const secretEntries = Object.entries(apiTokenConfig.secrets);
1792
- const inputSecrets = {};
1793
- if (tokenValue && secretEntries.length === 1) {
1794
- const [secretName] = secretEntries[0];
1795
- inputSecrets[secretName] = tokenValue;
1796
- } else {
1797
- if (apiTokenConfig.helpText) {
1798
- console.log();
1799
- console.log(renderHelpText(apiTokenConfig.helpText));
1800
- console.log();
1801
- }
1802
- for (const [secretName, secretConfig] of secretEntries) {
1803
- if (!secretConfig.required) continue;
1804
- const value = await promptPassword(
1805
- `${secretConfig.label}${secretConfig.placeholder ? source_default.dim(` (${secretConfig.placeholder})`) : ""}:`
1806
- );
1807
- if (!value) {
1808
- console.log(source_default.dim("Cancelled"));
1809
- return;
1810
- }
1811
- inputSecrets[secretName] = value;
1812
- }
1813
- }
1814
- for (const [name, value] of Object.entries(inputSecrets)) {
1815
- await setZeroSecret({
1816
- name,
1817
- value,
1818
- description: `API token for ${config.label} connector`
1819
- });
1820
- }
1821
- console.log(source_default.green(`
1822
- \u2713 Connector "${connectorType}" connected`));
1823
- }
1824
- async function connectComputer() {
1825
- await checkComputerDependencies();
1826
- console.log(source_default.cyan("Setting up computer connector..."));
1827
- const credentials = await createZeroComputerConnector();
1828
- await startComputerServices(credentials);
1829
- console.log(source_default.cyan("Disconnecting computer connector..."));
1830
- await deleteZeroComputerConnector();
1831
- console.log(source_default.green("\u2713 Disconnected computer"));
1832
- }
1833
- async function resolveAuthMethod(connectorType, tokenFlag) {
1834
- const config = CONNECTOR_TYPES[connectorType];
1835
- const oauthFlag = CONNECTOR_TYPES[connectorType].featureFlag;
1836
- const orgId = await getActiveOrg();
1837
- const oauthAvailable = "oauth" in config.authMethods && (!oauthFlag || isFeatureEnabled(oauthFlag, { orgId }));
1838
- const apiTokenAvailable = "api-token" in config.authMethods;
1839
- if (tokenFlag) {
1840
- if (!apiTokenAvailable) {
1841
- throw new Error(
1842
- `${config.label} does not support API token authentication`
1843
- );
1844
- }
1845
- return "api-token";
1846
- }
1847
- if (oauthAvailable && apiTokenAvailable) {
1848
- const selected = await promptSelect(
1849
- `How would you like to connect ${config.label}?`,
1850
- [
1851
- { title: "OAuth (Sign in with browser)", value: "oauth" },
1852
- {
1853
- title: `API Token (${config.authMethods["api-token"].label})`,
1854
- value: "api-token"
1855
- }
1856
- ]
1857
- );
1858
- if (!selected) {
1859
- console.log(source_default.dim("Cancelled"));
1860
- return null;
1861
- }
1862
- return selected;
1863
- }
1864
- if (apiTokenAvailable) return "api-token";
1865
- if (oauthAvailable) return "oauth";
1866
- throw new Error(
1867
- `${config.label} has no available auth methods. OAuth may not be enabled yet.`
1868
- );
1869
- }
1870
- async function connectViaOAuth(connectorType) {
1871
- console.log(`Connecting ${source_default.cyan(connectorType)}...`);
1872
- const session = await createZeroConnectorSession(connectorType);
1873
- const apiUrl = await getBaseUrl();
1874
- const verificationUrl = `${apiUrl}${session.verificationUrl}`;
1875
- console.log(source_default.green("\nSession created"));
1876
- console.log(source_default.cyan(`
1877
- To connect, visit: ${verificationUrl}`));
1878
- console.log(
1879
- `
1880
- The session expires in ${Math.floor(session.expiresIn / 60)} minutes.`
1881
- );
1882
- console.log("\nWaiting for authorization...");
1883
- const startTime = Date.now();
1884
- const maxWaitTime = session.expiresIn * 1e3;
1885
- const pollInterval = (session.interval || 5) * 1e3;
1886
- let isFirstPoll = true;
1887
- while (Date.now() - startTime < maxWaitTime) {
1888
- if (!isFirstPoll) {
1889
- await delay(pollInterval);
1890
- }
1891
- isFirstPoll = false;
1892
- const status = await getZeroConnectorSession(connectorType, session.id);
1893
- switch (status.status) {
1894
- case "complete":
1895
- console.log(
1896
- source_default.green(`
1897
-
1898
- \u2713 Connector "${connectorType}" connected`)
1899
- );
1900
- return;
1901
- case "expired":
1902
- throw new Error("Session expired, please try again");
1903
- case "error":
1904
- throw new Error(
1905
- `Connection failed: ${status.errorMessage || "Unknown error"}`
1906
- );
1907
- case "pending":
1908
- process.stdout.write(source_default.dim("."));
1909
- break;
1910
- }
1911
- }
1912
- throw new Error("Session timed out, please try again");
1913
- }
1914
- var connectCommand = new Command().name("connect").description("Connect a third-party service (e.g., GitHub)").argument("<type>", "Connector type (e.g., github)").option("--token <value>", "API token value (skip interactive prompt)").action(
1915
- withErrorHandler(async (type, options) => {
1916
- const parseResult = connectorTypeSchema.safeParse(type);
1917
- if (!parseResult.success) {
1918
- const available = Object.keys(CONNECTOR_TYPES).join(", ");
1919
- throw new Error(`Unknown connector type: ${type}`, {
1920
- cause: new Error(`Available connectors: ${available}`)
1921
- });
1922
- }
1923
- const connectorType = parseResult.data;
1924
- if (connectorType === "computer") {
1925
- await connectComputer();
1926
- return;
1927
- }
1928
- const authMethod = await resolveAuthMethod(connectorType, options.token);
1929
- if (!authMethod) return;
1930
- if (authMethod === "api-token") {
1931
- await connectViaApiToken(connectorType, options.token);
1932
- return;
1933
- }
1934
- await connectViaOAuth(connectorType);
1935
- })
1936
- );
1937
-
1938
- // src/commands/zero/connector/list.ts
1939
- init_esm_shims();
1940
- var listCommand6 = new Command().name("list").alias("ls").description("List all connectors and their status").action(
1941
- withErrorHandler(async () => {
1942
- const result = await listZeroConnectors();
1607
+ var listCommand6 = new Command().name("list").alias("ls").description("List all connectors and their status").option("--agent <id>", "Show per-agent authorization column").action(
1608
+ withErrorHandler(async (options) => {
1609
+ const [{ connectors }, orgId, agentCtx] = await Promise.all([
1610
+ listZeroConnectors(),
1611
+ getActiveOrg(),
1612
+ resolveAgentContext(options.agent)
1613
+ ]);
1943
1614
  const connectedMap = new Map(
1944
- result.connectors.map((c) => {
1615
+ connectors.map((c) => {
1945
1616
  return [c.type, c];
1946
1617
  })
1947
1618
  );
1948
- const orgId = await getActiveOrg();
1949
1619
  const allTypesRaw = Object.keys(CONNECTOR_TYPES);
1950
1620
  const allTypes = [];
1951
1621
  for (const type of allTypesRaw) {
@@ -1962,28 +1632,133 @@ var listCommand6 = new Command().name("list").alias("ls").description("List all
1962
1632
  return t.length;
1963
1633
  })
1964
1634
  );
1965
- const statusText = "STATUS";
1966
- const statusWidth = statusText.length;
1967
- const header = [
1635
+ const connectedAsHeader = "CONNECTED AS";
1636
+ const connectedCells = allTypes.map((type) => {
1637
+ return renderConnectedAsCell(connectedMap.get(type));
1638
+ });
1639
+ const connectedAsWidth = Math.max(
1640
+ connectedAsHeader.length,
1641
+ ...connectedCells.map((c) => {
1642
+ return stripAnsi(c).length;
1643
+ })
1644
+ );
1645
+ const authorizedHeader = agentCtx ? `AUTHORIZED FOR ${agentCtx.displayName}` : null;
1646
+ const headerParts = [
1968
1647
  "TYPE".padEnd(typeWidth),
1969
- statusText.padEnd(statusWidth),
1970
- "ACCOUNT"
1971
- ].join(" ");
1972
- console.log(source_default.dim(header));
1973
- for (const type of allTypes) {
1974
- const connector = connectedMap.get(type);
1975
- const scopeMismatch = connector !== void 0 && connector.authMethod === "oauth" && !hasRequiredScopes(type, connector.oauthScopes);
1976
- const status = connector ? connector.needsReconnect ? source_default.yellow("!".padEnd(statusWidth)) : scopeMismatch ? source_default.yellow("!".padEnd(statusWidth)) : source_default.green("\u2713".padEnd(statusWidth)) : source_default.dim("-".padEnd(statusWidth));
1977
- const account = connector?.needsReconnect ? source_default.yellow("(reconnect needed)") : scopeMismatch ? source_default.yellow("(permissions update available)") : connector?.externalUsername ? `@${connector.externalUsername}` : source_default.dim("-");
1978
- const row = [type.padEnd(typeWidth), status, account].join(" ");
1979
- console.log(row);
1648
+ connectedAsHeader.padEnd(connectedAsWidth)
1649
+ ];
1650
+ if (authorizedHeader) headerParts.push(authorizedHeader);
1651
+ console.log(source_default.dim(headerParts.join(" ")));
1652
+ for (let i = 0; i < allTypes.length; i++) {
1653
+ const type = allTypes[i];
1654
+ const connectedCell = padEndAnsi(connectedCells[i], connectedAsWidth);
1655
+ const parts = [type.padEnd(typeWidth), connectedCell];
1656
+ if (agentCtx) {
1657
+ parts.push(
1658
+ agentCtx.authorizedTypes.has(type) ? source_default.green("\u2713") : source_default.dim("-")
1659
+ );
1660
+ }
1661
+ console.log(parts.join(" "));
1980
1662
  }
1981
- console.log();
1982
- console.log(source_default.dim("To connect a service:"));
1983
- console.log(source_default.dim(" zero connector connect <type>"));
1984
1663
  })
1985
1664
  );
1986
1665
 
1666
+ // src/commands/zero/connector/search.ts
1667
+ init_esm_shims();
1668
+ var DEFAULT_LIMIT = 5;
1669
+ var EXACT_MATCH_THRESHOLD = 80;
1670
+ var ANSI_PATTERN2 = /\u001b\[[0-9;]*m/g;
1671
+ function stripAnsi2(s) {
1672
+ return s.replace(ANSI_PATTERN2, "");
1673
+ }
1674
+ function padEndAnsi2(s, width) {
1675
+ const visible = stripAnsi2(s).length;
1676
+ return s + " ".repeat(Math.max(0, width - visible));
1677
+ }
1678
+ function parseLimit(raw) {
1679
+ const n = Number.parseInt(raw, 10);
1680
+ if (!Number.isFinite(n) || n <= 0) {
1681
+ throw new Error(`--limit must be a positive integer, got "${raw}".`);
1682
+ }
1683
+ return n;
1684
+ }
1685
+ var searchCommand = new Command().name("search").description("Search connectors by type, label, env var, secret, or tag").argument("<keyword>", "Search keyword (case-insensitive)").option("--agent <id>", "Show per-agent authorization column").option(
1686
+ "--limit <n>",
1687
+ `Maximum number of results to display (default ${DEFAULT_LIMIT})`,
1688
+ parseLimit,
1689
+ DEFAULT_LIMIT
1690
+ ).action(
1691
+ withErrorHandler(
1692
+ async (keyword, options) => {
1693
+ const trimmed = keyword.trim();
1694
+ if (!trimmed) {
1695
+ throw new Error("Keyword cannot be empty.");
1696
+ }
1697
+ const [orgId, agentCtx] = await Promise.all([
1698
+ getActiveOrg(),
1699
+ resolveAgentContext(options.agent)
1700
+ ]);
1701
+ const isTypeAvailable = (type) => {
1702
+ const config = CONNECTOR_TYPES[type];
1703
+ const flag = config.featureFlag;
1704
+ const hasApiToken = "api-token" in config.authMethods;
1705
+ return !flag || isFeatureEnabled(flag, { orgId }) || hasApiToken;
1706
+ };
1707
+ const { results, total } = searchConnectors(
1708
+ trimmed,
1709
+ options.limit,
1710
+ isTypeAvailable
1711
+ );
1712
+ if (results.length === 0) {
1713
+ console.log("No matches found.");
1714
+ return;
1715
+ }
1716
+ const topScore = results[0].score;
1717
+ if (topScore < EXACT_MATCH_THRESHOLD) {
1718
+ console.log("No exact match. Showing closest:");
1719
+ }
1720
+ if (total > options.limit) {
1721
+ console.log(`Too many results (top ${options.limit} of ${total}):`);
1722
+ }
1723
+ const typeHeader = "TYPE";
1724
+ const labelHeader = "LABEL";
1725
+ const typeWidth = Math.max(
1726
+ typeHeader.length,
1727
+ ...results.map((r) => {
1728
+ return r.type.length;
1729
+ })
1730
+ );
1731
+ const labelWidth = Math.max(
1732
+ labelHeader.length,
1733
+ ...results.map((r) => {
1734
+ return CONNECTOR_TYPES[r.type].label.length;
1735
+ })
1736
+ );
1737
+ const headerParts = [
1738
+ typeHeader.padEnd(typeWidth),
1739
+ labelHeader.padEnd(labelWidth)
1740
+ ];
1741
+ if (agentCtx) {
1742
+ headerParts.push(`AUTHORIZED FOR ${agentCtx.displayName}`);
1743
+ }
1744
+ console.log(source_default.dim(headerParts.join(" ")));
1745
+ for (const result of results) {
1746
+ const config = CONNECTOR_TYPES[result.type];
1747
+ const parts = [
1748
+ result.type.padEnd(typeWidth),
1749
+ padEndAnsi2(config.label, labelWidth)
1750
+ ];
1751
+ if (agentCtx) {
1752
+ parts.push(
1753
+ agentCtx.authorizedTypes.has(result.type) ? source_default.green("\u2713") : source_default.dim("-")
1754
+ );
1755
+ }
1756
+ console.log(parts.join(" "));
1757
+ }
1758
+ }
1759
+ )
1760
+ );
1761
+
1987
1762
  // src/commands/zero/connector/status.ts
1988
1763
  init_esm_shims();
1989
1764
 
@@ -2101,10 +1876,30 @@ function toISODateTime(dateTimeStr) {
2101
1876
  return date.toISOString();
2102
1877
  }
2103
1878
 
1879
+ // src/commands/zero/doctor/platform-url.ts
1880
+ init_esm_shims();
1881
+ function toPlatformUrl(apiUrl) {
1882
+ const parsed = new URL(apiUrl);
1883
+ const parts = parsed.hostname.split(".");
1884
+ if (parts[0].endsWith("-www")) {
1885
+ parts[0] = parts[0].slice(0, -"-www".length) + "-app";
1886
+ } else if (parts[0] === "www" || parts[0] === "platform") {
1887
+ parts[0] = "app";
1888
+ } else if (parts[0] !== "app" && parts[0] !== "localhost") {
1889
+ parts.unshift("app");
1890
+ }
1891
+ parsed.hostname = parts.join(".");
1892
+ return parsed;
1893
+ }
1894
+ async function getPlatformOrigin() {
1895
+ const apiUrl = await getApiUrl();
1896
+ return toPlatformUrl(apiUrl).origin;
1897
+ }
1898
+
2104
1899
  // src/commands/zero/connector/status.ts
2105
1900
  var LABEL_WIDTH = 16;
2106
- var statusCommand2 = new Command().name("status").description("Show detailed status of a connector").argument("<type>", "Connector type (e.g., github)").action(
2107
- withErrorHandler(async (type) => {
1901
+ var statusCommand2 = new Command().name("status").description("Show detailed status of a connector").argument("<type>", "Connector type (e.g., github)").option("--agent <id>", "Show authorization state for the given agent").action(
1902
+ withErrorHandler(async (type, options) => {
2108
1903
  const parseResult = connectorTypeSchema.safeParse(type);
2109
1904
  if (!parseResult.success) {
2110
1905
  const available = Object.keys(CONNECTOR_TYPES).join(", ");
@@ -2112,7 +1907,10 @@ var statusCommand2 = new Command().name("status").description("Show detailed sta
2112
1907
  cause: new Error(`Available connectors: ${available}`)
2113
1908
  });
2114
1909
  }
2115
- const connector = await getZeroConnector(parseResult.data);
1910
+ const [connector, agentCtx] = await Promise.all([
1911
+ getZeroConnector(parseResult.data),
1912
+ resolveAgentContext(options.agent)
1913
+ ]);
2116
1914
  console.log(`Connector: ${source_default.cyan(type)}`);
2117
1915
  console.log();
2118
1916
  if (connector) {
@@ -2154,67 +1952,37 @@ var statusCommand2 = new Command().name("status").description("Show detailed sta
2154
1952
  `${"Last Updated:".padEnd(LABEL_WIDTH)}${formatDateTime(connector.updatedAt)}`
2155
1953
  );
2156
1954
  }
2157
- console.log();
2158
- console.log(source_default.dim("To disconnect:"));
2159
- console.log(source_default.dim(` zero connector disconnect ${type}`));
2160
1955
  } else {
2161
1956
  console.log(
2162
1957
  `${"Status:".padEnd(LABEL_WIDTH)}${source_default.dim("not connected")}`
2163
1958
  );
2164
- console.log();
2165
- console.log(source_default.dim("To connect:"));
2166
- console.log(source_default.dim(` zero connector connect ${type}`));
2167
1959
  }
2168
- })
2169
- );
2170
-
2171
- // src/commands/zero/connector/disconnect.ts
2172
- init_esm_shims();
2173
- var disconnectCommand = new Command().name("disconnect").description("Disconnect a third-party service").argument("<type>", "Connector type to disconnect (e.g., github)").action(
2174
- withErrorHandler(async (type) => {
2175
- const parseResult = connectorTypeSchema.safeParse(type);
2176
- if (!parseResult.success) {
2177
- const available = Object.keys(CONNECTOR_TYPES).join(", ");
2178
- throw new Error(`Unknown connector type: ${type}`, {
2179
- cause: new Error(`Available connectors: ${available}`)
2180
- });
1960
+ if (agentCtx) {
1961
+ const authorized = agentCtx.authorizedTypes.has(parseResult.data);
1962
+ const glyph = authorized ? source_default.green("\u2713") : source_default.dim("-");
1963
+ console.log();
1964
+ console.log(
1965
+ `${"Authorized:".padEnd(LABEL_WIDTH)}${glyph} for agent ${agentCtx.displayName}`
1966
+ );
1967
+ if (!authorized) {
1968
+ const origin = await getPlatformOrigin();
1969
+ const url = `${origin}/connectors/${parseResult.data}/authorize?agentId=${agentCtx.agentId}`;
1970
+ console.log(
1971
+ `${"".padEnd(LABEL_WIDTH)}${source_default.dim("Authorize:")} ${url}`
1972
+ );
1973
+ }
2181
1974
  }
2182
- const connectorType = parseResult.data;
2183
- await deleteZeroConnector(connectorType);
2184
- console.log(source_default.green(`\u2713 Disconnected ${type}`));
2185
1975
  })
2186
1976
  );
2187
1977
 
2188
1978
  // src/commands/zero/connector/index.ts
2189
- var zeroConnectorCommand = new Command().name("connector").description("Check or connect third-party services (GitHub, Slack, etc.)").addCommand(listCommand6).addCommand(statusCommand2).addCommand(connectCommand).addCommand(disconnectCommand);
1979
+ var zeroConnectorCommand = new Command().name("connector").description("Check third-party service connections (GitHub, Slack, etc.)").addCommand(listCommand6).addCommand(searchCommand).addCommand(statusCommand2);
2190
1980
 
2191
1981
  // src/commands/zero/doctor/index.ts
2192
1982
  init_esm_shims();
2193
1983
 
2194
1984
  // src/commands/zero/doctor/check-connector.ts
2195
1985
  init_esm_shims();
2196
-
2197
- // src/commands/zero/doctor/platform-url.ts
2198
- init_esm_shims();
2199
- function toPlatformUrl(apiUrl) {
2200
- const parsed = new URL(apiUrl);
2201
- const parts = parsed.hostname.split(".");
2202
- if (parts[0].endsWith("-www")) {
2203
- parts[0] = parts[0].slice(0, -"-www".length) + "-app";
2204
- } else if (parts[0] === "www" || parts[0] === "platform") {
2205
- parts[0] = "app";
2206
- } else if (parts[0] !== "app" && parts[0] !== "localhost") {
2207
- parts.unshift("app");
2208
- }
2209
- parsed.hostname = parts.join(".");
2210
- return parsed;
2211
- }
2212
- async function getPlatformOrigin() {
2213
- const apiUrl = await getApiUrl();
2214
- return toPlatformUrl(apiUrl).origin;
2215
- }
2216
-
2217
- // src/commands/zero/doctor/check-connector.ts
2218
1986
  function resolveConnectorFromUrl(url) {
2219
1987
  const allTypes = Object.keys(CONNECTOR_TYPES);
2220
1988
  const normalized = url.endsWith("/") ? url.slice(0, -1) : url;
@@ -4222,10 +3990,10 @@ Notes:
4222
3990
 
4223
3991
  // src/commands/zero/slack/download-file.ts
4224
3992
  init_esm_shims();
4225
- import { join as join2 } from "path";
3993
+ import { join } from "path";
4226
3994
  import { tmpdir } from "os";
4227
3995
  function defaultOutPath(fileId) {
4228
- return join2(tmpdir(), `slack-${fileId}`);
3996
+ return join(tmpdir(), `slack-${fileId}`);
4229
3997
  }
4230
3998
  var downloadFileCommand = new Command().name("download-file").description("Download a Slack file by id using the bot token").argument("<file-id>", "Slack file id (e.g. F01234ABCD)").option(
4231
3999
  "-o, --out <path>",
@@ -4520,7 +4288,7 @@ init_esm_shims();
4520
4288
  // src/lib/skill-directory.ts
4521
4289
  init_esm_shims();
4522
4290
  import { readFileSync as readFileSync6, readdirSync } from "fs";
4523
- import { join as join3 } from "path";
4291
+ import { join as join2 } from "path";
4524
4292
  var IGNORED_NAMES = /* @__PURE__ */ new Set(["node_modules", ".git", ".DS_Store"]);
4525
4293
  function readSkillDirectory(dirPath) {
4526
4294
  const files = [];
@@ -4530,11 +4298,11 @@ function readSkillDirectory(dirPath) {
4530
4298
  if (entry.name.startsWith(".") || IGNORED_NAMES.has(entry.name)) continue;
4531
4299
  const relPath = prefix ? `${prefix}/${entry.name}` : entry.name;
4532
4300
  if (entry.isDirectory()) {
4533
- walk(join3(dir, entry.name), relPath);
4301
+ walk(join2(dir, entry.name), relPath);
4534
4302
  } else {
4535
4303
  files.push({
4536
4304
  path: relPath,
4537
- content: readFileSync6(join3(dir, entry.name), "utf-8")
4305
+ content: readFileSync6(join2(dir, entry.name), "utf-8")
4538
4306
  });
4539
4307
  }
4540
4308
  }
@@ -4857,7 +4625,7 @@ function parseContextOptions(options) {
4857
4625
  }
4858
4626
  return { before, after };
4859
4627
  }
4860
- function parseLimit(value) {
4628
+ function parseLimit2(value) {
4861
4629
  if (!value) return void 0;
4862
4630
  const limit = parseInt(value, 10);
4863
4631
  if (isNaN(limit) || limit < 1 || limit > 50) {
@@ -4912,7 +4680,7 @@ function renderResults(response) {
4912
4680
  );
4913
4681
  }
4914
4682
  }
4915
- var searchCommand = new Command().name("search").description("Search agent events across runs").argument("<keyword>", "Search keyword").option("-A, --after-context <n>", "Show n events after each match").option("-B, --before-context <n>", "Show n events before each match").option("-C, --context <n>", "Show n events before and after each match").option("--agent <name>", "Filter by agent name").option("--run <id>", "Filter by specific run ID").option("--since <time>", "Search logs since (default: 7d)").option("--limit <n>", "Maximum number of matches (default: 20)").addHelpText(
4683
+ var searchCommand2 = new Command().name("search").description("Search agent events across runs").argument("<keyword>", "Search keyword").option("-A, --after-context <n>", "Show n events after each match").option("-B, --before-context <n>", "Show n events before each match").option("-C, --context <n>", "Show n events before and after each match").option("--agent <name>", "Filter by agent name").option("--run <id>", "Filter by specific run ID").option("--since <time>", "Search logs since (default: 7d)").option("--limit <n>", "Maximum number of matches (default: 20)").addHelpText(
4916
4684
  "after",
4917
4685
  `
4918
4686
  Examples:
@@ -4930,7 +4698,7 @@ Examples:
4930
4698
  process.exit(1);
4931
4699
  }
4932
4700
  const since = options.since ? parseTime(options.since) : Date.now() - SEVEN_DAYS_MS;
4933
- const limit = parseLimit(options.limit);
4701
+ const limit = parseLimit2(options.limit);
4934
4702
  const response = await searchZeroLogs({
4935
4703
  keyword,
4936
4704
  agent: options.agent,
@@ -5008,7 +4776,7 @@ async function showAgentEvents(runId, options) {
5008
4776
  renderAgentEvent(event, renderer);
5009
4777
  }
5010
4778
  }
5011
- var zeroLogsCommand = new Command().name("logs").description("View and search agent run logs").argument("[runId]", "Run ID to view agent events for").addCommand(listCommand11).addCommand(searchCommand).option(
4779
+ var zeroLogsCommand = new Command().name("logs").description("View and search agent run logs").argument("[runId]", "Run ID to view agent events for").addCommand(listCommand11).addCommand(searchCommand2).option(
5012
4780
  "--since <time>",
5013
4781
  "Show logs since timestamp (e.g., 5m, 2h, 1d, 2024-01-15T10:30:00Z)"
5014
4782
  ).option("--tail <n>", "Show last N entries (default: 5)").option("--head <n>", "Show first N entries").option("--all", "Fetch all log entries").addHelpText(
@@ -5117,7 +4885,7 @@ init_esm_shims();
5117
4885
  // src/lib/computer-use/desktop-server.ts
5118
4886
  init_esm_shims();
5119
4887
  import {
5120
- createServer as createServer2
4888
+ createServer
5121
4889
  } from "http";
5122
4890
  import { createServer as createNetServer } from "net";
5123
4891
 
@@ -5126,12 +4894,12 @@ init_esm_shims();
5126
4894
  import { execFile } from "child_process";
5127
4895
  import { readFile, unlink } from "fs/promises";
5128
4896
  import { randomUUID } from "crypto";
5129
- import { join as join4 } from "path";
4897
+ import { join as join3 } from "path";
5130
4898
  import { tmpdir as tmpdir2 } from "os";
5131
4899
  import { promisify } from "util";
5132
4900
  var execFileAsync = promisify(execFile);
5133
4901
  async function captureScreenshot() {
5134
- const tmpPath = join4(tmpdir2(), `vm0-screenshot-${randomUUID()}.jpg`);
4902
+ const tmpPath = join3(tmpdir2(), `vm0-screenshot-${randomUUID()}.jpg`);
5135
4903
  try {
5136
4904
  await execFileAsync("screencapture", ["-x", "-t", "jpg", tmpPath]);
5137
4905
  const info = await getScreenInfo();
@@ -5160,7 +4928,7 @@ async function captureScreenshot() {
5160
4928
  }
5161
4929
  }
5162
4930
  async function captureRegionScreenshot(region) {
5163
- const tmpPath = join4(tmpdir2(), `vm0-zoom-${randomUUID()}.jpg`);
4931
+ const tmpPath = join3(tmpdir2(), `vm0-zoom-${randomUUID()}.jpg`);
5164
4932
  try {
5165
4933
  const regionArg = `${region.x},${region.y},${region.width},${region.height}`;
5166
4934
  await execFileAsync("screencapture", [
@@ -5417,7 +5185,7 @@ async function scroll(x, y, direction, amount = DEFAULT_SCROLL_AMOUNT) {
5417
5185
 
5418
5186
  // src/lib/computer-use/clipboard.ts
5419
5187
  init_esm_shims();
5420
- import { execFile as execFile4, spawn as spawn2 } from "child_process";
5188
+ import { execFile as execFile4, spawn } from "child_process";
5421
5189
  import { promisify as promisify4 } from "util";
5422
5190
  var execFileAsync4 = promisify4(execFile4);
5423
5191
  async function readClipboard() {
@@ -5426,7 +5194,7 @@ async function readClipboard() {
5426
5194
  }
5427
5195
  async function writeClipboard(text) {
5428
5196
  return new Promise((resolve, reject) => {
5429
- const proc = spawn2("pbcopy", { stdio: ["pipe", "ignore", "ignore"] });
5197
+ const proc = spawn("pbcopy", { stdio: ["pipe", "ignore", "ignore"] });
5430
5198
  proc.on("error", reject);
5431
5199
  proc.on("close", (code) => {
5432
5200
  if (code === 0) {
@@ -5653,7 +5421,7 @@ async function handleOpenApplication(req, res) {
5653
5421
  res.writeHead(200, { "Content-Type": "application/json" });
5654
5422
  res.end(JSON.stringify({ ok: true }));
5655
5423
  }
5656
- async function getRandomPort2() {
5424
+ async function getRandomPort() {
5657
5425
  return new Promise((resolve, reject) => {
5658
5426
  const server = createNetServer();
5659
5427
  server.listen(0, "127.0.0.1", () => {
@@ -5727,7 +5495,7 @@ async function handleRequest(token, req, res) {
5727
5495
  }
5728
5496
  function startDesktopServer(token, port) {
5729
5497
  return new Promise((resolve, reject) => {
5730
- const server = createServer2((req, res) => {
5498
+ const server = createServer((req, res) => {
5731
5499
  handleRequest(token, req, res).catch(() => {
5732
5500
  if (!res.headersSent) {
5733
5501
  res.writeHead(500, { "Content-Type": "text/plain" });
@@ -5744,7 +5512,7 @@ function startDesktopServer(token, port) {
5744
5512
 
5745
5513
  // src/lib/computer-use/ngrok.ts
5746
5514
  init_esm_shims();
5747
- async function loadNgrok2() {
5515
+ async function loadNgrok() {
5748
5516
  try {
5749
5517
  const mod = await import("@ngrok/ngrok");
5750
5518
  return mod.default;
@@ -5756,7 +5524,7 @@ async function loadNgrok2() {
5756
5524
  }
5757
5525
  }
5758
5526
  async function startDesktopTunnel(ngrokToken, endpointPrefix, port) {
5759
- const ngrok = await loadNgrok2();
5527
+ const ngrok = await loadNgrok();
5760
5528
  await ngrok.forward({
5761
5529
  addr: `localhost:${port}`,
5762
5530
  authtoken: ngrokToken,
@@ -5764,7 +5532,7 @@ async function startDesktopTunnel(ngrokToken, endpointPrefix, port) {
5764
5532
  });
5765
5533
  }
5766
5534
  async function stopDesktopTunnel() {
5767
- const ngrok = await loadNgrok2();
5535
+ const ngrok = await loadNgrok();
5768
5536
  await ngrok.kill();
5769
5537
  }
5770
5538
 
@@ -5786,7 +5554,7 @@ var hostStartCommand = new Command().name("start").description("Start the comput
5786
5554
  }
5787
5555
  console.log(source_default.cyan("Registering computer-use host..."));
5788
5556
  const credentials = await registerComputerUseHost();
5789
- const port = await getRandomPort2();
5557
+ const port = await getRandomPort();
5790
5558
  const server = await startDesktopServer(credentials.token, port);
5791
5559
  try {
5792
5560
  await startDesktopTunnel(
@@ -5852,7 +5620,7 @@ var hostStopCommand = new Command().name("stop").description("Stop and unregiste
5852
5620
  // src/commands/zero/computer-use/client.ts
5853
5621
  init_esm_shims();
5854
5622
  import { writeFile, mkdir } from "fs/promises";
5855
- import { join as join5 } from "path";
5623
+ import { join as join4 } from "path";
5856
5624
 
5857
5625
  // src/lib/computer-use/client.ts
5858
5626
  init_esm_shims();
@@ -5920,7 +5688,7 @@ var clientScreenshotCommand = new Command().name("screenshot").description("Capt
5920
5688
  const dir = "/tmp/computer-use";
5921
5689
  await mkdir(dir, { recursive: true });
5922
5690
  const timestamp = Date.now();
5923
- const filePath = join5(dir, `screenshot-${timestamp}.${data.format}`);
5691
+ const filePath = join4(dir, `screenshot-${timestamp}.${data.format}`);
5924
5692
  const buffer = Buffer.from(data.image, "base64");
5925
5693
  await writeFile(filePath, buffer);
5926
5694
  process.stdout.write(`${filePath}
@@ -5948,7 +5716,7 @@ var clientZoomCommand = new Command().name("zoom").description("Capture a region
5948
5716
  const dir = "/tmp/computer-use";
5949
5717
  await mkdir(dir, { recursive: true });
5950
5718
  const timestamp = Date.now();
5951
- const filePath = join5(dir, `zoom-${timestamp}.${data.format}`);
5719
+ const filePath = join4(dir, `zoom-${timestamp}.${data.format}`);
5952
5720
  const buffer = Buffer.from(data.image, "base64");
5953
5721
  await writeFile(filePath, buffer);
5954
5722
  process.stdout.write(`${filePath}
@@ -6172,7 +5940,7 @@ function printCallInfo(call, callId) {
6172
5940
  // src/commands/zero/phone/call.ts
6173
5941
  var POLL_INTERVAL_MS = 1e4;
6174
5942
  var POLL_TIMEOUT_MS = 15 * 60 * 1e3;
6175
- var delay2 = {
5943
+ var delay = {
6176
5944
  ms: (ms) => {
6177
5945
  return new Promise((resolve) => {
6178
5946
  setTimeout(resolve, ms);
@@ -6243,7 +6011,7 @@ var callCommand = new Command().name("call").description("Initiate an outbound p
6243
6011
  );
6244
6012
  const startTime = Date.now();
6245
6013
  while (Date.now() - startTime < POLL_TIMEOUT_MS) {
6246
- await delay2.ms(POLL_INTERVAL_MS);
6014
+ await delay.ms(POLL_INTERVAL_MS);
6247
6015
  const detail = await getPhoneCallDetail(result.callId);
6248
6016
  const status = detail.call.status;
6249
6017
  const elapsed = Math.round((Date.now() - startTime) / 1e3);
@@ -6422,10 +6190,10 @@ init_esm_shims();
6422
6190
 
6423
6191
  // src/commands/zero/web/download-file.ts
6424
6192
  init_esm_shims();
6425
- import { join as join6 } from "path";
6193
+ import { join as join5 } from "path";
6426
6194
  import { tmpdir as tmpdir3 } from "os";
6427
6195
  function defaultOutPath2(fileId) {
6428
- return join6(tmpdir3(), `web-${fileId}`);
6196
+ return join5(tmpdir3(), `web-${fileId}`);
6429
6197
  }
6430
6198
  var downloadFileCommand2 = new Command().name("download-file").description("Download a web-uploaded file by id").argument("<file-id>", "File id (UUID returned by the upload API)").option(
6431
6199
  "-o, --out <path>",
@@ -6523,7 +6291,7 @@ function registerZeroCommands(prog, commands) {
6523
6291
  var program = new Command();
6524
6292
  program.name("zero").description(
6525
6293
  "Zero CLI \u2014 interact with the zero platform from inside the sandbox"
6526
- ).version("9.119.2").addHelpText(
6294
+ ).version("9.120.0").addHelpText(
6527
6295
  "after",
6528
6296
  `
6529
6297
  Examples: