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/README.md +18 -16
- package/dist/index.js +584 -266
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
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
|
|
7
|
+
import path24 from "path";
|
|
8
8
|
import crossSpawn from "cross-spawn";
|
|
9
9
|
import pc12 from "picocolors";
|
|
10
|
-
import * as
|
|
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((
|
|
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((
|
|
399
|
+
return [...text2.matchAll(KEY_SHAPE_PATTERN)].map((match27) => toSpan(match27, maskKey(match27[0])));
|
|
400
400
|
}
|
|
401
|
-
function toSpan(
|
|
402
|
-
return { start:
|
|
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
|
|
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
|
|
1392
|
-
const entry = { path:
|
|
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,
|
|
1394
|
+
return [entry, ...listInBody(keyValue.value.body, path25)];
|
|
1395
1395
|
});
|
|
1396
1396
|
}
|
|
1397
|
-
function isSamePath(
|
|
1398
|
-
return
|
|
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
|
|
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 =
|
|
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
|
|
1771
|
-
import { match as
|
|
1772
|
-
import * as
|
|
1773
|
-
var
|
|
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:
|
|
1876
|
+
binary: BINARY5,
|
|
1779
1877
|
protocols: ["openai-chat"],
|
|
1780
1878
|
projectSupport: { kind: "supported" },
|
|
1781
1879
|
detect: (ctx) => detectOpenCode(ctx),
|
|
1782
|
-
configPath: (ctx) =>
|
|
1783
|
-
plan: (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:
|
|
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) =>
|
|
1804
|
-
readSecrets: (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(
|
|
1812
|
-
return { isFound: true, detail: `${
|
|
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 =
|
|
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 =
|
|
1819
|
-
return [
|
|
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
|
|
1919
|
+
function resolveConfigPath3(ctx) {
|
|
1822
1920
|
const candidates = listConfigCandidates(ctx);
|
|
1823
1921
|
return findFirstExistingFile(candidates) ?? candidates[0];
|
|
1824
1922
|
}
|
|
1825
|
-
function
|
|
1923
|
+
function planConfig3(desired, ctx) {
|
|
1826
1924
|
return {
|
|
1827
1925
|
kind: "file",
|
|
1828
|
-
path:
|
|
1926
|
+
path: resolveConfigPath3(ctx),
|
|
1829
1927
|
hasSecret: true,
|
|
1830
|
-
edits: (current) => ({ format: "jsonc", updates:
|
|
1928
|
+
edits: (current) => ({ format: "jsonc", updates: listUpdates2(desired, parseJsoncConfig(current, ConfigSchema2)) })
|
|
1831
1929
|
};
|
|
1832
1930
|
}
|
|
1833
|
-
var
|
|
1834
|
-
var
|
|
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
|
|
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"],
|
|
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
|
|
1863
|
-
const updates = providerUpdates({ providerId:
|
|
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"],
|
|
1963
|
+
return [[["$schema"], CONFIG_SCHEMA_URL2], ...updates];
|
|
1866
1964
|
}
|
|
1867
1965
|
function isMissingSchemaUrl(config) {
|
|
1868
|
-
return
|
|
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
|
|
1871
|
-
const config = readJsoncFile(
|
|
1872
|
-
const apiKey = config.kind === "valid" ? config.value.provider?.[
|
|
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
|
|
1880
|
-
provider:
|
|
1977
|
+
var ProvidersSchema2 = v6.looseObject({
|
|
1978
|
+
provider: v6.fallback(v6.optional(v6.record(v6.string(), v6.unknown())), void 0)
|
|
1881
1979
|
});
|
|
1882
|
-
var
|
|
1883
|
-
function
|
|
1884
|
-
const config = readJsoncFile(
|
|
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 =
|
|
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
|
|
1893
|
-
$schema:
|
|
1894
|
-
provider:
|
|
1895
|
-
|
|
1896
|
-
[
|
|
1897
|
-
|
|
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
|
|
2237
|
+
import { match as match11 } from "ts-pattern";
|
|
1920
2238
|
|
|
1921
2239
|
// src/product/storage/env-file/syntax/posix-env-syntax.ts
|
|
1922
|
-
import
|
|
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 ${
|
|
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
|
|
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 = `. "${
|
|
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
|
|
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 =
|
|
2032
|
-
|
|
2033
|
-
|
|
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) =>
|
|
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
|
|
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 =
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
2298
|
-
var OptionalTextSchema2 =
|
|
2299
|
-
var OptionalCountSchema =
|
|
2300
|
-
var EndpointsSchema =
|
|
2301
|
-
var ModelSchema2 =
|
|
2302
|
-
|
|
2303
|
-
id:
|
|
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
|
-
|
|
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 =
|
|
2319
|
-
|
|
2320
|
-
|
|
2636
|
+
var ModelListSchema = v11.pipe(
|
|
2637
|
+
v11.object({ data: v11.array(v11.unknown()) }),
|
|
2638
|
+
v11.transform(({ data }) => keepUsableModels(data))
|
|
2321
2639
|
);
|
|
2322
|
-
var OpenAiErrorSchema =
|
|
2323
|
-
error:
|
|
2324
|
-
message:
|
|
2325
|
-
type:
|
|
2326
|
-
code:
|
|
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 =
|
|
2330
|
-
type:
|
|
2331
|
-
error:
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
2727
|
+
import { match as match13 } from "ts-pattern";
|
|
2410
2728
|
function isKeyAccepted(probe) {
|
|
2411
|
-
return
|
|
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
|
|
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
|
|
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
|
|
2922
|
+
import { match as match17 } from "ts-pattern";
|
|
2605
2923
|
|
|
2606
2924
|
// src/product/gateway/models/router-availability.ts
|
|
2607
|
-
import { match as
|
|
2925
|
+
import { match as match15 } from "ts-pattern";
|
|
2608
2926
|
function classifyRouterAvailability(probe) {
|
|
2609
|
-
return
|
|
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
|
|
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
|
|
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
|
|
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
|
|
2720
|
-
import * as
|
|
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
|
|
3041
|
+
return path12.join(home, ENV_DIRECTORY_NAME, PREFERENCES_FILE);
|
|
2724
3042
|
}
|
|
2725
|
-
var ModelIdSchema =
|
|
2726
|
-
var PreferencesSchema =
|
|
2727
|
-
defaultModels:
|
|
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 =
|
|
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(
|
|
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
|
|
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 =
|
|
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 (!
|
|
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
|
|
3299
|
+
import path13 from "path";
|
|
2982
3300
|
function resolveEnvFilePath(home, shell) {
|
|
2983
|
-
return
|
|
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
|
|
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
|
|
3066
|
-
import { match as
|
|
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 =
|
|
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:
|
|
3392
|
+
return { path: path14.join(path14.resolve(cwd, backupDirectory), stamp), gitIgnoredDirectory: null };
|
|
3075
3393
|
}
|
|
3076
|
-
return
|
|
3077
|
-
const directory =
|
|
3078
|
-
return { path:
|
|
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
|
|
3085
|
-
import * as
|
|
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
|
|
3406
|
+
import { match as match19 } from "ts-pattern";
|
|
3089
3407
|
var CONFIG_FORMATS = ["jsonc", "toml"];
|
|
3090
3408
|
function renderConfig(current, edits) {
|
|
3091
|
-
return
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
3427
|
+
import { match as match20 } from "ts-pattern";
|
|
3110
3428
|
var OWNER_ONLY_MODE = 384;
|
|
3111
3429
|
function resolveOwnerOnlyMode(platform) {
|
|
3112
|
-
return
|
|
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
|
|
3435
|
+
import path15 from "path";
|
|
3118
3436
|
function writeFileWithMode({ filePath, content, mode }) {
|
|
3119
|
-
fs5.mkdirSync(
|
|
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 =
|
|
3133
|
-
var RecordedKeySchema =
|
|
3450
|
+
var SegmentsSchema = v14.pipe(v14.tupleWithRest([v14.string()], v14.string()), v14.readonly());
|
|
3451
|
+
var RecordedKeySchema = v14.object({
|
|
3134
3452
|
keyPath: SegmentsSchema,
|
|
3135
|
-
original:
|
|
3136
|
-
createdParents:
|
|
3137
|
-
holdsSecret:
|
|
3453
|
+
original: v14.nullable(v14.string()),
|
|
3454
|
+
createdParents: v14.pipe(v14.array(SegmentsSchema), v14.readonly()),
|
|
3455
|
+
holdsSecret: v14.boolean()
|
|
3138
3456
|
});
|
|
3139
|
-
var RecordedFileSchema =
|
|
3457
|
+
var RecordedFileSchema = v14.object({
|
|
3140
3458
|
path: SegmentsSchema,
|
|
3141
|
-
format:
|
|
3142
|
-
origin:
|
|
3143
|
-
keys:
|
|
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 =
|
|
3146
|
-
var HarnessRecordSchema =
|
|
3147
|
-
files:
|
|
3148
|
-
envVariables:
|
|
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 =
|
|
3151
|
-
version:
|
|
3152
|
-
harnesses:
|
|
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 =
|
|
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 =
|
|
3502
|
+
var VersionSchema = v14.object({ version: v14.number() });
|
|
3185
3503
|
function isNewerRecord(json) {
|
|
3186
|
-
const parsed =
|
|
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:
|
|
3192
|
-
if (Object.keys(
|
|
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(
|
|
3197
|
-
const content = `${JSON.stringify(
|
|
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
|
|
3204
|
-
import { match as
|
|
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
|
|
3208
|
-
filePath:
|
|
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 =
|
|
3213
|
-
return { filePath:
|
|
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] =
|
|
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 =
|
|
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 =
|
|
3234
|
-
const [first] = relativePath.split(
|
|
3235
|
-
return relativePath !== "" && first !== ".." && !
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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 =
|
|
3480
|
-
fs8.mkdirSync(
|
|
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 =
|
|
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(
|
|
3517
|
-
const harnesses2 = Object.fromEntries(Object.entries(
|
|
3518
|
-
return { ...
|
|
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 =
|
|
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 =
|
|
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
|
|
3694
|
-
if (
|
|
3695
|
-
writeRestoreRecord({ filePath, record: removeHarnessRecords(
|
|
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
|
|
3710
|
-
import { match as
|
|
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:
|
|
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:
|
|
4049
|
+
function listNamesRecordedElsewhere({ candidates, record: record8 }) {
|
|
3732
4050
|
const selectedIds = candidates.map(({ target }) => target.id);
|
|
3733
|
-
const others = Object.entries(
|
|
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:
|
|
3738
|
-
if (
|
|
3739
|
-
const envVariables =
|
|
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(
|
|
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 =
|
|
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:
|
|
3799
|
-
const recorded = Object.values(
|
|
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:
|
|
3847
|
-
if (namedHarnesses.length === 0 && Object.keys(
|
|
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:
|
|
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:
|
|
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
|
|
3892
|
-
return { kind: "read", record:
|
|
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:
|
|
3903
|
-
if (namedHarnesses.length > 0) return namedHarnesses.map((harness) => toCandidate(harness.id,
|
|
3904
|
-
const recordedIds = Object.keys(
|
|
3905
|
-
if (params.shouldAssumeYes) return recordedIds.map((id) => toCandidate(id,
|
|
3906
|
-
const chosenIds = await ui.selectRestoreHarnesses(recordedIds.map((id) => describeChoice({ id, record:
|
|
3907
|
-
return chosenIds.map((id) => toCandidate(id,
|
|
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,
|
|
3910
|
-
return { target: { id, label: labelOf(id) }, record:
|
|
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:
|
|
3914
|
-
const harness =
|
|
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(
|
|
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:
|
|
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:
|
|
3925
|
-
const plan = planRestore({ candidates, record:
|
|
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
|
|
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 =
|
|
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
|
-
...
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
4330
|
-
const harness = readHarnessRecord(
|
|
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(
|
|
4651
|
+
return withHarnessRecord(record8, harnessId, { ...harness, files: [...others, mergeFile(existing, file)] });
|
|
4334
4652
|
}
|
|
4335
|
-
function addEnvOriginals({ record:
|
|
4336
|
-
const harness = readHarnessRecord(
|
|
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(
|
|
4657
|
+
return withHarnessRecord(record8, harnessId, { ...harness, envVariables: [...kept, ...added] });
|
|
4340
4658
|
}
|
|
4341
4659
|
var EMPTY_HARNESS_RECORD = { files: [], envVariables: [] };
|
|
4342
|
-
function readHarnessRecord(
|
|
4343
|
-
return
|
|
4660
|
+
function readHarnessRecord(record8, harnessId) {
|
|
4661
|
+
return record8.harnesses[harnessId] ?? EMPTY_HARNESS_RECORD;
|
|
4344
4662
|
}
|
|
4345
|
-
function withHarnessRecord(
|
|
4346
|
-
return { ...
|
|
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(
|
|
4354
|
-
return
|
|
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, (
|
|
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
|
-
(
|
|
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
|
-
},
|
|
4701
|
+
}, record8)
|
|
4384
4702
|
);
|
|
4385
4703
|
}
|
|
4386
4704
|
function updateRestoreRecord(location, ctx, update) {
|
|
4387
4705
|
if (location.gitIgnoredDirectory !== null) ignoreBackupsInGit(location.gitIgnoredDirectory);
|
|
4388
|
-
const
|
|
4389
|
-
writeRestoreRecord({ filePath: location.filePath, record: update(
|
|
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 =
|
|
4412
|
-
const
|
|
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:
|
|
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 =
|
|
4747
|
+
const relativePath = path22.join(ENV_FILE_BACKUP_DIRECTORY, path22.basename(envFile.path));
|
|
4430
4748
|
const { ctx } = params;
|
|
4431
|
-
const
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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
|
|
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
|
|
4643
|
-
if (
|
|
4644
|
-
const directory =
|
|
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(
|
|
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(
|
|
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 =
|
|
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:
|
|
5386
|
-
project: `.${
|
|
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 =
|
|
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(
|
|
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 });
|