nexos-connect 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -4,10 +4,10 @@
4
4
  import { spawnSync } from "child_process";
5
5
  import { createRequire } from "module";
6
6
  import os from "os";
7
- import path21 from "path";
7
+ import path24 from "path";
8
8
  import crossSpawn from "cross-spawn";
9
9
  import pc12 from "picocolors";
10
- import * as v12 from "valibot";
10
+ import * as v15 from "valibot";
11
11
 
12
12
  // src/cli/classify-failure.ts
13
13
  import { match } from "ts-pattern";
@@ -393,13 +393,13 @@ function buildIdentityMatching(secrets) {
393
393
  if (masksByForm.size === 0) return () => [];
394
394
  const alternatives = [...masksByForm.keys()].sort((first, second) => second.length - first.length);
395
395
  const pattern = new RegExp(alternatives.map(buildFormPattern).join("|"), "g");
396
- return (text2) => [...text2.matchAll(pattern)].map((match25) => toSpan(match25, masksByForm.get(match25[0]) ?? match25[0]));
396
+ return (text2) => [...text2.matchAll(pattern)].map((match27) => toSpan(match27, masksByForm.get(match27[0]) ?? match27[0]));
397
397
  }
398
398
  function findShapeSpans(text2) {
399
- return [...text2.matchAll(KEY_SHAPE_PATTERN)].map((match25) => toSpan(match25, maskKey(match25[0])));
399
+ return [...text2.matchAll(KEY_SHAPE_PATTERN)].map((match27) => toSpan(match27, maskKey(match27[0])));
400
400
  }
401
- function toSpan(match25, mask) {
402
- return { start: match25.index, end: match25.index + match25[0].length, mask };
401
+ function toSpan(match27, mask) {
402
+ return { start: match27.index, end: match27.index + match27[0].length, mask };
403
403
  }
404
404
  function pickOuterSpans(spans) {
405
405
  const picked = [];
@@ -716,7 +716,7 @@ function refuse({ question, answers }) {
716
716
  }
717
717
 
718
718
  // src/flows/key/find-existing-keys.ts
719
- import * as v6 from "valibot";
719
+ import * as v9 from "valibot";
720
720
 
721
721
  // src/harnesses/claude-code/claude-code.ts
722
722
  import path3 from "path";
@@ -1388,14 +1388,14 @@ function listKeyValues(program) {
1388
1388
  }
1389
1389
  function listInBody(keyValues, tablePath) {
1390
1390
  return keyValues.flatMap((keyValue) => {
1391
- const path22 = [...tablePath, ...keyValue.key.keys.map(readKeyName)];
1392
- const entry = { path: path22, keyValue, tablePath };
1391
+ const path25 = [...tablePath, ...keyValue.key.keys.map(readKeyName)];
1392
+ const entry = { path: path25, keyValue, tablePath };
1393
1393
  if (keyValue.value.type !== "TOMLInlineTable") return [entry];
1394
- return [entry, ...listInBody(keyValue.value.body, path22)];
1394
+ return [entry, ...listInBody(keyValue.value.body, path25)];
1395
1395
  });
1396
1396
  }
1397
- function isSamePath(path22, other) {
1398
- return path22.length === other.length && path22.every((key, index) => key === other[index]);
1397
+ function isSamePath(path25, other) {
1398
+ return path25.length === other.length && path25.every((key, index) => key === other[index]);
1399
1399
  }
1400
1400
  function replaceValue(source, node, value) {
1401
1401
  if (node.type === "TOMLValue" && node.value === value) return source.text;
@@ -1740,9 +1740,107 @@ function detectBinary(ctx) {
1740
1740
  return { isFound, detail: isFound ? `${BINARY3} found on PATH` : `${BINARY3} not found on PATH` };
1741
1741
  }
1742
1742
 
1743
+ // src/harnesses/crush/crush.ts
1744
+ import path5 from "path";
1745
+ import { match as match8 } from "ts-pattern";
1746
+ import * as v5 from "valibot";
1747
+
1748
+ // src/shared/text/read-literal-key.ts
1749
+ var COMMAND_PREFIX = "!";
1750
+ var VARIABLE_MARKER = "$";
1751
+ function readLiteralKey(value) {
1752
+ const key = readNonBlank(value);
1753
+ return key === void 0 || isConfigSubstitution(key) ? void 0 : key;
1754
+ }
1755
+ function isConfigSubstitution(value) {
1756
+ return value.startsWith(COMMAND_PREFIX) || value.includes(VARIABLE_MARKER);
1757
+ }
1758
+
1759
+ // src/harnesses/crush/crush.ts
1760
+ var BINARY4 = "crush";
1761
+ var PROVIDER_ID2 = "nexos";
1762
+ var PROVIDER_NAME2 = "nexos.ai";
1763
+ var PROVIDER_TYPE2 = "openai-compat";
1764
+ var CONFIG_SCHEMA_URL = "https://charm.land/crush.json";
1765
+ var crush = {
1766
+ kind: "configurable",
1767
+ id: "crush",
1768
+ label: "Crush",
1769
+ binary: BINARY4,
1770
+ protocols: ["openai-chat"],
1771
+ projectSupport: { kind: "supported" },
1772
+ detect: (ctx) => detectCrush(ctx),
1773
+ configPath: (ctx) => resolveConfigPath2(ctx),
1774
+ plan: (desired, ctx) => [planConfig2(desired, ctx)],
1775
+ readKey: (ctx) => readProviderKey(ctx),
1776
+ readSecrets: (ctx) => readProviderSecrets(ctx),
1777
+ postSetupNotes: [
1778
+ "Crush starts on the model setup chose. For another, run its `model large` command.",
1779
+ "Crush ranks its config, highest first: the project's crushrc, the project's .crush.json or crush.json, the user's crushrc, then the user's crush.json. A higher one that sets the same values overrides this file."
1780
+ ]
1781
+ };
1782
+ function detectCrush(ctx) {
1783
+ const detected = detectConfigFile([resolveConfigPath2(ctx)], ctx.home);
1784
+ if (detected.isFound || ctx.scope === "project" || !ctx.hasBinary(BINARY4)) return detected;
1785
+ return { isFound: true, detail: `${BINARY4} found on PATH` };
1786
+ }
1787
+ var USER_DIRECTORY = path5.join(".config", "crush");
1788
+ var USER_FILE = "crush.json";
1789
+ var PROJECT_FILE = ".crush.json";
1790
+ function resolveConfigPath2(ctx) {
1791
+ return match8(ctx.scope).with("user", () => path5.join(ctx.home, USER_DIRECTORY, USER_FILE)).with("project", () => path5.join(ctx.cwd, PROJECT_FILE)).exhaustive();
1792
+ }
1793
+ function planConfig2(desired, ctx) {
1794
+ return {
1795
+ kind: "file",
1796
+ path: resolveConfigPath2(ctx),
1797
+ hasSecret: true,
1798
+ edits: () => ({ format: "jsonc", updates: listUpdates(desired) })
1799
+ };
1800
+ }
1801
+ function listUpdates(desired) {
1802
+ const contextWindow = findContextWindow(desired.model);
1803
+ const modelEntry = {
1804
+ id: desired.model,
1805
+ name: desired.model,
1806
+ ...contextWindow === void 0 ? {} : { context_window: contextWindow }
1807
+ };
1808
+ const selection = { provider: PROVIDER_ID2, model: desired.model };
1809
+ return [
1810
+ [["$schema"], CONFIG_SCHEMA_URL],
1811
+ [["providers", PROVIDER_ID2, "type"], PROVIDER_TYPE2],
1812
+ [["providers", PROVIDER_ID2, "name"], PROVIDER_NAME2],
1813
+ [["providers", PROVIDER_ID2, "base_url"], desired.openaiBaseUrl],
1814
+ [["providers", PROVIDER_ID2, "api_key"], desired.apiKey],
1815
+ [["providers", PROVIDER_ID2, "models"], [modelEntry]],
1816
+ [["models", "large"], selection],
1817
+ [["models", "small"], selection]
1818
+ ];
1819
+ }
1820
+ var ConfigSchema = v5.looseObject({
1821
+ providers: v5.optional(
1822
+ v5.looseObject({ [PROVIDER_ID2]: v5.optional(v5.looseObject({ api_key: v5.optional(v5.string()) })) })
1823
+ )
1824
+ });
1825
+ function readProviderKey(ctx) {
1826
+ const config = readJsoncFile(resolveConfigPath2(ctx), ConfigSchema);
1827
+ const apiKey = config.kind === "valid" ? config.value.providers?.[PROVIDER_ID2]?.api_key : void 0;
1828
+ return readLiteralKey(apiKey);
1829
+ }
1830
+ var ProvidersSchema = v5.looseObject({ providers: v5.optional(v5.record(v5.string(), v5.unknown())) });
1831
+ var ProviderKeySchema = v5.looseObject({ api_key: v5.string() });
1832
+ function readProviderSecrets(ctx) {
1833
+ const config = readJsoncFile(resolveConfigPath2(ctx), ProvidersSchema);
1834
+ if (config.kind !== "valid") return [];
1835
+ return Object.values(config.value.providers ?? {}).flatMap((provider) => {
1836
+ const parsed = v5.safeParse(ProviderKeySchema, provider);
1837
+ return parsed.success ? readLiteralKey(parsed.output.api_key) ?? [] : [];
1838
+ });
1839
+ }
1840
+
1743
1841
  // src/harnesses/gemini-cli/gemini-cli.ts
1744
1842
  import fs3 from "fs";
1745
- import path5 from "path";
1843
+ import path6 from "path";
1746
1844
  var geminiCli = {
1747
1845
  kind: "unsupported",
1748
1846
  id: "gemini-cli",
@@ -1753,7 +1851,7 @@ var geminiCli = {
1753
1851
  };
1754
1852
  var CONFIG_DIRECTORY3 = ".gemini";
1755
1853
  function detectConfigDirectory(ctx) {
1756
- const directory = path5.join(ctx.home, CONFIG_DIRECTORY3);
1854
+ const directory = path6.join(ctx.home, CONFIG_DIRECTORY3);
1757
1855
  if (!isDirectory(directory)) return { isFound: false, detail: `no ${displayPath(directory, ctx.home)}` };
1758
1856
  return { isFound: true, detail: "installed, but not configurable through the nexos.ai Gateway" };
1759
1857
  }
@@ -1767,20 +1865,20 @@ function isDirectory(directoryPath) {
1767
1865
  }
1768
1866
 
1769
1867
  // src/harnesses/opencode/opencode.ts
1770
- import path6 from "path";
1771
- import { match as match8 } from "ts-pattern";
1772
- import * as v5 from "valibot";
1773
- var BINARY4 = "opencode";
1868
+ import path7 from "path";
1869
+ import { match as match9 } from "ts-pattern";
1870
+ import * as v6 from "valibot";
1871
+ var BINARY5 = "opencode";
1774
1872
  var opencode = {
1775
1873
  kind: "configurable",
1776
1874
  id: "opencode",
1777
1875
  label: "OpenCode",
1778
- binary: BINARY4,
1876
+ binary: BINARY5,
1779
1877
  protocols: ["openai-chat"],
1780
1878
  projectSupport: { kind: "supported" },
1781
1879
  detect: (ctx) => detectOpenCode(ctx),
1782
- configPath: (ctx) => resolveConfigPath2(ctx),
1783
- plan: (desired, ctx) => [planConfig2(desired, ctx)],
1880
+ configPath: (ctx) => resolveConfigPath3(ctx),
1881
+ plan: (desired, ctx) => [planConfig3(desired, ctx)],
1784
1882
  launch: (desired) => {
1785
1883
  const config = renderJsonc(null, [
1786
1884
  ...providerUpdates({ providerId: RUN_PROVIDER_ID2, apiKey: `{env:${RUN_KEY_VARIABLE}}`, desired }),
@@ -1788,7 +1886,7 @@ var opencode = {
1788
1886
  [["enabled_providers"], [RUN_PROVIDER_ID2]]
1789
1887
  ]);
1790
1888
  return {
1791
- binary: BINARY4,
1889
+ binary: BINARY5,
1792
1890
  arguments: [],
1793
1891
  env: {
1794
1892
  [CONFIG_CONTENT_VARIABLE]: config,
@@ -1800,49 +1898,49 @@ var opencode = {
1800
1898
  launchNotes: [
1801
1899
  'OpenCode adds the "nexos-run" model to its recent models, where it shows as unavailable in later sessions.'
1802
1900
  ],
1803
- readKey: (ctx) => readProviderKey(ctx),
1804
- readSecrets: (ctx) => readProviderSecrets(ctx),
1901
+ readKey: (ctx) => readProviderKey2(ctx),
1902
+ readSecrets: (ctx) => readProviderSecrets2(ctx),
1805
1903
  postSetupNotes: [
1806
1904
  "OpenCode keeps its own credentials in its auth.json; this key goes in the provider block instead, so no /connect step is needed."
1807
1905
  ]
1808
1906
  };
1809
1907
  function detectOpenCode(ctx) {
1810
1908
  const detected = detectConfigFile(listConfigCandidates(ctx), ctx.home);
1811
- if (detected.isFound || ctx.scope === "project" || !ctx.hasBinary(BINARY4)) return detected;
1812
- return { isFound: true, detail: `${BINARY4} found on PATH` };
1909
+ if (detected.isFound || ctx.scope === "project" || !ctx.hasBinary(BINARY5)) return detected;
1910
+ return { isFound: true, detail: `${BINARY5} found on PATH` };
1813
1911
  }
1814
- var USER_CONFIG_DIRECTORY = path6.join(".config", "opencode");
1912
+ var USER_CONFIG_DIRECTORY = path7.join(".config", "opencode");
1815
1913
  var JSON_CONFIG_FILE = "opencode.json";
1816
1914
  var JSONC_CONFIG_FILE = "opencode.jsonc";
1817
1915
  function listConfigCandidates(ctx) {
1818
- const directory = match8(ctx.scope).with("user", () => path6.join(ctx.home, USER_CONFIG_DIRECTORY)).with("project", () => ctx.cwd).exhaustive();
1819
- return [path6.join(directory, JSON_CONFIG_FILE), path6.join(directory, JSONC_CONFIG_FILE)];
1916
+ const directory = match9(ctx.scope).with("user", () => path7.join(ctx.home, USER_CONFIG_DIRECTORY)).with("project", () => ctx.cwd).exhaustive();
1917
+ return [path7.join(directory, JSON_CONFIG_FILE), path7.join(directory, JSONC_CONFIG_FILE)];
1820
1918
  }
1821
- function resolveConfigPath2(ctx) {
1919
+ function resolveConfigPath3(ctx) {
1822
1920
  const candidates = listConfigCandidates(ctx);
1823
1921
  return findFirstExistingFile(candidates) ?? candidates[0];
1824
1922
  }
1825
- function planConfig2(desired, ctx) {
1923
+ function planConfig3(desired, ctx) {
1826
1924
  return {
1827
1925
  kind: "file",
1828
- path: resolveConfigPath2(ctx),
1926
+ path: resolveConfigPath3(ctx),
1829
1927
  hasSecret: true,
1830
- edits: (current) => ({ format: "jsonc", updates: listUpdates(desired, parseJsoncConfig(current, ConfigSchema)) })
1928
+ edits: (current) => ({ format: "jsonc", updates: listUpdates2(desired, parseJsoncConfig(current, ConfigSchema2)) })
1831
1929
  };
1832
1930
  }
1833
- var PROVIDER_ID2 = "nexos";
1834
- var PROVIDER_NAME2 = "nexos.ai";
1931
+ var PROVIDER_ID3 = "nexos";
1932
+ var PROVIDER_NAME3 = "nexos.ai";
1835
1933
  var PROVIDER_PACKAGE = "@ai-sdk/openai-compatible";
1836
1934
  var RUN_PROVIDER_ID2 = "nexos-run";
1837
1935
  var RUN_KEY_VARIABLE = "NEXOS_API_KEY";
1838
1936
  var CONFIG_CONTENT_VARIABLE = "OPENCODE_CONFIG_CONTENT";
1839
1937
  var MODELS_FETCH_SWITCH = "OPENCODE_DISABLE_MODELS_FETCH";
1840
1938
  var SWITCH_ON = "1";
1841
- var CONFIG_SCHEMA_URL = "https://opencode.ai/config.json";
1939
+ var CONFIG_SCHEMA_URL2 = "https://opencode.ai/config.json";
1842
1940
  function providerUpdates({ providerId, apiKey, desired }) {
1843
1941
  return [
1844
1942
  [["provider", providerId, "npm"], PROVIDER_PACKAGE],
1845
- [["provider", providerId, "name"], PROVIDER_NAME2],
1943
+ [["provider", providerId, "name"], PROVIDER_NAME3],
1846
1944
  [["provider", providerId, "options", "baseURL"], desired.openaiBaseUrl],
1847
1945
  [["provider", providerId, "options", "apiKey"], apiKey],
1848
1946
  [["provider", providerId, "models", desired.model, "name"], desired.model],
@@ -1859,49 +1957,269 @@ function listLimitUpdates({ providerId, model }) {
1859
1957
  [["provider", providerId, "models", model, "limit", "output"], OUTPUT_TOKENS]
1860
1958
  ];
1861
1959
  }
1862
- function listUpdates(desired, config) {
1863
- const updates = providerUpdates({ providerId: PROVIDER_ID2, apiKey: desired.apiKey, desired });
1960
+ function listUpdates2(desired, config) {
1961
+ const updates = providerUpdates({ providerId: PROVIDER_ID3, apiKey: desired.apiKey, desired });
1864
1962
  if (!isMissingSchemaUrl(config)) return updates;
1865
- return [[["$schema"], CONFIG_SCHEMA_URL], ...updates];
1963
+ return [[["$schema"], CONFIG_SCHEMA_URL2], ...updates];
1866
1964
  }
1867
1965
  function isMissingSchemaUrl(config) {
1868
- return match8(config).with({ kind: "absent" }, () => true).with({ kind: "invalid" }, () => false).with({ kind: "valid" }, ({ value }) => value.$schema === void 0).exhaustive();
1966
+ return match9(config).with({ kind: "absent" }, () => true).with({ kind: "invalid" }, () => false).with({ kind: "valid" }, ({ value }) => value.$schema === void 0).exhaustive();
1869
1967
  }
1870
- function readProviderKey(ctx) {
1871
- const config = readJsoncFile(resolveConfigPath2(ctx), ConfigSchema);
1872
- const apiKey = config.kind === "valid" ? config.value.provider?.[PROVIDER_ID2]?.options?.apiKey : void 0;
1968
+ function readProviderKey2(ctx) {
1969
+ const config = readJsoncFile(resolveConfigPath3(ctx), ConfigSchema2);
1970
+ const apiKey = config.kind === "valid" ? config.value.provider?.[PROVIDER_ID3]?.options?.apiKey : void 0;
1873
1971
  return apiKey === void 0 || isSubstitution(apiKey) ? void 0 : apiKey;
1874
1972
  }
1875
1973
  var SUBSTITUTION_MARKERS = ["{env:", "{file:"];
1876
1974
  function isSubstitution(value) {
1877
1975
  return SUBSTITUTION_MARKERS.some((marker) => value.includes(marker));
1878
1976
  }
1879
- var ProvidersSchema = v5.looseObject({
1880
- provider: v5.fallback(v5.optional(v5.record(v5.string(), v5.unknown())), void 0)
1977
+ var ProvidersSchema2 = v6.looseObject({
1978
+ provider: v6.fallback(v6.optional(v6.record(v6.string(), v6.unknown())), void 0)
1881
1979
  });
1882
- var ProviderKeySchema = v5.looseObject({ options: v5.looseObject({ apiKey: v5.string() }) });
1883
- function readProviderSecrets(ctx) {
1884
- const config = readJsoncFile(resolveConfigPath2(ctx), ProvidersSchema);
1980
+ var ProviderKeySchema2 = v6.looseObject({ options: v6.looseObject({ apiKey: v6.string() }) });
1981
+ function readProviderSecrets2(ctx) {
1982
+ const config = readJsoncFile(resolveConfigPath3(ctx), ProvidersSchema2);
1885
1983
  if (config.kind !== "valid") return [];
1886
1984
  return Object.values(config.value.provider ?? {}).flatMap((provider) => {
1887
- const parsed = v5.safeParse(ProviderKeySchema, provider);
1985
+ const parsed = v6.safeParse(ProviderKeySchema2, provider);
1888
1986
  if (!parsed.success || isSubstitution(parsed.output.options.apiKey)) return [];
1889
1987
  return readNonBlank(parsed.output.options.apiKey) ?? [];
1890
1988
  });
1891
1989
  }
1892
- var ConfigSchema = v5.looseObject({
1893
- $schema: v5.optional(v5.unknown()),
1894
- provider: v5.optional(
1895
- v5.looseObject({
1896
- [PROVIDER_ID2]: v5.optional(
1897
- v5.looseObject({ options: v5.optional(v5.looseObject({ apiKey: v5.optional(v5.string()) })) })
1990
+ var ConfigSchema2 = v6.looseObject({
1991
+ $schema: v6.optional(v6.unknown()),
1992
+ provider: v6.optional(
1993
+ v6.looseObject({
1994
+ [PROVIDER_ID3]: v6.optional(
1995
+ v6.looseObject({ options: v6.optional(v6.looseObject({ apiKey: v6.optional(v6.string()) })) })
1898
1996
  )
1899
1997
  })
1900
1998
  )
1901
1999
  });
1902
2000
 
2001
+ // src/harnesses/pi/pi.ts
2002
+ import path8 from "path";
2003
+ import * as v7 from "valibot";
2004
+ var BINARY6 = "pi";
2005
+ var PROVIDER_ID4 = "nexos";
2006
+ var PROVIDER_NAME4 = "nexos.ai";
2007
+ var API_TYPE = "openai-completions";
2008
+ var pi = {
2009
+ kind: "configurable",
2010
+ id: "pi",
2011
+ label: "Pi",
2012
+ binary: BINARY6,
2013
+ protocols: ["openai-chat"],
2014
+ projectSupport: {
2015
+ kind: "unsupported",
2016
+ reason: "it reads models.json only from its agent directory (~/.pi/agent), never from the project"
2017
+ },
2018
+ detect: (ctx) => detectBinary2(ctx),
2019
+ configPath: (ctx) => resolveModelsPath(ctx),
2020
+ plan: (desired, ctx) => [planModels(desired, ctx), planSettings2(desired, ctx)],
2021
+ readKey: (ctx) => readProviderKey3(ctx),
2022
+ readSecrets: (ctx) => readProviderSecrets3(ctx),
2023
+ postSetupNotes: [
2024
+ "Pi starts on the model setup chose. Change it with /model, and press Ctrl+S to make that the default."
2025
+ ]
2026
+ };
2027
+ function detectBinary2(ctx) {
2028
+ const isFound = ctx.hasBinary(BINARY6);
2029
+ return { isFound, detail: isFound ? `${BINARY6} found on PATH` : `${BINARY6} not found on PATH` };
2030
+ }
2031
+ var DEFAULT_AGENT_DIRECTORY = path8.join(".pi", "agent");
2032
+ var MODELS_FILE = "models.json";
2033
+ var SETTINGS_FILE = "settings.json";
2034
+ var HOME_PREFIX = "~";
2035
+ function resolveAgentDirectory(ctx) {
2036
+ const configured = readNonBlank(ctx.env.PI_CODING_AGENT_DIR);
2037
+ return configured === void 0 ? path8.join(ctx.home, DEFAULT_AGENT_DIRECTORY) : resolveConfiguredPath(configured, ctx);
2038
+ }
2039
+ function resolveConfiguredPath(configured, ctx) {
2040
+ if (configured === HOME_PREFIX) return ctx.home;
2041
+ if (configured.startsWith(`${HOME_PREFIX}${path8.sep}`)) {
2042
+ return path8.join(ctx.home, configured.slice(HOME_PREFIX.length + 1));
2043
+ }
2044
+ return path8.resolve(ctx.cwd, configured);
2045
+ }
2046
+ function resolveModelsPath(ctx) {
2047
+ return path8.join(resolveAgentDirectory(ctx), MODELS_FILE);
2048
+ }
2049
+ function resolveSettingsPath2(ctx) {
2050
+ return path8.join(resolveAgentDirectory(ctx), SETTINGS_FILE);
2051
+ }
2052
+ function planModels(desired, ctx) {
2053
+ return {
2054
+ kind: "file",
2055
+ path: resolveModelsPath(ctx),
2056
+ hasSecret: true,
2057
+ edits: () => ({ format: "jsonc", updates: listModelUpdates(desired) })
2058
+ };
2059
+ }
2060
+ function planSettings2(desired, ctx) {
2061
+ return {
2062
+ kind: "file",
2063
+ path: resolveSettingsPath2(ctx),
2064
+ hasSecret: false,
2065
+ edits: () => ({
2066
+ format: "jsonc",
2067
+ updates: [
2068
+ [["defaultProvider"], PROVIDER_ID4],
2069
+ [["defaultModel"], desired.model]
2070
+ ]
2071
+ })
2072
+ };
2073
+ }
2074
+ function listModelUpdates(desired) {
2075
+ const contextWindow = findContextWindow(desired.model);
2076
+ const modelEntry = {
2077
+ id: desired.model,
2078
+ name: desired.model,
2079
+ ...contextWindow === void 0 ? {} : { contextWindow }
2080
+ };
2081
+ return [
2082
+ [["providers", PROVIDER_ID4, "baseUrl"], desired.openaiBaseUrl],
2083
+ [["providers", PROVIDER_ID4, "api"], API_TYPE],
2084
+ [["providers", PROVIDER_ID4, "apiKey"], desired.apiKey],
2085
+ [["providers", PROVIDER_ID4, "name"], PROVIDER_NAME4],
2086
+ [["providers", PROVIDER_ID4, "models"], [modelEntry]]
2087
+ ];
2088
+ }
2089
+ var ModelsSchema = v7.looseObject({
2090
+ providers: v7.optional(
2091
+ v7.looseObject({ [PROVIDER_ID4]: v7.optional(v7.looseObject({ apiKey: v7.optional(v7.string()) })) })
2092
+ )
2093
+ });
2094
+ function readProviderKey3(ctx) {
2095
+ const models = readJsoncFile(resolveModelsPath(ctx), ModelsSchema);
2096
+ const apiKey = models.kind === "valid" ? models.value.providers?.[PROVIDER_ID4]?.apiKey : void 0;
2097
+ return readLiteralKey(apiKey);
2098
+ }
2099
+ var ProvidersSchema3 = v7.looseObject({ providers: v7.optional(v7.record(v7.string(), v7.unknown())) });
2100
+ var ProviderKeySchema3 = v7.looseObject({ apiKey: v7.string() });
2101
+ function readProviderSecrets3(ctx) {
2102
+ const models = readJsoncFile(resolveModelsPath(ctx), ProvidersSchema3);
2103
+ if (models.kind !== "valid") return [];
2104
+ return Object.values(models.value.providers ?? {}).flatMap((provider) => {
2105
+ const parsed = v7.safeParse(ProviderKeySchema3, provider);
2106
+ return parsed.success ? readLiteralKey(parsed.output.apiKey) ?? [] : [];
2107
+ });
2108
+ }
2109
+
2110
+ // src/harnesses/qwen-code/qwen.ts
2111
+ import path9 from "path";
2112
+ import { match as match10 } from "ts-pattern";
2113
+ import * as v8 from "valibot";
2114
+ var BINARY7 = "qwen";
2115
+ var KEY_VARIABLE = "NEXOS_API_KEY";
2116
+ var AUTH_TYPE = "openai";
2117
+ var qwen = {
2118
+ kind: "configurable",
2119
+ id: "qwen-code",
2120
+ label: "Qwen Code",
2121
+ binary: BINARY7,
2122
+ protocols: ["openai-chat"],
2123
+ projectSupport: { kind: "supported" },
2124
+ detect: (ctx) => detectQwen(ctx),
2125
+ configPath: (ctx) => resolveSettingsPath3(ctx),
2126
+ plan: (desired, ctx) => [planSettings3(desired, ctx)],
2127
+ readKey: (ctx) => readProviderKey4(ctx),
2128
+ readSecrets: (ctx) => readProviderSecrets4(ctx),
2129
+ postSetupNotes: [
2130
+ "Qwen Code keeps the key in its settings file, so no /auth step is needed.",
2131
+ "Qwen Code ranks its settings, highest first: the system settings file, the project's .qwen/settings.json, then the user's ~/.qwen/settings.json. A higher one that sets the same values overrides this file."
2132
+ ]
2133
+ };
2134
+ function detectQwen(ctx) {
2135
+ const detected = detectConfigFile([resolveSettingsPath3(ctx)], ctx.home);
2136
+ if (detected.isFound || ctx.scope === "project" || !ctx.hasBinary(BINARY7)) return detected;
2137
+ return { isFound: true, detail: `${BINARY7} found on PATH` };
2138
+ }
2139
+ var USER_DIRECTORY2 = ".qwen";
2140
+ var PROJECT_DIRECTORY = ".qwen";
2141
+ var SETTINGS_FILE2 = "settings.json";
2142
+ function resolveSettingsPath3(ctx) {
2143
+ return match10(ctx.scope).with("user", () => path9.join(ctx.home, USER_DIRECTORY2, SETTINGS_FILE2)).with("project", () => path9.join(ctx.cwd, PROJECT_DIRECTORY, SETTINGS_FILE2)).exhaustive();
2144
+ }
2145
+ function planSettings3(desired, ctx) {
2146
+ return {
2147
+ kind: "file",
2148
+ path: resolveSettingsPath3(ctx),
2149
+ hasSecret: true,
2150
+ edits: (current) => ({ format: "jsonc", updates: listUpdates3(desired, current) })
2151
+ };
2152
+ }
2153
+ function listUpdates3(desired, current) {
2154
+ const provider = {
2155
+ id: desired.model,
2156
+ name: desired.model,
2157
+ baseUrl: desired.openaiBaseUrl,
2158
+ envKey: KEY_VARIABLE
2159
+ };
2160
+ return [
2161
+ [
2162
+ ["modelProviders", "openai"],
2163
+ [...listOtherOpenAiProviders(current), provider]
2164
+ ],
2165
+ [["env", KEY_VARIABLE], desired.apiKey],
2166
+ [["security", "auth", "selectedType"], AUTH_TYPE],
2167
+ [["model", "name"], desired.model]
2168
+ ];
2169
+ }
2170
+ var ModelProvidersSchema = v8.looseObject({
2171
+ modelProviders: v8.optional(v8.looseObject({ openai: v8.optional(v8.unknown()) }))
2172
+ });
2173
+ var NexosEntrySchema = v8.looseObject({ envKey: v8.literal(KEY_VARIABLE) });
2174
+ function listOtherOpenAiProviders(current) {
2175
+ const parsed = parseJsoncConfig(current, ModelProvidersSchema);
2176
+ if (parsed.kind !== "valid") return [];
2177
+ const openai = parsed.value.modelProviders?.openai;
2178
+ if (openai === void 0) return [];
2179
+ if (!Array.isArray(openai)) throw new MergeConflictError();
2180
+ return openai.filter((entry) => !v8.is(NexosEntrySchema, entry));
2181
+ }
2182
+ var SettingsSchema2 = v8.looseObject({
2183
+ env: v8.optional(v8.looseObject({ [KEY_VARIABLE]: v8.optional(v8.string()) }))
2184
+ });
2185
+ function readProviderKey4(ctx) {
2186
+ const settings2 = readJsoncFile(resolveSettingsPath3(ctx), SettingsSchema2);
2187
+ return readLiteralKey(settings2.kind === "valid" ? settings2.value.env?.[KEY_VARIABLE] : void 0);
2188
+ }
2189
+ var SecretsSchema = v8.looseObject({
2190
+ env: v8.optional(v8.record(v8.string(), v8.unknown())),
2191
+ security: v8.optional(v8.looseObject({ auth: v8.optional(v8.looseObject({ apiKey: v8.optional(v8.string()) })) })),
2192
+ modelProviders: v8.optional(v8.record(v8.string(), v8.unknown()))
2193
+ });
2194
+ var ProviderEntrySchema = v8.looseObject({ envKey: v8.string() });
2195
+ function readProviderSecrets4(ctx) {
2196
+ const settings2 = readJsoncFile(resolveSettingsPath3(ctx), SecretsSchema);
2197
+ if (settings2.kind !== "valid") return [];
2198
+ const { env = {}, security, modelProviders = {} } = settings2.value;
2199
+ const fromEnv = listProviderEnvKeys(modelProviders).flatMap((name) => readEnvSecret(env, name));
2200
+ const fromAuth = readOptionalKey(security?.auth?.apiKey);
2201
+ return [...fromEnv, ...fromAuth];
2202
+ }
2203
+ function listProviderEnvKeys(modelProviders) {
2204
+ return Object.values(modelProviders).flatMap((providers) => {
2205
+ if (!Array.isArray(providers)) return [];
2206
+ return providers.flatMap((entry) => {
2207
+ const parsed = v8.safeParse(ProviderEntrySchema, entry);
2208
+ return parsed.success ? [parsed.output.envKey] : [];
2209
+ });
2210
+ });
2211
+ }
2212
+ function readEnvSecret(env, name) {
2213
+ const value = env[name];
2214
+ return typeof value === "string" ? readOptionalKey(value) : [];
2215
+ }
2216
+ function readOptionalKey(value) {
2217
+ const key = readLiteralKey(value);
2218
+ return key === void 0 ? [] : [key];
2219
+ }
2220
+
1903
2221
  // src/harnesses/registry.ts
1904
- var harnesses = [claudeCode, codex, opencode, copilotCli, geminiCli];
2222
+ var harnesses = [claudeCode, codex, opencode, copilotCli, pi, crush, qwen, geminiCli];
1905
2223
  function findHarness(id) {
1906
2224
  return harnesses.find((harness) => harness.id === id);
1907
2225
  }
@@ -1916,10 +2234,10 @@ function splitLines(text2) {
1916
2234
  }
1917
2235
 
1918
2236
  // src/product/storage/env-file/syntax/env-syntaxes.ts
1919
- import { match as match9 } from "ts-pattern";
2237
+ import { match as match11 } from "ts-pattern";
1920
2238
 
1921
2239
  // src/product/storage/env-file/syntax/posix-env-syntax.ts
1922
- import path7 from "path";
2240
+ import path10 from "path";
1923
2241
 
1924
2242
  // src/product/storage/env-file/syntax/env-syntax.ts
1925
2243
  var SHELLS = ["posix", "powershell"];
@@ -1943,7 +2261,7 @@ function createAssignmentMatcher(pattern) {
1943
2261
  // src/product/storage/env-file/syntax/posix-env-syntax.ts
1944
2262
  var POSIX_FILE_NAME = "env";
1945
2263
  var POSIX_ASSIGNMENT = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)=(.*)$/;
1946
- var POSIX_LOAD_COMMAND = `source ${path7.posix.join("~", ENV_DIRECTORY_NAME, POSIX_FILE_NAME)}`;
2264
+ var POSIX_LOAD_COMMAND = `source ${path10.posix.join("~", ENV_DIRECTORY_NAME, POSIX_FILE_NAME)}`;
1947
2265
  var POSIX_ENV_SYNTAX = {
1948
2266
  fileName: POSIX_FILE_NAME,
1949
2267
  defaultLineEnding: "\n",
@@ -1974,10 +2292,10 @@ function decodePosixSegment([, singleQuoted, doubleQuoted, escaped, bare]) {
1974
2292
  }
1975
2293
 
1976
2294
  // src/product/storage/env-file/syntax/powershell-env-syntax.ts
1977
- import path8 from "path";
2295
+ import path11 from "path";
1978
2296
  var POWERSHELL_FILE_NAME = "env.ps1";
1979
2297
  var POWERSHELL_ASSIGNMENT = /^\s*\$\{?[Ee][Nn][Vv]:([A-Za-z_][A-Za-z0-9_]*)\}?\s*=\s*(.*)$/;
1980
- var POWERSHELL_LOAD_COMMAND = `. "${path8.win32.join("$HOME", ENV_DIRECTORY_NAME, POWERSHELL_FILE_NAME)}"`;
2298
+ var POWERSHELL_LOAD_COMMAND = `. "${path11.win32.join("$HOME", ENV_DIRECTORY_NAME, POWERSHELL_FILE_NAME)}"`;
1981
2299
  var POWERSHELL_ENV_SYNTAX = {
1982
2300
  fileName: POWERSHELL_FILE_NAME,
1983
2301
  defaultLineEnding: "\r\n",
@@ -2009,7 +2327,7 @@ var ENV_SYNTAXES = {
2009
2327
  powershell: POWERSHELL_ENV_SYNTAX
2010
2328
  };
2011
2329
  function resolveShell(platform) {
2012
- return match9(platform).returnType().with("win32", () => "powershell").otherwise(() => "posix");
2330
+ return match11(platform).returnType().with("win32", () => "powershell").otherwise(() => "posix");
2013
2331
  }
2014
2332
 
2015
2333
  // src/product/storage/env-file/parse-env-file.ts
@@ -2028,9 +2346,9 @@ function parseEnvFile(text2, shell) {
2028
2346
  }
2029
2347
 
2030
2348
  // src/flows/key/find-existing-keys.ts
2031
- var OfferableKeySchema = v6.pipe(
2032
- v6.string(),
2033
- v6.check((key) => !hasLineBreak(key))
2349
+ var OfferableKeySchema = v9.pipe(
2350
+ v9.string(),
2351
+ v9.check((key) => !hasLineBreak(key))
2034
2352
  );
2035
2353
  function findExistingKeys({ ctx, envPath, shell }) {
2036
2354
  const userContext = { ...ctx, scope: "user" };
@@ -2041,7 +2359,7 @@ function findExistingKeys({ ctx, envPath, shell }) {
2041
2359
  ...readFromEachHarness((harness) => harness.readSecrets?.(userContext) ?? []),
2042
2360
  ...readFromEachHarness((harness) => harness.readSecrets?.(projectContext) ?? [])
2043
2361
  ];
2044
- const offered = machineKeys.find((key) => v6.is(OfferableKeySchema, key));
2362
+ const offered = machineKeys.find((key) => v9.is(OfferableKeySchema, key));
2045
2363
  return { offered, all: [...machineKeys, ...otherSecrets] };
2046
2364
  }
2047
2365
  function readEnvFileKeys(envPath, shell) {
@@ -2076,7 +2394,7 @@ function readSafely(read) {
2076
2394
  }
2077
2395
 
2078
2396
  // src/flows/key/key-step.ts
2079
- import * as v7 from "valibot";
2397
+ import * as v10 from "valibot";
2080
2398
  async function resolveKey(input, deps) {
2081
2399
  const { keystore: keystore2, ui } = deps;
2082
2400
  if (input.shouldSaveKey) {
@@ -2101,11 +2419,11 @@ async function resolveKey(input, deps) {
2101
2419
  }
2102
2420
  return await promptForKey({ input, deps, availability, existing: existing.offered });
2103
2421
  }
2104
- var SavedKeySchema = v7.pipe(
2105
- v7.string(),
2106
- v7.trim(),
2107
- v7.nonEmpty(),
2108
- v7.check((key) => !hasLineBreak(key))
2422
+ var SavedKeySchema = v10.pipe(
2423
+ v10.string(),
2424
+ v10.trim(),
2425
+ v10.nonEmpty(),
2426
+ v10.check((key) => !hasLineBreak(key))
2109
2427
  );
2110
2428
  var FORGET_KEY_HINT = `npx ${COMMAND_NAME} ${FORGET_KEY_COMMAND}`;
2111
2429
  async function loadSaved({ keystore: keystore2, ui, masker: masker2 }) {
@@ -2121,7 +2439,7 @@ async function loadSaved({ keystore: keystore2, ui, masker: masker2 }) {
2121
2439
  return void 0;
2122
2440
  }
2123
2441
  if (saved === void 0) return void 0;
2124
- const parsed = v7.safeParse(SavedKeySchema, saved);
2442
+ const parsed = v10.safeParse(SavedKeySchema, saved);
2125
2443
  if (!parsed.success) {
2126
2444
  ui.dim(`The saved key is not usable. Run \`${FORGET_KEY_HINT}\` and save it again.`);
2127
2445
  return void 0;
@@ -2283,10 +2601,10 @@ function formatProbe(result) {
2283
2601
  }
2284
2602
 
2285
2603
  // src/flows/key/key-check.ts
2286
- import { match as match12 } from "ts-pattern";
2604
+ import { match as match14 } from "ts-pattern";
2287
2605
 
2288
2606
  // src/product/gateway/list-models.ts
2289
- import { P as P2, match as match10 } from "ts-pattern";
2607
+ import { P as P2, match as match12 } from "ts-pattern";
2290
2608
 
2291
2609
  // src/shared/text/format-count.ts
2292
2610
  function formatCount(count, noun) {
@@ -2294,20 +2612,20 @@ function formatCount(count, noun) {
2294
2612
  }
2295
2613
 
2296
2614
  // src/product/gateway/model-schema.ts
2297
- import * as v8 from "valibot";
2298
- var OptionalTextSchema2 = v8.fallback(v8.optional(v8.pipe(v8.string(), v8.nonEmpty())), void 0);
2299
- var OptionalCountSchema = v8.fallback(v8.optional(v8.pipe(v8.number(), v8.finite())), void 0);
2300
- var EndpointsSchema = v8.fallback(v8.pipe(v8.array(v8.unknown()), v8.transform(keepStrings)), []);
2301
- var ModelSchema2 = v8.pipe(
2302
- v8.object({
2303
- id: v8.pipe(v8.string(), v8.nonEmpty()),
2615
+ import * as v11 from "valibot";
2616
+ var OptionalTextSchema2 = v11.fallback(v11.optional(v11.pipe(v11.string(), v11.nonEmpty())), void 0);
2617
+ var OptionalCountSchema = v11.fallback(v11.optional(v11.pipe(v11.number(), v11.finite())), void 0);
2618
+ var EndpointsSchema = v11.fallback(v11.pipe(v11.array(v11.unknown()), v11.transform(keepStrings)), []);
2619
+ var ModelSchema2 = v11.pipe(
2620
+ v11.object({
2621
+ id: v11.pipe(v11.string(), v11.nonEmpty()),
2304
2622
  developer: OptionalTextSchema2,
2305
2623
  owned_by: OptionalTextSchema2,
2306
2624
  context_length: OptionalCountSchema,
2307
2625
  region: OptionalTextSchema2,
2308
2626
  endpoints: EndpointsSchema
2309
2627
  }),
2310
- v8.transform((entry) => ({
2628
+ v11.transform((entry) => ({
2311
2629
  id: entry.id,
2312
2630
  developer: entry.developer ?? entry.owned_by ?? null,
2313
2631
  contextLength: entry.context_length ?? null,
@@ -2315,23 +2633,23 @@ var ModelSchema2 = v8.pipe(
2315
2633
  endpoints: entry.endpoints
2316
2634
  }))
2317
2635
  );
2318
- var ModelListSchema = v8.pipe(
2319
- v8.object({ data: v8.array(v8.unknown()) }),
2320
- v8.transform(({ data }) => keepUsableModels(data))
2636
+ var ModelListSchema = v11.pipe(
2637
+ v11.object({ data: v11.array(v11.unknown()) }),
2638
+ v11.transform(({ data }) => keepUsableModels(data))
2321
2639
  );
2322
- var OpenAiErrorSchema = v8.object({
2323
- error: v8.object({
2324
- message: v8.string(),
2325
- type: v8.optional(v8.string()),
2326
- code: v8.optional(v8.nullable(v8.string()))
2640
+ var OpenAiErrorSchema = v11.object({
2641
+ error: v11.object({
2642
+ message: v11.string(),
2643
+ type: v11.optional(v11.string()),
2644
+ code: v11.optional(v11.nullable(v11.string()))
2327
2645
  })
2328
2646
  });
2329
- var AnthropicErrorSchema = v8.object({
2330
- type: v8.literal("error"),
2331
- error: v8.object({ type: v8.string(), message: v8.string() })
2647
+ var AnthropicErrorSchema = v11.object({
2648
+ type: v11.literal("error"),
2649
+ error: v11.object({ type: v11.string(), message: v11.string() })
2332
2650
  });
2333
2651
  function parseJsonBody(schema, text2) {
2334
- const result = v8.safeParse(schema, parseJson(text2));
2652
+ const result = v11.safeParse(schema, parseJson(text2));
2335
2653
  return result.success ? result.output : void 0;
2336
2654
  }
2337
2655
  function keepStrings(values) {
@@ -2340,7 +2658,7 @@ function keepStrings(values) {
2340
2658
  function keepUsableModels(entries) {
2341
2659
  const modelsById = /* @__PURE__ */ new Map();
2342
2660
  for (const entry of entries) {
2343
- const model = v8.safeParse(ModelSchema2, entry);
2661
+ const model = v11.safeParse(ModelSchema2, entry);
2344
2662
  if (model.success && !modelsById.has(model.output.id)) modelsById.set(model.output.id, model.output);
2345
2663
  }
2346
2664
  return [...modelsById.values()];
@@ -2402,13 +2720,13 @@ function readListing({ status, text: text2 }, masker2) {
2402
2720
  }
2403
2721
  var INVALID_KEY_ERROR_CODE = "invalid_api_key";
2404
2722
  function classifyRejection(status, errorCode) {
2405
- return match10({ status, errorCode }).returnType().with({ status: P2.union(...KEY_REJECTED_STATUSES) }, { errorCode: INVALID_KEY_ERROR_CODE }, () => "key-rejected").otherwise(() => "bad-status");
2723
+ return match12({ status, errorCode }).returnType().with({ status: P2.union(...KEY_REJECTED_STATUSES) }, { errorCode: INVALID_KEY_ERROR_CODE }, () => "key-rejected").otherwise(() => "bad-status");
2406
2724
  }
2407
2725
 
2408
2726
  // src/product/gateway/probe-result.ts
2409
- import { match as match11 } from "ts-pattern";
2727
+ import { match as match13 } from "ts-pattern";
2410
2728
  function isKeyAccepted(probe) {
2411
- return match11(probe.kind).with("passed", "bad-response", () => true).with("key-rejected", "model-rejected", "unreachable", "timed-out", "bad-status", () => false).exhaustive();
2729
+ return match13(probe.kind).with("passed", "bad-response", () => true).with("key-rejected", "model-rejected", "unreachable", "timed-out", "bad-status", () => false).exhaustive();
2412
2730
  }
2413
2731
 
2414
2732
  // src/flows/key/probe-summaries.ts
@@ -2476,7 +2794,7 @@ async function attemptKeyCheck(params) {
2476
2794
  }
2477
2795
  ui.print(formatProbes([probe]));
2478
2796
  const choice = await ui.chooseOnFetchFailure();
2479
- return await match12(choice).with("retry", async () => {
2797
+ return await match14(choice).with("retry", async () => {
2480
2798
  spinner.start("Checking again\u2026");
2481
2799
  return await attemptKeyCheck(params);
2482
2800
  }).with("manual", async () => ({
@@ -2496,7 +2814,7 @@ async function attemptKeyCheck(params) {
2496
2814
  })).exhaustive();
2497
2815
  }
2498
2816
  function summarizeKeyCheck(probe, modelCount) {
2499
- return match12(probe.kind).with("passed", () => `Key verified \u2014 ${formatCount(modelCount, "model")} available.`).with("bad-response", () => "Key verified, but the model list could not be read.").otherwise((kind) => PROBE_SUMMARIES[kind]);
2817
+ return match14(probe.kind).with("passed", () => `Key verified \u2014 ${formatCount(modelCount, "model")} available.`).with("bad-response", () => "Key verified, but the model list could not be read.").otherwise((kind) => PROBE_SUMMARIES[kind]);
2500
2818
  }
2501
2819
  var ASSUMED_YES_CONTINUATION = "Continuing despite the failed check (--yes).";
2502
2820
  function findContinuation({ shouldAssumeYes, requestedModel, masker: masker2 }) {
@@ -2601,16 +2919,16 @@ function findModelWarnings({
2601
2919
  }
2602
2920
 
2603
2921
  // src/flows/model/model-probe.ts
2604
- import { match as match15 } from "ts-pattern";
2922
+ import { match as match17 } from "ts-pattern";
2605
2923
 
2606
2924
  // src/product/gateway/models/router-availability.ts
2607
- import { match as match13 } from "ts-pattern";
2925
+ import { match as match15 } from "ts-pattern";
2608
2926
  function classifyRouterAvailability(probe) {
2609
- return match13(probe.kind).returnType().with("passed", () => "available").with("model-rejected", () => "unavailable").otherwise(() => "unknown");
2927
+ return match15(probe.kind).returnType().with("passed", () => "available").with("model-rejected", () => "unavailable").otherwise(() => "unknown");
2610
2928
  }
2611
2929
 
2612
2930
  // src/product/gateway/probe-anthropic.ts
2613
- import { P as P3, match as match14 } from "ts-pattern";
2931
+ import { P as P3, match as match16 } from "ts-pattern";
2614
2932
  var MESSAGES_PATH = "/v1/messages";
2615
2933
  var ANTHROPIC_VERSION = "2023-06-01";
2616
2934
  var PING_MAX_TOKENS = 1;
@@ -2666,7 +2984,7 @@ var MODEL_NOT_FOUND_STATUS = 404;
2666
2984
  var KEY_REJECTED_ERROR_TYPES = ["authentication_error", "permission_error"];
2667
2985
  var MODEL_NOT_FOUND_ERROR_TYPE = "not_found_error";
2668
2986
  function classifyRejection2(status, errorType) {
2669
- return match14({ status, errorType }).returnType().with(
2987
+ return match16({ status, errorType }).returnType().with(
2670
2988
  { status: P3.union(...KEY_REJECTED_STATUSES) },
2671
2989
  { errorType: P3.union(...KEY_REJECTED_ERROR_TYPES) },
2672
2990
  () => "key-rejected"
@@ -2710,21 +3028,21 @@ async function shouldContinueAfterProbe({
2710
3028
  return await ui.confirm(`Continue anyway? ${consequence}`, false);
2711
3029
  }
2712
3030
  function summarizeModelProbe(probe) {
2713
- return match15(probe.kind).with("passed", () => "Model verified.").otherwise((kind) => PROBE_SUMMARIES[kind]);
3031
+ return match17(probe.kind).with("passed", () => "Model verified.").otherwise((kind) => PROBE_SUMMARIES[kind]);
2714
3032
  }
2715
3033
 
2716
3034
  // src/product/storage/preferences/preferences.ts
2717
3035
  import { randomUUID } from "crypto";
2718
3036
  import fs4 from "fs";
2719
- import path9 from "path";
2720
- import * as v9 from "valibot";
3037
+ import path12 from "path";
3038
+ import * as v12 from "valibot";
2721
3039
  var PREFERENCES_FILE = "preferences.json";
2722
3040
  function resolvePreferencesPath(home) {
2723
- return path9.join(home, ENV_DIRECTORY_NAME, PREFERENCES_FILE);
3041
+ return path12.join(home, ENV_DIRECTORY_NAME, PREFERENCES_FILE);
2724
3042
  }
2725
- var ModelIdSchema = v9.pipe(v9.string(), v9.trim(), v9.nonEmpty(), v9.transform(normalizeModel));
2726
- var PreferencesSchema = v9.looseObject({
2727
- defaultModels: v9.optional(v9.record(v9.string(), ModelIdSchema), {})
3043
+ var ModelIdSchema = v12.pipe(v12.string(), v12.trim(), v12.nonEmpty(), v12.transform(normalizeModel));
3044
+ var PreferencesSchema = v12.looseObject({
3045
+ defaultModels: v12.optional(v12.record(v12.string(), ModelIdSchema), {})
2728
3046
  });
2729
3047
  var PreferencesError = class extends Error {
2730
3048
  name = "PreferencesError";
@@ -2743,7 +3061,7 @@ function readPreferences(home) {
2743
3061
  const filePath = resolvePreferencesPath(home);
2744
3062
  const text2 = readFileIfExists(filePath);
2745
3063
  if (text2 === null) return { defaultModels: {} };
2746
- const parsed = v9.safeParse(PreferencesSchema, parseJson2(text2));
3064
+ const parsed = v12.safeParse(PreferencesSchema, parseJson2(text2));
2747
3065
  if (!parsed.success) {
2748
3066
  throw new PreferencesError(
2749
3067
  `${displayPath(filePath, home)} is not a preferences file nexos-connect can read. Fix it or delete it.`
@@ -2760,7 +3078,7 @@ function parseJson2(text2) {
2760
3078
  }
2761
3079
  }
2762
3080
  function writeAtomically(filePath, content) {
2763
- fs4.mkdirSync(path9.dirname(filePath), { recursive: true });
3081
+ fs4.mkdirSync(path12.dirname(filePath), { recursive: true });
2764
3082
  const temporaryPath = `${filePath}.${randomUUID()}.tmp`;
2765
3083
  try {
2766
3084
  fs4.writeFileSync(temporaryPath, content);
@@ -2772,19 +3090,19 @@ function writeAtomically(filePath, content) {
2772
3090
  }
2773
3091
 
2774
3092
  // src/shared/file-system/describe-file-error.ts
2775
- import * as v10 from "valibot";
3093
+ import * as v13 from "valibot";
2776
3094
  function describeFileError({ error, action, home }) {
2777
3095
  const failure = readFileFailure(error);
2778
3096
  if (failure === void 0) return describeError(error);
2779
3097
  return `${displayPath(failure.path, home)}: cannot ${action} it (${failure.reason}). ${failure.nextStep}, then run again.`;
2780
3098
  }
2781
- var FileSystemErrorSchema = v10.object({ code: v10.string(), path: v10.string() });
3099
+ var FileSystemErrorSchema = v13.object({ code: v13.string(), path: v13.string() });
2782
3100
  var PERMISSION_CODES = ["EACCES", "EPERM", "EROFS"];
2783
3101
  function readFileFailure(error) {
2784
3102
  if (error instanceof Utf16FileError) {
2785
3103
  return { path: error.filePath, reason: "saved as UTF-16", nextStep: "Re-save it as UTF-8" };
2786
3104
  }
2787
- if (!v10.is(FileSystemErrorSchema, error)) return void 0;
3105
+ if (!v13.is(FileSystemErrorSchema, error)) return void 0;
2788
3106
  const nextStep = PERMISSION_CODES.includes(error.code) ? "Check its permissions" : "Check that path";
2789
3107
  return { path: error.path, reason: error.code, nextStep };
2790
3108
  }
@@ -2978,9 +3296,9 @@ async function probeRouterIfOffered({
2978
3296
  }
2979
3297
 
2980
3298
  // src/product/storage/env-file/env-file-path.ts
2981
- import path10 from "path";
3299
+ import path13 from "path";
2982
3300
  function resolveEnvFilePath(home, shell) {
2983
- return path10.join(home, ENV_DIRECTORY_NAME, ENV_SYNTAXES[shell].fileName);
3301
+ return path13.join(home, ENV_DIRECTORY_NAME, ENV_SYNTAXES[shell].fileName);
2984
3302
  }
2985
3303
 
2986
3304
  // src/commands/launch/launch.ts
@@ -3048,7 +3366,7 @@ function aKeyStepInput({ ctx, options, environmentKey, masker: masker2 }) {
3048
3366
  }
3049
3367
 
3050
3368
  // src/commands/restore/run-restore.ts
3051
- import path18 from "path";
3369
+ import path21 from "path";
3052
3370
 
3053
3371
  // src/cli/resolve-named-harnesses.ts
3054
3372
  function resolveNamedHarnesses(ids) {
@@ -3062,61 +3380,61 @@ function resolveNamedHarnesses(ids) {
3062
3380
  }
3063
3381
 
3064
3382
  // src/product/storage/backups/resolve-backup-root.ts
3065
- import path11 from "path";
3066
- import { match as match16 } from "ts-pattern";
3383
+ import path14 from "path";
3384
+ import { match as match18 } from "ts-pattern";
3067
3385
  var PROJECT_BACKUP_DIRECTORY = ".nexos-backup";
3068
- var USER_BACKUP_DIRECTORY = path11.join(".nexos", "backups");
3386
+ var USER_BACKUP_DIRECTORY = path14.join(".nexos", "backups");
3069
3387
  function formatBackupTimestamp(now) {
3070
3388
  return now.toISOString().replace(/[:.]/g, "-");
3071
3389
  }
3072
3390
  function resolveBackupRoot({ scope, home, cwd, stamp, backupDirectory }) {
3073
3391
  if (backupDirectory !== void 0) {
3074
- return { path: path11.join(path11.resolve(cwd, backupDirectory), stamp), gitIgnoredDirectory: null };
3392
+ return { path: path14.join(path14.resolve(cwd, backupDirectory), stamp), gitIgnoredDirectory: null };
3075
3393
  }
3076
- return match16(scope).returnType().with("user", () => ({ path: path11.join(home, USER_BACKUP_DIRECTORY, stamp), gitIgnoredDirectory: null })).with("project", () => {
3077
- const directory = path11.join(cwd, PROJECT_BACKUP_DIRECTORY);
3078
- return { path: path11.join(directory, stamp), gitIgnoredDirectory: directory };
3394
+ return match18(scope).returnType().with("user", () => ({ path: path14.join(home, USER_BACKUP_DIRECTORY, stamp), gitIgnoredDirectory: null })).with("project", () => {
3395
+ const directory = path14.join(cwd, PROJECT_BACKUP_DIRECTORY);
3396
+ return { path: path14.join(directory, stamp), gitIgnoredDirectory: directory };
3079
3397
  }).exhaustive();
3080
3398
  }
3081
3399
 
3082
3400
  // src/product/storage/restore-record/restore-record.ts
3083
3401
  import fs6 from "fs";
3084
- import path13 from "path";
3085
- import * as v11 from "valibot";
3402
+ import path16 from "path";
3403
+ import * as v14 from "valibot";
3086
3404
 
3087
3405
  // src/shared/file-formats/config-format.ts
3088
- import { match as match17 } from "ts-pattern";
3406
+ import { match as match19 } from "ts-pattern";
3089
3407
  var CONFIG_FORMATS = ["jsonc", "toml"];
3090
3408
  function renderConfig(current, edits) {
3091
- return match17(edits).with({ format: "jsonc" }, ({ updates }) => renderJsonc(current, updates)).with({ format: "toml" }, ({ updates }) => renderToml(current, updates)).exhaustive();
3409
+ return match19(edits).with({ format: "jsonc" }, ({ updates }) => renderJsonc(current, updates)).with({ format: "toml" }, ({ updates }) => renderToml(current, updates)).exhaustive();
3092
3410
  }
3093
3411
  function readOriginals(current, edits) {
3094
3412
  const keyPaths = edits.updates.map(([keyPath]) => keyPath);
3095
- return match17(edits.format).with("jsonc", () => readJsoncOriginals(current, keyPaths)).with("toml", () => readTomlOriginals(current, keyPaths)).exhaustive();
3413
+ return match19(edits.format).with("jsonc", () => readJsoncOriginals(current, keyPaths)).with("toml", () => readTomlOriginals(current, keyPaths)).exhaustive();
3096
3414
  }
3097
3415
  function restoreConfig({ format, current, originals }) {
3098
- return match17(format).with("jsonc", () => restoreJsonc(current, originals)).with("toml", () => restoreToml(current, originals)).exhaustive();
3416
+ return match19(format).with("jsonc", () => restoreJsonc(current, originals)).with("toml", () => restoreToml(current, originals)).exhaustive();
3099
3417
  }
3100
3418
  function isEmptyConfig(format, text2) {
3101
- const document = match17(format).with("jsonc", () => parseJsonc(text2)).with("toml", () => parseToml(text2)).exhaustive();
3419
+ const document = match19(format).with("jsonc", () => parseJsonc(text2)).with("toml", () => parseToml(text2)).exhaustive();
3102
3420
  return Object.keys(document).length === 0;
3103
3421
  }
3104
3422
  function parseValueSource(format, source) {
3105
- return match17(format).with("jsonc", () => parseJsoncValue(source)).with("toml", () => parseTomlValue(source)).exhaustive();
3423
+ return match19(format).with("jsonc", () => parseJsoncValue(source)).with("toml", () => parseTomlValue(source)).exhaustive();
3106
3424
  }
3107
3425
 
3108
3426
  // src/shared/file-system/resolve-owner-only-mode.ts
3109
- import { match as match18 } from "ts-pattern";
3427
+ import { match as match20 } from "ts-pattern";
3110
3428
  var OWNER_ONLY_MODE = 384;
3111
3429
  function resolveOwnerOnlyMode(platform) {
3112
- return match18(platform).returnType().with("win32", () => null).otherwise(() => OWNER_ONLY_MODE);
3430
+ return match20(platform).returnType().with("win32", () => null).otherwise(() => OWNER_ONLY_MODE);
3113
3431
  }
3114
3432
 
3115
3433
  // src/shared/file-system/write-file-with-mode.ts
3116
3434
  import fs5 from "fs";
3117
- import path12 from "path";
3435
+ import path15 from "path";
3118
3436
  function writeFileWithMode({ filePath, content, mode }) {
3119
- fs5.mkdirSync(path12.dirname(filePath), { recursive: true });
3437
+ fs5.mkdirSync(path15.dirname(filePath), { recursive: true });
3120
3438
  if (mode === null) {
3121
3439
  fs5.writeFileSync(filePath, content);
3122
3440
  return;
@@ -3129,27 +3447,27 @@ function writeFileWithMode({ filePath, content, mode }) {
3129
3447
  // src/product/storage/restore-record/restore-record.ts
3130
3448
  var FILE_ORIGINS = ["absent", "blank", "content"];
3131
3449
  var RECORD_VERSION = 1;
3132
- var SegmentsSchema = v11.pipe(v11.tupleWithRest([v11.string()], v11.string()), v11.readonly());
3133
- var RecordedKeySchema = v11.object({
3450
+ var SegmentsSchema = v14.pipe(v14.tupleWithRest([v14.string()], v14.string()), v14.readonly());
3451
+ var RecordedKeySchema = v14.object({
3134
3452
  keyPath: SegmentsSchema,
3135
- original: v11.nullable(v11.string()),
3136
- createdParents: v11.pipe(v11.array(SegmentsSchema), v11.readonly()),
3137
- holdsSecret: v11.boolean()
3453
+ original: v14.nullable(v14.string()),
3454
+ createdParents: v14.pipe(v14.array(SegmentsSchema), v14.readonly()),
3455
+ holdsSecret: v14.boolean()
3138
3456
  });
3139
- var RecordedFileSchema = v11.object({
3457
+ var RecordedFileSchema = v14.object({
3140
3458
  path: SegmentsSchema,
3141
- format: v11.picklist(CONFIG_FORMATS),
3142
- origin: v11.picklist(FILE_ORIGINS),
3143
- keys: v11.pipe(v11.array(RecordedKeySchema), v11.readonly())
3459
+ format: v14.picklist(CONFIG_FORMATS),
3460
+ origin: v14.picklist(FILE_ORIGINS),
3461
+ keys: v14.pipe(v14.array(RecordedKeySchema), v14.readonly())
3144
3462
  });
3145
- var RecordedEnvVariableSchema = v11.object({ name: v11.string(), original: v11.nullable(v11.string()) });
3146
- var HarnessRecordSchema = v11.object({
3147
- files: v11.pipe(v11.array(RecordedFileSchema), v11.readonly()),
3148
- envVariables: v11.pipe(v11.array(RecordedEnvVariableSchema), v11.readonly())
3463
+ var RecordedEnvVariableSchema = v14.object({ name: v14.string(), original: v14.nullable(v14.string()) });
3464
+ var HarnessRecordSchema = v14.object({
3465
+ files: v14.pipe(v14.array(RecordedFileSchema), v14.readonly()),
3466
+ envVariables: v14.pipe(v14.array(RecordedEnvVariableSchema), v14.readonly())
3149
3467
  });
3150
- var RestoreRecordSchema = v11.object({
3151
- version: v11.literal(RECORD_VERSION),
3152
- harnesses: v11.record(v11.string(), HarnessRecordSchema)
3468
+ var RestoreRecordSchema = v14.object({
3469
+ version: v14.literal(RECORD_VERSION),
3470
+ harnesses: v14.record(v14.string(), HarnessRecordSchema)
3153
3471
  });
3154
3472
  function createEmptyRestoreRecord() {
3155
3473
  return { version: RECORD_VERSION, harnesses: {} };
@@ -3165,7 +3483,7 @@ function readRestoreRecord({ filePath, home }) {
3165
3483
  if (isNewerRecord(json)) {
3166
3484
  throw new RestoreRecordError(`${shownPath} was written by a newer nexos-connect. Update it, then run again.`);
3167
3485
  }
3168
- const parsed = v11.safeParse(RestoreRecordSchema, json);
3486
+ const parsed = v14.safeParse(RestoreRecordSchema, json);
3169
3487
  if (!parsed.success) {
3170
3488
  throw new RestoreRecordError(
3171
3489
  `${shownPath} is not a restore record nexos-connect can read. Fix it, or move it aside to start a new one, then run again.`
@@ -3181,47 +3499,47 @@ function parseJson3(text2) {
3181
3499
  throw error;
3182
3500
  }
3183
3501
  }
3184
- var VersionSchema = v11.object({ version: v11.number() });
3502
+ var VersionSchema = v14.object({ version: v14.number() });
3185
3503
  function isNewerRecord(json) {
3186
- const parsed = v11.safeParse(VersionSchema, json);
3504
+ const parsed = v14.safeParse(VersionSchema, json);
3187
3505
  return parsed.success && parsed.output.version > RECORD_VERSION;
3188
3506
  }
3189
3507
  var RECORD_DIRECTORY_MODE = 448;
3190
3508
  var JSON_INDENT3 = 2;
3191
- function writeRestoreRecord({ filePath, record: record5, platform }) {
3192
- if (Object.keys(record5.harnesses).length === 0) {
3509
+ function writeRestoreRecord({ filePath, record: record8, platform }) {
3510
+ if (Object.keys(record8.harnesses).length === 0) {
3193
3511
  fs6.rmSync(filePath, { force: true });
3194
3512
  return;
3195
3513
  }
3196
- fs6.mkdirSync(path13.dirname(filePath), { recursive: true, mode: RECORD_DIRECTORY_MODE });
3197
- const content = `${JSON.stringify(record5, null, JSON_INDENT3)}
3514
+ fs6.mkdirSync(path16.dirname(filePath), { recursive: true, mode: RECORD_DIRECTORY_MODE });
3515
+ const content = `${JSON.stringify(record8, null, JSON_INDENT3)}
3198
3516
  `;
3199
3517
  writeFileWithMode({ filePath, content, mode: resolveOwnerOnlyMode(platform) });
3200
3518
  }
3201
3519
 
3202
3520
  // src/product/storage/restore-record/restore-record-location.ts
3203
- import path14 from "path";
3204
- import { match as match19 } from "ts-pattern";
3521
+ import path17 from "path";
3522
+ import { match as match21 } from "ts-pattern";
3205
3523
  var RESTORE_RECORD_FILE = "restore-record.json";
3206
3524
  function resolveRestoreRecordLocation(ctx) {
3207
- return match19(ctx.scope).returnType().with("user", () => ({
3208
- filePath: path14.join(ctx.home, ENV_DIRECTORY_NAME, RESTORE_RECORD_FILE),
3525
+ return match21(ctx.scope).returnType().with("user", () => ({
3526
+ filePath: path17.join(ctx.home, ENV_DIRECTORY_NAME, RESTORE_RECORD_FILE),
3209
3527
  root: ctx.home,
3210
3528
  gitIgnoredDirectory: null
3211
3529
  })).with("project", () => {
3212
- const directory = path14.join(ctx.cwd, PROJECT_BACKUP_DIRECTORY);
3213
- return { filePath: path14.join(directory, RESTORE_RECORD_FILE), root: ctx.cwd, gitIgnoredDirectory: directory };
3530
+ const directory = path17.join(ctx.cwd, PROJECT_BACKUP_DIRECTORY);
3531
+ return { filePath: path17.join(directory, RESTORE_RECORD_FILE), root: ctx.cwd, gitIgnoredDirectory: directory };
3214
3532
  }).exhaustive();
3215
3533
  }
3216
3534
  function toRecordedPath(filePath, root) {
3217
- const [first, ...rest] = path14.relative(root, filePath).split(path14.sep);
3535
+ const [first, ...rest] = path17.relative(root, filePath).split(path17.sep);
3218
3536
  if (first === void 0 || !isInsideRoot(filePath, root)) {
3219
3537
  throw new Error(`${filePath} is outside ${root}, so it cannot be recorded for a restore.`);
3220
3538
  }
3221
3539
  return [first, ...rest];
3222
3540
  }
3223
3541
  function fromRecordedPath(recordedPath, root) {
3224
- const filePath = path14.join(root, ...recordedPath);
3542
+ const filePath = path17.join(root, ...recordedPath);
3225
3543
  if (!isInsideRoot(filePath, root)) {
3226
3544
  throw new Error(
3227
3545
  `${filePath} is outside ${root}, so it cannot be restored. Remove it from the restore record, then run again.`
@@ -3230,9 +3548,9 @@ function fromRecordedPath(recordedPath, root) {
3230
3548
  return filePath;
3231
3549
  }
3232
3550
  function isInsideRoot(filePath, root) {
3233
- const relativePath = path14.relative(root, filePath);
3234
- const [first] = relativePath.split(path14.sep);
3235
- return relativePath !== "" && first !== ".." && !path14.isAbsolute(relativePath);
3551
+ const relativePath = path17.relative(root, filePath);
3552
+ const [first] = relativePath.split(path17.sep);
3553
+ return relativePath !== "" && first !== ".." && !path17.isAbsolute(relativePath);
3236
3554
  }
3237
3555
 
3238
3556
  // src/ui/report/format-restore-preview.ts
@@ -3301,7 +3619,7 @@ function buildCommonLengthTable({ beforeLines, afterLines }) {
3301
3619
 
3302
3620
  // src/ui/diff/render-diff.ts
3303
3621
  import pc3 from "picocolors";
3304
- import { match as match20 } from "ts-pattern";
3622
+ import { match as match22 } from "ts-pattern";
3305
3623
  var DIFF_CONTEXT_LINES = 2;
3306
3624
  var ADDED_PREFIX = " + ";
3307
3625
  var REMOVED_PREFIX = " - ";
@@ -3333,7 +3651,7 @@ function findVisibleIndexes(lines) {
3333
3651
  return visibleIndexes;
3334
3652
  }
3335
3653
  function renderLine(line) {
3336
- return match20(line.kind).with("added", () => pc3.green(`${ADDED_PREFIX}${line.text}`)).with("removed", () => pc3.red(`${REMOVED_PREFIX}${line.text}`)).with("unchanged", () => pc3.dim(`${UNCHANGED_PREFIX}${line.text}`)).exhaustive();
3654
+ return match22(line.kind).with("added", () => pc3.green(`${ADDED_PREFIX}${line.text}`)).with("removed", () => pc3.red(`${REMOVED_PREFIX}${line.text}`)).with("unchanged", () => pc3.dim(`${UNCHANGED_PREFIX}${line.text}`)).exhaustive();
3337
3655
  }
3338
3656
 
3339
3657
  // src/ui/report/format-preview.ts
@@ -3380,7 +3698,7 @@ function formatRestoredFile({ file, home, masker: masker2 }) {
3380
3698
 
3381
3699
  // src/ui/report/format-restore-result.ts
3382
3700
  import pc6 from "picocolors";
3383
- import { P as P4, match as match21 } from "ts-pattern";
3701
+ import { P as P4, match as match23 } from "ts-pattern";
3384
3702
 
3385
3703
  // src/commands/restore/restore-result.ts
3386
3704
  var RESTORE_OUTCOME_KINDS = ["restored", "unchanged", "nothing-to-restore", "failed"];
@@ -3412,7 +3730,7 @@ var RESTORE_OUTCOME_STYLE = {
3412
3730
  var NO_RECORD = "nexos-connect has no record of setting it up at this scope.";
3413
3731
  function formatOutcome(outcome, { home, masker: masker2 }) {
3414
3732
  const style = RESTORE_OUTCOME_STYLE[outcome.kind];
3415
- const body = match21(outcome).with({ kind: P4.union("restored", "unchanged") }, (planned) => formatPlannedRestore(planned, home, masker2)).with({ kind: "nothing-to-restore" }, () => [`${INDENT}${pc6.dim(NO_RECORD)}`]).with({ kind: "failed" }, ({ error }) => [`${INDENT}${pc6.red(masker2.maskText(error))}`]).exhaustive();
3733
+ const body = match23(outcome).with({ kind: P4.union("restored", "unchanged") }, (planned) => formatPlannedRestore(planned, home, masker2)).with({ kind: "nothing-to-restore" }, () => [`${INDENT}${pc6.dim(NO_RECORD)}`]).with({ kind: "failed" }, ({ error }) => [`${INDENT}${pc6.red(masker2.maskText(error))}`]).exhaustive();
3416
3734
  return ["", ` ${style.colour(`${style.glyph} ${outcome.target.label} \u2014 ${style.label}`)}`, ...body];
3417
3735
  }
3418
3736
  function formatPlannedRestore(outcome, home, masker2) {
@@ -3453,11 +3771,11 @@ function formatSummary(outcomes) {
3453
3771
 
3454
3772
  // src/commands/restore/apply-restore.ts
3455
3773
  import fs9 from "fs";
3456
- import path16 from "path";
3774
+ import path19 from "path";
3457
3775
 
3458
3776
  // src/product/storage/backups/back-up-file.ts
3459
3777
  import fs8 from "fs";
3460
- import path15 from "path";
3778
+ import path18 from "path";
3461
3779
 
3462
3780
  // src/shared/file-system/read-file-mode.ts
3463
3781
  import fs7 from "fs";
@@ -3476,15 +3794,15 @@ function readFileMode(filePath, platform) {
3476
3794
  var BACKUP_DIRECTORY_MODE = 448;
3477
3795
  var OWNER_PERMISSION_BITS = 448;
3478
3796
  function backUpFile({ source, root, relativePath, platform }) {
3479
- const destination = path15.join(root, relativePath);
3480
- fs8.mkdirSync(path15.dirname(destination), { recursive: true, mode: BACKUP_DIRECTORY_MODE });
3797
+ const destination = path18.join(root, relativePath);
3798
+ fs8.mkdirSync(path18.dirname(destination), { recursive: true, mode: BACKUP_DIRECTORY_MODE });
3481
3799
  fs8.copyFileSync(source, destination, fs8.constants.COPYFILE_EXCL);
3482
3800
  const mode = readFileMode(source, platform);
3483
3801
  if (mode !== null) fs8.chmodSync(destination, mode & OWNER_PERMISSION_BITS);
3484
3802
  }
3485
3803
  var IGNORE_EVERYTHING = "*\n";
3486
3804
  function ignoreBackupsInGit(directory) {
3487
- const ignoreFile = path15.join(directory, ".gitignore");
3805
+ const ignoreFile = path18.join(directory, ".gitignore");
3488
3806
  if (fs8.existsSync(ignoreFile)) return;
3489
3807
  fs8.mkdirSync(directory, { recursive: true, mode: BACKUP_DIRECTORY_MODE });
3490
3808
  fs8.writeFileSync(ignoreFile, IGNORE_EVERYTHING);
@@ -3513,9 +3831,9 @@ function writeEnvFile({ filePath, content, platform }) {
3513
3831
  }
3514
3832
 
3515
3833
  // src/product/storage/restore-record/remove-harness-records.ts
3516
- function removeHarnessRecords(record5, harnessIds) {
3517
- const harnesses2 = Object.fromEntries(Object.entries(record5.harnesses).filter(([id]) => !harnessIds.includes(id)));
3518
- return { ...record5, harnesses: harnesses2 };
3834
+ function removeHarnessRecords(record8, harnessIds) {
3835
+ const harnesses2 = Object.fromEntries(Object.entries(record8.harnesses).filter(([id]) => !harnessIds.includes(id)));
3836
+ return { ...record8, harnesses: harnesses2 };
3519
3837
  }
3520
3838
 
3521
3839
  // src/product/storage/env-file/render-env-file.ts
@@ -3647,7 +3965,7 @@ function restoreAgentFiles(outcome, params) {
3647
3965
  }
3648
3966
  function restoreFile({ file, target, params }) {
3649
3967
  const { ctx, backupRoot } = params;
3650
- const relativePath = path16.join(target.id, path16.basename(file.path));
3968
+ const relativePath = path19.join(target.id, path19.basename(file.path));
3651
3969
  const backup = backUpBeforeWriting({ filePath: file.path, before: file.before, backupRoot, relativePath, ctx });
3652
3970
  if (file.after === null) {
3653
3971
  fs9.rmSync(file.path);
@@ -3666,7 +3984,7 @@ function restoreSharedEnvFile({ outcomes, envFile, params }) {
3666
3984
  if (envFile === null || !envFile.isChanged) return { outcomes, writtenFiles: [] };
3667
3985
  const { ctx, backupRoot } = params;
3668
3986
  try {
3669
- const relativePath = path16.join(ENV_FILE_BACKUP_DIRECTORY, path16.basename(envFile.path));
3987
+ const relativePath = path19.join(ENV_FILE_BACKUP_DIRECTORY, path19.basename(envFile.path));
3670
3988
  const backup = backUpBeforeWriting({
3671
3989
  filePath: envFile.path,
3672
3990
  before: envFile.before,
@@ -3690,9 +4008,9 @@ function forgetRestored(outcomes, ctx) {
3690
4008
  if (restoredIds.length === 0) return [...outcomes];
3691
4009
  const { filePath } = resolveRestoreRecordLocation(ctx);
3692
4010
  try {
3693
- const record5 = readRestoreRecord({ filePath, home: ctx.home });
3694
- if (record5 !== null) {
3695
- writeRestoreRecord({ filePath, record: removeHarnessRecords(record5, restoredIds), platform: ctx.platform });
4011
+ const record8 = readRestoreRecord({ filePath, home: ctx.home });
4012
+ if (record8 !== null) {
4013
+ writeRestoreRecord({ filePath, record: removeHarnessRecords(record8, restoredIds), platform: ctx.platform });
3696
4014
  }
3697
4015
  return [...outcomes];
3698
4016
  } catch (error) {
@@ -3706,8 +4024,8 @@ function failIfPlanned(outcome, error) {
3706
4024
  }
3707
4025
 
3708
4026
  // src/commands/restore/restore-plan.ts
3709
- import path17 from "path";
3710
- import { match as match22 } from "ts-pattern";
4027
+ import path20 from "path";
4028
+ import { match as match24 } from "ts-pattern";
3711
4029
 
3712
4030
  // src/product/storage/env-file/read-env-file.ts
3713
4031
  var NO_ENV_FILE = { kind: "read", text: null };
@@ -3722,26 +4040,26 @@ function readEnvFile({ envPath, home }) {
3722
4040
  // src/commands/restore/restore-plan.ts
3723
4041
  function planRestore(params) {
3724
4042
  const { candidates, ctx, envPath } = params;
3725
- const hasEnvVariables2 = candidates.some(({ record: record5 }) => (record5?.envVariables.length ?? 0) > 0);
4043
+ const hasEnvVariables2 = candidates.some(({ record: record8 }) => (record8?.envVariables.length ?? 0) > 0);
3726
4044
  const envFileRead = hasEnvVariables2 ? readEnvFile({ envPath, home: ctx.home }) : NO_ENV_FILE;
3727
4045
  const namesKept = listNamesRecordedElsewhere(params);
3728
4046
  const outcomes = candidates.map((candidate) => planCandidate({ candidate, envFileRead, namesKept, params }));
3729
4047
  return { outcomes, envFile: planEnvFile({ outcomes, envFileRead, params }) };
3730
4048
  }
3731
- function listNamesRecordedElsewhere({ candidates, record: record5 }) {
4049
+ function listNamesRecordedElsewhere({ candidates, record: record8 }) {
3732
4050
  const selectedIds = candidates.map(({ target }) => target.id);
3733
- const others = Object.entries(record5.harnesses).filter(([id]) => !selectedIds.includes(id));
4051
+ const others = Object.entries(record8.harnesses).filter(([id]) => !selectedIds.includes(id));
3734
4052
  return new Set(others.flatMap(([, harness]) => harness.envVariables.map(({ name }) => name)));
3735
4053
  }
3736
4054
  function planCandidate({ candidate, envFileRead, namesKept, params }) {
3737
- const { target, record: record5 } = candidate;
3738
- if (record5 === null) return { kind: "nothing-to-restore", target };
3739
- const envVariables = record5.envVariables.filter(({ name }) => !namesKept.has(name));
4055
+ const { target, record: record8 } = candidate;
4056
+ if (record8 === null) return { kind: "nothing-to-restore", target };
4057
+ const envVariables = record8.envVariables.filter(({ name }) => !namesKept.has(name));
3740
4058
  if (envVariables.length > 0 && envFileRead.kind === "failed") {
3741
4059
  return { kind: "failed", target, error: envFileRead.error };
3742
4060
  }
3743
4061
  try {
3744
- const { files, warnings } = planRecordedFiles(record5.files, params.ctx);
4062
+ const { files, warnings } = planRecordedFiles(record8.files, params.ctx);
3745
4063
  const hasEnvChange = hasOwnEnvChange({ envFileRead, envVariables, shell: params.shell });
3746
4064
  const isChanged = hasEnvChange || files.some((file) => file.isChanged);
3747
4065
  return { kind: isChanged ? "restored" : "unchanged", target, files, envVariables, warnings };
@@ -3773,7 +4091,7 @@ function planRecordedFile(file, ctx) {
3773
4091
  };
3774
4092
  }
3775
4093
  function describeMissingFile(filePath, ctx) {
3776
- const backups = match22(ctx.scope).with("user", () => path17.join(ctx.home, USER_BACKUP_DIRECTORY)).with("project", () => path17.join(ctx.cwd, PROJECT_BACKUP_DIRECTORY)).exhaustive();
4094
+ const backups = match24(ctx.scope).with("user", () => path20.join(ctx.home, USER_BACKUP_DIRECTORY)).with("project", () => path20.join(ctx.cwd, PROJECT_BACKUP_DIRECTORY)).exhaustive();
3777
4095
  return `${displayPath(filePath, ctx.home)} no longer exists, so it was left as it is. Copies from before each run are under ${displayPath(backups, ctx.home)}, unless --backup-dir put them elsewhere.`;
3778
4096
  }
3779
4097
  function restoreFile2({ file, before, filePath, home }) {
@@ -3795,8 +4113,8 @@ function planEnvFile({ outcomes, envFileRead, params }) {
3795
4113
  }
3796
4114
 
3797
4115
  // src/commands/restore/restore-secrets.ts
3798
- function listRestoreSecrets({ record: record5, ctx, envPath, shell }) {
3799
- const recorded = Object.values(record5.harnesses).flatMap((harness) => [
4116
+ function listRestoreSecrets({ record: record8, ctx, envPath, shell }) {
4117
+ const recorded = Object.values(record8.harnesses).flatMap((harness) => [
3800
4118
  ...harness.files.flatMap(listFileSecrets),
3801
4119
  ...listEnvSecrets(harness)
3802
4120
  ]);
@@ -3843,15 +4161,15 @@ async function runRestore(params) {
3843
4161
  ui.outro(FINISHED_WITH_ERRORS);
3844
4162
  return EXIT_CODE.failed;
3845
4163
  }
3846
- const { record: record5 } = recordRead;
3847
- if (namedHarnesses.length === 0 && Object.keys(record5.harnesses).length === 0) {
4164
+ const { record: record8 } = recordRead;
4165
+ if (namedHarnesses.length === 0 && Object.keys(record8.harnesses).length === 0) {
3848
4166
  const recordPath = displayPath(resolveRestoreRecordLocation(ctx).filePath, ctx.home);
3849
4167
  ui.outro(`Nothing to restore \u2014 nexos-connect has not set up any agent at ${scope} scope (${recordPath}).`);
3850
4168
  return EXIT_CODE.ok;
3851
4169
  }
3852
4170
  const candidates = await selectCandidates({
3853
4171
  namedHarnesses,
3854
- record: record5,
4172
+ record: record8,
3855
4173
  ctx,
3856
4174
  shouldAssumeYes: options.shouldAssumeYes,
3857
4175
  ui
@@ -3861,7 +4179,7 @@ async function runRestore(params) {
3861
4179
  return EXIT_CODE.ok;
3862
4180
  }
3863
4181
  const run = { ...params, ui, ctx };
3864
- const plan = planSelected({ run, candidates, record: record5 });
4182
+ const plan = planSelected({ run, candidates, record: record8 });
3865
4183
  if (options.isDryRun) return report({ run, plan, mode: { kind: "dry-run" }, outro: DRY_RUN_OUTRO });
3866
4184
  const hasChanges = plan.outcomes.some((outcome) => outcome.kind === "restored");
3867
4185
  if (hasChanges && !options.shouldAssumeYes && !await ui.confirm(CONFIRMATION, true)) {
@@ -3888,8 +4206,8 @@ function listRecordedLabels(ctx) {
3888
4206
  }
3889
4207
  function readRecord(ctx) {
3890
4208
  try {
3891
- const record5 = readRestoreRecord({ filePath: resolveRestoreRecordLocation(ctx).filePath, home: ctx.home });
3892
- return { kind: "read", record: record5 ?? createEmptyRestoreRecord() };
4209
+ const record8 = readRestoreRecord({ filePath: resolveRestoreRecordLocation(ctx).filePath, home: ctx.home });
4210
+ return { kind: "read", record: record8 ?? createEmptyRestoreRecord() };
3893
4211
  } catch (error) {
3894
4212
  if (!(error instanceof RestoreRecordError)) throw error;
3895
4213
  return { kind: "failed", error: error.message };
@@ -3899,30 +4217,30 @@ function labelOf(harnessId) {
3899
4217
  return findHarness(harnessId)?.label ?? harnessId;
3900
4218
  }
3901
4219
  async function selectCandidates(params) {
3902
- const { namedHarnesses, record: record5, ctx, ui } = params;
3903
- if (namedHarnesses.length > 0) return namedHarnesses.map((harness) => toCandidate(harness.id, record5));
3904
- const recordedIds = Object.keys(record5.harnesses);
3905
- if (params.shouldAssumeYes) return recordedIds.map((id) => toCandidate(id, record5));
3906
- const chosenIds = await ui.selectRestoreHarnesses(recordedIds.map((id) => describeChoice({ id, record: record5, ctx })));
3907
- return chosenIds.map((id) => toCandidate(id, record5));
4220
+ const { namedHarnesses, record: record8, ctx, ui } = params;
4221
+ if (namedHarnesses.length > 0) return namedHarnesses.map((harness) => toCandidate(harness.id, record8));
4222
+ const recordedIds = Object.keys(record8.harnesses);
4223
+ if (params.shouldAssumeYes) return recordedIds.map((id) => toCandidate(id, record8));
4224
+ const chosenIds = await ui.selectRestoreHarnesses(recordedIds.map((id) => describeChoice({ id, record: record8, ctx })));
4225
+ return chosenIds.map((id) => toCandidate(id, record8));
3908
4226
  }
3909
- function toCandidate(id, record5) {
3910
- return { target: { id, label: labelOf(id) }, record: record5.harnesses[id] ?? null };
4227
+ function toCandidate(id, record8) {
4228
+ return { target: { id, label: labelOf(id) }, record: record8.harnesses[id] ?? null };
3911
4229
  }
3912
4230
  var ENV_VARIABLES_HINT = "environment variables";
3913
- function describeChoice({ id, record: record5, ctx }) {
3914
- const harness = record5.harnesses[id];
4231
+ function describeChoice({ id, record: record8, ctx }) {
4232
+ const harness = record8.harnesses[id];
3915
4233
  const root = resolveRestoreRecordLocation(ctx).root;
3916
- const files = (harness?.files ?? []).map((file) => displayPath(path18.join(root, ...file.path), ctx.home));
4234
+ const files = (harness?.files ?? []).map((file) => displayPath(path21.join(root, ...file.path), ctx.home));
3917
4235
  const variables = (harness?.envVariables.length ?? 0) > 0 ? [ENV_VARIABLES_HINT] : [];
3918
4236
  return { value: id, label: labelOf(id), hint: [...files, ...variables].join(", ") };
3919
4237
  }
3920
- function planSelected({ run, candidates, record: record5 }) {
4238
+ function planSelected({ run, candidates, record: record8 }) {
3921
4239
  const { ctx, masker: masker2, ui } = run;
3922
4240
  const shell = resolveShell(ctx.platform);
3923
4241
  const envPath = resolveEnvFilePath(ctx.home, shell);
3924
- masker2.registerSecrets(listRestoreSecrets({ record: record5, ctx, envPath, shell }));
3925
- const plan = planRestore({ candidates, record: record5, ctx, envPath, shell });
4242
+ masker2.registerSecrets(listRestoreSecrets({ record: record8, ctx, envPath, shell }));
4243
+ const plan = planRestore({ candidates, record: record8, ctx, envPath, shell });
3926
4244
  const files = plan.outcomes.flatMap((outcome) => isPlannedRestore(outcome) ? outcome.files : []);
3927
4245
  const hasFailedAgent = plan.outcomes.some((outcome) => outcome.kind === "failed");
3928
4246
  ui.print(
@@ -3981,7 +4299,7 @@ function findDetectionStatus(harness, detection) {
3981
4299
 
3982
4300
  // src/ui/report/format-result.ts
3983
4301
  import pc9 from "picocolors";
3984
- import { P as P5, match as match23 } from "ts-pattern";
4302
+ import { P as P5, match as match25 } from "ts-pattern";
3985
4303
 
3986
4304
  // src/commands/setup/setup-result.ts
3987
4305
  var OUTCOME_KINDS = ["updated", "unchanged", "not-found", "unsupported", "failed"];
@@ -4023,7 +4341,7 @@ function formatResult(params) {
4023
4341
  function formatOutcome2(outcome, params) {
4024
4342
  const { masker: masker2 } = params;
4025
4343
  const style = OUTCOME_STYLE[outcome.kind];
4026
- const body = match23(outcome).with({ kind: P5.union("updated", "unchanged") }, (planned) => formatPlannedOutcome(planned, params)).with({ kind: "not-found" }, ({ detail }) => [`${INDENT}${pc9.dim(masker2.maskText(detail))}`]).with({ kind: "unsupported" }, ({ harness }) => [`${INDENT}${pc9.dim(harness.reason)}`]).with({ kind: "failed" }, ({ error }) => [`${INDENT}${pc9.red(masker2.maskText(error))}`]).exhaustive();
4344
+ const body = match25(outcome).with({ kind: P5.union("updated", "unchanged") }, (planned) => formatPlannedOutcome(planned, params)).with({ kind: "not-found" }, ({ detail }) => [`${INDENT}${pc9.dim(masker2.maskText(detail))}`]).with({ kind: "unsupported" }, ({ harness }) => [`${INDENT}${pc9.dim(harness.reason)}`]).with({ kind: "failed" }, ({ error }) => [`${INDENT}${pc9.red(masker2.maskText(error))}`]).exhaustive();
4027
4345
  return ["", ` ${style.colour(`${style.glyph} ${outcome.harness.label} \u2014 ${style.label}`)}`, ...body];
4028
4346
  }
4029
4347
  function formatPlannedOutcome(outcome, { envFile, home, masker: masker2 }) {
@@ -4067,7 +4385,7 @@ function formatLoadStep({ outcomes, envFile, mode }) {
4067
4385
  ` ${formatList(readers)} ${verb} the key from the environment. Load it with:`,
4068
4386
  `${INDENT}${pc9.bold(loadCommand)}`,
4069
4387
  ` ${pc9.dim(`Until then ${pronoun} will not see the key.`)}`,
4070
- ...match23(envFile.shell).with("powershell", () => [
4388
+ ...match25(envFile.shell).with("powershell", () => [
4071
4389
  ` ${pc9.dim("That lasts for this session only. Add the same line to $PROFILE to keep it.")}`
4072
4390
  ]).with("posix", () => []).exhaustive()
4073
4391
  ];
@@ -4092,7 +4410,7 @@ function formatRunHint({ binary, label }) {
4092
4410
  }
4093
4411
 
4094
4412
  // src/commands/setup/answers/describe-key-destination.ts
4095
- import { match as match24 } from "ts-pattern";
4413
+ import { match as match26 } from "ts-pattern";
4096
4414
 
4097
4415
  // src/commands/setup/answers/scope.ts
4098
4416
  async function resolveScope({
@@ -4140,7 +4458,7 @@ function describeKeyDestination({ configurable, ctx, envPath }) {
4140
4458
  return `It will be written to ${formatList(configFiles)}, and to ${envFile}.`;
4141
4459
  }
4142
4460
  function displayScopedPath(filePath, ctx) {
4143
- return match24(ctx.scope).with("user", () => displayPath(filePath, ctx.home)).with("project", () => displayRelativePath(filePath, ctx.cwd)).exhaustive();
4461
+ return match26(ctx.scope).with("user", () => displayPath(filePath, ctx.home)).with("project", () => displayRelativePath(filePath, ctx.cwd)).exhaustive();
4144
4462
  }
4145
4463
 
4146
4464
  // src/commands/setup/answers/harness-selection.ts
@@ -4211,7 +4529,7 @@ function findIneligibility(harness, scope) {
4211
4529
  }
4212
4530
 
4213
4531
  // src/commands/setup/changes/apply-changes.ts
4214
- import path19 from "path";
4532
+ import path22 from "path";
4215
4533
 
4216
4534
  // src/commands/setup/changes/change-plan.ts
4217
4535
  import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
@@ -4326,32 +4644,32 @@ function classifyFileOrigin(before) {
4326
4644
  if (before === null) return "absent";
4327
4645
  return before.trim() === "" ? "blank" : "content";
4328
4646
  }
4329
- function addFileOriginals({ record: record5, harnessId, file }) {
4330
- const harness = readHarnessRecord(record5, harnessId);
4647
+ function addFileOriginals({ record: record8, harnessId, file }) {
4648
+ const harness = readHarnessRecord(record8, harnessId);
4331
4649
  const existing = harness.files.find((recorded) => isSamePath2(recorded.path, file.path));
4332
4650
  const others = harness.files.filter((recorded) => recorded !== existing);
4333
- return withHarnessRecord(record5, harnessId, { ...harness, files: [...others, mergeFile(existing, file)] });
4651
+ return withHarnessRecord(record8, harnessId, { ...harness, files: [...others, mergeFile(existing, file)] });
4334
4652
  }
4335
- function addEnvOriginals({ record: record5, harnessId, variables, isFileAbsent }) {
4336
- const harness = readHarnessRecord(record5, harnessId);
4653
+ function addEnvOriginals({ record: record8, harnessId, variables, isFileAbsent }) {
4654
+ const harness = readHarnessRecord(record8, harnessId);
4337
4655
  const kept = isFileAbsent ? [] : harness.envVariables;
4338
4656
  const added = variables.filter((variable) => !kept.some(({ name }) => name === variable.name));
4339
- return withHarnessRecord(record5, harnessId, { ...harness, envVariables: [...kept, ...added] });
4657
+ return withHarnessRecord(record8, harnessId, { ...harness, envVariables: [...kept, ...added] });
4340
4658
  }
4341
4659
  var EMPTY_HARNESS_RECORD = { files: [], envVariables: [] };
4342
- function readHarnessRecord(record5, harnessId) {
4343
- return record5.harnesses[harnessId] ?? EMPTY_HARNESS_RECORD;
4660
+ function readHarnessRecord(record8, harnessId) {
4661
+ return record8.harnesses[harnessId] ?? EMPTY_HARNESS_RECORD;
4344
4662
  }
4345
- function withHarnessRecord(record5, harnessId, harness) {
4346
- return { ...record5, harnesses: { ...record5.harnesses, [harnessId]: harness } };
4663
+ function withHarnessRecord(record8, harnessId, harness) {
4664
+ return { ...record8, harnesses: { ...record8.harnesses, [harnessId]: harness } };
4347
4665
  }
4348
4666
  function mergeFile(existing, file) {
4349
4667
  if (existing === void 0 || file.origin === "absent") return file;
4350
4668
  const added = file.keys.filter((key) => !existing.keys.some(({ keyPath }) => isSamePath2(keyPath, key.keyPath)));
4351
4669
  return { ...existing, keys: [...existing.keys, ...added] };
4352
4670
  }
4353
- function isSamePath2(path22, other) {
4354
- return path22.length === other.length && path22.every((segment, index) => segment === other[index]);
4671
+ function isSamePath2(path25, other) {
4672
+ return path25.length === other.length && path25.every((segment, index) => segment === other[index]);
4355
4673
  }
4356
4674
 
4357
4675
  // src/commands/setup/changes/record-originals.ts
@@ -4363,7 +4681,7 @@ function recordFileOriginals({ harnessId, file, ctx }) {
4363
4681
  origin: classifyFileOrigin(file.before),
4364
4682
  keys: file.originals
4365
4683
  };
4366
- updateRestoreRecord(location, ctx, (record5) => addFileOriginals({ record: record5, harnessId, file: recordedFile }));
4684
+ updateRestoreRecord(location, ctx, (record8) => addFileOriginals({ record: record8, harnessId, file: recordedFile }));
4367
4685
  }
4368
4686
  function recordEnvOriginals({ outcomes, envFile, ctx }) {
4369
4687
  const syntax = ENV_SYNTAXES[envFile.shell];
@@ -4372,7 +4690,7 @@ function recordEnvOriginals({ outcomes, envFile, ctx }) {
4372
4690
  updateRestoreRecord(
4373
4691
  resolveRestoreRecordLocation(ctx),
4374
4692
  ctx,
4375
- (record5) => outcomes.reduce((updated, outcome) => {
4693
+ (record8) => outcomes.reduce((updated, outcome) => {
4376
4694
  if (outcome.kind !== "updated") return updated;
4377
4695
  const variables = Object.keys(outcome.envVariables).map((name) => ({
4378
4696
  name,
@@ -4380,13 +4698,13 @@ function recordEnvOriginals({ outcomes, envFile, ctx }) {
4380
4698
  }));
4381
4699
  if (variables.length === 0) return updated;
4382
4700
  return addEnvOriginals({ record: updated, harnessId: outcome.harness.id, variables, isFileAbsent });
4383
- }, record5)
4701
+ }, record8)
4384
4702
  );
4385
4703
  }
4386
4704
  function updateRestoreRecord(location, ctx, update) {
4387
4705
  if (location.gitIgnoredDirectory !== null) ignoreBackupsInGit(location.gitIgnoredDirectory);
4388
- const record5 = readRestoreRecord({ filePath: location.filePath, home: ctx.home }) ?? createEmptyRestoreRecord();
4389
- writeRestoreRecord({ filePath: location.filePath, record: update(record5), platform: ctx.platform });
4706
+ const record8 = readRestoreRecord({ filePath: location.filePath, home: ctx.home }) ?? createEmptyRestoreRecord();
4707
+ writeRestoreRecord({ filePath: location.filePath, record: update(record8), platform: ctx.platform });
4390
4708
  }
4391
4709
 
4392
4710
  // src/commands/setup/changes/apply-changes.ts
@@ -4408,10 +4726,10 @@ function writeAgentFiles(outcome, params) {
4408
4726
  const writtenFiles = [];
4409
4727
  for (const file of outcome.files.filter((plan) => plan.isChanged)) {
4410
4728
  try {
4411
- const relativePath = path19.join(outcome.harness.id, path19.basename(file.path));
4412
- const record5 = () => recordFileOriginals({ harnessId: outcome.harness.id, file, ctx: params.ctx });
4729
+ const relativePath = path22.join(outcome.harness.id, path22.basename(file.path));
4730
+ const record8 = () => recordFileOriginals({ harnessId: outcome.harness.id, file, ctx: params.ctx });
4413
4731
  const write = () => writeFileWithMode({ filePath: file.path, content: file.after, mode: resolveWriteMode(file, params.ctx) });
4414
- writtenFiles.push(backUpThenWrite({ file, relativePath, record: record5, write, params }));
4732
+ writtenFiles.push(backUpThenWrite({ file, relativePath, record: record8, write, params }));
4415
4733
  } catch (error) {
4416
4734
  const reason = describeFileError({ error, action: "write", home: params.ctx.home });
4417
4735
  return { outcome: { kind: "failed", harness: outcome.harness, error: reason }, writtenFiles };
@@ -4426,11 +4744,11 @@ function replanEnvFile2(envFile, outcomes) {
4426
4744
  function writeSharedEnvFile({ outcomes, envFile, params }) {
4427
4745
  if (envFile === null || !envFile.isChanged) return { outcomes, writtenFiles: [] };
4428
4746
  try {
4429
- const relativePath = path19.join(ENV_FILE_BACKUP_DIRECTORY, path19.basename(envFile.path));
4747
+ const relativePath = path22.join(ENV_FILE_BACKUP_DIRECTORY, path22.basename(envFile.path));
4430
4748
  const { ctx } = params;
4431
- const record5 = () => recordEnvOriginals({ outcomes, envFile, ctx });
4749
+ const record8 = () => recordEnvOriginals({ outcomes, envFile, ctx });
4432
4750
  const write = () => writeEnvFile({ filePath: envFile.path, content: envFile.after, platform: ctx.platform });
4433
- return { outcomes, writtenFiles: [backUpThenWrite({ file: envFile, relativePath, record: record5, write, params })] };
4751
+ return { outcomes, writtenFiles: [backUpThenWrite({ file: envFile, relativePath, record: record8, write, params })] };
4434
4752
  } catch (error) {
4435
4753
  const reason = describeFileError({ error, action: "write", home: params.ctx.home });
4436
4754
  return { outcomes: outcomes.map((outcome) => failIfWritingEnvFile(outcome, reason)), writtenFiles: [] };
@@ -4440,10 +4758,10 @@ function failIfWritingEnvFile(outcome, error) {
4440
4758
  if (outcome.kind !== "updated" || Object.keys(outcome.envVariables).length === 0) return outcome;
4441
4759
  return { kind: "failed", harness: outcome.harness, error };
4442
4760
  }
4443
- function backUpThenWrite({ file, relativePath, record: record5, write, params }) {
4761
+ function backUpThenWrite({ file, relativePath, record: record8, write, params }) {
4444
4762
  const { ctx, backupRoot } = params;
4445
4763
  const backup = backUpBeforeWriting({ filePath: file.path, before: file.before, backupRoot, relativePath, ctx });
4446
- record5();
4764
+ record8();
4447
4765
  write();
4448
4766
  return backup;
4449
4767
  }
@@ -4626,7 +4944,7 @@ function abort(ui) {
4626
4944
 
4627
4945
  // src/harnesses/run-launch.ts
4628
4946
  import fs10 from "fs";
4629
- import path20 from "path";
4947
+ import path23 from "path";
4630
4948
  var LAUNCH_DIRECTORY_PREFIX = "nexos-connect-";
4631
4949
  var STALE_DIRECTORY_PATTERN = new RegExp(`^${LAUNCH_DIRECTORY_PREFIX}(\\d+)-`);
4632
4950
  var OWNER_ONLY_MODE2 = 384;
@@ -4639,11 +4957,11 @@ function sweepStaleDirectories(deps) {
4639
4957
  return;
4640
4958
  }
4641
4959
  for (const name of names) {
4642
- const match25 = STALE_DIRECTORY_PATTERN.exec(name);
4643
- if (match25 === null) continue;
4644
- const directory = path20.join(root, name);
4960
+ const match27 = STALE_DIRECTORY_PATTERN.exec(name);
4961
+ if (match27 === null) continue;
4962
+ const directory = path23.join(root, name);
4645
4963
  try {
4646
- if (deps.isAlive(Number(match25[1]))) continue;
4964
+ if (deps.isAlive(Number(match27[1]))) continue;
4647
4965
  if (deps.uid !== void 0 && fs10.statSync(directory).uid !== deps.uid) continue;
4648
4966
  fs10.rmSync(directory, { recursive: true, force: true });
4649
4967
  } catch {
@@ -4662,10 +4980,10 @@ async function runLaunch(recipe, passthrough, deps) {
4662
4980
  let commandArguments = recipe.arguments;
4663
4981
  if (recipe.files !== void 0 && recipe.files.length > 0) {
4664
4982
  try {
4665
- directory = fs10.mkdtempSync(path20.join(deps.tmpdir(), `${LAUNCH_DIRECTORY_PREFIX}${deps.pid}-`));
4983
+ directory = fs10.mkdtempSync(path23.join(deps.tmpdir(), `${LAUNCH_DIRECTORY_PREFIX}${deps.pid}-`));
4666
4984
  const byPlaceholder = /* @__PURE__ */ new Map();
4667
4985
  for (const file of recipe.files) {
4668
- const target = path20.join(directory, file.name);
4986
+ const target = path23.join(directory, file.name);
4669
4987
  fs10.writeFileSync(target, file.content, { mode: OWNER_ONLY_MODE2, flag: "wx" });
4670
4988
  byPlaceholder.set(file.placeholder, target);
4671
4989
  }
@@ -5382,8 +5700,8 @@ var HELP = renderHelp({
5382
5700
  predefinedModelIds: PREDEFINED_MODELS.map((model) => model.id),
5383
5701
  defaultModel: DEFAULT_MODEL,
5384
5702
  backupDirectories: {
5385
- user: path21.join("~", USER_BACKUP_DIRECTORY),
5386
- project: `.${path21.sep}${PROJECT_BACKUP_DIRECTORY}`
5703
+ user: path24.join("~", USER_BACKUP_DIRECTORY),
5704
+ project: `.${path24.sep}${PROJECT_BACKUP_DIRECTORY}`
5387
5705
  }
5388
5706
  });
5389
5707
  function isLaunchingHarness(harness) {
@@ -5397,7 +5715,7 @@ function describeUnknownAgent(binary) {
5397
5715
  if (isAgentName(binary)) return `Unknown agent "${binary}". ${oneOf}`;
5398
5716
  return `Unknown agent. ${oneOf}. To pass a key, use --key or set $${KEY_ENV_VARIABLE}.`;
5399
5717
  }
5400
- var PackageSchema = v12.object({ version: v12.string() });
5718
+ var PackageSchema = v15.object({ version: v15.string() });
5401
5719
  async function runCommand(ui) {
5402
5720
  const options = parseCliArguments(process.argv.slice(NODE_AND_SCRIPT_ARGUMENT_COUNT));
5403
5721
  if (options.shouldShowHelp) {
@@ -5405,7 +5723,7 @@ async function runCommand(ui) {
5405
5723
  return EXIT_CODE.ok;
5406
5724
  }
5407
5725
  if (options.shouldShowVersion) {
5408
- console.log(v12.parse(PackageSchema, createRequire(import.meta.url)("../package.json")).version);
5726
+ console.log(v15.parse(PackageSchema, createRequire(import.meta.url)("../package.json")).version);
5409
5727
  return EXIT_CODE.ok;
5410
5728
  }
5411
5729
  if (options.command.kind === "forget-key") return await forgetKey({ keystore, ui, masker });