@cleocode/caamp 1.4.0 → 1.5.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.
@@ -735,8 +735,7 @@ function transformOpenCode(_serverName, config) {
735
735
  }
736
736
  return {
737
737
  type: "local",
738
- command: config.command,
739
- args: config.args ?? [],
738
+ command: [config.command, ...config.args ?? []],
740
739
  enabled: true,
741
740
  ...config.env ? { environment: config.env } : {}
742
741
  };
@@ -1613,121 +1612,9 @@ async function configureProviderGlobalAndProject(provider, options) {
1613
1612
  };
1614
1613
  }
1615
1614
 
1616
- // src/core/lock-utils.ts
1617
- import { open, readFile as readFile6, writeFile as writeFile6, mkdir as mkdir4, rm as rm3, rename, stat } from "fs/promises";
1618
- import { existsSync as existsSync10 } from "fs";
1619
- var LOCK_GUARD_PATH = `${LOCK_FILE_PATH}.lock`;
1620
- var STALE_LOCK_MS = 5e3;
1621
- function sleep(ms) {
1622
- return new Promise((resolve4) => setTimeout(resolve4, ms));
1623
- }
1624
- async function removeStaleLock() {
1625
- try {
1626
- const info = await stat(LOCK_GUARD_PATH);
1627
- if (Date.now() - info.mtimeMs > STALE_LOCK_MS) {
1628
- await rm3(LOCK_GUARD_PATH, { force: true });
1629
- return true;
1630
- }
1631
- } catch {
1632
- }
1633
- return false;
1634
- }
1635
- async function acquireLockGuard(retries = 40, delayMs = 25) {
1636
- await mkdir4(AGENTS_HOME, { recursive: true });
1637
- for (let attempt = 0; attempt < retries; attempt += 1) {
1638
- try {
1639
- const handle = await open(LOCK_GUARD_PATH, "wx");
1640
- await handle.close();
1641
- return;
1642
- } catch (error) {
1643
- if (!(error instanceof Error) || !("code" in error) || error.code !== "EEXIST") {
1644
- throw error;
1645
- }
1646
- if (attempt === 0) {
1647
- const removed = await removeStaleLock();
1648
- if (removed) continue;
1649
- }
1650
- await sleep(delayMs);
1651
- }
1652
- }
1653
- throw new Error("Timed out waiting for lock file guard");
1654
- }
1655
- async function releaseLockGuard() {
1656
- await rm3(LOCK_GUARD_PATH, { force: true });
1657
- }
1658
- async function writeLockFileUnsafe(lock) {
1659
- const tmpPath = `${LOCK_FILE_PATH}.tmp-${process.pid}-${Date.now()}`;
1660
- await writeFile6(tmpPath, JSON.stringify(lock, null, 2) + "\n", "utf-8");
1661
- await rename(tmpPath, LOCK_FILE_PATH);
1662
- }
1663
- async function readLockFile() {
1664
- try {
1665
- if (!existsSync10(LOCK_FILE_PATH)) {
1666
- return { version: 1, skills: {}, mcpServers: {} };
1667
- }
1668
- const content = await readFile6(LOCK_FILE_PATH, "utf-8");
1669
- return JSON.parse(content);
1670
- } catch {
1671
- return { version: 1, skills: {}, mcpServers: {} };
1672
- }
1673
- }
1674
- async function updateLockFile(updater) {
1675
- await acquireLockGuard();
1676
- try {
1677
- const lock = await readLockFile();
1678
- await updater(lock);
1679
- await writeLockFileUnsafe(lock);
1680
- return lock;
1681
- } finally {
1682
- await releaseLockGuard();
1683
- }
1684
- }
1685
-
1686
- // src/core/mcp/lock.ts
1687
- async function recordMcpInstall(serverName, source, sourceType, agents, isGlobal, version) {
1688
- await updateLockFile((lock) => {
1689
- const now = (/* @__PURE__ */ new Date()).toISOString();
1690
- const existing = lock.mcpServers[serverName];
1691
- lock.mcpServers[serverName] = {
1692
- name: serverName,
1693
- scopedName: serverName,
1694
- source,
1695
- sourceType,
1696
- version: version ?? existing?.version,
1697
- installedAt: existing?.installedAt ?? now,
1698
- updatedAt: now,
1699
- agents: [.../* @__PURE__ */ new Set([...existing?.agents ?? [], ...agents])],
1700
- canonicalPath: "",
1701
- isGlobal
1702
- };
1703
- });
1704
- }
1705
- async function removeMcpFromLock(serverName) {
1706
- let removed = false;
1707
- await updateLockFile((lock) => {
1708
- if (!(serverName in lock.mcpServers)) return;
1709
- delete lock.mcpServers[serverName];
1710
- removed = true;
1711
- });
1712
- return removed;
1713
- }
1714
- async function getTrackedMcpServers() {
1715
- const lock = await readLockFile();
1716
- return lock.mcpServers;
1717
- }
1718
- async function saveLastSelectedAgents(agents) {
1719
- await updateLockFile((lock) => {
1720
- lock.lastSelectedAgents = agents;
1721
- });
1722
- }
1723
- async function getLastSelectedAgents() {
1724
- const lock = await readLockFile();
1725
- return lock.lastSelectedAgents;
1726
- }
1727
-
1728
1615
  // src/core/mcp/cleo.ts
1729
1616
  import { execFileSync as execFileSync2 } from "child_process";
1730
- import { existsSync as existsSync11 } from "fs";
1617
+ import { existsSync as existsSync10 } from "fs";
1731
1618
  import { homedir as homedir2 } from "os";
1732
1619
  import { isAbsolute as isAbsolute2, resolve as resolve3 } from "path";
1733
1620
  var CLEO_SERVER_NAMES = {
@@ -1821,7 +1708,7 @@ function checkCommandReachability(command) {
1821
1708
  if (hasPathSeparator || command.startsWith("~")) {
1822
1709
  const expanded = expandHome(command);
1823
1710
  const candidate = isAbsolute2(expanded) ? expanded : resolve3(process.cwd(), expanded);
1824
- if (existsSync11(candidate)) {
1711
+ if (existsSync10(candidate)) {
1825
1712
  return { reachable: true, method: "path", detail: candidate };
1826
1713
  }
1827
1714
  return { reachable: false, method: "path", detail: candidate };
@@ -1860,6 +1747,259 @@ function isCleoSource(source) {
1860
1747
  return source.trim().toLowerCase() === "cleo";
1861
1748
  }
1862
1749
 
1750
+ // src/core/lock-utils.ts
1751
+ import { open, readFile as readFile6, writeFile as writeFile6, mkdir as mkdir4, rm as rm3, rename, stat } from "fs/promises";
1752
+ import { existsSync as existsSync11 } from "fs";
1753
+ var LOCK_GUARD_PATH = `${LOCK_FILE_PATH}.lock`;
1754
+ var STALE_LOCK_MS = 5e3;
1755
+ function sleep(ms) {
1756
+ return new Promise((resolve4) => setTimeout(resolve4, ms));
1757
+ }
1758
+ async function removeStaleLock() {
1759
+ try {
1760
+ const info = await stat(LOCK_GUARD_PATH);
1761
+ if (Date.now() - info.mtimeMs > STALE_LOCK_MS) {
1762
+ await rm3(LOCK_GUARD_PATH, { force: true });
1763
+ return true;
1764
+ }
1765
+ } catch {
1766
+ }
1767
+ return false;
1768
+ }
1769
+ async function acquireLockGuard(retries = 40, delayMs = 25) {
1770
+ await mkdir4(AGENTS_HOME, { recursive: true });
1771
+ for (let attempt = 0; attempt < retries; attempt += 1) {
1772
+ try {
1773
+ const handle = await open(LOCK_GUARD_PATH, "wx");
1774
+ await handle.close();
1775
+ return;
1776
+ } catch (error) {
1777
+ if (!(error instanceof Error) || !("code" in error) || error.code !== "EEXIST") {
1778
+ throw error;
1779
+ }
1780
+ if (attempt === 0) {
1781
+ const removed = await removeStaleLock();
1782
+ if (removed) continue;
1783
+ }
1784
+ await sleep(delayMs);
1785
+ }
1786
+ }
1787
+ throw new Error("Timed out waiting for lock file guard");
1788
+ }
1789
+ async function releaseLockGuard() {
1790
+ await rm3(LOCK_GUARD_PATH, { force: true });
1791
+ }
1792
+ async function writeLockFileUnsafe(lock) {
1793
+ const tmpPath = `${LOCK_FILE_PATH}.tmp-${process.pid}-${Date.now()}`;
1794
+ await writeFile6(tmpPath, JSON.stringify(lock, null, 2) + "\n", "utf-8");
1795
+ await rename(tmpPath, LOCK_FILE_PATH);
1796
+ }
1797
+ async function readLockFile() {
1798
+ try {
1799
+ if (!existsSync11(LOCK_FILE_PATH)) {
1800
+ return { version: 1, skills: {}, mcpServers: {} };
1801
+ }
1802
+ const content = await readFile6(LOCK_FILE_PATH, "utf-8");
1803
+ return JSON.parse(content);
1804
+ } catch {
1805
+ return { version: 1, skills: {}, mcpServers: {} };
1806
+ }
1807
+ }
1808
+ async function updateLockFile(updater) {
1809
+ await acquireLockGuard();
1810
+ try {
1811
+ const lock = await readLockFile();
1812
+ await updater(lock);
1813
+ await writeLockFileUnsafe(lock);
1814
+ return lock;
1815
+ } finally {
1816
+ await releaseLockGuard();
1817
+ }
1818
+ }
1819
+
1820
+ // src/core/mcp/lock.ts
1821
+ async function recordMcpInstall(serverName, source, sourceType, agents, isGlobal, version) {
1822
+ await updateLockFile((lock) => {
1823
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1824
+ const existing = lock.mcpServers[serverName];
1825
+ lock.mcpServers[serverName] = {
1826
+ name: serverName,
1827
+ scopedName: serverName,
1828
+ source,
1829
+ sourceType,
1830
+ version: version ?? existing?.version,
1831
+ installedAt: existing?.installedAt ?? now,
1832
+ updatedAt: now,
1833
+ agents: [.../* @__PURE__ */ new Set([...existing?.agents ?? [], ...agents])],
1834
+ canonicalPath: "",
1835
+ isGlobal
1836
+ };
1837
+ });
1838
+ }
1839
+ async function removeMcpFromLock(serverName) {
1840
+ let removed = false;
1841
+ await updateLockFile((lock) => {
1842
+ if (!(serverName in lock.mcpServers)) return;
1843
+ delete lock.mcpServers[serverName];
1844
+ removed = true;
1845
+ });
1846
+ return removed;
1847
+ }
1848
+ async function getTrackedMcpServers() {
1849
+ const lock = await readLockFile();
1850
+ return lock.mcpServers;
1851
+ }
1852
+ async function saveLastSelectedAgents(agents) {
1853
+ await updateLockFile((lock) => {
1854
+ lock.lastSelectedAgents = agents;
1855
+ });
1856
+ }
1857
+ async function getLastSelectedAgents() {
1858
+ const lock = await readLockFile();
1859
+ return lock.lastSelectedAgents;
1860
+ }
1861
+
1862
+ // src/core/mcp/reconcile.ts
1863
+ function inferCleoLockData(config, channel) {
1864
+ const command = typeof config.command === "string" ? config.command : "";
1865
+ const args = Array.isArray(config.args) ? config.args.filter((a) => typeof a === "string") : [];
1866
+ const packageArg = args.find(
1867
+ (a) => a.includes(CLEO_MCP_NPM_PACKAGE)
1868
+ );
1869
+ if (packageArg) {
1870
+ const version = extractVersionTag(packageArg);
1871
+ return {
1872
+ source: packageArg,
1873
+ sourceType: "package",
1874
+ version
1875
+ };
1876
+ }
1877
+ if (channel === "dev" || command.includes("/") || command.includes("\\")) {
1878
+ return {
1879
+ source: command,
1880
+ sourceType: "command",
1881
+ version: void 0
1882
+ };
1883
+ }
1884
+ const full = args.length > 0 ? `${command} ${args.join(" ")}` : command;
1885
+ return {
1886
+ source: full || "unknown",
1887
+ sourceType: "command",
1888
+ version: void 0
1889
+ };
1890
+ }
1891
+ async function reconcileCleoLock(options = {}) {
1892
+ const result = {
1893
+ backfilled: [],
1894
+ pruned: [],
1895
+ alreadyTracked: 0,
1896
+ errors: []
1897
+ };
1898
+ const lockEntries = await getTrackedMcpServers();
1899
+ const providers = getInstalledProviders();
1900
+ const targetProviders = options.providerIds?.length ? providers.filter((p) => options.providerIds.includes(p.id)) : providers;
1901
+ const scopes = [];
1902
+ if (options.global && !options.project) {
1903
+ scopes.push("global");
1904
+ } else if (options.project && !options.global) {
1905
+ scopes.push("project");
1906
+ } else {
1907
+ scopes.push("project", "global");
1908
+ }
1909
+ const groups = /* @__PURE__ */ new Map();
1910
+ const liveCleoServerNames = /* @__PURE__ */ new Set();
1911
+ for (const scope of scopes) {
1912
+ for (const provider of targetProviders) {
1913
+ let entries;
1914
+ try {
1915
+ entries = await listMcpServers(provider, scope);
1916
+ } catch {
1917
+ result.errors.push({
1918
+ message: `Failed to read config for ${provider.id} (${scope})`
1919
+ });
1920
+ continue;
1921
+ }
1922
+ for (const entry of entries) {
1923
+ const channel = resolveChannelFromServerName(entry.name);
1924
+ if (!channel) continue;
1925
+ liveCleoServerNames.add(entry.name);
1926
+ const isGlobal = scope === "global";
1927
+ const groupKey = `${entry.name}:${isGlobal ? "global" : "project"}`;
1928
+ if (lockEntries[entry.name] !== void 0) {
1929
+ const existing2 = groups.get(groupKey);
1930
+ if (!existing2) {
1931
+ result.alreadyTracked++;
1932
+ }
1933
+ continue;
1934
+ }
1935
+ const existing = groups.get(groupKey);
1936
+ if (existing) {
1937
+ if (!existing.agents.includes(provider.id)) {
1938
+ existing.agents.push(provider.id);
1939
+ }
1940
+ } else {
1941
+ groups.set(groupKey, {
1942
+ serverName: entry.name,
1943
+ channel,
1944
+ scope,
1945
+ agents: [provider.id],
1946
+ config: entry.config
1947
+ });
1948
+ }
1949
+ }
1950
+ }
1951
+ }
1952
+ for (const group of groups.values()) {
1953
+ const inferred = inferCleoLockData(group.config, group.channel);
1954
+ if (!options.dryRun) {
1955
+ try {
1956
+ await recordMcpInstall(
1957
+ group.serverName,
1958
+ inferred.source,
1959
+ inferred.sourceType,
1960
+ group.agents,
1961
+ group.scope === "global",
1962
+ inferred.version
1963
+ );
1964
+ } catch (err) {
1965
+ result.errors.push({
1966
+ message: `Failed to backfill ${group.serverName}: ${err instanceof Error ? err.message : String(err)}`
1967
+ });
1968
+ continue;
1969
+ }
1970
+ }
1971
+ result.backfilled.push({
1972
+ serverName: group.serverName,
1973
+ channel: group.channel,
1974
+ scope: group.scope,
1975
+ agents: group.agents,
1976
+ source: inferred.source,
1977
+ sourceType: inferred.sourceType,
1978
+ version: inferred.version
1979
+ });
1980
+ }
1981
+ if (options.prune) {
1982
+ for (const [serverName] of Object.entries(lockEntries)) {
1983
+ const channel = resolveChannelFromServerName(serverName);
1984
+ if (!channel) continue;
1985
+ if (!liveCleoServerNames.has(serverName)) {
1986
+ if (!options.dryRun) {
1987
+ try {
1988
+ await removeMcpFromLock(serverName);
1989
+ } catch (err) {
1990
+ result.errors.push({
1991
+ message: `Failed to prune ${serverName}: ${err instanceof Error ? err.message : String(err)}`
1992
+ });
1993
+ continue;
1994
+ }
1995
+ }
1996
+ result.pruned.push(serverName);
1997
+ }
1998
+ }
1999
+ }
2000
+ return result;
2001
+ }
2002
+
1863
2003
  // src/core/sources/parser.ts
1864
2004
  var GITHUB_SHORTHAND = /^([a-zA-Z0-9_.-]+)\/([a-zA-Z0-9_.-]+)(?:\/(.+))?$/;
1865
2005
  var GITHUB_URL = /^https?:\/\/(?:www\.)?github\.com\/([^/]+)\/([^/]+)(?:\/(?:tree|blob)\/([^/]+)(?:\/(.+))?)?/;
@@ -3824,12 +3964,6 @@ export {
3824
3964
  applyMcpInstallWithPolicy,
3825
3965
  updateInstructionsSingleOperation,
3826
3966
  configureProviderGlobalAndProject,
3827
- readLockFile,
3828
- recordMcpInstall,
3829
- removeMcpFromLock,
3830
- getTrackedMcpServers,
3831
- saveLastSelectedAgents,
3832
- getLastSelectedAgents,
3833
3967
  normalizeCleoChannel,
3834
3968
  resolveCleoServerName,
3835
3969
  resolveChannelFromServerName,
@@ -3838,6 +3972,14 @@ export {
3838
3972
  parseEnvAssignments,
3839
3973
  extractVersionTag,
3840
3974
  isCleoSource,
3975
+ readLockFile,
3976
+ recordMcpInstall,
3977
+ removeMcpFromLock,
3978
+ getTrackedMcpServers,
3979
+ saveLastSelectedAgents,
3980
+ getLastSelectedAgents,
3981
+ inferCleoLockData,
3982
+ reconcileCleoLock,
3841
3983
  parseSource,
3842
3984
  isMarketplaceScoped,
3843
3985
  formatNetworkError,
@@ -3877,4 +4019,4 @@ export {
3877
4019
  toSarif,
3878
4020
  validateSkill
3879
4021
  };
3880
- //# sourceMappingURL=chunk-QZOOTKAJ.js.map
4022
+ //# sourceMappingURL=chunk-ZEXRZTQX.js.map