zelari-code 1.23.0 → 1.25.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 +3 -0
- package/bin/zelari-code.js +44 -12
- package/dist/cli/atMentions.js +179 -0
- package/dist/cli/atMentions.js.map +1 -0
- package/dist/cli/companion/config.js +172 -0
- package/dist/cli/companion/config.js.map +1 -0
- package/dist/cli/companion/runManager.js +250 -0
- package/dist/cli/companion/runManager.js.map +1 -0
- package/dist/cli/companion/serve.js +376 -0
- package/dist/cli/companion/serve.js.map +1 -0
- package/dist/cli/components/InputBar.js +1 -1
- package/dist/cli/components/InputBar.js.map +1 -1
- package/dist/cli/generateSkillFromUrl.js +236 -0
- package/dist/cli/generateSkillFromUrl.js.map +1 -0
- package/dist/cli/headless.js +6 -0
- package/dist/cli/headless.js.map +1 -1
- package/dist/cli/hooks/useSlashDispatch.js +19 -6
- package/dist/cli/hooks/useSlashDispatch.js.map +1 -1
- package/dist/cli/main.bundled.js +2617 -769
- package/dist/cli/main.bundled.js.map +4 -4
- package/dist/cli/main.js +155 -0
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/missionSlice.js +19 -0
- package/dist/cli/missionSlice.js.map +1 -1
- package/dist/cli/provider/openai-compatible.js +164 -41
- package/dist/cli/provider/openai-compatible.js.map +1 -1
- package/dist/cli/runHeadless.js +33 -0
- package/dist/cli/runHeadless.js.map +1 -1
- package/dist/cli/skillCategories.js +15 -0
- package/dist/cli/skillCategories.js.map +1 -0
- package/dist/cli/skillConfigIo.js +285 -0
- package/dist/cli/skillConfigIo.js.map +1 -0
- package/dist/cli/slashCommands.js +14 -5
- package/dist/cli/slashCommands.js.map +1 -1
- package/dist/cli/slashHandlers/skills.js +30 -0
- package/dist/cli/slashHandlers/skills.js.map +1 -1
- package/dist/cli/traceStore.js +84 -0
- package/dist/cli/traceStore.js.map +1 -0
- package/dist/cli/triggerLock.js +79 -0
- package/dist/cli/triggerLock.js.map +1 -0
- package/dist/cli/zelariMission.js +77 -0
- package/dist/cli/zelariMission.js.map +1 -1
- package/dist/cli/zelariMission.test.js +44 -0
- package/dist/cli/zelariMission.test.js.map +1 -0
- package/package.json +4 -3
- package/scripts/start-companion-serve.ps1 +43 -0
package/dist/cli/main.bundled.js
CHANGED
|
@@ -4,6 +4,12 @@ var __defProp = Object.defineProperty;
|
|
|
4
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
8
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
9
|
+
}) : x)(function(x) {
|
|
10
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
11
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
12
|
+
});
|
|
7
13
|
var __esm = (fn, res) => function __init() {
|
|
8
14
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
9
15
|
};
|
|
@@ -21,6 +27,87 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
21
27
|
};
|
|
22
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
23
29
|
|
|
30
|
+
// src/cli/modelPricing.ts
|
|
31
|
+
function envOverride(model) {
|
|
32
|
+
const key = `ANATHEMA_PRICE_${model.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`;
|
|
33
|
+
const raw = process.env[key] ?? process.env.ANATHEMA_PRICE_DEFAULT;
|
|
34
|
+
return parseRateOverride(raw);
|
|
35
|
+
}
|
|
36
|
+
function parseRateOverride(raw) {
|
|
37
|
+
if (!raw) return null;
|
|
38
|
+
const [inStr, outStr] = raw.split("/");
|
|
39
|
+
const input = Number.parseFloat(inStr);
|
|
40
|
+
const output = outStr !== void 0 ? Number.parseFloat(outStr) : input;
|
|
41
|
+
if (!Number.isFinite(input) || !Number.isFinite(output)) return null;
|
|
42
|
+
return { input, output };
|
|
43
|
+
}
|
|
44
|
+
function getModelRate(model) {
|
|
45
|
+
if (!model) return DEFAULT_RATE;
|
|
46
|
+
return envOverride(model) ?? PRICES_PER_MILLION[model] ?? DEFAULT_RATE;
|
|
47
|
+
}
|
|
48
|
+
function calculateCost(model, promptTokens, completionTokens, cachedPromptTokens = 0) {
|
|
49
|
+
if (!Number.isFinite(promptTokens) || promptTokens < 0) return 0;
|
|
50
|
+
if (!Number.isFinite(completionTokens) || completionTokens < 0) return 0;
|
|
51
|
+
const rate = getModelRate(model);
|
|
52
|
+
const cached2 = Number.isFinite(cachedPromptTokens) && cachedPromptTokens > 0 ? Math.min(cachedPromptTokens, promptTokens) : 0;
|
|
53
|
+
const uncachedPrompt = promptTokens - cached2;
|
|
54
|
+
const cachedRate = rate.cachedInput ?? rate.input * DEFAULT_CACHE_DISCOUNT;
|
|
55
|
+
const inputCost = uncachedPrompt / 1e6 * rate.input + cached2 / 1e6 * cachedRate;
|
|
56
|
+
const outputCost = completionTokens / 1e6 * rate.output;
|
|
57
|
+
return Number((inputCost + outputCost).toFixed(6));
|
|
58
|
+
}
|
|
59
|
+
function formatCost(usd) {
|
|
60
|
+
if (!Number.isFinite(usd) || usd < 0) return "$0.0000";
|
|
61
|
+
if (usd === 0) return "$0.0000";
|
|
62
|
+
if (usd < 1e-4) return "<$0.0001";
|
|
63
|
+
return `$${usd.toFixed(4)}`;
|
|
64
|
+
}
|
|
65
|
+
function formatTokens(tokens) {
|
|
66
|
+
if (!Number.isFinite(tokens) || tokens < 0) return "0";
|
|
67
|
+
if (tokens < 1e3) return `${tokens}`;
|
|
68
|
+
if (tokens < 1e6) return `${(tokens / 1e3).toFixed(1)}k`;
|
|
69
|
+
if (tokens < 1e9) return `${(tokens / 1e6).toFixed(2)}M`;
|
|
70
|
+
return `${(tokens / 1e9).toFixed(2)}B`;
|
|
71
|
+
}
|
|
72
|
+
var PRICES_PER_MILLION, DEFAULT_RATE, DEFAULT_CACHE_DISCOUNT;
|
|
73
|
+
var init_modelPricing = __esm({
|
|
74
|
+
"src/cli/modelPricing.ts"() {
|
|
75
|
+
"use strict";
|
|
76
|
+
PRICES_PER_MILLION = {
|
|
77
|
+
// xAI Grok (list prices; grok-4.5 default reasoning_effort is "high")
|
|
78
|
+
"grok-4.5": { input: 2, output: 6, cachedInput: 0.5 },
|
|
79
|
+
"grok-4.3": { input: 1.25, output: 2.5, cachedInput: 0.2 },
|
|
80
|
+
"grok-4": { input: 3, output: 15 },
|
|
81
|
+
"grok-4-fast": { input: 0.2, output: 0.5 },
|
|
82
|
+
"grok-3": { input: 3, output: 15 },
|
|
83
|
+
"grok-3-mini": { input: 0.3, output: 0.5 },
|
|
84
|
+
"grok-2-vision": { input: 2, output: 10 },
|
|
85
|
+
// GLM / Z.AI
|
|
86
|
+
"glm-4.6": { input: 0.6, output: 2.2 },
|
|
87
|
+
"glm-4.5": { input: 0.5, output: 2 },
|
|
88
|
+
"glm-4.5-air": { input: 0.1, output: 0.6 },
|
|
89
|
+
"glm-z1": { input: 0.5, output: 2 },
|
|
90
|
+
// MiniMax
|
|
91
|
+
"MiniMax-M2.5": { input: 0.2, output: 1.1 },
|
|
92
|
+
"MiniMax-M2": { input: 0.2, output: 1.1 },
|
|
93
|
+
"MiniMax-M2-her": { input: 0.3, output: 1.2 },
|
|
94
|
+
// DeepSeek (global platform) — estimated list prices; override via
|
|
95
|
+
// ANATHEMA_PRICE_DEEPSEEK_V4_FLASH / ANATHEMA_PRICE_DEEPSEEK_V4_PRO.
|
|
96
|
+
// DeepSeek prompt-cache hits are ~10× cheaper than a cache miss.
|
|
97
|
+
"deepseek-v4-flash": { input: 0.14, output: 0.28, cachedInput: 0.014 },
|
|
98
|
+
"deepseek-v4-pro": { input: 0.55, output: 2.19, cachedInput: 0.055 },
|
|
99
|
+
// OpenAI (for openai-compatible fallback)
|
|
100
|
+
"gpt-4o": { input: 2.5, output: 10 },
|
|
101
|
+
"gpt-4o-mini": { input: 0.15, output: 0.6 },
|
|
102
|
+
"gpt-4-turbo": { input: 10, output: 30 },
|
|
103
|
+
"o1-preview": { input: 15, output: 60 },
|
|
104
|
+
"o1-mini": { input: 3, output: 12 }
|
|
105
|
+
};
|
|
106
|
+
DEFAULT_RATE = { input: 1, output: 3 };
|
|
107
|
+
DEFAULT_CACHE_DISCOUNT = 0.25;
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
|
|
24
111
|
// src/cli/grokOAuth.ts
|
|
25
112
|
var grokOAuth_exports = {};
|
|
26
113
|
__export(grokOAuth_exports, {
|
|
@@ -308,7 +395,7 @@ async function refreshGrokToken(options) {
|
|
|
308
395
|
return parseTokenResponseBody(obj, accessToken);
|
|
309
396
|
}
|
|
310
397
|
async function openBrowser(url2) {
|
|
311
|
-
const { spawn:
|
|
398
|
+
const { spawn: spawn12 } = await import("node:child_process");
|
|
312
399
|
const cmd = (() => {
|
|
313
400
|
switch (process.platform) {
|
|
314
401
|
case "darwin":
|
|
@@ -319,18 +406,18 @@ async function openBrowser(url2) {
|
|
|
319
406
|
return { bin: "xdg-open", args: [url2] };
|
|
320
407
|
}
|
|
321
408
|
})();
|
|
322
|
-
return new Promise((
|
|
409
|
+
return new Promise((resolve3, reject) => {
|
|
323
410
|
try {
|
|
324
|
-
const child =
|
|
411
|
+
const child = spawn12(cmd.bin, cmd.args, {
|
|
325
412
|
stdio: "ignore",
|
|
326
413
|
detached: true
|
|
327
414
|
});
|
|
328
415
|
child.on("error", (err) => reject(err));
|
|
329
416
|
child.on("spawn", () => {
|
|
330
417
|
child.unref();
|
|
331
|
-
|
|
418
|
+
resolve3();
|
|
332
419
|
});
|
|
333
|
-
setTimeout(() =>
|
|
420
|
+
setTimeout(() => resolve3(), 100);
|
|
334
421
|
} catch (err) {
|
|
335
422
|
reject(err);
|
|
336
423
|
}
|
|
@@ -754,6 +841,180 @@ var init_providerConfig = __esm({
|
|
|
754
841
|
}
|
|
755
842
|
});
|
|
756
843
|
|
|
844
|
+
// src/cli/modelDiscovery.ts
|
|
845
|
+
import { promises as fs3, existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
|
|
846
|
+
import { homedir } from "node:os";
|
|
847
|
+
import path4 from "node:path";
|
|
848
|
+
async function resolveDiscoveryBaseUrl(provider, options) {
|
|
849
|
+
if (options.baseUrl) return options.baseUrl;
|
|
850
|
+
const { getCustomEndpoint: getCustomEndpoint2 } = await Promise.resolve().then(() => (init_providerConfig(), providerConfig_exports));
|
|
851
|
+
const custom2 = getCustomEndpoint2(provider);
|
|
852
|
+
if (custom2) return custom2;
|
|
853
|
+
if (provider === "openai-compatible") {
|
|
854
|
+
const envBase = process.env.OPENAI_BASE_URL;
|
|
855
|
+
if (envBase && envBase.trim().length > 0) return envBase;
|
|
856
|
+
}
|
|
857
|
+
return PROVIDER_BASE_URLS[provider];
|
|
858
|
+
}
|
|
859
|
+
function defaultModelsFilePath() {
|
|
860
|
+
return process.env.ANATHEMA_MODELS_FILE ?? path4.join(homedir(), ".tmp", "zelari-code", "models.json");
|
|
861
|
+
}
|
|
862
|
+
function getModelsFilePath() {
|
|
863
|
+
return defaultModelsFilePath();
|
|
864
|
+
}
|
|
865
|
+
function loadModelsRegistry(file2 = getModelsFilePath()) {
|
|
866
|
+
if (!existsSync3(file2)) return {};
|
|
867
|
+
try {
|
|
868
|
+
const raw = readFileSync3(file2, "utf-8");
|
|
869
|
+
const parsed = JSON.parse(raw);
|
|
870
|
+
if (!parsed || typeof parsed !== "object") return {};
|
|
871
|
+
return parsed;
|
|
872
|
+
} catch {
|
|
873
|
+
return {};
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
function getCachedModels(provider, file2 = getModelsFilePath()) {
|
|
877
|
+
const registry3 = loadModelsRegistry(file2);
|
|
878
|
+
return registry3[provider];
|
|
879
|
+
}
|
|
880
|
+
function isModelsCacheStale(provider, maxAgeMs = 6 * 60 * 60 * 1e3, file2 = getModelsFilePath(), now = Date.now()) {
|
|
881
|
+
const entry = getCachedModels(provider, file2);
|
|
882
|
+
if (!entry) return true;
|
|
883
|
+
return now - entry.fetchedAt > maxAgeMs;
|
|
884
|
+
}
|
|
885
|
+
async function withRegistryLock(fn) {
|
|
886
|
+
const next = writeChain.then(async () => fn());
|
|
887
|
+
writeChain = next.catch(() => void 0);
|
|
888
|
+
return next;
|
|
889
|
+
}
|
|
890
|
+
async function readModifyWriteRegistry(mutator, file2 = getModelsFilePath()) {
|
|
891
|
+
return withRegistryLock(async () => {
|
|
892
|
+
const onDisk = loadModelsRegistry(file2);
|
|
893
|
+
const merged = mutator(onDisk);
|
|
894
|
+
await fs3.mkdir(path4.dirname(file2), { recursive: true });
|
|
895
|
+
const tmp = `${file2}.tmp-${process.pid}-${Date.now()}`;
|
|
896
|
+
await fs3.writeFile(tmp, JSON.stringify(merged, null, 2), "utf-8");
|
|
897
|
+
await fs3.rename(tmp, file2);
|
|
898
|
+
return merged;
|
|
899
|
+
});
|
|
900
|
+
}
|
|
901
|
+
async function resolveAuthToken(provider, options) {
|
|
902
|
+
if (options.authToken) return options.authToken;
|
|
903
|
+
const { resolveApiKeyWithMeta: resolveApiKeyWithMeta2, getOAuthToken: getOAuthToken2 } = await Promise.resolve().then(() => (init_keyStore(), keyStore_exports));
|
|
904
|
+
if (provider === "grok") {
|
|
905
|
+
const oauth = getOAuthToken2("grok");
|
|
906
|
+
if (oauth?.apiKey) return oauth.apiKey;
|
|
907
|
+
}
|
|
908
|
+
const resolved = await resolveApiKeyWithMeta2(provider);
|
|
909
|
+
return resolved?.apiKey;
|
|
910
|
+
}
|
|
911
|
+
function parseOpenAIModelsResponse(json2, baseUrl) {
|
|
912
|
+
if (!json2 || typeof json2 !== "object") return [];
|
|
913
|
+
const obj = json2;
|
|
914
|
+
if (!Array.isArray(obj.data)) return [];
|
|
915
|
+
const models = [];
|
|
916
|
+
for (const m of obj.data) {
|
|
917
|
+
if (!m || typeof m.id !== "string") continue;
|
|
918
|
+
const out = { id: m.id };
|
|
919
|
+
if (typeof m.created === "number") out.created = m.created;
|
|
920
|
+
if (typeof m.owned_by === "string") out.ownedBy = m.owned_by;
|
|
921
|
+
const ctx = m.context_length;
|
|
922
|
+
if (typeof ctx === "number") out.contextLength = ctx;
|
|
923
|
+
models.push(out);
|
|
924
|
+
}
|
|
925
|
+
models.sort((a, b) => a.id.localeCompare(b.id));
|
|
926
|
+
return models;
|
|
927
|
+
}
|
|
928
|
+
async function discoverModelsForProvider(provider, options = {}) {
|
|
929
|
+
const baseUrl = await resolveDiscoveryBaseUrl(provider, options);
|
|
930
|
+
if (!baseUrl || baseUrl.trim().length === 0) {
|
|
931
|
+
throw new ModelDiscoveryError(
|
|
932
|
+
`No base URL for provider "${provider}" \u2014 set one with /provider custom <url> before discovering models`,
|
|
933
|
+
"no_base_url"
|
|
934
|
+
);
|
|
935
|
+
}
|
|
936
|
+
const url2 = `${baseUrl.replace(/\/$/, "")}/models`;
|
|
937
|
+
const authToken = await resolveAuthToken(provider, options);
|
|
938
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
939
|
+
let response;
|
|
940
|
+
try {
|
|
941
|
+
const headers = { Accept: "application/json" };
|
|
942
|
+
if (authToken) headers.Authorization = `Bearer ${authToken}`;
|
|
943
|
+
response = await fetchImpl(url2, { method: "GET", headers });
|
|
944
|
+
} catch (err) {
|
|
945
|
+
throw new ModelDiscoveryError(
|
|
946
|
+
`Network error contacting ${url2}: ${err instanceof Error ? err.message : String(err)}`,
|
|
947
|
+
"network_error"
|
|
948
|
+
);
|
|
949
|
+
}
|
|
950
|
+
if (!response.ok) {
|
|
951
|
+
const body = await response.text().catch(() => "");
|
|
952
|
+
throw new ModelDiscoveryError(
|
|
953
|
+
`HTTP ${response.status} from ${url2}: ${body.slice(0, 200)}`,
|
|
954
|
+
`http_${response.status}`
|
|
955
|
+
);
|
|
956
|
+
}
|
|
957
|
+
let json2;
|
|
958
|
+
try {
|
|
959
|
+
json2 = await response.json();
|
|
960
|
+
} catch (err) {
|
|
961
|
+
throw new ModelDiscoveryError(
|
|
962
|
+
`Invalid JSON from ${url2}: ${err instanceof Error ? err.message : String(err)}`,
|
|
963
|
+
"invalid_json"
|
|
964
|
+
);
|
|
965
|
+
}
|
|
966
|
+
const models = parseOpenAIModelsResponse(json2, baseUrl);
|
|
967
|
+
if (models.length === 0) {
|
|
968
|
+
throw new ModelDiscoveryError(
|
|
969
|
+
`Provider ${provider} returned 0 models \u2014 refusing to overwrite cache`,
|
|
970
|
+
"empty_response"
|
|
971
|
+
);
|
|
972
|
+
}
|
|
973
|
+
const entry = {
|
|
974
|
+
models,
|
|
975
|
+
fetchedAt: Date.now(),
|
|
976
|
+
baseUrl
|
|
977
|
+
};
|
|
978
|
+
if (!options.skipCacheWrite) {
|
|
979
|
+
const file2 = getModelsFilePath();
|
|
980
|
+
await readModifyWriteRegistry((current) => {
|
|
981
|
+
current[provider] = entry;
|
|
982
|
+
return current;
|
|
983
|
+
}, file2);
|
|
984
|
+
}
|
|
985
|
+
return entry;
|
|
986
|
+
}
|
|
987
|
+
function discoverModelsInBackground(provider, options = {}) {
|
|
988
|
+
discoverModelsForProvider(provider, options).catch((err) => {
|
|
989
|
+
if (err instanceof ModelDiscoveryError && options.onError) {
|
|
990
|
+
options.onError(err);
|
|
991
|
+
}
|
|
992
|
+
});
|
|
993
|
+
}
|
|
994
|
+
var PROVIDER_BASE_URLS, writeChain, ModelDiscoveryError;
|
|
995
|
+
var init_modelDiscovery = __esm({
|
|
996
|
+
"src/cli/modelDiscovery.ts"() {
|
|
997
|
+
"use strict";
|
|
998
|
+
PROVIDER_BASE_URLS = {
|
|
999
|
+
"grok": "https://api.x.ai/v1",
|
|
1000
|
+
// Must match PROVIDER_ENDPOINTS in provider/openai-compatible.ts (chat host).
|
|
1001
|
+
"glm": "https://api.z.ai/api/coding/paas/v4",
|
|
1002
|
+
"minimax": "https://api.minimax.io/v1",
|
|
1003
|
+
// Must match PROVIDER_ENDPOINTS in provider/openai-compatible.ts (chat host).
|
|
1004
|
+
"deepseek": "https://api.deepseek.com",
|
|
1005
|
+
"openai-compatible": "https://api.x.ai/v1"
|
|
1006
|
+
};
|
|
1007
|
+
writeChain = Promise.resolve();
|
|
1008
|
+
ModelDiscoveryError = class extends Error {
|
|
1009
|
+
constructor(message, code) {
|
|
1010
|
+
super(message);
|
|
1011
|
+
this.code = code;
|
|
1012
|
+
this.name = "ModelDiscoveryError";
|
|
1013
|
+
}
|
|
1014
|
+
};
|
|
1015
|
+
}
|
|
1016
|
+
});
|
|
1017
|
+
|
|
757
1018
|
// packages/core/dist/agents/skills.js
|
|
758
1019
|
function getSkillsByCategory(cat) {
|
|
759
1020
|
return SKILL_CATALOG.filter((s) => s.category === cat);
|
|
@@ -1543,10 +1804,10 @@ function mergeDefs(...defs) {
|
|
|
1543
1804
|
function cloneDef(schema) {
|
|
1544
1805
|
return mergeDefs(schema._zod.def);
|
|
1545
1806
|
}
|
|
1546
|
-
function getElementAtPath(obj,
|
|
1547
|
-
if (!
|
|
1807
|
+
function getElementAtPath(obj, path40) {
|
|
1808
|
+
if (!path40)
|
|
1548
1809
|
return obj;
|
|
1549
|
-
return
|
|
1810
|
+
return path40.reduce((acc, key) => acc?.[key], obj);
|
|
1550
1811
|
}
|
|
1551
1812
|
function promiseAllObject(promisesObj) {
|
|
1552
1813
|
const keys = Object.keys(promisesObj);
|
|
@@ -1874,11 +2135,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
1874
2135
|
}
|
|
1875
2136
|
return false;
|
|
1876
2137
|
}
|
|
1877
|
-
function prefixIssues(
|
|
2138
|
+
function prefixIssues(path40, issues) {
|
|
1878
2139
|
return issues.map((iss) => {
|
|
1879
2140
|
var _a3;
|
|
1880
2141
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
1881
|
-
iss.path.unshift(
|
|
2142
|
+
iss.path.unshift(path40);
|
|
1882
2143
|
return iss;
|
|
1883
2144
|
});
|
|
1884
2145
|
}
|
|
@@ -2096,16 +2357,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
|
|
|
2096
2357
|
}
|
|
2097
2358
|
function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
2098
2359
|
const fieldErrors = { _errors: [] };
|
|
2099
|
-
const processError = (error52,
|
|
2360
|
+
const processError = (error52, path40 = []) => {
|
|
2100
2361
|
for (const issue2 of error52.issues) {
|
|
2101
2362
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
2102
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
2363
|
+
issue2.errors.map((issues) => processError({ issues }, [...path40, ...issue2.path]));
|
|
2103
2364
|
} else if (issue2.code === "invalid_key") {
|
|
2104
|
-
processError({ issues: issue2.issues }, [...
|
|
2365
|
+
processError({ issues: issue2.issues }, [...path40, ...issue2.path]);
|
|
2105
2366
|
} else if (issue2.code === "invalid_element") {
|
|
2106
|
-
processError({ issues: issue2.issues }, [...
|
|
2367
|
+
processError({ issues: issue2.issues }, [...path40, ...issue2.path]);
|
|
2107
2368
|
} else {
|
|
2108
|
-
const fullpath = [...
|
|
2369
|
+
const fullpath = [...path40, ...issue2.path];
|
|
2109
2370
|
if (fullpath.length === 0) {
|
|
2110
2371
|
fieldErrors._errors.push(mapper(issue2));
|
|
2111
2372
|
} else {
|
|
@@ -2132,17 +2393,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
|
2132
2393
|
}
|
|
2133
2394
|
function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
2134
2395
|
const result = { errors: [] };
|
|
2135
|
-
const processError = (error52,
|
|
2396
|
+
const processError = (error52, path40 = []) => {
|
|
2136
2397
|
var _a3, _b;
|
|
2137
2398
|
for (const issue2 of error52.issues) {
|
|
2138
2399
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
2139
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
2400
|
+
issue2.errors.map((issues) => processError({ issues }, [...path40, ...issue2.path]));
|
|
2140
2401
|
} else if (issue2.code === "invalid_key") {
|
|
2141
|
-
processError({ issues: issue2.issues }, [...
|
|
2402
|
+
processError({ issues: issue2.issues }, [...path40, ...issue2.path]);
|
|
2142
2403
|
} else if (issue2.code === "invalid_element") {
|
|
2143
|
-
processError({ issues: issue2.issues }, [...
|
|
2404
|
+
processError({ issues: issue2.issues }, [...path40, ...issue2.path]);
|
|
2144
2405
|
} else {
|
|
2145
|
-
const fullpath = [...
|
|
2406
|
+
const fullpath = [...path40, ...issue2.path];
|
|
2146
2407
|
if (fullpath.length === 0) {
|
|
2147
2408
|
result.errors.push(mapper(issue2));
|
|
2148
2409
|
continue;
|
|
@@ -2174,8 +2435,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
|
2174
2435
|
}
|
|
2175
2436
|
function toDotPath(_path) {
|
|
2176
2437
|
const segs = [];
|
|
2177
|
-
const
|
|
2178
|
-
for (const seg of
|
|
2438
|
+
const path40 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
2439
|
+
for (const seg of path40) {
|
|
2179
2440
|
if (typeof seg === "number")
|
|
2180
2441
|
segs.push(`[${seg}]`);
|
|
2181
2442
|
else if (typeof seg === "symbol")
|
|
@@ -15678,13 +15939,13 @@ function resolveRef(ref, ctx) {
|
|
|
15678
15939
|
if (!ref.startsWith("#")) {
|
|
15679
15940
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
15680
15941
|
}
|
|
15681
|
-
const
|
|
15682
|
-
if (
|
|
15942
|
+
const path40 = ref.slice(1).split("/").filter(Boolean);
|
|
15943
|
+
if (path40.length === 0) {
|
|
15683
15944
|
return ctx.rootSchema;
|
|
15684
15945
|
}
|
|
15685
15946
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
15686
|
-
if (
|
|
15687
|
-
const key =
|
|
15947
|
+
if (path40[0] === defsKey) {
|
|
15948
|
+
const key = path40[1];
|
|
15688
15949
|
if (!key || !ctx.defs[key]) {
|
|
15689
15950
|
throw new Error(`Reference not found: ${ref}`);
|
|
15690
15951
|
}
|
|
@@ -16737,12 +16998,12 @@ function withNodeDirOnPath(base) {
|
|
|
16737
16998
|
const nodeDir = dirname(process.execPath);
|
|
16738
16999
|
if (!nodeDir)
|
|
16739
17000
|
return env;
|
|
16740
|
-
const
|
|
17001
|
+
const sep2 = process.platform === "win32" ? ";" : ":";
|
|
16741
17002
|
const current = env.PATH ?? env.Path ?? "";
|
|
16742
|
-
const parts = current.split(
|
|
17003
|
+
const parts = current.split(sep2).filter((p3) => p3.length > 0);
|
|
16743
17004
|
const has = parts.some((p3) => p3.toLowerCase() === nodeDir.toLowerCase());
|
|
16744
17005
|
if (!has) {
|
|
16745
|
-
env.PATH = `${nodeDir}${
|
|
17006
|
+
env.PATH = `${nodeDir}${sep2}${current}`;
|
|
16746
17007
|
}
|
|
16747
17008
|
} catch {
|
|
16748
17009
|
}
|
|
@@ -16769,7 +17030,7 @@ var init_shell = __esm({
|
|
|
16769
17030
|
timeoutMs: 6e4,
|
|
16770
17031
|
inputSchema: BashArgsSchema,
|
|
16771
17032
|
execute: async (args, ctx) => {
|
|
16772
|
-
return new Promise((
|
|
17033
|
+
return new Promise((resolve3) => {
|
|
16773
17034
|
const start = Date.now();
|
|
16774
17035
|
const cwd = args.cwd ?? ctx.cwd;
|
|
16775
17036
|
const resolved = resolveShell();
|
|
@@ -16817,14 +17078,14 @@ var init_shell = __esm({
|
|
|
16817
17078
|
});
|
|
16818
17079
|
const timer = setTimeout(() => {
|
|
16819
17080
|
child.kill("SIGTERM");
|
|
16820
|
-
|
|
17081
|
+
resolve3(typedErr(`Bash command timed out after ${args.timeoutMs}ms. If it was waiting for interactive input: stdin is closed \u2014 pass non-interactive flags or create the files directly instead of retrying.`));
|
|
16821
17082
|
}, args.timeoutMs);
|
|
16822
17083
|
child.on("close", (code) => {
|
|
16823
17084
|
clearTimeout(timer);
|
|
16824
17085
|
const cappedStdout = stdout.slice(0, maxBuffer);
|
|
16825
17086
|
const cappedStderr = stderr.slice(0, maxBuffer);
|
|
16826
17087
|
const interactive = INTERACTIVE_CANCEL_RE.test(cappedStdout) || INTERACTIVE_CANCEL_RE.test(cappedStderr);
|
|
16827
|
-
|
|
17088
|
+
resolve3(typedOk({
|
|
16828
17089
|
stdout: cappedStdout,
|
|
16829
17090
|
stderr: cappedStderr,
|
|
16830
17091
|
exitCode: code ?? -1,
|
|
@@ -16835,7 +17096,7 @@ var init_shell = __esm({
|
|
|
16835
17096
|
});
|
|
16836
17097
|
child.on("error", (err) => {
|
|
16837
17098
|
clearTimeout(timer);
|
|
16838
|
-
|
|
17099
|
+
resolve3(typedErr(err.message));
|
|
16839
17100
|
});
|
|
16840
17101
|
});
|
|
16841
17102
|
}
|
|
@@ -17848,11 +18109,11 @@ var init_tools = __esm({
|
|
|
17848
18109
|
if (!ctx.addDocument)
|
|
17849
18110
|
return "Knowledge vault tool not available.";
|
|
17850
18111
|
const title = args["title"] || "New Document";
|
|
17851
|
-
const
|
|
18112
|
+
const path40 = args["path"] || `notes/${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
|
|
17852
18113
|
const content = args["content"] || "";
|
|
17853
18114
|
const tags = args["tags"] || [];
|
|
17854
18115
|
ctx.addDocument({
|
|
17855
|
-
path:
|
|
18116
|
+
path: path40,
|
|
17856
18117
|
title,
|
|
17857
18118
|
content,
|
|
17858
18119
|
format: "markdown",
|
|
@@ -17861,7 +18122,7 @@ var init_tools = __esm({
|
|
|
17861
18122
|
workspaceId: ctx.workspaceId
|
|
17862
18123
|
});
|
|
17863
18124
|
ctx.addActivity("vault", "created document", title);
|
|
17864
|
-
return `Document "${title}" created at "${
|
|
18125
|
+
return `Document "${title}" created at "${path40}".`;
|
|
17865
18126
|
}
|
|
17866
18127
|
}
|
|
17867
18128
|
];
|
|
@@ -20548,8 +20809,8 @@ function wrapLegacyStream(legacyStream, params, _messages, _tools) {
|
|
|
20548
20809
|
}
|
|
20549
20810
|
if (done)
|
|
20550
20811
|
break;
|
|
20551
|
-
await new Promise((
|
|
20552
|
-
resolveNext =
|
|
20812
|
+
await new Promise((resolve3) => {
|
|
20813
|
+
resolveNext = resolve3;
|
|
20553
20814
|
});
|
|
20554
20815
|
}
|
|
20555
20816
|
await promise2;
|
|
@@ -20668,19 +20929,60 @@ __export(openai_compatible_exports, {
|
|
|
20668
20929
|
resolveBaseUrl: () => resolveBaseUrl
|
|
20669
20930
|
});
|
|
20670
20931
|
function abortableSleep(ms, signal) {
|
|
20671
|
-
return new Promise((
|
|
20672
|
-
if (signal?.aborted) return
|
|
20673
|
-
const t = setTimeout(
|
|
20932
|
+
return new Promise((resolve3) => {
|
|
20933
|
+
if (signal?.aborted) return resolve3();
|
|
20934
|
+
const t = setTimeout(resolve3, ms);
|
|
20674
20935
|
signal?.addEventListener(
|
|
20675
20936
|
"abort",
|
|
20676
20937
|
() => {
|
|
20677
20938
|
clearTimeout(t);
|
|
20678
|
-
|
|
20939
|
+
resolve3();
|
|
20679
20940
|
},
|
|
20680
20941
|
{ once: true }
|
|
20681
20942
|
);
|
|
20682
20943
|
});
|
|
20683
20944
|
}
|
|
20945
|
+
function isTimeoutAbortMessage(msg) {
|
|
20946
|
+
const m = msg.toLowerCase();
|
|
20947
|
+
return m.includes("aborted due to timeout") || m.includes("timeout") || m.includes("the operation was aborted");
|
|
20948
|
+
}
|
|
20949
|
+
async function readChunkWithTimeout(reader, opts) {
|
|
20950
|
+
if (opts.signal?.aborted) {
|
|
20951
|
+
throw new Error("aborted");
|
|
20952
|
+
}
|
|
20953
|
+
const remaining = opts.deadlineMs - Date.now();
|
|
20954
|
+
if (remaining <= 0) {
|
|
20955
|
+
throw new Error(
|
|
20956
|
+
`Provider stream exceeded max duration (${Math.round(PROVIDER_STREAM_MAX_MS / 1e3)}s). Raise ZELARI_PROVIDER_STREAM_MAX_MS if needed.`
|
|
20957
|
+
);
|
|
20958
|
+
}
|
|
20959
|
+
const waitMs = Math.min(opts.idleMs, remaining);
|
|
20960
|
+
let idleTimer;
|
|
20961
|
+
let onAbort;
|
|
20962
|
+
try {
|
|
20963
|
+
return await Promise.race([
|
|
20964
|
+
reader.read(),
|
|
20965
|
+
new Promise((_, reject) => {
|
|
20966
|
+
idleTimer = setTimeout(() => {
|
|
20967
|
+
reject(
|
|
20968
|
+
new Error(
|
|
20969
|
+
`Provider stream idle for ${Math.round(waitMs / 1e3)}s (no tokens). The model/gateway stalled \u2014 try again or switch model. Override with ZELARI_PROVIDER_STREAM_IDLE_MS.`
|
|
20970
|
+
)
|
|
20971
|
+
);
|
|
20972
|
+
}, waitMs);
|
|
20973
|
+
if (opts.signal) {
|
|
20974
|
+
onAbort = () => reject(new Error("aborted"));
|
|
20975
|
+
opts.signal.addEventListener("abort", onAbort, { once: true });
|
|
20976
|
+
}
|
|
20977
|
+
})
|
|
20978
|
+
]);
|
|
20979
|
+
} finally {
|
|
20980
|
+
if (idleTimer) clearTimeout(idleTimer);
|
|
20981
|
+
if (onAbort && opts.signal) {
|
|
20982
|
+
opts.signal.removeEventListener("abort", onAbort);
|
|
20983
|
+
}
|
|
20984
|
+
}
|
|
20985
|
+
}
|
|
20684
20986
|
function backoffDelay(attempt, retryAfterHeader) {
|
|
20685
20987
|
if (retryAfterHeader) {
|
|
20686
20988
|
const seconds = Number.parseFloat(retryAfterHeader);
|
|
@@ -20782,31 +21084,46 @@ function openaiCompatibleProvider(config2) {
|
|
|
20782
21084
|
return;
|
|
20783
21085
|
}
|
|
20784
21086
|
try {
|
|
20785
|
-
const
|
|
20786
|
-
const
|
|
20787
|
-
|
|
20788
|
-
|
|
20789
|
-
|
|
20790
|
-
|
|
20791
|
-
|
|
20792
|
-
|
|
20793
|
-
|
|
20794
|
-
|
|
20795
|
-
|
|
20796
|
-
|
|
20797
|
-
|
|
20798
|
-
|
|
20799
|
-
|
|
20800
|
-
|
|
20801
|
-
|
|
21087
|
+
const connectController = new AbortController();
|
|
21088
|
+
const connectTimer = setTimeout(() => {
|
|
21089
|
+
connectController.abort(
|
|
21090
|
+
new Error(
|
|
21091
|
+
`Provider connect timeout after ${Math.round(PROVIDER_CONNECT_TIMEOUT_MS / 1e3)}s (no response headers). Override ZELARI_PROVIDER_CONNECT_TIMEOUT_MS.`
|
|
21092
|
+
)
|
|
21093
|
+
);
|
|
21094
|
+
}, PROVIDER_CONNECT_TIMEOUT_MS);
|
|
21095
|
+
const signals = [connectController.signal];
|
|
21096
|
+
if (params.signal) signals.push(params.signal);
|
|
21097
|
+
const fetchSignal = signals.length === 1 ? signals[0] : AbortSignal.any(signals);
|
|
21098
|
+
try {
|
|
21099
|
+
response = await fetch(`${config2.baseUrl}/chat/completions`, {
|
|
21100
|
+
method: "POST",
|
|
21101
|
+
headers: {
|
|
21102
|
+
"Content-Type": "application/json",
|
|
21103
|
+
Authorization: `Bearer ${config2.apiKey}`
|
|
21104
|
+
},
|
|
21105
|
+
body: JSON.stringify(body),
|
|
21106
|
+
// Cancel aborts the HTTP request; stream idle is enforced below
|
|
21107
|
+
// per-chunk so active multi-minute streams are not killed.
|
|
21108
|
+
signal: fetchSignal
|
|
21109
|
+
});
|
|
21110
|
+
} finally {
|
|
21111
|
+
clearTimeout(connectTimer);
|
|
21112
|
+
}
|
|
20802
21113
|
} catch (err) {
|
|
20803
21114
|
lastStatus = 0;
|
|
20804
|
-
|
|
21115
|
+
const asErr = err instanceof Error ? err : null;
|
|
21116
|
+
const causeMsg = asErr?.cause instanceof Error ? asErr.cause.message : typeof asErr?.cause === "string" ? String(asErr.cause) : "";
|
|
21117
|
+
lastErrText = (causeMsg && causeMsg.includes("Provider connect") ? causeMsg : null) || asErr?.message || String(err);
|
|
21118
|
+
if (isTimeoutAbortMessage(lastErrText) && !lastErrText.includes("Provider connect")) {
|
|
21119
|
+
lastErrText = `Provider connect timeout after ${Math.round(PROVIDER_CONNECT_TIMEOUT_MS / 1e3)}s (no response headers). Last error: ${lastErrText}`;
|
|
21120
|
+
}
|
|
20805
21121
|
if (params.signal?.aborted) {
|
|
20806
21122
|
yield { kind: "error", message: "aborted" };
|
|
20807
21123
|
return;
|
|
20808
21124
|
}
|
|
20809
|
-
|
|
21125
|
+
const maxAttempts = isTimeoutAbortMessage(lastErrText) ? Math.min(1, MAX_RETRIES) : MAX_RETRIES;
|
|
21126
|
+
if (attempt < maxAttempts) {
|
|
20810
21127
|
await abortableSleep(backoffDelay(attempt, null), params.signal);
|
|
20811
21128
|
continue;
|
|
20812
21129
|
}
|
|
@@ -20861,9 +21178,30 @@ function openaiCompatibleProvider(config2) {
|
|
|
20861
21178
|
}
|
|
20862
21179
|
toolCallAccumulator.clear();
|
|
20863
21180
|
};
|
|
21181
|
+
const streamDeadline = Date.now() + PROVIDER_STREAM_MAX_MS;
|
|
20864
21182
|
try {
|
|
20865
21183
|
while (true) {
|
|
20866
|
-
|
|
21184
|
+
let chunk;
|
|
21185
|
+
try {
|
|
21186
|
+
chunk = await readChunkWithTimeout(reader, {
|
|
21187
|
+
idleMs: PROVIDER_STREAM_IDLE_MS,
|
|
21188
|
+
deadlineMs: streamDeadline,
|
|
21189
|
+
signal: params.signal
|
|
21190
|
+
});
|
|
21191
|
+
} catch (err) {
|
|
21192
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
21193
|
+
if (msg === "aborted" || params.signal?.aborted) {
|
|
21194
|
+
yield { kind: "error", message: "aborted" };
|
|
21195
|
+
return;
|
|
21196
|
+
}
|
|
21197
|
+
try {
|
|
21198
|
+
await reader.cancel(msg);
|
|
21199
|
+
} catch {
|
|
21200
|
+
}
|
|
21201
|
+
yield { kind: "error", message: msg };
|
|
21202
|
+
return;
|
|
21203
|
+
}
|
|
21204
|
+
const { value, done } = chunk;
|
|
20867
21205
|
if (done) break;
|
|
20868
21206
|
buffer += decoder.decode(value, { stream: true });
|
|
20869
21207
|
const lines = buffer.split("\n");
|
|
@@ -20989,7 +21327,7 @@ async function providerConfigFor(providerId) {
|
|
|
20989
21327
|
providerId
|
|
20990
21328
|
};
|
|
20991
21329
|
}
|
|
20992
|
-
var RETRYABLE_STATUSES, MAX_RETRIES, BACKOFF_BASE_MS, BACKOFF_CAP_MS,
|
|
21330
|
+
var RETRYABLE_STATUSES, MAX_RETRIES, BACKOFF_BASE_MS, BACKOFF_CAP_MS, PROVIDER_CONNECT_TIMEOUT_MS, PROVIDER_STREAM_IDLE_MS, PROVIDER_STREAM_MAX_MS, PROVIDER_ENDPOINTS;
|
|
20993
21331
|
var init_openai_compatible = __esm({
|
|
20994
21332
|
"src/cli/provider/openai-compatible.ts"() {
|
|
20995
21333
|
"use strict";
|
|
@@ -21003,10 +21341,20 @@ var init_openai_compatible = __esm({
|
|
|
21003
21341
|
})();
|
|
21004
21342
|
BACKOFF_BASE_MS = 500;
|
|
21005
21343
|
BACKOFF_CAP_MS = 8e3;
|
|
21006
|
-
|
|
21007
|
-
const raw = process.env.
|
|
21344
|
+
PROVIDER_CONNECT_TIMEOUT_MS = (() => {
|
|
21345
|
+
const raw = process.env.ZELARI_PROVIDER_CONNECT_TIMEOUT_MS;
|
|
21346
|
+
const n = raw ? Number.parseInt(raw, 10) : 9e4;
|
|
21347
|
+
return Number.isFinite(n) && n >= 5e3 ? n : 9e4;
|
|
21348
|
+
})();
|
|
21349
|
+
PROVIDER_STREAM_IDLE_MS = (() => {
|
|
21350
|
+
const raw = process.env.ZELARI_PROVIDER_STREAM_IDLE_MS ?? process.env.ZELARI_PROVIDER_TIMEOUT_MS;
|
|
21008
21351
|
const n = raw ? Number.parseInt(raw, 10) : 3e5;
|
|
21009
|
-
return Number.isFinite(n) && n >=
|
|
21352
|
+
return Number.isFinite(n) && n >= 15e3 ? n : 3e5;
|
|
21353
|
+
})();
|
|
21354
|
+
PROVIDER_STREAM_MAX_MS = (() => {
|
|
21355
|
+
const raw = process.env.ZELARI_PROVIDER_STREAM_MAX_MS;
|
|
21356
|
+
const n = raw ? Number.parseInt(raw, 10) : 18e5;
|
|
21357
|
+
return Number.isFinite(n) && n >= 6e4 ? n : 18e5;
|
|
21010
21358
|
})();
|
|
21011
21359
|
PROVIDER_ENDPOINTS = {
|
|
21012
21360
|
"openai-compatible": "https://api.x.ai/v1",
|
|
@@ -21059,9 +21407,9 @@ function spillToolOutput(fullText, meta3) {
|
|
|
21059
21407
|
const rnd = randomBytes(3).toString("hex");
|
|
21060
21408
|
const safeTool = (meta3?.toolName ?? "tool").replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 32);
|
|
21061
21409
|
const file2 = `${stamp}-${safeTool}-${hash3}-${rnd}.txt`;
|
|
21062
|
-
const
|
|
21063
|
-
writeFileSync5(
|
|
21064
|
-
return
|
|
21410
|
+
const path40 = join(dir, file2);
|
|
21411
|
+
writeFileSync5(path40, fullText, "utf8");
|
|
21412
|
+
return path40;
|
|
21065
21413
|
} catch {
|
|
21066
21414
|
return null;
|
|
21067
21415
|
}
|
|
@@ -21107,10 +21455,10 @@ function truncateToolResult(text, capOrOpts = TOOL_RESULT_LINE_CAP) {
|
|
|
21107
21455
|
${tail}`;
|
|
21108
21456
|
}
|
|
21109
21457
|
if (doSpill) {
|
|
21110
|
-
const
|
|
21111
|
-
if (
|
|
21458
|
+
const path40 = spillToolOutput(text, { toolName: opts.toolName });
|
|
21459
|
+
if (path40) {
|
|
21112
21460
|
const spillNote = `
|
|
21113
|
-
\u2026 [full output spilled to: ${
|
|
21461
|
+
\u2026 [full output spilled to: ${path40} \u2014 re-read with read_file if you need the complete text] \u2026`;
|
|
21114
21462
|
if (preview.includes("] \u2026\n")) {
|
|
21115
21463
|
preview = preview.replace("] \u2026\n", `] \u2026${spillNote}
|
|
21116
21464
|
`);
|
|
@@ -21581,14 +21929,14 @@ var init_engine = __esm({
|
|
|
21581
21929
|
parse: parseRuffJson
|
|
21582
21930
|
}
|
|
21583
21931
|
];
|
|
21584
|
-
defaultRunner = (cmd, args, opts) => new Promise((
|
|
21932
|
+
defaultRunner = (cmd, args, opts) => new Promise((resolve3) => {
|
|
21585
21933
|
let stdout = "";
|
|
21586
21934
|
let stderr = "";
|
|
21587
21935
|
let settled = false;
|
|
21588
21936
|
const done = (r) => {
|
|
21589
21937
|
if (settled) return;
|
|
21590
21938
|
settled = true;
|
|
21591
|
-
|
|
21939
|
+
resolve3(r);
|
|
21592
21940
|
};
|
|
21593
21941
|
let child;
|
|
21594
21942
|
try {
|
|
@@ -22257,12 +22605,12 @@ var init_client = __esm({
|
|
|
22257
22605
|
if (this.closed) return Promise.reject(new Error("LSP transport is closed"));
|
|
22258
22606
|
const id = this.nextId++;
|
|
22259
22607
|
const msg = { jsonrpc: "2.0", id, method, ...params !== void 0 ? { params } : {} };
|
|
22260
|
-
return new Promise((
|
|
22608
|
+
return new Promise((resolve3, reject) => {
|
|
22261
22609
|
const timer = setTimeout(() => {
|
|
22262
22610
|
this.pending.delete(id);
|
|
22263
22611
|
reject(new Error(`LSP request "${method}" timed out after ${this.timeoutMs}ms`));
|
|
22264
22612
|
}, this.timeoutMs);
|
|
22265
|
-
this.pending.set(id, { resolve, reject, timer });
|
|
22613
|
+
this.pending.set(id, { resolve: resolve3, reject, timer });
|
|
22266
22614
|
this.transport.send(encodeMessage(msg));
|
|
22267
22615
|
});
|
|
22268
22616
|
}
|
|
@@ -22528,8 +22876,8 @@ var init_manager = __esm({
|
|
|
22528
22876
|
if (cached2 !== void 0) return cached2;
|
|
22529
22877
|
let resolveInit;
|
|
22530
22878
|
let rejectInit;
|
|
22531
|
-
const initialized = new Promise((
|
|
22532
|
-
resolveInit =
|
|
22879
|
+
const initialized = new Promise((resolve3, reject) => {
|
|
22880
|
+
resolveInit = resolve3;
|
|
22533
22881
|
rejectInit = reject;
|
|
22534
22882
|
});
|
|
22535
22883
|
let entry = null;
|
|
@@ -23248,8 +23596,8 @@ async function runBrowserCheck(options, loader) {
|
|
|
23248
23596
|
const failedRequests = [];
|
|
23249
23597
|
const evaluateResults = [];
|
|
23250
23598
|
const base = { ok: false, consoleErrors, pageErrors, failedRequests };
|
|
23251
|
-
const
|
|
23252
|
-
const pw = await
|
|
23599
|
+
const resolve3 = loader ?? (() => loadPlaywright(options.cwd ?? process.cwd()));
|
|
23600
|
+
const pw = await resolve3();
|
|
23253
23601
|
if (!pw) {
|
|
23254
23602
|
return {
|
|
23255
23603
|
...base,
|
|
@@ -23542,21 +23890,21 @@ function normalizeAuth(auth) {
|
|
|
23542
23890
|
return "agent";
|
|
23543
23891
|
}
|
|
23544
23892
|
function readSecrets() {
|
|
23545
|
-
const
|
|
23546
|
-
if (!existsSync11(
|
|
23893
|
+
const path40 = getSshSecretsPath();
|
|
23894
|
+
if (!existsSync11(path40)) return {};
|
|
23547
23895
|
try {
|
|
23548
|
-
return JSON.parse(readFileSync8(
|
|
23896
|
+
return JSON.parse(readFileSync8(path40, "utf8"));
|
|
23549
23897
|
} catch {
|
|
23550
23898
|
return {};
|
|
23551
23899
|
}
|
|
23552
23900
|
}
|
|
23553
23901
|
function writeSecrets(data) {
|
|
23554
|
-
const
|
|
23555
|
-
mkdirSync7(dirname2(
|
|
23556
|
-
writeFileSync6(
|
|
23902
|
+
const path40 = getSshSecretsPath();
|
|
23903
|
+
mkdirSync7(dirname2(path40), { recursive: true });
|
|
23904
|
+
writeFileSync6(path40, `${JSON.stringify(data, null, 2)}
|
|
23557
23905
|
`, "utf8");
|
|
23558
23906
|
try {
|
|
23559
|
-
chmodSync(
|
|
23907
|
+
chmodSync(path40, 384);
|
|
23560
23908
|
} catch {
|
|
23561
23909
|
}
|
|
23562
23910
|
}
|
|
@@ -23585,10 +23933,10 @@ function deleteSshPassword(id) {
|
|
|
23585
23933
|
writeSecrets({ passwords });
|
|
23586
23934
|
}
|
|
23587
23935
|
function readStore2() {
|
|
23588
|
-
const
|
|
23589
|
-
if (!existsSync11(
|
|
23936
|
+
const path40 = getSshTargetsPath();
|
|
23937
|
+
if (!existsSync11(path40)) return [];
|
|
23590
23938
|
try {
|
|
23591
|
-
const parsed = JSON.parse(readFileSync8(
|
|
23939
|
+
const parsed = JSON.parse(readFileSync8(path40, "utf8"));
|
|
23592
23940
|
const list = Array.isArray(parsed.targets) ? parsed.targets : [];
|
|
23593
23941
|
return list.filter(
|
|
23594
23942
|
(t) => t && typeof t.id === "string" && typeof t.host === "string" && typeof t.user === "string"
|
|
@@ -23603,11 +23951,11 @@ function readStore2() {
|
|
|
23603
23951
|
}
|
|
23604
23952
|
}
|
|
23605
23953
|
function writeStore2(targets) {
|
|
23606
|
-
const
|
|
23607
|
-
mkdirSync7(dirname2(
|
|
23954
|
+
const path40 = getSshTargetsPath();
|
|
23955
|
+
mkdirSync7(dirname2(path40), { recursive: true });
|
|
23608
23956
|
const clean = targets.map(({ hasPassword: _hp, ...t }) => t);
|
|
23609
23957
|
writeFileSync6(
|
|
23610
|
-
|
|
23958
|
+
path40,
|
|
23611
23959
|
`${JSON.stringify({ targets: clean }, null, 2)}
|
|
23612
23960
|
`,
|
|
23613
23961
|
"utf8"
|
|
@@ -23741,9 +24089,9 @@ exec node "$(dirname "$0")/askpass.cjs"
|
|
|
23741
24089
|
return sh;
|
|
23742
24090
|
}
|
|
23743
24091
|
function runSsh(target, remoteCommand, timeoutMs = 6e4) {
|
|
23744
|
-
return new Promise((
|
|
24092
|
+
return new Promise((resolve3) => {
|
|
23745
24093
|
if (target.auth === "password" && !getSshPassword(target.id)) {
|
|
23746
|
-
|
|
24094
|
+
resolve3({
|
|
23747
24095
|
code: 1,
|
|
23748
24096
|
stdout: "",
|
|
23749
24097
|
stderr: "No password stored for this target \u2014 edit target and set password"
|
|
@@ -23775,7 +24123,7 @@ function runSsh(target, remoteCommand, timeoutMs = 6e4) {
|
|
|
23775
24123
|
});
|
|
23776
24124
|
const timer = setTimeout(() => {
|
|
23777
24125
|
child.kill("SIGTERM");
|
|
23778
|
-
|
|
24126
|
+
resolve3({
|
|
23779
24127
|
code: 124,
|
|
23780
24128
|
stdout,
|
|
23781
24129
|
stderr: stderr + "\n[ssh] timeout"
|
|
@@ -23783,7 +24131,7 @@ function runSsh(target, remoteCommand, timeoutMs = 6e4) {
|
|
|
23783
24131
|
}, timeoutMs);
|
|
23784
24132
|
child.on("error", (err) => {
|
|
23785
24133
|
clearTimeout(timer);
|
|
23786
|
-
|
|
24134
|
+
resolve3({
|
|
23787
24135
|
code: 127,
|
|
23788
24136
|
stdout,
|
|
23789
24137
|
stderr: err.message.includes("ENOENT") ? "ssh not found on PATH \u2014 install OpenSSH client" : err.message
|
|
@@ -23791,7 +24139,7 @@ function runSsh(target, remoteCommand, timeoutMs = 6e4) {
|
|
|
23791
24139
|
});
|
|
23792
24140
|
child.on("close", (code) => {
|
|
23793
24141
|
clearTimeout(timer);
|
|
23794
|
-
|
|
24142
|
+
resolve3({ code: code ?? 1, stdout, stderr });
|
|
23795
24143
|
});
|
|
23796
24144
|
});
|
|
23797
24145
|
}
|
|
@@ -23853,11 +24201,11 @@ function formatSshTargetsForPrompt() {
|
|
|
23853
24201
|
];
|
|
23854
24202
|
for (const t of targets) {
|
|
23855
24203
|
const tags = t.tags?.length ? ` tags=[${t.tags.join(",")}]` : "";
|
|
23856
|
-
const
|
|
24204
|
+
const path40 = t.defaultRemotePath ? ` remotePath=${t.defaultRemotePath}` : "";
|
|
23857
24205
|
const allow = t.allowedCommands?.length ? ` allowed=${t.allowedCommands.join("|")}` : " allowed=status-only";
|
|
23858
24206
|
const auth = t.auth === "password" ? " auth=password" : t.auth === "keyPath" ? " auth=key" : " auth=agent";
|
|
23859
24207
|
lines.push(
|
|
23860
|
-
`- id=${t.id} name=${t.name} ${t.user}@${t.host}:${t.port ?? 22}${auth}${
|
|
24208
|
+
`- id=${t.id} name=${t.name} ${t.user}@${t.host}:${t.port ?? 22}${auth}${path40}${tags}${allow}`
|
|
23861
24209
|
);
|
|
23862
24210
|
}
|
|
23863
24211
|
return lines.join("\n");
|
|
@@ -24010,7 +24358,7 @@ async function readChecks(cwd) {
|
|
|
24010
24358
|
}
|
|
24011
24359
|
}
|
|
24012
24360
|
function runShell(command, cwd, timeoutMs, signal) {
|
|
24013
|
-
return new Promise((
|
|
24361
|
+
return new Promise((resolve3) => {
|
|
24014
24362
|
const isWin = process.platform === "win32";
|
|
24015
24363
|
const child = spawn5(isWin ? "cmd.exe" : "/bin/sh", isWin ? ["/c", command] : ["-c", command], {
|
|
24016
24364
|
cwd,
|
|
@@ -24024,7 +24372,7 @@ function runShell(command, cwd, timeoutMs, signal) {
|
|
|
24024
24372
|
const finish = (exitCode) => {
|
|
24025
24373
|
if (settled) return;
|
|
24026
24374
|
settled = true;
|
|
24027
|
-
|
|
24375
|
+
resolve3({ exitCode, stdout, stderr });
|
|
24028
24376
|
};
|
|
24029
24377
|
const timer = setTimeout(() => {
|
|
24030
24378
|
try {
|
|
@@ -25857,11 +26205,11 @@ var init_synthesisAudit = __esm({
|
|
|
25857
26205
|
import { existsSync as existsSync13, readFileSync as readFileSync10, writeFileSync as writeFileSync7 } from "node:fs";
|
|
25858
26206
|
import { join as join6 } from "node:path";
|
|
25859
26207
|
function loadNfrSpec(zelariRoot) {
|
|
25860
|
-
const
|
|
25861
|
-
if (!existsSync13(
|
|
26208
|
+
const path40 = join6(zelariRoot, "nfr-spec.json");
|
|
26209
|
+
if (!existsSync13(path40))
|
|
25862
26210
|
return null;
|
|
25863
26211
|
try {
|
|
25864
|
-
const raw = JSON.parse(readFileSync10(
|
|
26212
|
+
const raw = JSON.parse(readFileSync10(path40, "utf8"));
|
|
25865
26213
|
if (raw.version !== 1 || !Array.isArray(raw.targets))
|
|
25866
26214
|
return null;
|
|
25867
26215
|
return raw;
|
|
@@ -28167,9 +28515,9 @@ var init_types3 = __esm({
|
|
|
28167
28515
|
import { readFileSync as readFileSync15 } from "node:fs";
|
|
28168
28516
|
import { join as join12 } from "node:path";
|
|
28169
28517
|
function readLessonsDeduped(zelariRoot) {
|
|
28170
|
-
const
|
|
28518
|
+
const path40 = join12(zelariRoot, LESSONS_FILE);
|
|
28171
28519
|
try {
|
|
28172
|
-
const raw = readFileSync15(
|
|
28520
|
+
const raw = readFileSync15(path40, "utf8");
|
|
28173
28521
|
const byId = /* @__PURE__ */ new Map();
|
|
28174
28522
|
for (const line of raw.split(/\r?\n/)) {
|
|
28175
28523
|
if (!line.trim())
|
|
@@ -28270,8 +28618,8 @@ function keywordsFrom(check2, signature) {
|
|
|
28270
28618
|
return [.../* @__PURE__ */ new Set([...fromId, ...words])].slice(0, 12);
|
|
28271
28619
|
}
|
|
28272
28620
|
function writeLesson(zelariRoot, lesson) {
|
|
28273
|
-
const
|
|
28274
|
-
appendFileSync2(
|
|
28621
|
+
const path40 = join13(zelariRoot, LESSONS_FILE);
|
|
28622
|
+
appendFileSync2(path40, `${JSON.stringify(lesson)}
|
|
28275
28623
|
`, "utf8");
|
|
28276
28624
|
}
|
|
28277
28625
|
function findSimilar(lessons, signature) {
|
|
@@ -30428,28 +30776,28 @@ var init_storage = __esm({
|
|
|
30428
30776
|
VALID_SCALARS = /^(true|false|null|~)$/i;
|
|
30429
30777
|
Storage = class {
|
|
30430
30778
|
/** Read a Markdown file with frontmatter. Throws if not found. */
|
|
30431
|
-
read(
|
|
30432
|
-
if (!existsSync23(
|
|
30433
|
-
throw new Error(`File not found: ${
|
|
30779
|
+
read(path40) {
|
|
30780
|
+
if (!existsSync23(path40)) {
|
|
30781
|
+
throw new Error(`File not found: ${path40}`);
|
|
30434
30782
|
}
|
|
30435
|
-
const md = readFileSync20(
|
|
30783
|
+
const md = readFileSync20(path40, "utf8");
|
|
30436
30784
|
return parseFrontmatter(md);
|
|
30437
30785
|
}
|
|
30438
30786
|
/** Read a Markdown file; returns null if not found. */
|
|
30439
|
-
readIfExists(
|
|
30440
|
-
if (!existsSync23(
|
|
30441
|
-
return this.read(
|
|
30787
|
+
readIfExists(path40) {
|
|
30788
|
+
if (!existsSync23(path40)) return null;
|
|
30789
|
+
return this.read(path40);
|
|
30442
30790
|
}
|
|
30443
30791
|
/**
|
|
30444
30792
|
* Write a Markdown file atomically (tmp + rename). Creates parent dirs.
|
|
30445
30793
|
* The meta object is serialized as YAML frontmatter; body as Markdown.
|
|
30446
30794
|
*/
|
|
30447
|
-
write(
|
|
30448
|
-
mkdirSync9(dirname4(
|
|
30449
|
-
const tmp =
|
|
30795
|
+
write(path40, meta3, body) {
|
|
30796
|
+
mkdirSync9(dirname4(path40), { recursive: true });
|
|
30797
|
+
const tmp = path40 + ".tmp-" + process.pid;
|
|
30450
30798
|
const md = serializeFrontmatter(meta3, body);
|
|
30451
30799
|
writeFileSync13(tmp, md, "utf8");
|
|
30452
|
-
renameSync2(tmp,
|
|
30800
|
+
renameSync2(tmp, path40);
|
|
30453
30801
|
}
|
|
30454
30802
|
/** List all .md files in a directory (non-recursive). */
|
|
30455
30803
|
listMarkdown(dir) {
|
|
@@ -30463,8 +30811,8 @@ var init_storage = __esm({
|
|
|
30463
30811
|
const prev2 = this.chains.get(key) ?? Promise.resolve();
|
|
30464
30812
|
let release = () => {
|
|
30465
30813
|
};
|
|
30466
|
-
const next = new Promise((
|
|
30467
|
-
release =
|
|
30814
|
+
const next = new Promise((resolve3) => {
|
|
30815
|
+
release = resolve3;
|
|
30468
30816
|
});
|
|
30469
30817
|
const chained = prev2.then(() => next);
|
|
30470
30818
|
this.chains.set(key, chained);
|
|
@@ -30527,8 +30875,8 @@ function readPlan(ctx) {
|
|
|
30527
30875
|
} catch {
|
|
30528
30876
|
}
|
|
30529
30877
|
}
|
|
30530
|
-
const
|
|
30531
|
-
const doc = ctx.storage.readIfExists(
|
|
30878
|
+
const path40 = workspaceFile(ctx.rootDir, "plan");
|
|
30879
|
+
const doc = ctx.storage.readIfExists(path40);
|
|
30532
30880
|
if (!doc) return { phases: [], tasks: [], milestones: [] };
|
|
30533
30881
|
const meta3 = doc.meta;
|
|
30534
30882
|
return {
|
|
@@ -30693,7 +31041,7 @@ function addMilestoneRecord(ctx, summary, input) {
|
|
|
30693
31041
|
dueDate: input.dueDate,
|
|
30694
31042
|
targetVersion: version2
|
|
30695
31043
|
});
|
|
30696
|
-
const
|
|
31044
|
+
const path40 = join23(ctx.rootDir, "milestones", `${id}.md`);
|
|
30697
31045
|
const meta3 = {
|
|
30698
31046
|
kind: "milestone",
|
|
30699
31047
|
id,
|
|
@@ -30710,7 +31058,7 @@ function addMilestoneRecord(ctx, summary, input) {
|
|
|
30710
31058
|
`Target version: ${version2}`,
|
|
30711
31059
|
""
|
|
30712
31060
|
].join("\n");
|
|
30713
|
-
ctx.storage.write(
|
|
31061
|
+
ctx.storage.write(path40, meta3, body);
|
|
30714
31062
|
return { id, created: true };
|
|
30715
31063
|
}
|
|
30716
31064
|
function readPlanSummary(ctx) {
|
|
@@ -30914,7 +31262,7 @@ function addIdeaStub(ctx) {
|
|
|
30914
31262
|
const tags = args["tags"] ?? [];
|
|
30915
31263
|
const category = args["category"] ?? "General";
|
|
30916
31264
|
const id = `${nextAdrId(ctx)}-${slugify3(title)}`;
|
|
30917
|
-
const
|
|
31265
|
+
const path40 = workspaceArtifact(ctx.rootDir, "decisions", id);
|
|
30918
31266
|
const meta3 = {
|
|
30919
31267
|
kind: "adr",
|
|
30920
31268
|
status: "proposed",
|
|
@@ -30940,7 +31288,7 @@ function addIdeaStub(ctx) {
|
|
|
30940
31288
|
...consequences.map((c) => `- ${c}`),
|
|
30941
31289
|
""
|
|
30942
31290
|
].join("\n");
|
|
30943
|
-
ctx.storage.write(
|
|
31291
|
+
ctx.storage.write(path40, meta3, body);
|
|
30944
31292
|
return `ADR ${id} created: "${title}". Status: proposed. Promote to accepted via /update ADR or manual edit.`;
|
|
30945
31293
|
});
|
|
30946
31294
|
}
|
|
@@ -31022,14 +31370,14 @@ function createDocumentStub(ctx) {
|
|
|
31022
31370
|
ctx.storage.write(risksPath, riskMeta, content);
|
|
31023
31371
|
return `Document "${title}" created at risks.md (workspace root).`;
|
|
31024
31372
|
}
|
|
31025
|
-
const
|
|
31373
|
+
const path40 = workspaceArtifact(ctx.rootDir, "docs", slug);
|
|
31026
31374
|
const meta3 = {
|
|
31027
31375
|
kind: "doc",
|
|
31028
31376
|
id: slug,
|
|
31029
31377
|
date: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
|
|
31030
31378
|
tags
|
|
31031
31379
|
};
|
|
31032
|
-
ctx.storage.write(
|
|
31380
|
+
ctx.storage.write(path40, meta3, content);
|
|
31033
31381
|
return `Document "${title}" created at docs/${slug}.md.`;
|
|
31034
31382
|
});
|
|
31035
31383
|
}
|
|
@@ -31349,7 +31697,7 @@ ${fallback.output}`
|
|
|
31349
31697
|
return primary;
|
|
31350
31698
|
}
|
|
31351
31699
|
function runNpm(executor, args, mode, npmCliPath) {
|
|
31352
|
-
return new Promise((
|
|
31700
|
+
return new Promise((resolve3) => {
|
|
31353
31701
|
let stdout = "";
|
|
31354
31702
|
let stderr = "";
|
|
31355
31703
|
const stdio = ["ignore", "pipe", "pipe"];
|
|
@@ -31361,7 +31709,7 @@ function runNpm(executor, args, mode, npmCliPath) {
|
|
|
31361
31709
|
stderr += chunk.toString();
|
|
31362
31710
|
});
|
|
31363
31711
|
child.on("error", (err) => {
|
|
31364
|
-
|
|
31712
|
+
resolve3({
|
|
31365
31713
|
ok: false,
|
|
31366
31714
|
output: stdout + stderr,
|
|
31367
31715
|
error: err.message,
|
|
@@ -31370,7 +31718,7 @@ function runNpm(executor, args, mode, npmCliPath) {
|
|
|
31370
31718
|
});
|
|
31371
31719
|
child.on("close", (code) => {
|
|
31372
31720
|
const ok = code === 0;
|
|
31373
|
-
|
|
31721
|
+
resolve3({
|
|
31374
31722
|
ok,
|
|
31375
31723
|
output: stdout + stderr,
|
|
31376
31724
|
error: ok ? void 0 : `npm exited with code ${code}`,
|
|
@@ -31494,7 +31842,7 @@ var init_mcpClient = __esm({
|
|
|
31494
31842
|
return Promise.reject(new Error(`[mcp:${this.serverName}] not started`));
|
|
31495
31843
|
const id = this.nextId++;
|
|
31496
31844
|
const payload = JSON.stringify({ jsonrpc: "2.0", id, method, params });
|
|
31497
|
-
return new Promise((
|
|
31845
|
+
return new Promise((resolve3, reject) => {
|
|
31498
31846
|
const timer = setTimeout(() => {
|
|
31499
31847
|
this.pending.delete(id);
|
|
31500
31848
|
reject(
|
|
@@ -31503,7 +31851,7 @@ var init_mcpClient = __esm({
|
|
|
31503
31851
|
)
|
|
31504
31852
|
);
|
|
31505
31853
|
}, timeoutMs);
|
|
31506
|
-
this.pending.set(id, { resolve, reject, timer });
|
|
31854
|
+
this.pending.set(id, { resolve: resolve3, reject, timer });
|
|
31507
31855
|
child.stdin.write(payload + "\n", (err) => {
|
|
31508
31856
|
if (err) {
|
|
31509
31857
|
clearTimeout(timer);
|
|
@@ -31573,10 +31921,10 @@ function getUserMcpPath() {
|
|
|
31573
31921
|
function getProjectMcpPath(projectRoot) {
|
|
31574
31922
|
return join24(projectRoot, ".zelari", "mcp.json");
|
|
31575
31923
|
}
|
|
31576
|
-
function readFile2(
|
|
31577
|
-
if (!existsSync26(
|
|
31924
|
+
function readFile2(path40) {
|
|
31925
|
+
if (!existsSync26(path40)) return {};
|
|
31578
31926
|
try {
|
|
31579
|
-
const parsed = JSON.parse(readFileSync22(
|
|
31927
|
+
const parsed = JSON.parse(readFileSync22(path40, "utf8"));
|
|
31580
31928
|
const out = {};
|
|
31581
31929
|
for (const [name, cfg] of Object.entries(parsed.mcpServers ?? {})) {
|
|
31582
31930
|
if (!cfg || typeof cfg.command !== "string" || !cfg.command.trim()) continue;
|
|
@@ -31592,10 +31940,10 @@ function readFile2(path38) {
|
|
|
31592
31940
|
return {};
|
|
31593
31941
|
}
|
|
31594
31942
|
}
|
|
31595
|
-
function writeFile(
|
|
31596
|
-
mkdirSync11(dirname6(
|
|
31943
|
+
function writeFile(path40, servers) {
|
|
31944
|
+
mkdirSync11(dirname6(path40), { recursive: true });
|
|
31597
31945
|
const body = { mcpServers: servers };
|
|
31598
|
-
writeFileSync15(
|
|
31946
|
+
writeFileSync15(path40, `${JSON.stringify(body, null, 2)}
|
|
31599
31947
|
`, "utf8");
|
|
31600
31948
|
}
|
|
31601
31949
|
function listMcpServers(projectRoot) {
|
|
@@ -31628,9 +31976,9 @@ function upsertMcpServer(opts) {
|
|
|
31628
31976
|
if (!opts.config.command?.trim()) {
|
|
31629
31977
|
return { ok: false, error: "command is required" };
|
|
31630
31978
|
}
|
|
31631
|
-
let
|
|
31979
|
+
let path40;
|
|
31632
31980
|
if (opts.scope === "user") {
|
|
31633
|
-
|
|
31981
|
+
path40 = getUserMcpPath();
|
|
31634
31982
|
} else {
|
|
31635
31983
|
const root = opts.projectRoot?.trim();
|
|
31636
31984
|
if (!root) {
|
|
@@ -31639,30 +31987,30 @@ function upsertMcpServer(opts) {
|
|
|
31639
31987
|
error: "projectRoot required for project scope (Open Folder first)"
|
|
31640
31988
|
};
|
|
31641
31989
|
}
|
|
31642
|
-
|
|
31990
|
+
path40 = getProjectMcpPath(root);
|
|
31643
31991
|
}
|
|
31644
|
-
const current = readFile2(
|
|
31992
|
+
const current = readFile2(path40);
|
|
31645
31993
|
current[name] = {
|
|
31646
31994
|
command: opts.config.command.trim(),
|
|
31647
31995
|
args: opts.config.args,
|
|
31648
31996
|
env: opts.config.env,
|
|
31649
31997
|
enabled: opts.config.enabled !== false
|
|
31650
31998
|
};
|
|
31651
|
-
writeFile(
|
|
31652
|
-
return { ok: true, path:
|
|
31999
|
+
writeFile(path40, current);
|
|
32000
|
+
return { ok: true, path: path40 };
|
|
31653
32001
|
}
|
|
31654
32002
|
function removeMcpServer(opts) {
|
|
31655
|
-
const
|
|
31656
|
-
if (!
|
|
32003
|
+
const path40 = opts.scope === "user" ? getUserMcpPath() : opts.projectRoot ? getProjectMcpPath(opts.projectRoot) : null;
|
|
32004
|
+
if (!path40) {
|
|
31657
32005
|
return { ok: false, error: "projectRoot required for project scope" };
|
|
31658
32006
|
}
|
|
31659
|
-
const current = readFile2(
|
|
32007
|
+
const current = readFile2(path40);
|
|
31660
32008
|
if (!(opts.name in current)) {
|
|
31661
|
-
return { ok: false, error: `Server "${opts.name}" not found in ${
|
|
32009
|
+
return { ok: false, error: `Server "${opts.name}" not found in ${path40}` };
|
|
31662
32010
|
}
|
|
31663
32011
|
delete current[opts.name];
|
|
31664
|
-
writeFile(
|
|
31665
|
-
return { ok: true, path:
|
|
32012
|
+
writeFile(path40, current);
|
|
32013
|
+
return { ok: true, path: path40 };
|
|
31666
32014
|
}
|
|
31667
32015
|
var init_mcpConfigIo = __esm({
|
|
31668
32016
|
"src/cli/mcp/mcpConfigIo.ts"() {
|
|
@@ -31999,10 +32347,10 @@ import { createHash as createHash5 } from "node:crypto";
|
|
|
31999
32347
|
import { join as join26 } from "node:path";
|
|
32000
32348
|
import { readFile as readFile3 } from "node:fs/promises";
|
|
32001
32349
|
async function readPackageJson2(projectRoot) {
|
|
32002
|
-
const
|
|
32003
|
-
if (!existsSync28(
|
|
32350
|
+
const path40 = join26(projectRoot, "package.json");
|
|
32351
|
+
if (!existsSync28(path40)) return null;
|
|
32004
32352
|
try {
|
|
32005
|
-
return JSON.parse(await readFile3(
|
|
32353
|
+
return JSON.parse(await readFile3(path40, "utf8"));
|
|
32006
32354
|
} catch {
|
|
32007
32355
|
return null;
|
|
32008
32356
|
}
|
|
@@ -32084,9 +32432,9 @@ async function genBuild(ctx) {
|
|
|
32084
32432
|
].join("\n");
|
|
32085
32433
|
}
|
|
32086
32434
|
async function genOpenQuestions(ctx) {
|
|
32087
|
-
const
|
|
32088
|
-
if (!existsSync28(
|
|
32089
|
-
const content = readFileSync24(
|
|
32435
|
+
const path40 = join26(ctx.rootDir, "risks.md");
|
|
32436
|
+
if (!existsSync28(path40)) return "_No open questions._";
|
|
32437
|
+
const content = readFileSync24(path40, "utf8");
|
|
32090
32438
|
const lines = content.split("\n");
|
|
32091
32439
|
const questions = [];
|
|
32092
32440
|
let currentTitle = "";
|
|
@@ -32653,8 +33001,8 @@ async function runPostCouncilHook(ctx, options) {
|
|
|
32653
33001
|
sources: scope.sources
|
|
32654
33002
|
} : void 0
|
|
32655
33003
|
});
|
|
32656
|
-
const
|
|
32657
|
-
completionHook = { ran: true, path:
|
|
33004
|
+
const path40 = writeCouncilCompletion(ctx.rootDir, completion);
|
|
33005
|
+
completionHook = { ran: true, path: path40, completion };
|
|
32658
33006
|
} catch (err) {
|
|
32659
33007
|
completionHook = {
|
|
32660
33008
|
ran: true,
|
|
@@ -33211,18 +33559,49 @@ var init_fileBackend = __esm({
|
|
|
33211
33559
|
}
|
|
33212
33560
|
});
|
|
33213
33561
|
|
|
33562
|
+
// src/cli/traceStore.ts
|
|
33563
|
+
import { promises as fs17 } from "node:fs";
|
|
33564
|
+
import * as path29 from "node:path";
|
|
33565
|
+
function traceDir(projectRoot) {
|
|
33566
|
+
return path29.join(projectRoot, ".zelari", "trace");
|
|
33567
|
+
}
|
|
33568
|
+
function tracePath(projectRoot, missionId) {
|
|
33569
|
+
return path29.join(traceDir(projectRoot), `${missionId}.json`);
|
|
33570
|
+
}
|
|
33571
|
+
async function saveTrace(projectRoot, missionId, entries) {
|
|
33572
|
+
const dir = traceDir(projectRoot);
|
|
33573
|
+
await fs17.mkdir(dir, { recursive: true });
|
|
33574
|
+
const payload = {
|
|
33575
|
+
missionId,
|
|
33576
|
+
ts: Date.now(),
|
|
33577
|
+
entries
|
|
33578
|
+
};
|
|
33579
|
+
await fs17.writeFile(
|
|
33580
|
+
tracePath(projectRoot, missionId),
|
|
33581
|
+
JSON.stringify(payload, null, 2) + "\n",
|
|
33582
|
+
"utf8"
|
|
33583
|
+
);
|
|
33584
|
+
}
|
|
33585
|
+
var init_traceStore = __esm({
|
|
33586
|
+
"src/cli/traceStore.ts"() {
|
|
33587
|
+
"use strict";
|
|
33588
|
+
}
|
|
33589
|
+
});
|
|
33590
|
+
|
|
33214
33591
|
// src/cli/zelariMission.ts
|
|
33215
33592
|
var zelariMission_exports = {};
|
|
33216
33593
|
__export(zelariMission_exports, {
|
|
33217
33594
|
formatBriefForChat: () => formatBriefForChat,
|
|
33218
33595
|
isMissionAutoStart: () => isMissionAutoStart,
|
|
33596
|
+
resolveMaxCost: () => resolveMaxCost,
|
|
33219
33597
|
resolveMaxIterations: () => resolveMaxIterations,
|
|
33220
33598
|
resolveMaxStall: () => resolveMaxStall,
|
|
33599
|
+
resolveMaxTokens: () => resolveMaxTokens,
|
|
33221
33600
|
runZelariMission: () => runZelariMission
|
|
33222
33601
|
});
|
|
33223
33602
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
33224
|
-
import { promises as
|
|
33225
|
-
import * as
|
|
33603
|
+
import { promises as fs18 } from "node:fs";
|
|
33604
|
+
import * as path30 from "node:path";
|
|
33226
33605
|
function resolveMaxIterations(env = process.env) {
|
|
33227
33606
|
const raw = env.ZELARI_MISSION_MAX_ITER;
|
|
33228
33607
|
const n = raw ? Number.parseInt(raw, 10) : DEFAULT_MAX_ITER;
|
|
@@ -33234,17 +33613,35 @@ function resolveMaxStall(env = process.env) {
|
|
|
33234
33613
|
const n = Number.parseInt(raw, 10);
|
|
33235
33614
|
return Number.isFinite(n) && n >= 0 ? n : DEFAULT_MAX_STALL;
|
|
33236
33615
|
}
|
|
33616
|
+
function resolveMaxCost(env = process.env) {
|
|
33617
|
+
const raw = env.ZELARI_MISSION_MAX_COST;
|
|
33618
|
+
if (!raw) return void 0;
|
|
33619
|
+
const n = Number.parseFloat(raw);
|
|
33620
|
+
return Number.isFinite(n) && n > 0 ? n : void 0;
|
|
33621
|
+
}
|
|
33622
|
+
function resolveMaxTokens(env = process.env) {
|
|
33623
|
+
const raw = env.ZELARI_MISSION_MAX_TOKENS;
|
|
33624
|
+
if (!raw) return void 0;
|
|
33625
|
+
const n = Number.parseInt(raw, 10);
|
|
33626
|
+
return Number.isFinite(n) && n > 0 ? n : void 0;
|
|
33627
|
+
}
|
|
33237
33628
|
function isMissionAutoStart(env = process.env) {
|
|
33238
33629
|
return env.ZELARI_MISSION_AUTO === "1";
|
|
33239
33630
|
}
|
|
33240
33631
|
async function writeMissionState(projectRoot, state2) {
|
|
33241
|
-
const dir =
|
|
33242
|
-
await
|
|
33243
|
-
await
|
|
33244
|
-
|
|
33632
|
+
const dir = path30.join(projectRoot, ".zelari");
|
|
33633
|
+
await fs18.mkdir(dir, { recursive: true });
|
|
33634
|
+
await fs18.writeFile(
|
|
33635
|
+
path30.join(dir, "mission-state.json"),
|
|
33245
33636
|
JSON.stringify(state2, null, 2) + "\n",
|
|
33246
33637
|
"utf8"
|
|
33247
33638
|
);
|
|
33639
|
+
if (state2.trace?.length) {
|
|
33640
|
+
try {
|
|
33641
|
+
await saveTrace(projectRoot, state2.missionId, state2.trace);
|
|
33642
|
+
} catch {
|
|
33643
|
+
}
|
|
33644
|
+
}
|
|
33248
33645
|
}
|
|
33249
33646
|
function buildSlicePrompt(brief, userMessage, runMode, iteration) {
|
|
33250
33647
|
if (runMode === "design-phase") {
|
|
@@ -33282,6 +33679,8 @@ async function runZelariMission(userMessage, brief, deps) {
|
|
|
33282
33679
|
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
33283
33680
|
const maxIter = deps.maxIterations ?? resolveMaxIterations(deps.env);
|
|
33284
33681
|
const maxStall = resolveMaxStall(deps.env);
|
|
33682
|
+
const maxCost = resolveMaxCost(deps.env);
|
|
33683
|
+
const maxTokens = resolveMaxTokens(deps.env);
|
|
33285
33684
|
const missionId = deps.missionId ?? `m_${randomUUID5().slice(0, 8)}`;
|
|
33286
33685
|
const startedAt = now().toISOString();
|
|
33287
33686
|
const state2 = {
|
|
@@ -33317,6 +33716,8 @@ async function runZelariMission(userMessage, brief, deps) {
|
|
|
33317
33716
|
let step = 0;
|
|
33318
33717
|
let implStep = 0;
|
|
33319
33718
|
let pendingDesign = designFirst;
|
|
33719
|
+
let cumulativeCostUsd = 0;
|
|
33720
|
+
let cumulativeTokens = 0;
|
|
33320
33721
|
while (true) {
|
|
33321
33722
|
const runMode = pendingDesign ? "design-phase" : "implementation";
|
|
33322
33723
|
if (runMode === "implementation") {
|
|
@@ -33333,6 +33734,8 @@ async function runZelariMission(userMessage, brief, deps) {
|
|
|
33333
33734
|
const promptIter = runMode === "implementation" ? implStep : 1;
|
|
33334
33735
|
const slicePrompt = buildSlicePrompt(brief, userMessage, runMode, promptIter);
|
|
33335
33736
|
const implementerRetry = runMode === "implementation" && implStep > 1;
|
|
33737
|
+
const sliceStartedAt = now().toISOString();
|
|
33738
|
+
const sliceStartMs = now().getTime();
|
|
33336
33739
|
if (runMode === "design-phase") {
|
|
33337
33740
|
deps.emit(
|
|
33338
33741
|
`[zelari] design-phase (fuori budget) \xB7 step ${step} \xB7 slice ${brief.sliceMvp.id}`
|
|
@@ -33369,6 +33772,8 @@ async function runZelariMission(userMessage, brief, deps) {
|
|
|
33369
33772
|
);
|
|
33370
33773
|
return state2;
|
|
33371
33774
|
}
|
|
33775
|
+
if (typeof result.costUsd === "number") cumulativeCostUsd += result.costUsd;
|
|
33776
|
+
if (typeof result.costTokens === "number") cumulativeTokens += result.costTokens;
|
|
33372
33777
|
await deps.memory.add(
|
|
33373
33778
|
JSON.stringify({
|
|
33374
33779
|
iteration: step,
|
|
@@ -33394,6 +33799,20 @@ async function runZelariMission(userMessage, brief, deps) {
|
|
|
33394
33799
|
}
|
|
33395
33800
|
state2.lastCompletionOk = completionOk;
|
|
33396
33801
|
state2.updatedAt = now().toISOString();
|
|
33802
|
+
state2.cumulativeCostUsd = cumulativeCostUsd;
|
|
33803
|
+
state2.cumulativeTokens = cumulativeTokens;
|
|
33804
|
+
if (!state2.trace) state2.trace = [];
|
|
33805
|
+
state2.trace.push({
|
|
33806
|
+
sliceId: brief.sliceMvp.id,
|
|
33807
|
+
iteration: step,
|
|
33808
|
+
runMode,
|
|
33809
|
+
completionOk,
|
|
33810
|
+
degraded: result.degraded,
|
|
33811
|
+
costTokens: typeof result.costTokens === "number" ? result.costTokens : void 0,
|
|
33812
|
+
costUsd: typeof result.costUsd === "number" ? result.costUsd : void 0,
|
|
33813
|
+
startedAt: sliceStartedAt,
|
|
33814
|
+
durationMs: now().getTime() - sliceStartMs
|
|
33815
|
+
});
|
|
33397
33816
|
if (runMode === "design-phase") {
|
|
33398
33817
|
pendingDesign = false;
|
|
33399
33818
|
await writeMissionState(deps.projectRoot, state2);
|
|
@@ -33457,6 +33876,16 @@ async function runZelariMission(userMessage, brief, deps) {
|
|
|
33457
33876
|
return state2;
|
|
33458
33877
|
}
|
|
33459
33878
|
}
|
|
33879
|
+
if (maxCost !== void 0 && cumulativeCostUsd >= maxCost || maxTokens !== void 0 && cumulativeTokens >= maxTokens) {
|
|
33880
|
+
state2.status = "stopped";
|
|
33881
|
+
state2.updatedAt = now().toISOString();
|
|
33882
|
+
await writeMissionState(deps.projectRoot, state2);
|
|
33883
|
+
const reason = maxCost !== void 0 && cumulativeCostUsd >= maxCost ? `budget USD ${formatCost(cumulativeCostUsd)} \u2265 ${formatCost(maxCost)}` : `token ${formatTokens(cumulativeTokens)} \u2265 ${formatTokens(maxTokens)}`;
|
|
33884
|
+
deps.emit(
|
|
33885
|
+
`[zelari] fermata: ${reason}. Imposta ZELARI_MISSION_MAX_COST / ZELARI_MISSION_MAX_TOKENS pi\xF9 alto, o usa un modello pi\xF9 economico. Stato salvato in .zelari/mission-state.json`
|
|
33886
|
+
);
|
|
33887
|
+
return state2;
|
|
33888
|
+
}
|
|
33460
33889
|
await writeMissionState(deps.projectRoot, state2);
|
|
33461
33890
|
}
|
|
33462
33891
|
state2.status = "stopped";
|
|
@@ -33475,6 +33904,8 @@ var init_zelariMission = __esm({
|
|
|
33475
33904
|
init_checkpointManager();
|
|
33476
33905
|
init_fileStateStore();
|
|
33477
33906
|
init_commitHelpers();
|
|
33907
|
+
init_modelPricing();
|
|
33908
|
+
init_traceStore();
|
|
33478
33909
|
DEFAULT_MAX_ITER = 6;
|
|
33479
33910
|
DEFAULT_MAX_STALL = 2;
|
|
33480
33911
|
}
|
|
@@ -33632,6 +34063,8 @@ async function runAgentMissionSlice(deps) {
|
|
|
33632
34063
|
});
|
|
33633
34064
|
let text = "";
|
|
33634
34065
|
let errored2 = false;
|
|
34066
|
+
let promptTokens = 0;
|
|
34067
|
+
let completionTokens = 0;
|
|
33635
34068
|
try {
|
|
33636
34069
|
for await (const event of harness.run()) {
|
|
33637
34070
|
counter.onEvent(event);
|
|
@@ -33642,6 +34075,11 @@ async function runAgentMissionSlice(deps) {
|
|
|
33642
34075
|
if (event.type === "agent_end" && event.reason === "error") {
|
|
33643
34076
|
errored2 = true;
|
|
33644
34077
|
}
|
|
34078
|
+
if (event.type === "message_end" && event.usage) {
|
|
34079
|
+
const u = event.usage;
|
|
34080
|
+
promptTokens += u.promptTokens ?? 0;
|
|
34081
|
+
completionTokens += u.completionTokens ?? 0;
|
|
34082
|
+
}
|
|
33645
34083
|
if (event.type === "error" && event.severity === "fatal") {
|
|
33646
34084
|
errored2 = true;
|
|
33647
34085
|
}
|
|
@@ -33664,7 +34102,9 @@ async function runAgentMissionSlice(deps) {
|
|
|
33664
34102
|
successfulWrites: counter.state.successfulWrites,
|
|
33665
34103
|
emittedWrites: counter.state.emittedWrites,
|
|
33666
34104
|
errored: errored2,
|
|
33667
|
-
messages: harness.getMessages()
|
|
34105
|
+
messages: harness.getMessages(),
|
|
34106
|
+
promptTokens,
|
|
34107
|
+
completionTokens
|
|
33668
34108
|
};
|
|
33669
34109
|
}
|
|
33670
34110
|
const initial = [
|
|
@@ -33676,6 +34116,8 @@ async function runAgentMissionSlice(deps) {
|
|
|
33676
34116
|
let totalEmitted = pass.emittedWrites;
|
|
33677
34117
|
let synthesisText = pass.text;
|
|
33678
34118
|
let errored = pass.errored;
|
|
34119
|
+
let totalPromptTokens = pass.promptTokens;
|
|
34120
|
+
let totalCompletionTokens = pass.completionTokens;
|
|
33679
34121
|
if (writeRetry && totalWrites === 0 && !errored) {
|
|
33680
34122
|
deps.emit?.(
|
|
33681
34123
|
"[zelari] build@agent: 0 write \u2014 forcing implementation retry"
|
|
@@ -33695,6 +34137,8 @@ async function runAgentMissionSlice(deps) {
|
|
|
33695
34137
|
${retry.text}` : retry.text;
|
|
33696
34138
|
}
|
|
33697
34139
|
errored = errored || retry.errored;
|
|
34140
|
+
totalPromptTokens += retry.promptTokens;
|
|
34141
|
+
totalCompletionTokens += retry.completionTokens;
|
|
33698
34142
|
}
|
|
33699
34143
|
const cleaned = cleanAgentContent(synthesisText, {
|
|
33700
34144
|
stripQuestion: false,
|
|
@@ -33741,12 +34185,16 @@ ${retry.text}` : retry.text;
|
|
|
33741
34185
|
else degraded = true;
|
|
33742
34186
|
}
|
|
33743
34187
|
void totalEmitted;
|
|
34188
|
+
const costTokens = totalPromptTokens + totalCompletionTokens;
|
|
34189
|
+
const costUsd = calculateCost(deps.model, totalPromptTokens, totalCompletionTokens);
|
|
33744
34190
|
return {
|
|
33745
34191
|
completionOk,
|
|
33746
34192
|
ran: true,
|
|
33747
34193
|
synthesisText: cleaned || void 0,
|
|
33748
34194
|
writeCount: totalWrites,
|
|
33749
|
-
degraded
|
|
34195
|
+
degraded,
|
|
34196
|
+
costTokens,
|
|
34197
|
+
costUsd
|
|
33750
34198
|
};
|
|
33751
34199
|
}
|
|
33752
34200
|
var MUTATING, AGENT_MISSION_IMPLEMENTER_PREAMBLE;
|
|
@@ -33759,6 +34207,7 @@ var init_missionSlice = __esm({
|
|
|
33759
34207
|
init_dist();
|
|
33760
34208
|
init_buildPolicy();
|
|
33761
34209
|
init_envNumber();
|
|
34210
|
+
init_modelPricing();
|
|
33762
34211
|
MUTATING = /* @__PURE__ */ new Set(["write_file", "edit_file", "apply_diff"]);
|
|
33763
34212
|
AGENT_MISSION_IMPLEMENTER_PREAMBLE = "You are the sole implementer for this Zelari mission slice. A multi-agent council may already have produced a plan under `.zelari/` \u2014 treat it as a SPEC to apply on disk. You MUST create or modify real project files with write_file / edit_file. Prose without successful writes is a failed slice.";
|
|
33764
34213
|
}
|
|
@@ -33885,14 +34334,14 @@ function agentProbeEnv() {
|
|
|
33885
34334
|
try {
|
|
33886
34335
|
const nodeDir = dirname7(process.execPath);
|
|
33887
34336
|
if (!nodeDir) return env;
|
|
33888
|
-
const
|
|
34337
|
+
const sep2 = process.platform === "win32" ? ";" : ":";
|
|
33889
34338
|
const current = env.PATH ?? env.Path ?? "";
|
|
33890
|
-
const parts = current.split(
|
|
34339
|
+
const parts = current.split(sep2).filter((p3) => p3.length > 0);
|
|
33891
34340
|
const has = parts.some(
|
|
33892
34341
|
(p3) => p3.toLowerCase() === nodeDir.toLowerCase()
|
|
33893
34342
|
);
|
|
33894
34343
|
if (!has) {
|
|
33895
|
-
env.PATH = `${nodeDir}${
|
|
34344
|
+
env.PATH = `${nodeDir}${sep2}${current}`;
|
|
33896
34345
|
}
|
|
33897
34346
|
} catch {
|
|
33898
34347
|
}
|
|
@@ -34148,10 +34597,10 @@ var init_prereqChecks = __esm({
|
|
|
34148
34597
|
|
|
34149
34598
|
// src/cli/plugins/prefs.ts
|
|
34150
34599
|
import { existsSync as existsSync33, readFileSync as readFileSync28, writeFileSync as writeFileSync18, mkdirSync as mkdirSync13 } from "node:fs";
|
|
34151
|
-
import
|
|
34600
|
+
import path32 from "node:path";
|
|
34152
34601
|
import os9 from "node:os";
|
|
34153
34602
|
function getPluginPrefsPath() {
|
|
34154
|
-
return process.env.ZELARI_PLUGINS_PREFS_FILE ??
|
|
34603
|
+
return process.env.ZELARI_PLUGINS_PREFS_FILE ?? path32.join(os9.homedir(), ".tmp", "zelari-code", "plugins.json");
|
|
34155
34604
|
}
|
|
34156
34605
|
function getPluginPrefs() {
|
|
34157
34606
|
const file2 = getPluginPrefsPath();
|
|
@@ -34172,7 +34621,7 @@ function getPluginPrefs() {
|
|
|
34172
34621
|
}
|
|
34173
34622
|
function writePluginPrefs(prefs) {
|
|
34174
34623
|
const file2 = getPluginPrefsPath();
|
|
34175
|
-
mkdirSync13(
|
|
34624
|
+
mkdirSync13(path32.dirname(file2), { recursive: true });
|
|
34176
34625
|
writeFileSync18(file2, JSON.stringify(prefs, null, 2), {
|
|
34177
34626
|
encoding: "utf-8",
|
|
34178
34627
|
mode: 384
|
|
@@ -34209,7 +34658,7 @@ __export(registry_exports, {
|
|
|
34209
34658
|
isBinaryOnPath: () => isBinaryOnPath
|
|
34210
34659
|
});
|
|
34211
34660
|
import { existsSync as existsSync34 } from "node:fs";
|
|
34212
|
-
import
|
|
34661
|
+
import path33 from "node:path";
|
|
34213
34662
|
function detectLocalBin(bin) {
|
|
34214
34663
|
return (cwd) => {
|
|
34215
34664
|
try {
|
|
@@ -34227,9 +34676,9 @@ function isBinaryOnPath(bin, opts = {}) {
|
|
|
34227
34676
|
const platform = opts.platform ?? process.platform;
|
|
34228
34677
|
const exists = opts.exists ?? existsSync34;
|
|
34229
34678
|
const pathEnv = opts.pathEnv ?? process.env.PATH ?? "";
|
|
34230
|
-
const pathMod = platform === "win32" ?
|
|
34231
|
-
const
|
|
34232
|
-
const dirs = pathEnv.split(
|
|
34679
|
+
const pathMod = platform === "win32" ? path33.win32 : path33.posix;
|
|
34680
|
+
const sep2 = platform === "win32" ? ";" : ":";
|
|
34681
|
+
const dirs = pathEnv.split(sep2).filter((d) => d.length > 0);
|
|
34233
34682
|
const candidates = [bin];
|
|
34234
34683
|
if (platform === "win32") {
|
|
34235
34684
|
const pathExt = opts.pathExt ?? process.env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM";
|
|
@@ -34357,32 +34806,1268 @@ var init_registry2 = __esm({
|
|
|
34357
34806
|
}
|
|
34358
34807
|
});
|
|
34359
34808
|
|
|
34809
|
+
// src/cli/atMentions.ts
|
|
34810
|
+
var atMentions_exports = {};
|
|
34811
|
+
__export(atMentions_exports, {
|
|
34812
|
+
expandAtMentions: () => expandAtMentions,
|
|
34813
|
+
extractAtMentions: () => extractAtMentions,
|
|
34814
|
+
hasAtMentions: () => hasAtMentions
|
|
34815
|
+
});
|
|
34816
|
+
import { existsSync as existsSync37, readFileSync as readFileSync30, statSync as statSync6 } from "node:fs";
|
|
34817
|
+
import { isAbsolute, relative as relative3, resolve, sep } from "node:path";
|
|
34818
|
+
function isProbablyText(name, head) {
|
|
34819
|
+
if (/\.(txt|md|markdown|json|jsonc|ts|tsx|js|jsx|mjs|cjs|css|scss|html|htm|xml|yml|yaml|toml|ini|cfg|conf|rs|go|py|java|kt|swift|c|cc|cpp|h|hpp|cs|rb|php|sh|bash|zsh|ps1|sql|graphql|env|gitignore|dockerfile|makefile|cmake|lock|svg)$/i.test(
|
|
34820
|
+
name
|
|
34821
|
+
)) {
|
|
34822
|
+
return true;
|
|
34823
|
+
}
|
|
34824
|
+
return !head.includes("\0") && /[\x09\x0a\x0d\x20-\x7e]/.test(head.slice(0, 200));
|
|
34825
|
+
}
|
|
34826
|
+
function underRoot(abs, root) {
|
|
34827
|
+
const a = resolve(abs);
|
|
34828
|
+
const r = resolve(root);
|
|
34829
|
+
const prefix = r.endsWith(sep) ? r : r + sep;
|
|
34830
|
+
return a === r || a.startsWith(prefix);
|
|
34831
|
+
}
|
|
34832
|
+
function extractAtMentions(text) {
|
|
34833
|
+
const out = [];
|
|
34834
|
+
const seen = /* @__PURE__ */ new Set();
|
|
34835
|
+
AT_PATH_RE.lastIndex = 0;
|
|
34836
|
+
let m;
|
|
34837
|
+
while ((m = AT_PATH_RE.exec(text)) !== null) {
|
|
34838
|
+
const token = (m[2] ?? "").trim();
|
|
34839
|
+
if (!token || token.includes("@")) continue;
|
|
34840
|
+
const key = token.replace(/\\/g, "/").toLowerCase();
|
|
34841
|
+
if (seen.has(key)) continue;
|
|
34842
|
+
seen.add(key);
|
|
34843
|
+
out.push(token);
|
|
34844
|
+
}
|
|
34845
|
+
return out;
|
|
34846
|
+
}
|
|
34847
|
+
function resolveMention(token, cwd) {
|
|
34848
|
+
const abs = isAbsolute(token) ? resolve(token) : resolve(cwd, token);
|
|
34849
|
+
if (!underRoot(abs, cwd) && !isAbsolute(token)) {
|
|
34850
|
+
if (!underRoot(abs, cwd)) {
|
|
34851
|
+
return {
|
|
34852
|
+
raw: token,
|
|
34853
|
+
path: token,
|
|
34854
|
+
absolute: abs,
|
|
34855
|
+
isDir: false,
|
|
34856
|
+
note: "outside project root \u2014 skipped"
|
|
34857
|
+
};
|
|
34858
|
+
}
|
|
34859
|
+
}
|
|
34860
|
+
if (isAbsolute(token) && !underRoot(abs, cwd)) {
|
|
34861
|
+
return {
|
|
34862
|
+
raw: token,
|
|
34863
|
+
path: token,
|
|
34864
|
+
absolute: abs,
|
|
34865
|
+
isDir: false,
|
|
34866
|
+
note: "outside project root \u2014 skipped"
|
|
34867
|
+
};
|
|
34868
|
+
}
|
|
34869
|
+
if (!existsSync37(abs)) {
|
|
34870
|
+
return {
|
|
34871
|
+
raw: token,
|
|
34872
|
+
path: token,
|
|
34873
|
+
absolute: abs,
|
|
34874
|
+
isDir: false,
|
|
34875
|
+
note: "not found"
|
|
34876
|
+
};
|
|
34877
|
+
}
|
|
34878
|
+
let st;
|
|
34879
|
+
try {
|
|
34880
|
+
st = statSync6(abs);
|
|
34881
|
+
} catch {
|
|
34882
|
+
return {
|
|
34883
|
+
raw: token,
|
|
34884
|
+
path: token,
|
|
34885
|
+
absolute: abs,
|
|
34886
|
+
isDir: false,
|
|
34887
|
+
note: "unreadable"
|
|
34888
|
+
};
|
|
34889
|
+
}
|
|
34890
|
+
const rel2 = relative3(cwd, abs).replace(/\\/g, "/") || ".";
|
|
34891
|
+
if (st.isDirectory()) {
|
|
34892
|
+
return {
|
|
34893
|
+
raw: token,
|
|
34894
|
+
path: rel2,
|
|
34895
|
+
absolute: abs,
|
|
34896
|
+
isDir: true,
|
|
34897
|
+
note: "directory \u2014 list/read with tools as needed"
|
|
34898
|
+
};
|
|
34899
|
+
}
|
|
34900
|
+
if (st.size > ATTACH_FILE_MAX_BYTES) {
|
|
34901
|
+
return {
|
|
34902
|
+
raw: token,
|
|
34903
|
+
path: rel2,
|
|
34904
|
+
absolute: abs,
|
|
34905
|
+
isDir: false,
|
|
34906
|
+
note: `too large (${Math.round(st.size / 1024)} KB) \u2014 path only`
|
|
34907
|
+
};
|
|
34908
|
+
}
|
|
34909
|
+
try {
|
|
34910
|
+
const buf = readFileSync30(abs);
|
|
34911
|
+
const head = buf.subarray(0, 800).toString("utf8");
|
|
34912
|
+
if (!isProbablyText(abs, head)) {
|
|
34913
|
+
return {
|
|
34914
|
+
raw: token,
|
|
34915
|
+
path: rel2,
|
|
34916
|
+
absolute: abs,
|
|
34917
|
+
isDir: false,
|
|
34918
|
+
note: "binary \u2014 path only"
|
|
34919
|
+
};
|
|
34920
|
+
}
|
|
34921
|
+
let text = buf.toString("utf8");
|
|
34922
|
+
if (text.charCodeAt(0) === 65279) text = text.slice(1);
|
|
34923
|
+
if (text.length > ATTACH_TEXT_MAX) {
|
|
34924
|
+
text = text.slice(0, ATTACH_TEXT_MAX) + `
|
|
34925
|
+
|
|
34926
|
+
\u2026 [truncated, ${text.length - ATTACH_TEXT_MAX} more chars]`;
|
|
34927
|
+
}
|
|
34928
|
+
return { raw: token, path: rel2, absolute: abs, isDir: false, text };
|
|
34929
|
+
} catch (err) {
|
|
34930
|
+
return {
|
|
34931
|
+
raw: token,
|
|
34932
|
+
path: rel2,
|
|
34933
|
+
absolute: abs,
|
|
34934
|
+
isDir: false,
|
|
34935
|
+
note: err instanceof Error ? err.message : "read failed"
|
|
34936
|
+
};
|
|
34937
|
+
}
|
|
34938
|
+
}
|
|
34939
|
+
function expandAtMentions(userText, cwd = process.cwd()) {
|
|
34940
|
+
const tokens = extractAtMentions(userText);
|
|
34941
|
+
if (tokens.length === 0) {
|
|
34942
|
+
return { text: userText, hits: [] };
|
|
34943
|
+
}
|
|
34944
|
+
const hits = [];
|
|
34945
|
+
for (const t of tokens) {
|
|
34946
|
+
const hit = resolveMention(t, cwd);
|
|
34947
|
+
if (hit) hits.push(hit);
|
|
34948
|
+
}
|
|
34949
|
+
if (hits.length === 0) {
|
|
34950
|
+
return { text: userText, hits: [] };
|
|
34951
|
+
}
|
|
34952
|
+
const blocks = hits.map((h) => {
|
|
34953
|
+
const label = h.path || h.raw;
|
|
34954
|
+
if (h.text != null && h.text.length > 0) {
|
|
34955
|
+
return `--- File: ${label} ---
|
|
34956
|
+
${h.text}
|
|
34957
|
+
--- End file ---`;
|
|
34958
|
+
}
|
|
34959
|
+
const extra = h.note ? ` (${h.note})` : "";
|
|
34960
|
+
return `--- ${h.isDir ? "Dir" : "File"}: ${label}${extra} ---`;
|
|
34961
|
+
});
|
|
34962
|
+
const text = `${userText.trim()}
|
|
34963
|
+
|
|
34964
|
+
[Tagged paths]
|
|
34965
|
+
${blocks.join("\n\n")}`;
|
|
34966
|
+
return { text, hits };
|
|
34967
|
+
}
|
|
34968
|
+
function hasAtMentions(text) {
|
|
34969
|
+
return extractAtMentions(text).length > 0;
|
|
34970
|
+
}
|
|
34971
|
+
var ATTACH_TEXT_MAX, ATTACH_FILE_MAX_BYTES, AT_PATH_RE;
|
|
34972
|
+
var init_atMentions = __esm({
|
|
34973
|
+
"src/cli/atMentions.ts"() {
|
|
34974
|
+
"use strict";
|
|
34975
|
+
ATTACH_TEXT_MAX = 48e3;
|
|
34976
|
+
ATTACH_FILE_MAX_BYTES = 512e3;
|
|
34977
|
+
AT_PATH_RE = /(^|[\s([{])@((?:\.{1,2}\/)?[A-Za-z0-9_.+-]+(?:[\\/][A-Za-z0-9_.+-]+)*)/g;
|
|
34978
|
+
}
|
|
34979
|
+
});
|
|
34980
|
+
|
|
34981
|
+
// src/cli/triggerLock.ts
|
|
34982
|
+
var triggerLock_exports = {};
|
|
34983
|
+
__export(triggerLock_exports, {
|
|
34984
|
+
acquireLock: () => acquireLock,
|
|
34985
|
+
lockPath: () => lockPath,
|
|
34986
|
+
releaseLock: () => releaseLock
|
|
34987
|
+
});
|
|
34988
|
+
import { promises as fs23 } from "node:fs";
|
|
34989
|
+
import * as path38 from "node:path";
|
|
34990
|
+
function lockPath(projectRoot) {
|
|
34991
|
+
return path38.join(projectRoot, ".zelari", "trigger.lock");
|
|
34992
|
+
}
|
|
34993
|
+
function isPidAlive(pid) {
|
|
34994
|
+
try {
|
|
34995
|
+
process.kill(pid, 0);
|
|
34996
|
+
return true;
|
|
34997
|
+
} catch (err) {
|
|
34998
|
+
const code = err.code;
|
|
34999
|
+
return code === "EPERM";
|
|
35000
|
+
}
|
|
35001
|
+
}
|
|
35002
|
+
async function acquireLock(projectRoot, now = () => /* @__PURE__ */ new Date()) {
|
|
35003
|
+
const lp = lockPath(projectRoot);
|
|
35004
|
+
const dir = path38.dirname(lp);
|
|
35005
|
+
await fs23.mkdir(dir, { recursive: true });
|
|
35006
|
+
try {
|
|
35007
|
+
const raw = await fs23.readFile(lp, "utf8");
|
|
35008
|
+
const existing = JSON.parse(raw);
|
|
35009
|
+
if (existing.pid && isPidAlive(existing.pid)) {
|
|
35010
|
+
return { acquired: false, heldBy: existing.pid, lockPath: lp };
|
|
35011
|
+
}
|
|
35012
|
+
} catch {
|
|
35013
|
+
}
|
|
35014
|
+
const payload = {
|
|
35015
|
+
pid: process.pid,
|
|
35016
|
+
acquiredAt: now().toISOString()
|
|
35017
|
+
};
|
|
35018
|
+
await fs23.writeFile(lp, JSON.stringify(payload, null, 2) + "\n", "utf8");
|
|
35019
|
+
return { acquired: true, lockPath: lp };
|
|
35020
|
+
}
|
|
35021
|
+
async function releaseLock(projectRoot) {
|
|
35022
|
+
const lp = lockPath(projectRoot);
|
|
35023
|
+
try {
|
|
35024
|
+
await fs23.unlink(lp);
|
|
35025
|
+
} catch {
|
|
35026
|
+
}
|
|
35027
|
+
}
|
|
35028
|
+
var init_triggerLock = __esm({
|
|
35029
|
+
"src/cli/triggerLock.ts"() {
|
|
35030
|
+
"use strict";
|
|
35031
|
+
}
|
|
35032
|
+
});
|
|
35033
|
+
|
|
35034
|
+
// src/cli/desktopConfig.ts
|
|
35035
|
+
function wantsPrintConfig(argv) {
|
|
35036
|
+
return argv.includes("--print-config");
|
|
35037
|
+
}
|
|
35038
|
+
function wantsSetKey(argv) {
|
|
35039
|
+
return argv.includes("--set-key");
|
|
35040
|
+
}
|
|
35041
|
+
function wantsDiscoverModels(argv) {
|
|
35042
|
+
return argv.includes("--discover-models");
|
|
35043
|
+
}
|
|
35044
|
+
function parseSetConfigFlags(argv) {
|
|
35045
|
+
if (!argv.includes("--set-config")) {
|
|
35046
|
+
return { request: null };
|
|
35047
|
+
}
|
|
35048
|
+
let provider;
|
|
35049
|
+
let model;
|
|
35050
|
+
let endpoint;
|
|
35051
|
+
let endpointClear = false;
|
|
35052
|
+
for (let i = 0; i < argv.length; i++) {
|
|
35053
|
+
const arg = argv[i];
|
|
35054
|
+
if (arg === "--provider") {
|
|
35055
|
+
provider = argv[i + 1];
|
|
35056
|
+
i++;
|
|
35057
|
+
} else if (arg === "--model") {
|
|
35058
|
+
model = argv[i + 1];
|
|
35059
|
+
i++;
|
|
35060
|
+
} else if (arg === "--endpoint") {
|
|
35061
|
+
endpoint = argv[i + 1];
|
|
35062
|
+
i++;
|
|
35063
|
+
} else if (arg === "--endpoint-clear") {
|
|
35064
|
+
endpointClear = true;
|
|
35065
|
+
}
|
|
35066
|
+
}
|
|
35067
|
+
if (!provider && !model && !endpoint && !endpointClear) {
|
|
35068
|
+
return {
|
|
35069
|
+
request: null,
|
|
35070
|
+
error: "--set-config requires --provider, --model, --endpoint, and/or --endpoint-clear"
|
|
35071
|
+
};
|
|
35072
|
+
}
|
|
35073
|
+
if (provider !== void 0 && provider.trim().length === 0) {
|
|
35074
|
+
return { request: null, error: "--provider cannot be empty" };
|
|
35075
|
+
}
|
|
35076
|
+
if (model !== void 0 && model.trim().length === 0) {
|
|
35077
|
+
return { request: null, error: "--model cannot be empty" };
|
|
35078
|
+
}
|
|
35079
|
+
if (endpoint !== void 0 && endpoint.trim().length === 0) {
|
|
35080
|
+
return { request: null, error: "--endpoint cannot be empty" };
|
|
35081
|
+
}
|
|
35082
|
+
if (endpoint && endpointClear) {
|
|
35083
|
+
return { request: null, error: "--endpoint and --endpoint-clear conflict" };
|
|
35084
|
+
}
|
|
35085
|
+
return {
|
|
35086
|
+
request: {
|
|
35087
|
+
provider: provider?.trim(),
|
|
35088
|
+
model: model?.trim(),
|
|
35089
|
+
endpoint: endpoint?.trim(),
|
|
35090
|
+
endpointClear: endpointClear || void 0
|
|
35091
|
+
}
|
|
35092
|
+
};
|
|
35093
|
+
}
|
|
35094
|
+
function parseSetKeyFlags(argv) {
|
|
35095
|
+
if (!argv.includes("--set-key")) {
|
|
35096
|
+
return { request: null };
|
|
35097
|
+
}
|
|
35098
|
+
let provider;
|
|
35099
|
+
let key;
|
|
35100
|
+
for (let i = 0; i < argv.length; i++) {
|
|
35101
|
+
const arg = argv[i];
|
|
35102
|
+
if (arg === "--provider") {
|
|
35103
|
+
provider = argv[i + 1];
|
|
35104
|
+
i++;
|
|
35105
|
+
} else if (arg === "--key") {
|
|
35106
|
+
key = argv[i + 1];
|
|
35107
|
+
i++;
|
|
35108
|
+
}
|
|
35109
|
+
}
|
|
35110
|
+
if (!provider || provider.trim().length === 0) {
|
|
35111
|
+
return { request: null, error: "--set-key requires --provider <id>" };
|
|
35112
|
+
}
|
|
35113
|
+
if (!key || key.trim().length === 0) {
|
|
35114
|
+
return { request: null, error: "--set-key requires --key <secret>" };
|
|
35115
|
+
}
|
|
35116
|
+
return {
|
|
35117
|
+
request: {
|
|
35118
|
+
provider: provider.trim(),
|
|
35119
|
+
key: key.trim()
|
|
35120
|
+
}
|
|
35121
|
+
};
|
|
35122
|
+
}
|
|
35123
|
+
function parseDiscoverModelsFlags(argv) {
|
|
35124
|
+
if (!argv.includes("--discover-models")) {
|
|
35125
|
+
return { provider: null, present: false };
|
|
35126
|
+
}
|
|
35127
|
+
let provider;
|
|
35128
|
+
for (let i = 0; i < argv.length; i++) {
|
|
35129
|
+
if (argv[i] === "--provider") {
|
|
35130
|
+
provider = argv[i + 1];
|
|
35131
|
+
i++;
|
|
35132
|
+
}
|
|
35133
|
+
}
|
|
35134
|
+
return {
|
|
35135
|
+
present: true,
|
|
35136
|
+
provider: provider?.trim() || null
|
|
35137
|
+
};
|
|
35138
|
+
}
|
|
35139
|
+
function buildDesktopConfigSnapshot() {
|
|
35140
|
+
const config2 = getProviderConfig();
|
|
35141
|
+
const providers = PROVIDERS.map((p3) => {
|
|
35142
|
+
const cached2 = getCachedModels(p3.id);
|
|
35143
|
+
const models = cached2?.models.map((m) => m.id) ?? [];
|
|
35144
|
+
const defaultModel = config2.modelByProvider[p3.id] ?? "";
|
|
35145
|
+
if (defaultModel && !models.includes(defaultModel)) {
|
|
35146
|
+
models.unshift(defaultModel);
|
|
35147
|
+
}
|
|
35148
|
+
const custom2 = getCustomEndpoint(p3.id);
|
|
35149
|
+
const builtin = p3.baseUrl ?? null;
|
|
35150
|
+
return {
|
|
35151
|
+
id: p3.id,
|
|
35152
|
+
displayName: p3.displayName,
|
|
35153
|
+
hasKey: !!resolveApiKey(p3.id),
|
|
35154
|
+
envVar: p3.envVar,
|
|
35155
|
+
models,
|
|
35156
|
+
defaultModel,
|
|
35157
|
+
endpoint: custom2 ?? null,
|
|
35158
|
+
baseUrl: custom2 ?? builtin
|
|
35159
|
+
};
|
|
35160
|
+
});
|
|
35161
|
+
return {
|
|
35162
|
+
activeProviderId: config2.activeProviderId,
|
|
35163
|
+
modelByProvider: { ...config2.modelByProvider },
|
|
35164
|
+
providers,
|
|
35165
|
+
cliVersion: getCurrentVersion(),
|
|
35166
|
+
configPaths: {
|
|
35167
|
+
provider: getProviderConfigPath(),
|
|
35168
|
+
keys: getKeyStorePath()
|
|
35169
|
+
}
|
|
35170
|
+
};
|
|
35171
|
+
}
|
|
35172
|
+
function printDesktopConfig() {
|
|
35173
|
+
const snap = buildDesktopConfigSnapshot();
|
|
35174
|
+
process.stdout.write(JSON.stringify(snap, null, 2) + "\n");
|
|
35175
|
+
}
|
|
35176
|
+
function applySetConfig(req) {
|
|
35177
|
+
try {
|
|
35178
|
+
const config2 = getProviderConfig();
|
|
35179
|
+
let targetProvider = req.provider ?? config2.activeProviderId;
|
|
35180
|
+
if (req.provider) {
|
|
35181
|
+
const exists = PROVIDERS.some((p3) => p3.id === req.provider);
|
|
35182
|
+
if (!exists) {
|
|
35183
|
+
return {
|
|
35184
|
+
ok: false,
|
|
35185
|
+
error: `unknown provider '${req.provider}'. Available: ${PROVIDERS.map((p3) => p3.id).join(", ")}`
|
|
35186
|
+
};
|
|
35187
|
+
}
|
|
35188
|
+
setActiveProviderId(req.provider);
|
|
35189
|
+
targetProvider = req.provider;
|
|
35190
|
+
}
|
|
35191
|
+
if (req.endpointClear) {
|
|
35192
|
+
clearCustomEndpoint(targetProvider);
|
|
35193
|
+
}
|
|
35194
|
+
if (req.endpoint) {
|
|
35195
|
+
setCustomEndpoint(targetProvider, req.endpoint);
|
|
35196
|
+
}
|
|
35197
|
+
if (req.model) {
|
|
35198
|
+
setModelForProvider(targetProvider, req.model);
|
|
35199
|
+
}
|
|
35200
|
+
const after = getProviderConfig();
|
|
35201
|
+
const ep = getCustomEndpoint(after.activeProviderId);
|
|
35202
|
+
return {
|
|
35203
|
+
ok: true,
|
|
35204
|
+
message: `activeProvider=${after.activeProviderId} model=${after.modelByProvider[after.activeProviderId]}` + (ep ? ` endpoint=${ep}` : "")
|
|
35205
|
+
};
|
|
35206
|
+
} catch (err) {
|
|
35207
|
+
return {
|
|
35208
|
+
ok: false,
|
|
35209
|
+
error: err instanceof Error ? err.message : String(err)
|
|
35210
|
+
};
|
|
35211
|
+
}
|
|
35212
|
+
}
|
|
35213
|
+
function applySetKey(req) {
|
|
35214
|
+
try {
|
|
35215
|
+
const exists = PROVIDERS.some((p3) => p3.id === req.provider);
|
|
35216
|
+
if (!exists) {
|
|
35217
|
+
return {
|
|
35218
|
+
ok: false,
|
|
35219
|
+
error: `unknown provider '${req.provider}'. Available: ${PROVIDERS.map((p3) => p3.id).join(", ")}`
|
|
35220
|
+
};
|
|
35221
|
+
}
|
|
35222
|
+
setApiKey(req.provider, req.key);
|
|
35223
|
+
setActiveProviderId(req.provider);
|
|
35224
|
+
return {
|
|
35225
|
+
ok: true,
|
|
35226
|
+
provider: req.provider,
|
|
35227
|
+
masked: maskKey(req.key)
|
|
35228
|
+
};
|
|
35229
|
+
} catch (err) {
|
|
35230
|
+
return {
|
|
35231
|
+
ok: false,
|
|
35232
|
+
error: err instanceof Error ? err.message : String(err)
|
|
35233
|
+
};
|
|
35234
|
+
}
|
|
35235
|
+
}
|
|
35236
|
+
async function runDiscoverModels(providerArg) {
|
|
35237
|
+
const config2 = getProviderConfig();
|
|
35238
|
+
const providerId = (providerArg ?? config2.activeProviderId).trim();
|
|
35239
|
+
if (!DISCOVERABLE.includes(providerId)) {
|
|
35240
|
+
if (providerId === "custom") {
|
|
35241
|
+
} else {
|
|
35242
|
+
return {
|
|
35243
|
+
ok: false,
|
|
35244
|
+
error: `provider '${providerId}' is not discoverable. Use: ${DISCOVERABLE.join(", ")}`
|
|
35245
|
+
};
|
|
35246
|
+
}
|
|
35247
|
+
}
|
|
35248
|
+
const discoveryId = providerId === "custom" ? "openai-compatible" : providerId;
|
|
35249
|
+
try {
|
|
35250
|
+
const entry = await discoverModelsForProvider(discoveryId);
|
|
35251
|
+
return {
|
|
35252
|
+
ok: true,
|
|
35253
|
+
payload: {
|
|
35254
|
+
ok: true,
|
|
35255
|
+
provider: providerId,
|
|
35256
|
+
models: entry.models.map((m) => m.id),
|
|
35257
|
+
fetchedAt: entry.fetchedAt,
|
|
35258
|
+
baseUrl: entry.baseUrl
|
|
35259
|
+
}
|
|
35260
|
+
};
|
|
35261
|
+
} catch (err) {
|
|
35262
|
+
return {
|
|
35263
|
+
ok: false,
|
|
35264
|
+
error: err instanceof Error ? err.message : String(err)
|
|
35265
|
+
};
|
|
35266
|
+
}
|
|
35267
|
+
}
|
|
35268
|
+
var DISCOVERABLE;
|
|
35269
|
+
var init_desktopConfig = __esm({
|
|
35270
|
+
"src/cli/desktopConfig.ts"() {
|
|
35271
|
+
"use strict";
|
|
35272
|
+
init_keyStore();
|
|
35273
|
+
init_providerConfig();
|
|
35274
|
+
init_modelDiscovery();
|
|
35275
|
+
init_updater();
|
|
35276
|
+
DISCOVERABLE = [
|
|
35277
|
+
"grok",
|
|
35278
|
+
"glm",
|
|
35279
|
+
"minimax",
|
|
35280
|
+
"deepseek",
|
|
35281
|
+
"openai-compatible"
|
|
35282
|
+
];
|
|
35283
|
+
}
|
|
35284
|
+
});
|
|
35285
|
+
|
|
35286
|
+
// src/cli/companion/config.ts
|
|
35287
|
+
import {
|
|
35288
|
+
existsSync as existsSync40,
|
|
35289
|
+
mkdirSync as mkdirSync17,
|
|
35290
|
+
readFileSync as readFileSync33,
|
|
35291
|
+
writeFileSync as writeFileSync21
|
|
35292
|
+
} from "node:fs";
|
|
35293
|
+
import { join as join35 } from "node:path";
|
|
35294
|
+
import { homedir as homedir11 } from "node:os";
|
|
35295
|
+
import { createHash as createHash6, randomBytes as randomBytes2, timingSafeEqual } from "node:crypto";
|
|
35296
|
+
function getZelariHome() {
|
|
35297
|
+
return join35(homedir11(), ".zelari-code");
|
|
35298
|
+
}
|
|
35299
|
+
function getCompanionConfigPath() {
|
|
35300
|
+
return join35(getZelariHome(), "companion.json");
|
|
35301
|
+
}
|
|
35302
|
+
function getCompanionTokenPath() {
|
|
35303
|
+
return join35(getZelariHome(), "companion.token");
|
|
35304
|
+
}
|
|
35305
|
+
function ensureHome() {
|
|
35306
|
+
const home = getZelariHome();
|
|
35307
|
+
if (!existsSync40(home)) {
|
|
35308
|
+
mkdirSync17(home, { recursive: true });
|
|
35309
|
+
}
|
|
35310
|
+
}
|
|
35311
|
+
function loadCompanionConfig() {
|
|
35312
|
+
const path40 = getCompanionConfigPath();
|
|
35313
|
+
if (!existsSync40(path40)) {
|
|
35314
|
+
return { projects: [] };
|
|
35315
|
+
}
|
|
35316
|
+
try {
|
|
35317
|
+
const raw = JSON.parse(readFileSync33(path40, "utf8"));
|
|
35318
|
+
const projects = Array.isArray(raw.projects) ? raw.projects.filter(
|
|
35319
|
+
(p3) => p3 && typeof p3.path === "string" && p3.path.trim() && typeof (p3.id ?? p3.name) === "string"
|
|
35320
|
+
).map((p3) => ({
|
|
35321
|
+
id: String(p3.id || p3.name).trim().toLowerCase().replace(/[^a-z0-9-_]+/g, "-").slice(0, 64),
|
|
35322
|
+
name: String(p3.name || p3.id || "project").trim() || "project",
|
|
35323
|
+
path: String(p3.path).trim()
|
|
35324
|
+
})) : [];
|
|
35325
|
+
return {
|
|
35326
|
+
projects,
|
|
35327
|
+
bind: typeof raw.bind === "string" ? raw.bind : void 0,
|
|
35328
|
+
port: typeof raw.port === "number" ? raw.port : void 0
|
|
35329
|
+
};
|
|
35330
|
+
} catch {
|
|
35331
|
+
return { projects: [] };
|
|
35332
|
+
}
|
|
35333
|
+
}
|
|
35334
|
+
function saveCompanionConfig(cfg) {
|
|
35335
|
+
ensureHome();
|
|
35336
|
+
writeFileSync21(
|
|
35337
|
+
getCompanionConfigPath(),
|
|
35338
|
+
JSON.stringify(
|
|
35339
|
+
{
|
|
35340
|
+
bind: cfg.bind,
|
|
35341
|
+
port: cfg.port,
|
|
35342
|
+
projects: cfg.projects
|
|
35343
|
+
},
|
|
35344
|
+
null,
|
|
35345
|
+
2
|
|
35346
|
+
) + "\n",
|
|
35347
|
+
"utf8"
|
|
35348
|
+
);
|
|
35349
|
+
}
|
|
35350
|
+
function loadOrCreateToken(explicit) {
|
|
35351
|
+
if (explicit && explicit.trim()) {
|
|
35352
|
+
return { token: explicit.trim(), created: false };
|
|
35353
|
+
}
|
|
35354
|
+
ensureHome();
|
|
35355
|
+
const path40 = getCompanionTokenPath();
|
|
35356
|
+
if (existsSync40(path40)) {
|
|
35357
|
+
const t = readFileSync33(path40, "utf8").trim();
|
|
35358
|
+
if (t) return { token: t, created: false };
|
|
35359
|
+
}
|
|
35360
|
+
const token = randomBytes2(24).toString("base64url");
|
|
35361
|
+
writeFileSync21(path40, token + "\n", "utf8");
|
|
35362
|
+
try {
|
|
35363
|
+
const fs24 = __require("node:fs");
|
|
35364
|
+
fs24.chmodSync?.(path40, 384);
|
|
35365
|
+
} catch {
|
|
35366
|
+
}
|
|
35367
|
+
return { token, created: true };
|
|
35368
|
+
}
|
|
35369
|
+
function tokenMatches(expected, provided) {
|
|
35370
|
+
if (!provided) return false;
|
|
35371
|
+
const a = createHash6("sha256").update(expected).digest();
|
|
35372
|
+
const b = createHash6("sha256").update(provided).digest();
|
|
35373
|
+
try {
|
|
35374
|
+
return timingSafeEqual(a, b);
|
|
35375
|
+
} catch {
|
|
35376
|
+
return false;
|
|
35377
|
+
}
|
|
35378
|
+
}
|
|
35379
|
+
function slugFromPath(p3) {
|
|
35380
|
+
const base = p3.replace(/[/\\]+$/, "").split(/[/\\]/).pop() || "project";
|
|
35381
|
+
return base.toLowerCase().replace(/[^a-z0-9-_]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48) || "project";
|
|
35382
|
+
}
|
|
35383
|
+
function mergeProjects(cfg, extraPaths) {
|
|
35384
|
+
const byId = /* @__PURE__ */ new Map();
|
|
35385
|
+
for (const p3 of cfg.projects) {
|
|
35386
|
+
byId.set(p3.id, p3);
|
|
35387
|
+
}
|
|
35388
|
+
for (const raw of extraPaths) {
|
|
35389
|
+
const path40 = raw.trim();
|
|
35390
|
+
if (!path40) continue;
|
|
35391
|
+
let id = slugFromPath(path40);
|
|
35392
|
+
let n = 2;
|
|
35393
|
+
while (byId.has(id) && byId.get(id).path !== path40) {
|
|
35394
|
+
id = `${slugFromPath(path40)}-${n++}`;
|
|
35395
|
+
}
|
|
35396
|
+
byId.set(id, {
|
|
35397
|
+
id,
|
|
35398
|
+
name: slugFromPath(path40),
|
|
35399
|
+
path: path40
|
|
35400
|
+
});
|
|
35401
|
+
}
|
|
35402
|
+
return [...byId.values()];
|
|
35403
|
+
}
|
|
35404
|
+
function resolveProjectPath(projects, cwdOrId) {
|
|
35405
|
+
if (!projects.length) {
|
|
35406
|
+
return {
|
|
35407
|
+
ok: false,
|
|
35408
|
+
error: "No projects configured. Pass --project <path> or edit ~/.zelari-code/companion.json"
|
|
35409
|
+
};
|
|
35410
|
+
}
|
|
35411
|
+
if (!cwdOrId || !String(cwdOrId).trim()) {
|
|
35412
|
+
return { ok: true, project: projects[0] };
|
|
35413
|
+
}
|
|
35414
|
+
const key = String(cwdOrId).trim();
|
|
35415
|
+
const byId = projects.find((p3) => p3.id === key || p3.name === key);
|
|
35416
|
+
if (byId) return { ok: true, project: byId };
|
|
35417
|
+
const norm = key.replace(/\\/g, "/").toLowerCase();
|
|
35418
|
+
const byPath = projects.find(
|
|
35419
|
+
(p3) => p3.path.replace(/\\/g, "/").toLowerCase() === norm
|
|
35420
|
+
);
|
|
35421
|
+
if (byPath) return { ok: true, project: byPath };
|
|
35422
|
+
const under = projects.find((p3) => {
|
|
35423
|
+
const root = p3.path.replace(/\\/g, "/").toLowerCase().replace(/\/$/, "");
|
|
35424
|
+
return norm === root || norm.startsWith(root + "/");
|
|
35425
|
+
});
|
|
35426
|
+
if (under) {
|
|
35427
|
+
return {
|
|
35428
|
+
ok: true,
|
|
35429
|
+
project: { ...under, path: key }
|
|
35430
|
+
};
|
|
35431
|
+
}
|
|
35432
|
+
return {
|
|
35433
|
+
ok: false,
|
|
35434
|
+
error: `cwd/project not in allowlist: ${key}. Allowed: ${projects.map((p3) => p3.id).join(", ")}`
|
|
35435
|
+
};
|
|
35436
|
+
}
|
|
35437
|
+
var DEFAULT_COMPANION_PORT, DEFAULT_COMPANION_BIND;
|
|
35438
|
+
var init_config = __esm({
|
|
35439
|
+
"src/cli/companion/config.ts"() {
|
|
35440
|
+
"use strict";
|
|
35441
|
+
DEFAULT_COMPANION_PORT = 7421;
|
|
35442
|
+
DEFAULT_COMPANION_BIND = "127.0.0.1";
|
|
35443
|
+
}
|
|
35444
|
+
});
|
|
35445
|
+
|
|
35446
|
+
// src/cli/companion/runManager.ts
|
|
35447
|
+
import { spawn as spawn11 } from "node:child_process";
|
|
35448
|
+
import { createInterface } from "node:readline";
|
|
35449
|
+
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
35450
|
+
import { writeFileSync as writeFileSync22, unlinkSync as unlinkSync2 } from "node:fs";
|
|
35451
|
+
import { join as join36 } from "node:path";
|
|
35452
|
+
import { tmpdir as tmpdir3 } from "node:os";
|
|
35453
|
+
var RunManager;
|
|
35454
|
+
var init_runManager = __esm({
|
|
35455
|
+
"src/cli/companion/runManager.ts"() {
|
|
35456
|
+
"use strict";
|
|
35457
|
+
RunManager = class {
|
|
35458
|
+
active = null;
|
|
35459
|
+
recent = [];
|
|
35460
|
+
getActive() {
|
|
35461
|
+
return this.active?.run ?? null;
|
|
35462
|
+
}
|
|
35463
|
+
getRun(id) {
|
|
35464
|
+
if (this.active?.run.id === id) return this.active.run;
|
|
35465
|
+
return this.recent.find((r) => r.id === id) ?? null;
|
|
35466
|
+
}
|
|
35467
|
+
listRecent(limit = 20) {
|
|
35468
|
+
const cur = this.active ? [this.active.run] : [];
|
|
35469
|
+
return [...cur, ...this.recent].slice(0, limit);
|
|
35470
|
+
}
|
|
35471
|
+
subscribe(runId, fn) {
|
|
35472
|
+
if (this.active?.run.id === runId) {
|
|
35473
|
+
for (const ev of this.active.run.events) {
|
|
35474
|
+
try {
|
|
35475
|
+
fn(ev);
|
|
35476
|
+
} catch {
|
|
35477
|
+
}
|
|
35478
|
+
}
|
|
35479
|
+
this.active.listeners.add(fn);
|
|
35480
|
+
return () => {
|
|
35481
|
+
this.active?.listeners.delete(fn);
|
|
35482
|
+
};
|
|
35483
|
+
}
|
|
35484
|
+
const past = this.recent.find((r) => r.id === runId);
|
|
35485
|
+
if (past) {
|
|
35486
|
+
for (const ev of past.events) {
|
|
35487
|
+
try {
|
|
35488
|
+
fn(ev);
|
|
35489
|
+
} catch {
|
|
35490
|
+
}
|
|
35491
|
+
}
|
|
35492
|
+
}
|
|
35493
|
+
return () => {
|
|
35494
|
+
};
|
|
35495
|
+
}
|
|
35496
|
+
emit(ev) {
|
|
35497
|
+
if (!this.active) return;
|
|
35498
|
+
this.active.run.events.push(ev);
|
|
35499
|
+
if (this.active.run.events.length > 5e3) {
|
|
35500
|
+
this.active.run.events.splice(0, this.active.run.events.length - 4e3);
|
|
35501
|
+
}
|
|
35502
|
+
for (const fn of this.active.listeners) {
|
|
35503
|
+
try {
|
|
35504
|
+
fn(ev);
|
|
35505
|
+
} catch {
|
|
35506
|
+
}
|
|
35507
|
+
}
|
|
35508
|
+
}
|
|
35509
|
+
start(args) {
|
|
35510
|
+
if (this.active) {
|
|
35511
|
+
return {
|
|
35512
|
+
ok: false,
|
|
35513
|
+
error: `A run is already active (${this.active.run.id}). Cancel it first.`
|
|
35514
|
+
};
|
|
35515
|
+
}
|
|
35516
|
+
const prompt = args.prompt?.trim();
|
|
35517
|
+
if (!prompt) return { ok: false, error: "prompt is required" };
|
|
35518
|
+
const id = randomUUID6();
|
|
35519
|
+
const mode = (args.mode || "agent").toLowerCase();
|
|
35520
|
+
const phase2 = (args.phase || "build").toLowerCase();
|
|
35521
|
+
const run = {
|
|
35522
|
+
id,
|
|
35523
|
+
status: "running",
|
|
35524
|
+
prompt,
|
|
35525
|
+
mode,
|
|
35526
|
+
phase: phase2,
|
|
35527
|
+
cwd: args.cwd,
|
|
35528
|
+
createdAt: Date.now(),
|
|
35529
|
+
events: []
|
|
35530
|
+
};
|
|
35531
|
+
const cliEntry = process.argv[1];
|
|
35532
|
+
if (!cliEntry) {
|
|
35533
|
+
return { ok: false, error: "Cannot resolve CLI entry (process.argv[1])" };
|
|
35534
|
+
}
|
|
35535
|
+
const argv = [
|
|
35536
|
+
cliEntry,
|
|
35537
|
+
"--headless",
|
|
35538
|
+
"--task",
|
|
35539
|
+
prompt,
|
|
35540
|
+
"--output",
|
|
35541
|
+
"json",
|
|
35542
|
+
"--mode",
|
|
35543
|
+
mode === "council" || mode === "zelari" ? mode : "agent",
|
|
35544
|
+
"--phase",
|
|
35545
|
+
phase2 === "plan" ? "plan" : "build"
|
|
35546
|
+
];
|
|
35547
|
+
if (args.provider?.trim()) {
|
|
35548
|
+
argv.push("--provider", args.provider.trim());
|
|
35549
|
+
}
|
|
35550
|
+
if (args.model?.trim()) {
|
|
35551
|
+
argv.push("--model", args.model.trim());
|
|
35552
|
+
}
|
|
35553
|
+
let historyFile;
|
|
35554
|
+
if (args.history && Array.isArray(args.history) && args.history.length > 0) {
|
|
35555
|
+
historyFile = join36(tmpdir3(), `zelari-companion-hist-${id}.json`);
|
|
35556
|
+
try {
|
|
35557
|
+
writeFileSync22(historyFile, JSON.stringify(args.history), "utf8");
|
|
35558
|
+
argv.push("--history-file", historyFile);
|
|
35559
|
+
} catch {
|
|
35560
|
+
historyFile = void 0;
|
|
35561
|
+
}
|
|
35562
|
+
}
|
|
35563
|
+
const child = spawn11(process.execPath, argv, {
|
|
35564
|
+
cwd: args.cwd,
|
|
35565
|
+
env: {
|
|
35566
|
+
...process.env,
|
|
35567
|
+
ZELARI_SKIP_PREFLIGHT: "1",
|
|
35568
|
+
ANATHEMA_DEV: "1",
|
|
35569
|
+
FORCE_COLOR: "0"
|
|
35570
|
+
},
|
|
35571
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
35572
|
+
windowsHide: true
|
|
35573
|
+
});
|
|
35574
|
+
this.active = { run, child, listeners: /* @__PURE__ */ new Set(), historyFile };
|
|
35575
|
+
this.emit({
|
|
35576
|
+
type: "log",
|
|
35577
|
+
message: `[companion] run ${id} started mode=${mode} phase=${phase2} cwd=${args.cwd}`
|
|
35578
|
+
});
|
|
35579
|
+
if (child.stdout) {
|
|
35580
|
+
const rl = createInterface({ input: child.stdout });
|
|
35581
|
+
rl.on("line", (line) => {
|
|
35582
|
+
const t = line.trim();
|
|
35583
|
+
if (!t) return;
|
|
35584
|
+
try {
|
|
35585
|
+
const ev = JSON.parse(t);
|
|
35586
|
+
this.emit(ev);
|
|
35587
|
+
} catch {
|
|
35588
|
+
this.emit({ type: "log", message: t });
|
|
35589
|
+
}
|
|
35590
|
+
});
|
|
35591
|
+
}
|
|
35592
|
+
child.stderr?.on("data", (buf) => {
|
|
35593
|
+
const text = buf.toString("utf8").trim();
|
|
35594
|
+
if (!text) return;
|
|
35595
|
+
for (const line of text.split(/\r?\n/)) {
|
|
35596
|
+
if (line.trim()) {
|
|
35597
|
+
this.emit({ type: "log", message: line.trim() });
|
|
35598
|
+
}
|
|
35599
|
+
}
|
|
35600
|
+
});
|
|
35601
|
+
child.on("error", (err) => {
|
|
35602
|
+
run.status = "error";
|
|
35603
|
+
run.error = err.message;
|
|
35604
|
+
run.finishedAt = Date.now();
|
|
35605
|
+
this.emit({
|
|
35606
|
+
type: "error",
|
|
35607
|
+
severity: "fatal",
|
|
35608
|
+
message: err.message,
|
|
35609
|
+
code: "spawn"
|
|
35610
|
+
});
|
|
35611
|
+
this.finishActive();
|
|
35612
|
+
});
|
|
35613
|
+
child.on("close", (code) => {
|
|
35614
|
+
if (run.status === "running") {
|
|
35615
|
+
run.status = code === 0 ? "completed" : "error";
|
|
35616
|
+
run.exitCode = code;
|
|
35617
|
+
run.finishedAt = Date.now();
|
|
35618
|
+
if (code !== 0 && !run.error) {
|
|
35619
|
+
run.error = `exit ${code}`;
|
|
35620
|
+
}
|
|
35621
|
+
}
|
|
35622
|
+
this.emit({
|
|
35623
|
+
type: "run_finished",
|
|
35624
|
+
runId: id,
|
|
35625
|
+
status: run.status,
|
|
35626
|
+
exitCode: code
|
|
35627
|
+
});
|
|
35628
|
+
this.finishActive();
|
|
35629
|
+
});
|
|
35630
|
+
return { ok: true, run };
|
|
35631
|
+
}
|
|
35632
|
+
cancel(runId) {
|
|
35633
|
+
if (!this.active) {
|
|
35634
|
+
return { ok: false, error: "No active run" };
|
|
35635
|
+
}
|
|
35636
|
+
if (runId && this.active.run.id !== runId) {
|
|
35637
|
+
return { ok: false, error: `Run ${runId} is not active` };
|
|
35638
|
+
}
|
|
35639
|
+
const { run, child } = this.active;
|
|
35640
|
+
run.status = "cancelled";
|
|
35641
|
+
run.finishedAt = Date.now();
|
|
35642
|
+
this.emit({
|
|
35643
|
+
type: "log",
|
|
35644
|
+
message: `[companion] cancelling run ${run.id}`
|
|
35645
|
+
});
|
|
35646
|
+
try {
|
|
35647
|
+
child.kill("SIGTERM");
|
|
35648
|
+
} catch {
|
|
35649
|
+
}
|
|
35650
|
+
setTimeout(() => {
|
|
35651
|
+
try {
|
|
35652
|
+
if (!child.killed) child.kill("SIGKILL");
|
|
35653
|
+
} catch {
|
|
35654
|
+
}
|
|
35655
|
+
}, 1500);
|
|
35656
|
+
return { ok: true };
|
|
35657
|
+
}
|
|
35658
|
+
finishActive() {
|
|
35659
|
+
if (!this.active) return;
|
|
35660
|
+
const { run, historyFile } = this.active;
|
|
35661
|
+
if (historyFile) {
|
|
35662
|
+
try {
|
|
35663
|
+
unlinkSync2(historyFile);
|
|
35664
|
+
} catch {
|
|
35665
|
+
}
|
|
35666
|
+
}
|
|
35667
|
+
this.recent.unshift(run);
|
|
35668
|
+
if (this.recent.length > 30) this.recent.length = 30;
|
|
35669
|
+
this.active = null;
|
|
35670
|
+
}
|
|
35671
|
+
};
|
|
35672
|
+
}
|
|
35673
|
+
});
|
|
35674
|
+
|
|
35675
|
+
// src/cli/companion/serve.ts
|
|
35676
|
+
var serve_exports = {};
|
|
35677
|
+
__export(serve_exports, {
|
|
35678
|
+
parseServeFlags: () => parseServeFlags,
|
|
35679
|
+
runCompanionServe: () => runCompanionServe
|
|
35680
|
+
});
|
|
35681
|
+
import { createServer } from "node:http";
|
|
35682
|
+
import { existsSync as existsSync41 } from "node:fs";
|
|
35683
|
+
import { resolve as resolve2 } from "node:path";
|
|
35684
|
+
function readBody(req, max = 2e6) {
|
|
35685
|
+
return new Promise((resolveBody, reject) => {
|
|
35686
|
+
const chunks = [];
|
|
35687
|
+
let size = 0;
|
|
35688
|
+
req.on("data", (c) => {
|
|
35689
|
+
size += c.length;
|
|
35690
|
+
if (size > max) {
|
|
35691
|
+
reject(new Error("body too large"));
|
|
35692
|
+
req.destroy();
|
|
35693
|
+
return;
|
|
35694
|
+
}
|
|
35695
|
+
chunks.push(c);
|
|
35696
|
+
});
|
|
35697
|
+
req.on("end", () => resolveBody(Buffer.concat(chunks).toString("utf8")));
|
|
35698
|
+
req.on("error", reject);
|
|
35699
|
+
});
|
|
35700
|
+
}
|
|
35701
|
+
function sendJson(res, status, body, extraHeaders) {
|
|
35702
|
+
const data = JSON.stringify(body);
|
|
35703
|
+
res.writeHead(status, {
|
|
35704
|
+
"content-type": "application/json; charset=utf-8",
|
|
35705
|
+
"content-length": Buffer.byteLength(data),
|
|
35706
|
+
"access-control-allow-origin": "*",
|
|
35707
|
+
"access-control-allow-headers": "authorization, content-type",
|
|
35708
|
+
"access-control-allow-methods": "GET, POST, OPTIONS",
|
|
35709
|
+
...extraHeaders
|
|
35710
|
+
});
|
|
35711
|
+
res.end(data);
|
|
35712
|
+
}
|
|
35713
|
+
function getBearer(req) {
|
|
35714
|
+
const h = req.headers.authorization;
|
|
35715
|
+
if (!h) return null;
|
|
35716
|
+
const m = /^Bearer\s+(.+)$/i.exec(h.trim());
|
|
35717
|
+
return m?.[1]?.trim() || null;
|
|
35718
|
+
}
|
|
35719
|
+
function parseUrl(req) {
|
|
35720
|
+
return new URL(req.url || "/", `http://${req.headers.host || "localhost"}`);
|
|
35721
|
+
}
|
|
35722
|
+
async function runCompanionServe(opts = {}) {
|
|
35723
|
+
const fileCfg = loadCompanionConfig();
|
|
35724
|
+
const bind = (opts.bind || fileCfg.bind || DEFAULT_COMPANION_BIND).trim();
|
|
35725
|
+
const port = opts.port ?? fileCfg.port ?? DEFAULT_COMPANION_PORT;
|
|
35726
|
+
const { token, created } = loadOrCreateToken(opts.token);
|
|
35727
|
+
let projects = mergeProjects(fileCfg, opts.projects ?? []);
|
|
35728
|
+
projects = projects.filter((p3) => {
|
|
35729
|
+
const abs = resolve2(p3.path);
|
|
35730
|
+
if (!existsSync41(abs)) {
|
|
35731
|
+
process.stderr.write(
|
|
35732
|
+
`[zelari-code serve] skip missing project path: ${p3.path}
|
|
35733
|
+
`
|
|
35734
|
+
);
|
|
35735
|
+
return false;
|
|
35736
|
+
}
|
|
35737
|
+
p3.path = abs;
|
|
35738
|
+
return true;
|
|
35739
|
+
});
|
|
35740
|
+
if (opts.persistProjects && projects.length > 0) {
|
|
35741
|
+
saveCompanionConfig({
|
|
35742
|
+
...fileCfg,
|
|
35743
|
+
bind,
|
|
35744
|
+
port,
|
|
35745
|
+
projects
|
|
35746
|
+
});
|
|
35747
|
+
}
|
|
35748
|
+
if (projects.length === 0) {
|
|
35749
|
+
const cwd = resolve2(process.cwd());
|
|
35750
|
+
projects = [
|
|
35751
|
+
{
|
|
35752
|
+
id: "default",
|
|
35753
|
+
name: "default",
|
|
35754
|
+
path: cwd
|
|
35755
|
+
}
|
|
35756
|
+
];
|
|
35757
|
+
process.stderr.write(
|
|
35758
|
+
`[zelari-code serve] no projects configured \u2014 using cwd as default: ${cwd}
|
|
35759
|
+
Add more: zelari-code serve --project <path>
|
|
35760
|
+
Or edit ~/.zelari-code/companion.json
|
|
35761
|
+
`
|
|
35762
|
+
);
|
|
35763
|
+
}
|
|
35764
|
+
const runs = new RunManager();
|
|
35765
|
+
const server = createServer(async (req, res) => {
|
|
35766
|
+
if (req.method === "OPTIONS") {
|
|
35767
|
+
res.writeHead(204, {
|
|
35768
|
+
"access-control-allow-origin": "*",
|
|
35769
|
+
"access-control-allow-headers": "authorization, content-type",
|
|
35770
|
+
"access-control-allow-methods": "GET, POST, OPTIONS"
|
|
35771
|
+
});
|
|
35772
|
+
res.end();
|
|
35773
|
+
return;
|
|
35774
|
+
}
|
|
35775
|
+
const url2 = parseUrl(req);
|
|
35776
|
+
const path40 = url2.pathname.replace(/\/+$/, "") || "/";
|
|
35777
|
+
try {
|
|
35778
|
+
if (req.method === "GET" && (path40 === "/health" || path40 === "/v1/health")) {
|
|
35779
|
+
sendJson(res, 200, {
|
|
35780
|
+
ok: true,
|
|
35781
|
+
service: "zelari-companion",
|
|
35782
|
+
version: getCurrentVersion(),
|
|
35783
|
+
bind,
|
|
35784
|
+
port,
|
|
35785
|
+
projects: projects.length,
|
|
35786
|
+
activeRun: runs.getActive()?.id ?? null
|
|
35787
|
+
});
|
|
35788
|
+
return;
|
|
35789
|
+
}
|
|
35790
|
+
if (path40.startsWith("/v1")) {
|
|
35791
|
+
if (!tokenMatches(token, getBearer(req))) {
|
|
35792
|
+
sendJson(res, 401, { ok: false, error: "unauthorized" });
|
|
35793
|
+
return;
|
|
35794
|
+
}
|
|
35795
|
+
}
|
|
35796
|
+
if (req.method === "GET" && path40 === "/v1/config") {
|
|
35797
|
+
const snap = buildDesktopConfigSnapshot();
|
|
35798
|
+
sendJson(res, 200, { ok: true, ...snap });
|
|
35799
|
+
return;
|
|
35800
|
+
}
|
|
35801
|
+
if (req.method === "GET" && path40 === "/v1/projects") {
|
|
35802
|
+
sendJson(res, 200, {
|
|
35803
|
+
ok: true,
|
|
35804
|
+
projects: projects.map((p3) => ({
|
|
35805
|
+
id: p3.id,
|
|
35806
|
+
name: p3.name,
|
|
35807
|
+
path: p3.path
|
|
35808
|
+
}))
|
|
35809
|
+
});
|
|
35810
|
+
return;
|
|
35811
|
+
}
|
|
35812
|
+
if (req.method === "GET" && path40 === "/v1/runs") {
|
|
35813
|
+
sendJson(res, 200, {
|
|
35814
|
+
ok: true,
|
|
35815
|
+
active: runs.getActive(),
|
|
35816
|
+
recent: runs.listRecent().map((r) => ({
|
|
35817
|
+
id: r.id,
|
|
35818
|
+
status: r.status,
|
|
35819
|
+
mode: r.mode,
|
|
35820
|
+
phase: r.phase,
|
|
35821
|
+
cwd: r.cwd,
|
|
35822
|
+
createdAt: r.createdAt,
|
|
35823
|
+
finishedAt: r.finishedAt,
|
|
35824
|
+
exitCode: r.exitCode,
|
|
35825
|
+
promptPreview: r.prompt.slice(0, 120)
|
|
35826
|
+
}))
|
|
35827
|
+
});
|
|
35828
|
+
return;
|
|
35829
|
+
}
|
|
35830
|
+
if (req.method === "POST" && path40 === "/v1/runs") {
|
|
35831
|
+
const raw = await readBody(req);
|
|
35832
|
+
let body = {};
|
|
35833
|
+
try {
|
|
35834
|
+
body = raw ? JSON.parse(raw) : {};
|
|
35835
|
+
} catch {
|
|
35836
|
+
sendJson(res, 400, { ok: false, error: "invalid JSON body" });
|
|
35837
|
+
return;
|
|
35838
|
+
}
|
|
35839
|
+
const prompt = String(body.prompt ?? body.task ?? "").trim();
|
|
35840
|
+
const mode = String(body.mode ?? "agent");
|
|
35841
|
+
const phase2 = String(body.phase ?? "build");
|
|
35842
|
+
const cwdArg = body.cwd != null ? String(body.cwd) : body.projectId != null ? String(body.projectId) : null;
|
|
35843
|
+
const resolved = resolveProjectPath(projects, cwdArg);
|
|
35844
|
+
if (!resolved.ok) {
|
|
35845
|
+
sendJson(res, 400, { ok: false, error: resolved.error });
|
|
35846
|
+
return;
|
|
35847
|
+
}
|
|
35848
|
+
const history2 = Array.isArray(body.history) ? body.history : void 0;
|
|
35849
|
+
const result = runs.start({
|
|
35850
|
+
prompt,
|
|
35851
|
+
mode,
|
|
35852
|
+
phase: phase2,
|
|
35853
|
+
cwd: resolved.project.path,
|
|
35854
|
+
provider: body.provider != null ? String(body.provider) : void 0,
|
|
35855
|
+
model: body.model != null ? String(body.model) : void 0,
|
|
35856
|
+
history: history2
|
|
35857
|
+
});
|
|
35858
|
+
if (!result.ok) {
|
|
35859
|
+
sendJson(res, 409, { ok: false, error: result.error });
|
|
35860
|
+
return;
|
|
35861
|
+
}
|
|
35862
|
+
sendJson(res, 201, {
|
|
35863
|
+
ok: true,
|
|
35864
|
+
run: {
|
|
35865
|
+
id: result.run.id,
|
|
35866
|
+
status: result.run.status,
|
|
35867
|
+
mode: result.run.mode,
|
|
35868
|
+
phase: result.run.phase,
|
|
35869
|
+
cwd: result.run.cwd,
|
|
35870
|
+
createdAt: result.run.createdAt
|
|
35871
|
+
},
|
|
35872
|
+
eventsUrl: `/v1/runs/${result.run.id}/events`,
|
|
35873
|
+
cancelUrl: `/v1/runs/${result.run.id}/cancel`
|
|
35874
|
+
});
|
|
35875
|
+
return;
|
|
35876
|
+
}
|
|
35877
|
+
const eventsMatch = /^\/v1\/runs\/([^/]+)\/events$/.exec(path40);
|
|
35878
|
+
if (req.method === "GET" && eventsMatch) {
|
|
35879
|
+
const runId = eventsMatch[1];
|
|
35880
|
+
const run = runs.getRun(runId);
|
|
35881
|
+
if (!run) {
|
|
35882
|
+
sendJson(res, 404, { ok: false, error: "run not found" });
|
|
35883
|
+
return;
|
|
35884
|
+
}
|
|
35885
|
+
res.writeHead(200, {
|
|
35886
|
+
"content-type": "text/event-stream; charset=utf-8",
|
|
35887
|
+
"cache-control": "no-cache, no-transform",
|
|
35888
|
+
connection: "keep-alive",
|
|
35889
|
+
"access-control-allow-origin": "*",
|
|
35890
|
+
"access-control-allow-headers": "authorization, content-type"
|
|
35891
|
+
});
|
|
35892
|
+
res.write(`: connected run=${runId}
|
|
35893
|
+
|
|
35894
|
+
`);
|
|
35895
|
+
const writeEv = (ev) => {
|
|
35896
|
+
try {
|
|
35897
|
+
res.write(`data: ${JSON.stringify(ev)}
|
|
35898
|
+
|
|
35899
|
+
`);
|
|
35900
|
+
} catch {
|
|
35901
|
+
}
|
|
35902
|
+
};
|
|
35903
|
+
const unsub = runs.subscribe(runId, writeEv);
|
|
35904
|
+
if (run.status !== "running" && run.status !== "queued") {
|
|
35905
|
+
writeEv({
|
|
35906
|
+
type: "run_finished",
|
|
35907
|
+
runId,
|
|
35908
|
+
status: run.status,
|
|
35909
|
+
exitCode: run.exitCode ?? null
|
|
35910
|
+
});
|
|
35911
|
+
unsub();
|
|
35912
|
+
res.end();
|
|
35913
|
+
return;
|
|
35914
|
+
}
|
|
35915
|
+
const heartbeat = setInterval(() => {
|
|
35916
|
+
try {
|
|
35917
|
+
res.write(`: ping
|
|
35918
|
+
|
|
35919
|
+
`);
|
|
35920
|
+
} catch {
|
|
35921
|
+
}
|
|
35922
|
+
}, 15e3);
|
|
35923
|
+
const onClose = () => {
|
|
35924
|
+
clearInterval(heartbeat);
|
|
35925
|
+
unsub();
|
|
35926
|
+
};
|
|
35927
|
+
req.on("close", onClose);
|
|
35928
|
+
const check2 = setInterval(() => {
|
|
35929
|
+
const r = runs.getRun(runId);
|
|
35930
|
+
if (!r || r.status !== "running" && r.status !== "queued") {
|
|
35931
|
+
clearInterval(check2);
|
|
35932
|
+
clearInterval(heartbeat);
|
|
35933
|
+
unsub();
|
|
35934
|
+
try {
|
|
35935
|
+
res.end();
|
|
35936
|
+
} catch {
|
|
35937
|
+
}
|
|
35938
|
+
}
|
|
35939
|
+
}, 500);
|
|
35940
|
+
return;
|
|
35941
|
+
}
|
|
35942
|
+
const cancelMatch = /^\/v1\/runs\/([^/]+)\/cancel$/.exec(path40);
|
|
35943
|
+
if (req.method === "POST" && cancelMatch) {
|
|
35944
|
+
const runId = cancelMatch[1];
|
|
35945
|
+
const result = runs.cancel(runId);
|
|
35946
|
+
if (!result.ok) {
|
|
35947
|
+
sendJson(res, 404, { ok: false, error: result.error });
|
|
35948
|
+
return;
|
|
35949
|
+
}
|
|
35950
|
+
sendJson(res, 200, { ok: true, cancelled: runId });
|
|
35951
|
+
return;
|
|
35952
|
+
}
|
|
35953
|
+
sendJson(res, 404, { ok: false, error: "not found" });
|
|
35954
|
+
} catch (err) {
|
|
35955
|
+
sendJson(res, 500, {
|
|
35956
|
+
ok: false,
|
|
35957
|
+
error: err instanceof Error ? err.message : String(err)
|
|
35958
|
+
});
|
|
35959
|
+
}
|
|
35960
|
+
});
|
|
35961
|
+
await new Promise((resolveListen, reject) => {
|
|
35962
|
+
server.once("error", reject);
|
|
35963
|
+
server.listen(port, bind, () => resolveListen());
|
|
35964
|
+
});
|
|
35965
|
+
server.ref();
|
|
35966
|
+
try {
|
|
35967
|
+
process.stdin?.resume?.();
|
|
35968
|
+
} catch {
|
|
35969
|
+
}
|
|
35970
|
+
const tokenHint = created ? `NEW token saved to ~/.zelari-code/companion.token` : `token: ~/.zelari-code/companion.token`;
|
|
35971
|
+
const displayHost = bind === "0.0.0.0" || bind === "::" ? "127.0.0.1 (and LAN/Tailscale interfaces)" : bind;
|
|
35972
|
+
process.stderr.write(
|
|
35973
|
+
`
|
|
35974
|
+
[zelari-code serve] companion host listening
|
|
35975
|
+
URL http://${displayHost === "127.0.0.1 (and LAN/Tailscale interfaces)" ? "127.0.0.1" : bind}:${port}
|
|
35976
|
+
Bind ${bind}
|
|
35977
|
+
Health GET /health
|
|
35978
|
+
Auth Authorization: Bearer <token>
|
|
35979
|
+
${tokenHint}
|
|
35980
|
+
Projects (${projects.length}): ${projects.map((p3) => p3.id).join(", ")}
|
|
35981
|
+
Phone use http://<PC-LAN-or-Tailscale-IP>:${port} (not 127.0.0.1 on the phone)
|
|
35982
|
+
Stop Ctrl+C (keep this window open)
|
|
35983
|
+
|
|
35984
|
+
`
|
|
35985
|
+
);
|
|
35986
|
+
if (created) {
|
|
35987
|
+
process.stderr.write(` Token (copy now): ${token}
|
|
35988
|
+
|
|
35989
|
+
`);
|
|
35990
|
+
}
|
|
35991
|
+
await new Promise((resolveStop) => {
|
|
35992
|
+
let stopped = false;
|
|
35993
|
+
const stop = (sig) => {
|
|
35994
|
+
if (stopped) return;
|
|
35995
|
+
stopped = true;
|
|
35996
|
+
process.stderr.write(`[zelari-code serve] shutting down (${sig})\u2026
|
|
35997
|
+
`);
|
|
35998
|
+
try {
|
|
35999
|
+
runs.cancel();
|
|
36000
|
+
} catch {
|
|
36001
|
+
}
|
|
36002
|
+
server.close(() => resolveStop());
|
|
36003
|
+
setTimeout(() => resolveStop(), 3e3).unref();
|
|
36004
|
+
};
|
|
36005
|
+
process.once("SIGINT", () => stop("SIGINT"));
|
|
36006
|
+
process.once("SIGTERM", () => stop("SIGTERM"));
|
|
36007
|
+
process.once("SIGHUP", () => stop("SIGHUP"));
|
|
36008
|
+
});
|
|
36009
|
+
}
|
|
36010
|
+
function parseServeFlags(argv) {
|
|
36011
|
+
if (!argv.includes("serve") && !argv.includes("--serve")) {
|
|
36012
|
+
return null;
|
|
36013
|
+
}
|
|
36014
|
+
const get = (flag) => {
|
|
36015
|
+
const i = argv.indexOf(flag);
|
|
36016
|
+
return i >= 0 && argv[i + 1] ? argv[i + 1] : void 0;
|
|
36017
|
+
};
|
|
36018
|
+
const projects = [];
|
|
36019
|
+
for (let i = 0; i < argv.length; i++) {
|
|
36020
|
+
if (argv[i] === "--project" && argv[i + 1]) {
|
|
36021
|
+
projects.push(argv[i + 1]);
|
|
36022
|
+
i++;
|
|
36023
|
+
}
|
|
36024
|
+
}
|
|
36025
|
+
const portRaw = get("--port");
|
|
36026
|
+
const port = portRaw ? Number.parseInt(portRaw, 10) : void 0;
|
|
36027
|
+
return {
|
|
36028
|
+
bind: get("--bind"),
|
|
36029
|
+
port: Number.isFinite(port) ? port : void 0,
|
|
36030
|
+
token: get("--token"),
|
|
36031
|
+
projects,
|
|
36032
|
+
persistProjects: argv.includes("--save-projects")
|
|
36033
|
+
};
|
|
36034
|
+
}
|
|
36035
|
+
var init_serve = __esm({
|
|
36036
|
+
"src/cli/companion/serve.ts"() {
|
|
36037
|
+
"use strict";
|
|
36038
|
+
init_updater();
|
|
36039
|
+
init_desktopConfig();
|
|
36040
|
+
init_config();
|
|
36041
|
+
init_runManager();
|
|
36042
|
+
}
|
|
36043
|
+
});
|
|
36044
|
+
|
|
34360
36045
|
// src/cli/utils/doctor.ts
|
|
34361
36046
|
var doctor_exports = {};
|
|
34362
36047
|
__export(doctor_exports, {
|
|
34363
36048
|
runDoctor: () => runDoctor
|
|
34364
36049
|
});
|
|
34365
36050
|
import { execSync as execSync2 } from "node:child_process";
|
|
34366
|
-
import { existsSync as
|
|
36051
|
+
import { existsSync as existsSync42, readFileSync as readFileSync34, readlinkSync, statSync as statSync7 } from "node:fs";
|
|
34367
36052
|
import { createRequire as createRequire3 } from "node:module";
|
|
34368
36053
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
34369
|
-
import
|
|
36054
|
+
import path39 from "node:path";
|
|
34370
36055
|
function findPackageRoot(start) {
|
|
34371
36056
|
let dir = start;
|
|
34372
36057
|
for (let i = 0; i < 6; i += 1) {
|
|
34373
|
-
const candidate =
|
|
34374
|
-
if (
|
|
36058
|
+
const candidate = path39.join(dir, "package.json");
|
|
36059
|
+
if (existsSync42(candidate)) {
|
|
34375
36060
|
try {
|
|
34376
|
-
const pkg = JSON.parse(
|
|
36061
|
+
const pkg = JSON.parse(readFileSync34(candidate, "utf8"));
|
|
34377
36062
|
if (pkg.name === "zelari-code") return dir;
|
|
34378
36063
|
} catch {
|
|
34379
36064
|
}
|
|
34380
36065
|
}
|
|
34381
|
-
const parent =
|
|
36066
|
+
const parent = path39.dirname(dir);
|
|
34382
36067
|
if (parent === dir) break;
|
|
34383
36068
|
dir = parent;
|
|
34384
36069
|
}
|
|
34385
|
-
return
|
|
36070
|
+
return path39.resolve(__dirname3, "..", "..", "..");
|
|
34386
36071
|
}
|
|
34387
36072
|
function tryExec(cmd) {
|
|
34388
36073
|
try {
|
|
@@ -34396,8 +36081,8 @@ function tryExec(cmd) {
|
|
|
34396
36081
|
}
|
|
34397
36082
|
function readPackageJson3() {
|
|
34398
36083
|
try {
|
|
34399
|
-
const pkgPath =
|
|
34400
|
-
return JSON.parse(
|
|
36084
|
+
const pkgPath = path39.join(packageRoot, "package.json");
|
|
36085
|
+
return JSON.parse(readFileSync34(pkgPath, "utf8"));
|
|
34401
36086
|
} catch {
|
|
34402
36087
|
return null;
|
|
34403
36088
|
}
|
|
@@ -34412,17 +36097,17 @@ function checkShim(pkgName) {
|
|
|
34412
36097
|
}
|
|
34413
36098
|
const isWin = process.platform === "win32";
|
|
34414
36099
|
const shimName = isWin ? "zelari-code.cmd" : "zelari-code";
|
|
34415
|
-
const shimPath =
|
|
34416
|
-
if (!
|
|
36100
|
+
const shimPath = path39.join(prefix, shimName);
|
|
36101
|
+
if (!existsSync42(shimPath)) {
|
|
34417
36102
|
return FAIL(
|
|
34418
36103
|
`shim not found at ${shimPath}
|
|
34419
36104
|
fix: npm install -g ${pkgName}@latest --force`
|
|
34420
36105
|
);
|
|
34421
36106
|
}
|
|
34422
36107
|
try {
|
|
34423
|
-
const st =
|
|
36108
|
+
const st = statSync7(shimPath);
|
|
34424
36109
|
if (isWin) {
|
|
34425
|
-
const content =
|
|
36110
|
+
const content = readFileSync34(shimPath, "utf8");
|
|
34426
36111
|
if (content.includes(`${pkgName}\\bin\\`) || content.includes(`${pkgName}/bin/`)) {
|
|
34427
36112
|
return OK(`shim OK at ${shimPath} (${st.size} bytes)`);
|
|
34428
36113
|
}
|
|
@@ -34440,8 +36125,8 @@ function checkShim(pkgName) {
|
|
|
34440
36125
|
fix: npm install -g ${pkgName}@latest --force`
|
|
34441
36126
|
);
|
|
34442
36127
|
}
|
|
34443
|
-
const resolved =
|
|
34444
|
-
const expected =
|
|
36128
|
+
const resolved = path39.resolve(path39.dirname(shimPath), target);
|
|
36129
|
+
const expected = path39.join(
|
|
34445
36130
|
prefix,
|
|
34446
36131
|
"node_modules",
|
|
34447
36132
|
pkgName,
|
|
@@ -34480,15 +36165,15 @@ function checkNode(pkg) {
|
|
|
34480
36165
|
return OK(`node ${raw}`);
|
|
34481
36166
|
}
|
|
34482
36167
|
function checkBundle() {
|
|
34483
|
-
const bundle =
|
|
34484
|
-
if (!
|
|
36168
|
+
const bundle = path39.join(packageRoot, "dist", "cli", "main.bundled.js");
|
|
36169
|
+
if (!existsSync42(bundle)) {
|
|
34485
36170
|
return FAIL(
|
|
34486
36171
|
`dist/cli/main.bundled.js missing at ${bundle}
|
|
34487
36172
|
fix: npm run build:cli (then reinstall or run via tsx)`
|
|
34488
36173
|
);
|
|
34489
36174
|
}
|
|
34490
36175
|
try {
|
|
34491
|
-
const st =
|
|
36176
|
+
const st = statSync7(bundle);
|
|
34492
36177
|
return OK(`bundle OK (${(st.size / 1024 / 1024).toFixed(2)} MB)`);
|
|
34493
36178
|
} catch (err) {
|
|
34494
36179
|
return FAIL(
|
|
@@ -34501,7 +36186,7 @@ function checkRuntimeDeps() {
|
|
|
34501
36186
|
const missing = [];
|
|
34502
36187
|
for (const dep of required2) {
|
|
34503
36188
|
try {
|
|
34504
|
-
const localReq = createRequire3(
|
|
36189
|
+
const localReq = createRequire3(path39.join(packageRoot, "package.json"));
|
|
34505
36190
|
localReq.resolve(dep);
|
|
34506
36191
|
} catch {
|
|
34507
36192
|
missing.push(dep);
|
|
@@ -34677,7 +36362,7 @@ var init_doctor = __esm({
|
|
|
34677
36362
|
"use strict";
|
|
34678
36363
|
init_prereqChecks();
|
|
34679
36364
|
require3 = createRequire3(import.meta.url);
|
|
34680
|
-
__dirname3 =
|
|
36365
|
+
__dirname3 = path39.dirname(fileURLToPath2(import.meta.url));
|
|
34681
36366
|
packageRoot = findPackageRoot(__dirname3);
|
|
34682
36367
|
OK = (message) => ({
|
|
34683
36368
|
ok: true,
|
|
@@ -34880,7 +36565,7 @@ function InputBarImpl({ value, onChange, onSubmit, disabled }) {
|
|
|
34880
36565
|
value,
|
|
34881
36566
|
onChange: stableChange,
|
|
34882
36567
|
onSubmit: stableSubmit,
|
|
34883
|
-
placeholder: disabled ? "..." : "
|
|
36568
|
+
placeholder: disabled ? "..." : "Prompt, /skills, or @path"
|
|
34884
36569
|
}
|
|
34885
36570
|
));
|
|
34886
36571
|
}
|
|
@@ -35285,83 +36970,7 @@ function StreamingTail(m) {
|
|
|
35285
36970
|
// src/cli/components/StatusBar.tsx
|
|
35286
36971
|
import React6 from "react";
|
|
35287
36972
|
import { Box as Box5, Text as Text6 } from "ink";
|
|
35288
|
-
|
|
35289
|
-
// src/cli/modelPricing.ts
|
|
35290
|
-
var PRICES_PER_MILLION = {
|
|
35291
|
-
// xAI Grok (list prices; grok-4.5 default reasoning_effort is "high")
|
|
35292
|
-
"grok-4.5": { input: 2, output: 6, cachedInput: 0.5 },
|
|
35293
|
-
"grok-4.3": { input: 1.25, output: 2.5, cachedInput: 0.2 },
|
|
35294
|
-
"grok-4": { input: 3, output: 15 },
|
|
35295
|
-
"grok-4-fast": { input: 0.2, output: 0.5 },
|
|
35296
|
-
"grok-3": { input: 3, output: 15 },
|
|
35297
|
-
"grok-3-mini": { input: 0.3, output: 0.5 },
|
|
35298
|
-
"grok-2-vision": { input: 2, output: 10 },
|
|
35299
|
-
// GLM / Z.AI
|
|
35300
|
-
"glm-4.6": { input: 0.6, output: 2.2 },
|
|
35301
|
-
"glm-4.5": { input: 0.5, output: 2 },
|
|
35302
|
-
"glm-4.5-air": { input: 0.1, output: 0.6 },
|
|
35303
|
-
"glm-z1": { input: 0.5, output: 2 },
|
|
35304
|
-
// MiniMax
|
|
35305
|
-
"MiniMax-M2.5": { input: 0.2, output: 1.1 },
|
|
35306
|
-
"MiniMax-M2": { input: 0.2, output: 1.1 },
|
|
35307
|
-
"MiniMax-M2-her": { input: 0.3, output: 1.2 },
|
|
35308
|
-
// DeepSeek (global platform) — estimated list prices; override via
|
|
35309
|
-
// ANATHEMA_PRICE_DEEPSEEK_V4_FLASH / ANATHEMA_PRICE_DEEPSEEK_V4_PRO.
|
|
35310
|
-
// DeepSeek prompt-cache hits are ~10× cheaper than a cache miss.
|
|
35311
|
-
"deepseek-v4-flash": { input: 0.14, output: 0.28, cachedInput: 0.014 },
|
|
35312
|
-
"deepseek-v4-pro": { input: 0.55, output: 2.19, cachedInput: 0.055 },
|
|
35313
|
-
// OpenAI (for openai-compatible fallback)
|
|
35314
|
-
"gpt-4o": { input: 2.5, output: 10 },
|
|
35315
|
-
"gpt-4o-mini": { input: 0.15, output: 0.6 },
|
|
35316
|
-
"gpt-4-turbo": { input: 10, output: 30 },
|
|
35317
|
-
"o1-preview": { input: 15, output: 60 },
|
|
35318
|
-
"o1-mini": { input: 3, output: 12 }
|
|
35319
|
-
};
|
|
35320
|
-
var DEFAULT_RATE = { input: 1, output: 3 };
|
|
35321
|
-
var DEFAULT_CACHE_DISCOUNT = 0.25;
|
|
35322
|
-
function envOverride(model) {
|
|
35323
|
-
const key = `ANATHEMA_PRICE_${model.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`;
|
|
35324
|
-
const raw = process.env[key] ?? process.env.ANATHEMA_PRICE_DEFAULT;
|
|
35325
|
-
return parseRateOverride(raw);
|
|
35326
|
-
}
|
|
35327
|
-
function parseRateOverride(raw) {
|
|
35328
|
-
if (!raw) return null;
|
|
35329
|
-
const [inStr, outStr] = raw.split("/");
|
|
35330
|
-
const input = Number.parseFloat(inStr);
|
|
35331
|
-
const output = outStr !== void 0 ? Number.parseFloat(outStr) : input;
|
|
35332
|
-
if (!Number.isFinite(input) || !Number.isFinite(output)) return null;
|
|
35333
|
-
return { input, output };
|
|
35334
|
-
}
|
|
35335
|
-
function getModelRate(model) {
|
|
35336
|
-
if (!model) return DEFAULT_RATE;
|
|
35337
|
-
return envOverride(model) ?? PRICES_PER_MILLION[model] ?? DEFAULT_RATE;
|
|
35338
|
-
}
|
|
35339
|
-
function calculateCost(model, promptTokens, completionTokens, cachedPromptTokens = 0) {
|
|
35340
|
-
if (!Number.isFinite(promptTokens) || promptTokens < 0) return 0;
|
|
35341
|
-
if (!Number.isFinite(completionTokens) || completionTokens < 0) return 0;
|
|
35342
|
-
const rate = getModelRate(model);
|
|
35343
|
-
const cached2 = Number.isFinite(cachedPromptTokens) && cachedPromptTokens > 0 ? Math.min(cachedPromptTokens, promptTokens) : 0;
|
|
35344
|
-
const uncachedPrompt = promptTokens - cached2;
|
|
35345
|
-
const cachedRate = rate.cachedInput ?? rate.input * DEFAULT_CACHE_DISCOUNT;
|
|
35346
|
-
const inputCost = uncachedPrompt / 1e6 * rate.input + cached2 / 1e6 * cachedRate;
|
|
35347
|
-
const outputCost = completionTokens / 1e6 * rate.output;
|
|
35348
|
-
return Number((inputCost + outputCost).toFixed(6));
|
|
35349
|
-
}
|
|
35350
|
-
function formatCost(usd) {
|
|
35351
|
-
if (!Number.isFinite(usd) || usd < 0) return "$0.0000";
|
|
35352
|
-
if (usd === 0) return "$0.0000";
|
|
35353
|
-
if (usd < 1e-4) return "<$0.0001";
|
|
35354
|
-
return `$${usd.toFixed(4)}`;
|
|
35355
|
-
}
|
|
35356
|
-
function formatTokens(tokens) {
|
|
35357
|
-
if (!Number.isFinite(tokens) || tokens < 0) return "0";
|
|
35358
|
-
if (tokens < 1e3) return `${tokens}`;
|
|
35359
|
-
if (tokens < 1e6) return `${(tokens / 1e3).toFixed(1)}k`;
|
|
35360
|
-
if (tokens < 1e9) return `${(tokens / 1e6).toFixed(2)}M`;
|
|
35361
|
-
return `${(tokens / 1e9).toFixed(2)}B`;
|
|
35362
|
-
}
|
|
35363
|
-
|
|
35364
|
-
// src/cli/components/StatusBar.tsx
|
|
36973
|
+
init_modelPricing();
|
|
35365
36974
|
function StatusBar({
|
|
35366
36975
|
model,
|
|
35367
36976
|
provider,
|
|
@@ -35507,175 +37116,8 @@ function StartupBanner({
|
|
|
35507
37116
|
return /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text9, { color: "cyan" }, "zelari-code v", version2, " \u2014 ", providerId, "/", model), /* @__PURE__ */ React9.createElement(Text9, { dimColor: true }, "cwd: ", cwd), /* @__PURE__ */ React9.createElement(Text9, { dimColor: true }, "/help \xB7 /plan \xB7 /build \xB7 /view-plan \xB7 shift+tab mode"));
|
|
35508
37117
|
}
|
|
35509
37118
|
|
|
35510
|
-
// src/cli/modelDiscovery.ts
|
|
35511
|
-
import { promises as fs3, existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
|
|
35512
|
-
import { homedir } from "node:os";
|
|
35513
|
-
import path4 from "node:path";
|
|
35514
|
-
var PROVIDER_BASE_URLS = {
|
|
35515
|
-
"grok": "https://api.x.ai/v1",
|
|
35516
|
-
// Must match PROVIDER_ENDPOINTS in provider/openai-compatible.ts (chat host).
|
|
35517
|
-
"glm": "https://api.z.ai/api/coding/paas/v4",
|
|
35518
|
-
"minimax": "https://api.minimax.io/v1",
|
|
35519
|
-
// Must match PROVIDER_ENDPOINTS in provider/openai-compatible.ts (chat host).
|
|
35520
|
-
"deepseek": "https://api.deepseek.com",
|
|
35521
|
-
"openai-compatible": "https://api.x.ai/v1"
|
|
35522
|
-
};
|
|
35523
|
-
async function resolveDiscoveryBaseUrl(provider, options) {
|
|
35524
|
-
if (options.baseUrl) return options.baseUrl;
|
|
35525
|
-
const { getCustomEndpoint: getCustomEndpoint2 } = await Promise.resolve().then(() => (init_providerConfig(), providerConfig_exports));
|
|
35526
|
-
const custom2 = getCustomEndpoint2(provider);
|
|
35527
|
-
if (custom2) return custom2;
|
|
35528
|
-
if (provider === "openai-compatible") {
|
|
35529
|
-
const envBase = process.env.OPENAI_BASE_URL;
|
|
35530
|
-
if (envBase && envBase.trim().length > 0) return envBase;
|
|
35531
|
-
}
|
|
35532
|
-
return PROVIDER_BASE_URLS[provider];
|
|
35533
|
-
}
|
|
35534
|
-
function defaultModelsFilePath() {
|
|
35535
|
-
return process.env.ANATHEMA_MODELS_FILE ?? path4.join(homedir(), ".tmp", "zelari-code", "models.json");
|
|
35536
|
-
}
|
|
35537
|
-
function getModelsFilePath() {
|
|
35538
|
-
return defaultModelsFilePath();
|
|
35539
|
-
}
|
|
35540
|
-
function loadModelsRegistry(file2 = getModelsFilePath()) {
|
|
35541
|
-
if (!existsSync3(file2)) return {};
|
|
35542
|
-
try {
|
|
35543
|
-
const raw = readFileSync3(file2, "utf-8");
|
|
35544
|
-
const parsed = JSON.parse(raw);
|
|
35545
|
-
if (!parsed || typeof parsed !== "object") return {};
|
|
35546
|
-
return parsed;
|
|
35547
|
-
} catch {
|
|
35548
|
-
return {};
|
|
35549
|
-
}
|
|
35550
|
-
}
|
|
35551
|
-
function getCachedModels(provider, file2 = getModelsFilePath()) {
|
|
35552
|
-
const registry3 = loadModelsRegistry(file2);
|
|
35553
|
-
return registry3[provider];
|
|
35554
|
-
}
|
|
35555
|
-
function isModelsCacheStale(provider, maxAgeMs = 6 * 60 * 60 * 1e3, file2 = getModelsFilePath(), now = Date.now()) {
|
|
35556
|
-
const entry = getCachedModels(provider, file2);
|
|
35557
|
-
if (!entry) return true;
|
|
35558
|
-
return now - entry.fetchedAt > maxAgeMs;
|
|
35559
|
-
}
|
|
35560
|
-
var writeChain = Promise.resolve();
|
|
35561
|
-
async function withRegistryLock(fn) {
|
|
35562
|
-
const next = writeChain.then(async () => fn());
|
|
35563
|
-
writeChain = next.catch(() => void 0);
|
|
35564
|
-
return next;
|
|
35565
|
-
}
|
|
35566
|
-
async function readModifyWriteRegistry(mutator, file2 = getModelsFilePath()) {
|
|
35567
|
-
return withRegistryLock(async () => {
|
|
35568
|
-
const onDisk = loadModelsRegistry(file2);
|
|
35569
|
-
const merged = mutator(onDisk);
|
|
35570
|
-
await fs3.mkdir(path4.dirname(file2), { recursive: true });
|
|
35571
|
-
const tmp = `${file2}.tmp-${process.pid}-${Date.now()}`;
|
|
35572
|
-
await fs3.writeFile(tmp, JSON.stringify(merged, null, 2), "utf-8");
|
|
35573
|
-
await fs3.rename(tmp, file2);
|
|
35574
|
-
return merged;
|
|
35575
|
-
});
|
|
35576
|
-
}
|
|
35577
|
-
async function resolveAuthToken(provider, options) {
|
|
35578
|
-
if (options.authToken) return options.authToken;
|
|
35579
|
-
const { resolveApiKeyWithMeta: resolveApiKeyWithMeta2, getOAuthToken: getOAuthToken2 } = await Promise.resolve().then(() => (init_keyStore(), keyStore_exports));
|
|
35580
|
-
if (provider === "grok") {
|
|
35581
|
-
const oauth = getOAuthToken2("grok");
|
|
35582
|
-
if (oauth?.apiKey) return oauth.apiKey;
|
|
35583
|
-
}
|
|
35584
|
-
const resolved = await resolveApiKeyWithMeta2(provider);
|
|
35585
|
-
return resolved?.apiKey;
|
|
35586
|
-
}
|
|
35587
|
-
function parseOpenAIModelsResponse(json2, baseUrl) {
|
|
35588
|
-
if (!json2 || typeof json2 !== "object") return [];
|
|
35589
|
-
const obj = json2;
|
|
35590
|
-
if (!Array.isArray(obj.data)) return [];
|
|
35591
|
-
const models = [];
|
|
35592
|
-
for (const m of obj.data) {
|
|
35593
|
-
if (!m || typeof m.id !== "string") continue;
|
|
35594
|
-
const out = { id: m.id };
|
|
35595
|
-
if (typeof m.created === "number") out.created = m.created;
|
|
35596
|
-
if (typeof m.owned_by === "string") out.ownedBy = m.owned_by;
|
|
35597
|
-
const ctx = m.context_length;
|
|
35598
|
-
if (typeof ctx === "number") out.contextLength = ctx;
|
|
35599
|
-
models.push(out);
|
|
35600
|
-
}
|
|
35601
|
-
models.sort((a, b) => a.id.localeCompare(b.id));
|
|
35602
|
-
return models;
|
|
35603
|
-
}
|
|
35604
|
-
async function discoverModelsForProvider(provider, options = {}) {
|
|
35605
|
-
const baseUrl = await resolveDiscoveryBaseUrl(provider, options);
|
|
35606
|
-
if (!baseUrl || baseUrl.trim().length === 0) {
|
|
35607
|
-
throw new ModelDiscoveryError(
|
|
35608
|
-
`No base URL for provider "${provider}" \u2014 set one with /provider custom <url> before discovering models`,
|
|
35609
|
-
"no_base_url"
|
|
35610
|
-
);
|
|
35611
|
-
}
|
|
35612
|
-
const url2 = `${baseUrl.replace(/\/$/, "")}/models`;
|
|
35613
|
-
const authToken = await resolveAuthToken(provider, options);
|
|
35614
|
-
const fetchImpl = options.fetchImpl ?? fetch;
|
|
35615
|
-
let response;
|
|
35616
|
-
try {
|
|
35617
|
-
const headers = { Accept: "application/json" };
|
|
35618
|
-
if (authToken) headers.Authorization = `Bearer ${authToken}`;
|
|
35619
|
-
response = await fetchImpl(url2, { method: "GET", headers });
|
|
35620
|
-
} catch (err) {
|
|
35621
|
-
throw new ModelDiscoveryError(
|
|
35622
|
-
`Network error contacting ${url2}: ${err instanceof Error ? err.message : String(err)}`,
|
|
35623
|
-
"network_error"
|
|
35624
|
-
);
|
|
35625
|
-
}
|
|
35626
|
-
if (!response.ok) {
|
|
35627
|
-
const body = await response.text().catch(() => "");
|
|
35628
|
-
throw new ModelDiscoveryError(
|
|
35629
|
-
`HTTP ${response.status} from ${url2}: ${body.slice(0, 200)}`,
|
|
35630
|
-
`http_${response.status}`
|
|
35631
|
-
);
|
|
35632
|
-
}
|
|
35633
|
-
let json2;
|
|
35634
|
-
try {
|
|
35635
|
-
json2 = await response.json();
|
|
35636
|
-
} catch (err) {
|
|
35637
|
-
throw new ModelDiscoveryError(
|
|
35638
|
-
`Invalid JSON from ${url2}: ${err instanceof Error ? err.message : String(err)}`,
|
|
35639
|
-
"invalid_json"
|
|
35640
|
-
);
|
|
35641
|
-
}
|
|
35642
|
-
const models = parseOpenAIModelsResponse(json2, baseUrl);
|
|
35643
|
-
if (models.length === 0) {
|
|
35644
|
-
throw new ModelDiscoveryError(
|
|
35645
|
-
`Provider ${provider} returned 0 models \u2014 refusing to overwrite cache`,
|
|
35646
|
-
"empty_response"
|
|
35647
|
-
);
|
|
35648
|
-
}
|
|
35649
|
-
const entry = {
|
|
35650
|
-
models,
|
|
35651
|
-
fetchedAt: Date.now(),
|
|
35652
|
-
baseUrl
|
|
35653
|
-
};
|
|
35654
|
-
if (!options.skipCacheWrite) {
|
|
35655
|
-
const file2 = getModelsFilePath();
|
|
35656
|
-
await readModifyWriteRegistry((current) => {
|
|
35657
|
-
current[provider] = entry;
|
|
35658
|
-
return current;
|
|
35659
|
-
}, file2);
|
|
35660
|
-
}
|
|
35661
|
-
return entry;
|
|
35662
|
-
}
|
|
35663
|
-
function discoverModelsInBackground(provider, options = {}) {
|
|
35664
|
-
discoverModelsForProvider(provider, options).catch((err) => {
|
|
35665
|
-
if (err instanceof ModelDiscoveryError && options.onError) {
|
|
35666
|
-
options.onError(err);
|
|
35667
|
-
}
|
|
35668
|
-
});
|
|
35669
|
-
}
|
|
35670
|
-
var ModelDiscoveryError = class extends Error {
|
|
35671
|
-
constructor(message, code) {
|
|
35672
|
-
super(message);
|
|
35673
|
-
this.code = code;
|
|
35674
|
-
this.name = "ModelDiscoveryError";
|
|
35675
|
-
}
|
|
35676
|
-
};
|
|
35677
|
-
|
|
35678
37119
|
// src/cli/app.tsx
|
|
37120
|
+
init_modelDiscovery();
|
|
35679
37121
|
init_skills2();
|
|
35680
37122
|
|
|
35681
37123
|
// src/cli/hooks/useGitChanges.ts
|
|
@@ -35736,12 +37178,12 @@ function mergeChanges(unstaged, staged, untrackedPaths) {
|
|
|
35736
37178
|
return [...byPath.values()].sort((a, b) => churn(b) - churn(a));
|
|
35737
37179
|
}
|
|
35738
37180
|
function runGit(args, cwd) {
|
|
35739
|
-
return new Promise((
|
|
37181
|
+
return new Promise((resolve3, reject) => {
|
|
35740
37182
|
execFile(
|
|
35741
37183
|
"git",
|
|
35742
37184
|
args,
|
|
35743
37185
|
{ cwd, timeout: 5e3, windowsHide: true, maxBuffer: 4 * 1024 * 1024 },
|
|
35744
|
-
(err, stdout) => err ? reject(err) :
|
|
37186
|
+
(err, stdout) => err ? reject(err) : resolve3(stdout)
|
|
35745
37187
|
);
|
|
35746
37188
|
});
|
|
35747
37189
|
}
|
|
@@ -38391,12 +39833,12 @@ var MetricsLogger = class {
|
|
|
38391
39833
|
}
|
|
38392
39834
|
/** Append a single record synchronously (file I/O). */
|
|
38393
39835
|
append(rec) {
|
|
38394
|
-
return new Promise((
|
|
39836
|
+
return new Promise((resolve3, reject) => {
|
|
38395
39837
|
try {
|
|
38396
39838
|
this.maybeRotate();
|
|
38397
39839
|
const line = JSON.stringify(rec) + "\n";
|
|
38398
39840
|
appendFileSync(this.file, line, { encoding: "utf-8" });
|
|
38399
|
-
|
|
39841
|
+
resolve3();
|
|
38400
39842
|
} catch (err) {
|
|
38401
39843
|
reject(err);
|
|
38402
39844
|
}
|
|
@@ -38540,7 +39982,7 @@ init_toolRegistry();
|
|
|
38540
39982
|
init_toolPermissions();
|
|
38541
39983
|
function createPermissionAskHandler(opts) {
|
|
38542
39984
|
const { setPicker: setPicker2, appendSystem: appendSystem2 } = opts;
|
|
38543
|
-
return (req) => new Promise((
|
|
39985
|
+
return (req) => new Promise((resolve3) => {
|
|
38544
39986
|
const cats = req.categories;
|
|
38545
39987
|
const catLabel = cats.length === 1 ? cats[0] : cats.join("+") || "action";
|
|
38546
39988
|
const title = `Allow tool "${req.toolName}"?`;
|
|
@@ -38557,7 +39999,7 @@ ${detail}
|
|
|
38557
39999
|
settled = true;
|
|
38558
40000
|
setPicker2(null);
|
|
38559
40001
|
if (note) appendSystem2?.(note, Date.now());
|
|
38560
|
-
|
|
40002
|
+
resolve3(ok);
|
|
38561
40003
|
};
|
|
38562
40004
|
const items = [
|
|
38563
40005
|
{ value: "allow", label: "Allow once" },
|
|
@@ -38705,6 +40147,9 @@ function finalizeStreamingAssistant(setMessages) {
|
|
|
38705
40147
|
// src/cli/hooks/useChatTurn.ts
|
|
38706
40148
|
init_conversationContext();
|
|
38707
40149
|
|
|
40150
|
+
// src/cli/hooks/chatStats.ts
|
|
40151
|
+
init_modelPricing();
|
|
40152
|
+
|
|
38708
40153
|
// src/cli/state/promptCacheStats.ts
|
|
38709
40154
|
function emptyPromptCacheStats() {
|
|
38710
40155
|
return {
|
|
@@ -38948,10 +40393,10 @@ function useChatTurn(params) {
|
|
|
38948
40393
|
}
|
|
38949
40394
|
setBusy(true);
|
|
38950
40395
|
const workPhase = getPhase();
|
|
38951
|
-
const onAskUser = setPicker2 ? (req) => new Promise((
|
|
40396
|
+
const onAskUser = setPicker2 ? (req) => new Promise((resolve3) => {
|
|
38952
40397
|
const choices = req.choices ?? [];
|
|
38953
40398
|
if (choices.length < 2) {
|
|
38954
|
-
|
|
40399
|
+
resolve3(null);
|
|
38955
40400
|
return;
|
|
38956
40401
|
}
|
|
38957
40402
|
setLastClarification({
|
|
@@ -38972,7 +40417,7 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
|
|
|
38972
40417
|
if (settled) return;
|
|
38973
40418
|
settled = true;
|
|
38974
40419
|
setPicker2(null);
|
|
38975
|
-
|
|
40420
|
+
resolve3(value);
|
|
38976
40421
|
};
|
|
38977
40422
|
setPicker2({
|
|
38978
40423
|
kind: "clarification",
|
|
@@ -39568,10 +41013,10 @@ async function dispatchCouncilPromptImpl(text, deps, overrides = {}) {
|
|
|
39568
41013
|
const { composeProjectContext: composeProjectContext2 } = await Promise.resolve().then(() => (init_composeContext(), composeContext_exports));
|
|
39569
41014
|
const { FeedbackStore: FeedbackStore2 } = await Promise.resolve().then(() => (init_councilFeedback(), councilFeedback_exports));
|
|
39570
41015
|
const workPhase = getPhase();
|
|
39571
|
-
const onAskUserCouncil = setPicker2 ? (req) => new Promise((
|
|
41016
|
+
const onAskUserCouncil = setPicker2 ? (req) => new Promise((resolve3) => {
|
|
39572
41017
|
const choices = req.choices ?? [];
|
|
39573
41018
|
if (choices.length < 2) {
|
|
39574
|
-
|
|
41019
|
+
resolve3(null);
|
|
39575
41020
|
return;
|
|
39576
41021
|
}
|
|
39577
41022
|
setLastClarification({
|
|
@@ -39592,7 +41037,7 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il membro riprende dop
|
|
|
39592
41037
|
if (settled) return;
|
|
39593
41038
|
settled = true;
|
|
39594
41039
|
setPicker2(null);
|
|
39595
|
-
|
|
41040
|
+
resolve3(value);
|
|
39596
41041
|
};
|
|
39597
41042
|
setPicker2({
|
|
39598
41043
|
kind: "clarification",
|
|
@@ -39721,10 +41166,10 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il membro riprende dop
|
|
|
39721
41166
|
appendSystem(setMessages, message, Date.now());
|
|
39722
41167
|
},
|
|
39723
41168
|
// v1.8.0: pause council when a member asks a structured question.
|
|
39724
|
-
onClarification: setPicker2 ? (req) => new Promise((
|
|
41169
|
+
onClarification: setPicker2 ? (req) => new Promise((resolve3) => {
|
|
39725
41170
|
const choices = req.choices ?? [];
|
|
39726
41171
|
if (choices.length < 2) {
|
|
39727
|
-
|
|
41172
|
+
resolve3(null);
|
|
39728
41173
|
return;
|
|
39729
41174
|
}
|
|
39730
41175
|
setLastClarification({
|
|
@@ -39745,7 +41190,7 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il membro riprende dop
|
|
|
39745
41190
|
if (settled) return;
|
|
39746
41191
|
settled = true;
|
|
39747
41192
|
setPicker2(null);
|
|
39748
|
-
|
|
41193
|
+
resolve3(value);
|
|
39749
41194
|
};
|
|
39750
41195
|
setPicker2({
|
|
39751
41196
|
kind: "clarification",
|
|
@@ -40357,7 +41802,8 @@ function handleSlashCommand(text, availableSkills) {
|
|
|
40357
41802
|
/provider <name> \u2014 switch the active provider directly
|
|
40358
41803
|
/provider custom <baseUrl> \u2014 point the active provider at a self-hosted endpoint (Ollama, LM Studio, vLLM, ...)
|
|
40359
41804
|
/provider custom clear \u2014 clear the custom endpoint override
|
|
40360
|
-
/skill
|
|
41805
|
+
/skill [name] [input] \u2014 list+pick a skill (no args) or invoke /skill <name>
|
|
41806
|
+
/skills \u2014 interactive skill picker (same as /skill with no args)
|
|
40361
41807
|
/skill-stats [name] \u2014 show invocation stats (success rate, avg duration, total tokens)
|
|
40362
41808
|
/council <input> \u2014 invoke the multi-agent council on input
|
|
40363
41809
|
/zelari <input> \u2014 run an autonomous mission (multi-run council until the MVP slice is complete)
|
|
@@ -40605,13 +42051,19 @@ ${formatSkillList(availableSkills)}`
|
|
|
40605
42051
|
...note ? { feedbackNote: note } : {}
|
|
40606
42052
|
};
|
|
40607
42053
|
}
|
|
42054
|
+
case "skills": {
|
|
42055
|
+
return {
|
|
42056
|
+
handled: true,
|
|
42057
|
+
kind: "skill_picker",
|
|
42058
|
+
message: formatSkillList(availableSkills)
|
|
42059
|
+
};
|
|
42060
|
+
}
|
|
40608
42061
|
case "skill": {
|
|
40609
42062
|
const skillId = args[0];
|
|
40610
42063
|
if (!skillId) {
|
|
40611
42064
|
return {
|
|
40612
42065
|
handled: true,
|
|
40613
|
-
kind: "
|
|
40614
|
-
skillError: "Usage: /skill <name> [input]",
|
|
42066
|
+
kind: "skill_picker",
|
|
40615
42067
|
message: formatSkillList(availableSkills)
|
|
40616
42068
|
};
|
|
40617
42069
|
}
|
|
@@ -40620,7 +42072,7 @@ ${formatSkillList(availableSkills)}`
|
|
|
40620
42072
|
return {
|
|
40621
42073
|
handled: true,
|
|
40622
42074
|
kind: "skill",
|
|
40623
|
-
skillError: `Unknown skill: "${skillId}". Use /
|
|
42075
|
+
skillError: `Unknown skill: "${skillId}". Use /skills to pick one.`,
|
|
40624
42076
|
message: formatSkillList(availableSkills)
|
|
40625
42077
|
};
|
|
40626
42078
|
}
|
|
@@ -40628,7 +42080,8 @@ ${formatSkillList(availableSkills)}`
|
|
|
40628
42080
|
const expandedSkill = expandSkillTemplate(skill, { input });
|
|
40629
42081
|
return { handled: true, kind: "skill", expandedSkill };
|
|
40630
42082
|
}
|
|
40631
|
-
case "skill_stats":
|
|
42083
|
+
case "skill_stats":
|
|
42084
|
+
case "skill-stats": {
|
|
40632
42085
|
const skillId = args[0];
|
|
40633
42086
|
return {
|
|
40634
42087
|
handled: true,
|
|
@@ -40904,7 +42357,7 @@ ${formatSkillList(availableSkills)}`
|
|
|
40904
42357
|
// src/cli/gitOps.ts
|
|
40905
42358
|
import { execFile as execFile3 } from "node:child_process";
|
|
40906
42359
|
import { promisify as promisify2 } from "node:util";
|
|
40907
|
-
import
|
|
42360
|
+
import path31 from "node:path";
|
|
40908
42361
|
var execFileAsync2 = promisify2(execFile3);
|
|
40909
42362
|
async function git2(cwd, args) {
|
|
40910
42363
|
try {
|
|
@@ -40950,7 +42403,7 @@ async function undoWorkingChanges(opts = {}) {
|
|
|
40950
42403
|
};
|
|
40951
42404
|
}
|
|
40952
42405
|
function defaultProjectRoot() {
|
|
40953
|
-
return
|
|
42406
|
+
return path31.resolve(__dirname, "..", "..", "..");
|
|
40954
42407
|
}
|
|
40955
42408
|
|
|
40956
42409
|
// src/cli/slashHandlers/git.ts
|
|
@@ -41474,7 +42927,7 @@ ${browsers.output}` : "") + (browsers.ok ? "" : `
|
|
|
41474
42927
|
return result;
|
|
41475
42928
|
}
|
|
41476
42929
|
function runPlaywrightInstallChromium(cwd, executor) {
|
|
41477
|
-
return new Promise((
|
|
42930
|
+
return new Promise((resolve3) => {
|
|
41478
42931
|
let stdout = "";
|
|
41479
42932
|
let stderr = "";
|
|
41480
42933
|
const stdio = ["ignore", "pipe", "pipe"];
|
|
@@ -41487,7 +42940,7 @@ function runPlaywrightInstallChromium(cwd, executor) {
|
|
|
41487
42940
|
stderr += chunk.toString();
|
|
41488
42941
|
});
|
|
41489
42942
|
child.on("error", (err) => {
|
|
41490
|
-
|
|
42943
|
+
resolve3({
|
|
41491
42944
|
ok: false,
|
|
41492
42945
|
output: stdout + stderr,
|
|
41493
42946
|
error: err.message,
|
|
@@ -41496,7 +42949,7 @@ function runPlaywrightInstallChromium(cwd, executor) {
|
|
|
41496
42949
|
});
|
|
41497
42950
|
child.on("close", (code) => {
|
|
41498
42951
|
const ok = code === 0;
|
|
41499
|
-
|
|
42952
|
+
resolve3({
|
|
41500
42953
|
ok,
|
|
41501
42954
|
output: stdout + stderr,
|
|
41502
42955
|
exitCode: code,
|
|
@@ -41506,7 +42959,7 @@ function runPlaywrightInstallChromium(cwd, executor) {
|
|
|
41506
42959
|
});
|
|
41507
42960
|
}
|
|
41508
42961
|
function runNpm2(executor, args, cwd, mode, npmCliPath) {
|
|
41509
|
-
return new Promise((
|
|
42962
|
+
return new Promise((resolve3) => {
|
|
41510
42963
|
let stdout = "";
|
|
41511
42964
|
let stderr = "";
|
|
41512
42965
|
const stdio = ["ignore", "pipe", "pipe"];
|
|
@@ -41518,7 +42971,7 @@ function runNpm2(executor, args, cwd, mode, npmCliPath) {
|
|
|
41518
42971
|
stderr += chunk.toString();
|
|
41519
42972
|
});
|
|
41520
42973
|
child.on("error", (err) => {
|
|
41521
|
-
|
|
42974
|
+
resolve3({
|
|
41522
42975
|
ok: false,
|
|
41523
42976
|
output: stdout + stderr,
|
|
41524
42977
|
error: err.message,
|
|
@@ -41527,7 +42980,7 @@ function runNpm2(executor, args, cwd, mode, npmCliPath) {
|
|
|
41527
42980
|
});
|
|
41528
42981
|
child.on("close", (code) => {
|
|
41529
42982
|
const ok = code === 0;
|
|
41530
|
-
|
|
42983
|
+
resolve3({
|
|
41531
42984
|
ok,
|
|
41532
42985
|
output: stdout + stderr,
|
|
41533
42986
|
exitCode: code,
|
|
@@ -41590,17 +43043,17 @@ ${result.output.split("\n").slice(-8).join("\n")}` : "";
|
|
|
41590
43043
|
}
|
|
41591
43044
|
|
|
41592
43045
|
// src/cli/slashHandlers/promoteMember.ts
|
|
41593
|
-
import { promises as
|
|
41594
|
-
import
|
|
43046
|
+
import { promises as fs19 } from "node:fs";
|
|
43047
|
+
import path34 from "node:path";
|
|
41595
43048
|
import os10 from "node:os";
|
|
41596
43049
|
async function handlePromoteMember(ctx, memberId) {
|
|
41597
43050
|
try {
|
|
41598
43051
|
const { promoteMember: promoteMember2 } = await Promise.resolve().then(() => (init_council(), council_exports));
|
|
41599
43052
|
const { skill, markdown } = promoteMember2(memberId);
|
|
41600
|
-
const skillDir = process.env.ANATHEMA_SKILL_DIR ??
|
|
41601
|
-
await
|
|
41602
|
-
const filePath =
|
|
41603
|
-
await
|
|
43053
|
+
const skillDir = process.env.ANATHEMA_SKILL_DIR ?? path34.join(os10.homedir(), ".tmp", "zelari-code", "skills");
|
|
43054
|
+
await fs19.mkdir(skillDir, { recursive: true });
|
|
43055
|
+
const filePath = path34.join(skillDir, `${skill.id}.md`);
|
|
43056
|
+
await fs19.writeFile(filePath, markdown, "utf8");
|
|
41604
43057
|
appendSystem(
|
|
41605
43058
|
ctx.setMessages,
|
|
41606
43059
|
`[promote-member] ${skill.name} (${memberId}) \u2192 ${filePath}
|
|
@@ -41616,25 +43069,25 @@ async function handlePromoteMember(ctx, memberId) {
|
|
|
41616
43069
|
}
|
|
41617
43070
|
|
|
41618
43071
|
// src/cli/branchManager.ts
|
|
41619
|
-
import { promises as
|
|
41620
|
-
import
|
|
43072
|
+
import { promises as fs20, existsSync as existsSync35, readFileSync as readFileSync29, writeFileSync as writeFileSync19, mkdirSync as mkdirSync14, statSync as statSync4, rmSync as rmSync2 } from "node:fs";
|
|
43073
|
+
import path35 from "node:path";
|
|
41621
43074
|
import os11 from "node:os";
|
|
41622
43075
|
var META_FILENAME = "meta.json";
|
|
41623
43076
|
var SESSIONS_SUBDIR = "sessions";
|
|
41624
43077
|
function getBranchesBaseDir() {
|
|
41625
|
-
return process.env.ANATHEMA_BRANCHES_DIR ??
|
|
43078
|
+
return process.env.ANATHEMA_BRANCHES_DIR ?? path35.join(os11.homedir(), ".tmp", "zelari-code", "branches");
|
|
41626
43079
|
}
|
|
41627
43080
|
function getSessionsBaseDir() {
|
|
41628
|
-
return process.env.ANATHEMA_SESSIONS_DIR ??
|
|
43081
|
+
return process.env.ANATHEMA_SESSIONS_DIR ?? path35.join(os11.homedir(), ".tmp", "zelari-code", "sessions");
|
|
41629
43082
|
}
|
|
41630
43083
|
function branchPathFor(name, baseDir) {
|
|
41631
|
-
return
|
|
43084
|
+
return path35.join(baseDir, name);
|
|
41632
43085
|
}
|
|
41633
43086
|
function metaPathFor(name, baseDir) {
|
|
41634
|
-
return
|
|
43087
|
+
return path35.join(baseDir, name, META_FILENAME);
|
|
41635
43088
|
}
|
|
41636
43089
|
function sessionsPathFor(name, baseDir) {
|
|
41637
|
-
return
|
|
43090
|
+
return path35.join(baseDir, name, SESSIONS_SUBDIR);
|
|
41638
43091
|
}
|
|
41639
43092
|
function readBranchMeta(name, baseDir) {
|
|
41640
43093
|
const metaPath = metaPathFor(name, baseDir);
|
|
@@ -41659,13 +43112,13 @@ function readBranchMeta(name, baseDir) {
|
|
|
41659
43112
|
}
|
|
41660
43113
|
function writeBranchMeta(name, baseDir, meta3) {
|
|
41661
43114
|
const metaPath = metaPathFor(name, baseDir);
|
|
41662
|
-
mkdirSync14(
|
|
43115
|
+
mkdirSync14(path35.dirname(metaPath), { recursive: true });
|
|
41663
43116
|
writeFileSync19(metaPath, JSON.stringify(meta3, null, 2), "utf-8");
|
|
41664
43117
|
}
|
|
41665
43118
|
async function countSessions(name, baseDir) {
|
|
41666
43119
|
const sessionsPath = sessionsPathFor(name, baseDir);
|
|
41667
43120
|
try {
|
|
41668
|
-
const entries = await
|
|
43121
|
+
const entries = await fs20.readdir(sessionsPath);
|
|
41669
43122
|
return entries.filter((e) => e.endsWith(".jsonl")).length;
|
|
41670
43123
|
} catch (err) {
|
|
41671
43124
|
if (err.code === "ENOENT") return 0;
|
|
@@ -41710,15 +43163,15 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
|
|
|
41710
43163
|
if (branchExists(name, baseDir)) {
|
|
41711
43164
|
throw new BranchAlreadyExistsError(name);
|
|
41712
43165
|
}
|
|
41713
|
-
const sourcePath =
|
|
43166
|
+
const sourcePath = path35.join(sessionsBaseDir, `${fromSessionId}.jsonl`);
|
|
41714
43167
|
if (!existsSync35(sourcePath)) {
|
|
41715
43168
|
throw new SessionNotFoundError(`Source session "${fromSessionId}" not found at ${sourcePath}`);
|
|
41716
43169
|
}
|
|
41717
43170
|
const branchPath = branchPathFor(name, baseDir);
|
|
41718
43171
|
const branchSessionsPath = sessionsPathFor(name, baseDir);
|
|
41719
43172
|
mkdirSync14(branchSessionsPath, { recursive: true });
|
|
41720
|
-
const destPath =
|
|
41721
|
-
await
|
|
43173
|
+
const destPath = path35.join(branchSessionsPath, `${fromSessionId}.jsonl`);
|
|
43174
|
+
await fs20.copyFile(sourcePath, destPath);
|
|
41722
43175
|
const meta3 = {
|
|
41723
43176
|
name,
|
|
41724
43177
|
createdAt: Date.now(),
|
|
@@ -41736,7 +43189,7 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
|
|
|
41736
43189
|
async function listBranches(baseDir = getBranchesBaseDir()) {
|
|
41737
43190
|
let entries;
|
|
41738
43191
|
try {
|
|
41739
|
-
entries = await
|
|
43192
|
+
entries = await fs20.readdir(baseDir);
|
|
41740
43193
|
} catch (err) {
|
|
41741
43194
|
if (err.code === "ENOENT") return [];
|
|
41742
43195
|
throw err;
|
|
@@ -41817,26 +43270,26 @@ async function handleBranchCheckout(ctx, branchName) {
|
|
|
41817
43270
|
}
|
|
41818
43271
|
|
|
41819
43272
|
// src/cli/slashHandlers/workspace.ts
|
|
41820
|
-
import { promises as
|
|
41821
|
-
import
|
|
43273
|
+
import { promises as fs21 } from "node:fs";
|
|
43274
|
+
import path36 from "node:path";
|
|
41822
43275
|
async function handleWorkspaceShow(ctx, what) {
|
|
41823
43276
|
try {
|
|
41824
|
-
const zelari =
|
|
43277
|
+
const zelari = path36.join(process.cwd(), ".zelari");
|
|
41825
43278
|
let content;
|
|
41826
43279
|
switch (what) {
|
|
41827
43280
|
case "plan": {
|
|
41828
|
-
const planPath =
|
|
43281
|
+
const planPath = path36.join(zelari, "plan.md");
|
|
41829
43282
|
try {
|
|
41830
|
-
content = await
|
|
43283
|
+
content = await fs21.readFile(planPath, "utf-8");
|
|
41831
43284
|
} catch {
|
|
41832
43285
|
content = "(no plan.md yet \u2014 run a council session first)";
|
|
41833
43286
|
}
|
|
41834
43287
|
break;
|
|
41835
43288
|
}
|
|
41836
43289
|
case "decisions": {
|
|
41837
|
-
const decisionsDir =
|
|
43290
|
+
const decisionsDir = path36.join(zelari, "decisions");
|
|
41838
43291
|
try {
|
|
41839
|
-
const files = (await
|
|
43292
|
+
const files = (await fs21.readdir(decisionsDir)).filter((f) => f.endsWith(".md")).sort();
|
|
41840
43293
|
if (files.length === 0) {
|
|
41841
43294
|
content = "(no ADRs yet \u2014 invoke /council to generate some)";
|
|
41842
43295
|
} else {
|
|
@@ -41844,7 +43297,7 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
41844
43297
|
`];
|
|
41845
43298
|
const { parseFrontmatter: parseFrontmatter2 } = await Promise.resolve().then(() => (init_storage(), storage_exports));
|
|
41846
43299
|
for (const f of files) {
|
|
41847
|
-
const raw = await
|
|
43300
|
+
const raw = await fs21.readFile(path36.join(decisionsDir, f), "utf-8");
|
|
41848
43301
|
const { meta: meta3, body } = parseFrontmatter2(raw);
|
|
41849
43302
|
const title = meta3.title ?? body.split("\n")[0]?.replace(/^#\s*/, "").trim() ?? f;
|
|
41850
43303
|
lines.push(`- **${f.replace(/\.md$/, "")}** [${meta3.status ?? "unknown"}] ${title}`);
|
|
@@ -41857,27 +43310,27 @@ async function handleWorkspaceShow(ctx, what) {
|
|
|
41857
43310
|
break;
|
|
41858
43311
|
}
|
|
41859
43312
|
case "risks": {
|
|
41860
|
-
const risksPath =
|
|
43313
|
+
const risksPath = path36.join(zelari, "risks.md");
|
|
41861
43314
|
try {
|
|
41862
|
-
content = await
|
|
43315
|
+
content = await fs21.readFile(risksPath, "utf-8");
|
|
41863
43316
|
} catch {
|
|
41864
43317
|
content = "(no risks.md yet)";
|
|
41865
43318
|
}
|
|
41866
43319
|
break;
|
|
41867
43320
|
}
|
|
41868
43321
|
case "agents": {
|
|
41869
|
-
const agentsPath =
|
|
43322
|
+
const agentsPath = path36.join(process.cwd(), "AGENTS.MD");
|
|
41870
43323
|
try {
|
|
41871
|
-
content = await
|
|
43324
|
+
content = await fs21.readFile(agentsPath, "utf-8");
|
|
41872
43325
|
} catch {
|
|
41873
43326
|
content = "(no AGENTS.MD yet at project root \u2014 run `/workspace sync` after a council session)";
|
|
41874
43327
|
}
|
|
41875
43328
|
break;
|
|
41876
43329
|
}
|
|
41877
43330
|
case "docs": {
|
|
41878
|
-
const docsDir =
|
|
43331
|
+
const docsDir = path36.join(zelari, "docs");
|
|
41879
43332
|
try {
|
|
41880
|
-
const files = (await
|
|
43333
|
+
const files = (await fs21.readdir(docsDir)).filter((f) => f.endsWith(".md")).sort();
|
|
41881
43334
|
content = files.length ? `# Docs (${files.length})
|
|
41882
43335
|
|
|
41883
43336
|
` + files.map((f) => `- ${f}`).join("\n") : "(no docs drafts yet)";
|
|
@@ -41917,8 +43370,8 @@ async function handleWorkspaceReset(ctx, force) {
|
|
|
41917
43370
|
return;
|
|
41918
43371
|
}
|
|
41919
43372
|
try {
|
|
41920
|
-
const target =
|
|
41921
|
-
await
|
|
43373
|
+
const target = path36.join(process.cwd(), ".zelari");
|
|
43374
|
+
await fs21.rm(target, { recursive: true, force: true });
|
|
41922
43375
|
appendSystem(ctx.setMessages, "[workspace] .zelari/ removed");
|
|
41923
43376
|
} catch (err) {
|
|
41924
43377
|
appendSystem(ctx.setMessages, `[workspace reset error] ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -41980,6 +43433,7 @@ async function validateApiKey(providerId, apiKey, options = {}) {
|
|
|
41980
43433
|
|
|
41981
43434
|
// src/cli/slashHandlers/provider.ts
|
|
41982
43435
|
init_providerConfig();
|
|
43436
|
+
init_modelDiscovery();
|
|
41983
43437
|
var UNKNOWN_PROVIDER_MSG = (id) => `[provider] unknown: ${id}. Available: openai-compatible, minimax, glm, grok, deepseek, custom`;
|
|
41984
43438
|
var DISCOVERABLE_PROVIDERS = ["grok", "glm", "minimax", "deepseek", "openai-compatible"];
|
|
41985
43439
|
function handleProviderList(ctx) {
|
|
@@ -42276,16 +43730,16 @@ function handleModelsRefresh(ctx) {
|
|
|
42276
43730
|
}
|
|
42277
43731
|
|
|
42278
43732
|
// src/cli/slashHandlers/skills.ts
|
|
42279
|
-
import
|
|
43733
|
+
import path37 from "node:path";
|
|
42280
43734
|
import os12 from "node:os";
|
|
42281
43735
|
|
|
42282
43736
|
// src/cli/skillHistory.ts
|
|
42283
|
-
import { promises as
|
|
43737
|
+
import { promises as fs22, existsSync as existsSync36, statSync as statSync5, renameSync as renameSync4, appendFileSync as appendFileSync3, mkdirSync as mkdirSync15 } from "node:fs";
|
|
42284
43738
|
var SKILL_HISTORY_ROTATE_BYTES = 10 * 1024 * 1024;
|
|
42285
43739
|
async function readSkillHistory(file2) {
|
|
42286
43740
|
let raw = "";
|
|
42287
43741
|
try {
|
|
42288
|
-
raw = await
|
|
43742
|
+
raw = await fs22.readFile(file2, "utf-8");
|
|
42289
43743
|
} catch {
|
|
42290
43744
|
return [];
|
|
42291
43745
|
}
|
|
@@ -42377,8 +43831,32 @@ async function applySteerInterrupt(options) {
|
|
|
42377
43831
|
}
|
|
42378
43832
|
|
|
42379
43833
|
// src/cli/slashHandlers/skills.ts
|
|
43834
|
+
function handleSkillPicker(ctx, skills, openPicker, fallbackMessage) {
|
|
43835
|
+
if (!openPicker) {
|
|
43836
|
+
appendSystem(
|
|
43837
|
+
ctx.setMessages,
|
|
43838
|
+
fallbackMessage ?? formatSkillList(skills)
|
|
43839
|
+
);
|
|
43840
|
+
return;
|
|
43841
|
+
}
|
|
43842
|
+
if (skills.length === 0) {
|
|
43843
|
+
appendSystem(ctx.setMessages, "[skills] no skills registered");
|
|
43844
|
+
return;
|
|
43845
|
+
}
|
|
43846
|
+
const items = [...skills].sort((a, b) => a.id.localeCompare(b.id)).map((s) => ({
|
|
43847
|
+
value: s.id,
|
|
43848
|
+
label: s.name || s.id,
|
|
43849
|
+
hint: [s.category, s.estimatedCost, s.description?.slice(0, 60)].filter(Boolean).join(" \xB7 ")
|
|
43850
|
+
}));
|
|
43851
|
+
openPicker({
|
|
43852
|
+
kind: "skill",
|
|
43853
|
+
title: "Select a skill",
|
|
43854
|
+
items,
|
|
43855
|
+
commandPrefix: "/skill"
|
|
43856
|
+
});
|
|
43857
|
+
}
|
|
42380
43858
|
async function handleSkillStats(ctx, skillId) {
|
|
42381
|
-
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ??
|
|
43859
|
+
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path37.join(os12.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
|
|
42382
43860
|
try {
|
|
42383
43861
|
const records = await readSkillHistory(historyFile);
|
|
42384
43862
|
const stats = getSkillStats(records, skillId);
|
|
@@ -42394,7 +43872,7 @@ async function handleSkillCompare(ctx, ids, fallbackMessage) {
|
|
|
42394
43872
|
appendSystem(ctx.setMessages, fallbackMessage ?? "[skill-compare] missing args");
|
|
42395
43873
|
return;
|
|
42396
43874
|
}
|
|
42397
|
-
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ??
|
|
43875
|
+
const historyFile = process.env.ANATHEMA_SKILL_HISTORY_FILE ?? path37.join(os12.homedir(), ".tmp", "zelari-code", "skill-history.jsonl");
|
|
42398
43876
|
try {
|
|
42399
43877
|
const formatted = await compareSkillsFromFile(ids[0], ids[1], historyFile);
|
|
42400
43878
|
appendSystem(ctx.setMessages, formatted);
|
|
@@ -42450,6 +43928,7 @@ function handleClearChat(setMessages, setSessionActive) {
|
|
|
42450
43928
|
}
|
|
42451
43929
|
|
|
42452
43930
|
// src/cli/hooks/useSlashDispatch.ts
|
|
43931
|
+
init_atMentions();
|
|
42453
43932
|
function useSlashDispatch(params) {
|
|
42454
43933
|
const {
|
|
42455
43934
|
skills,
|
|
@@ -42490,19 +43969,27 @@ function useSlashDispatch(params) {
|
|
|
42490
43969
|
const steerCtx = { ...skillCtx, harnessRef, setQueueCount, dispatchPrompt };
|
|
42491
43970
|
const { onNewSession, onExit } = params;
|
|
42492
43971
|
if (!result.handled) {
|
|
43972
|
+
const expanded = expandAtMentions(value, process.cwd());
|
|
43973
|
+
const promptText = expanded.text;
|
|
42493
43974
|
appendUser(setMessages, value);
|
|
43975
|
+
if (expanded.hits.length > 0) {
|
|
43976
|
+
appendSystem(
|
|
43977
|
+
setMessages,
|
|
43978
|
+
`[tagged] ${expanded.hits.map((h) => h.path).join(", ")}`
|
|
43979
|
+
);
|
|
43980
|
+
}
|
|
42494
43981
|
setSessionActive(true);
|
|
42495
43982
|
if (mode === "zelari") {
|
|
42496
43983
|
setInput("");
|
|
42497
|
-
await dispatchZelariPrompt(
|
|
43984
|
+
await dispatchZelariPrompt(promptText);
|
|
42498
43985
|
return;
|
|
42499
43986
|
}
|
|
42500
43987
|
if (mode === "council") {
|
|
42501
43988
|
setInput("");
|
|
42502
|
-
await dispatchCouncilPrompt(
|
|
43989
|
+
await dispatchCouncilPrompt(promptText);
|
|
42503
43990
|
return;
|
|
42504
43991
|
}
|
|
42505
|
-
await dispatchPrompt(
|
|
43992
|
+
await dispatchPrompt(promptText);
|
|
42506
43993
|
setInput("");
|
|
42507
43994
|
return;
|
|
42508
43995
|
}
|
|
@@ -42852,11 +44339,20 @@ function useSlashDispatch(params) {
|
|
|
42852
44339
|
setInput("");
|
|
42853
44340
|
return;
|
|
42854
44341
|
}
|
|
44342
|
+
if (result.kind === "skill_picker") {
|
|
44343
|
+
handleSkillPicker(skillCtx, skills, params.openPicker, result.message);
|
|
44344
|
+
setInput("");
|
|
44345
|
+
return;
|
|
44346
|
+
}
|
|
42855
44347
|
if (result.kind === "skill" && result.expandedSkill) {
|
|
42856
44348
|
const sysMsg = result.message ?? `[skill] ${result.expandedSkill.skillId} \u2014 prompt ready (dispatch lands in Phase 14.7)`;
|
|
42857
44349
|
appendSystem(setMessages, sysMsg);
|
|
42858
44350
|
setInput("");
|
|
42859
|
-
|
|
44351
|
+
const skillPrompt = expandAtMentions(
|
|
44352
|
+
result.expandedSkill.prompt,
|
|
44353
|
+
process.cwd()
|
|
44354
|
+
).text;
|
|
44355
|
+
await dispatchPrompt(skillPrompt, {
|
|
42860
44356
|
requiredTools: result.expandedSkill.requiredTools
|
|
42861
44357
|
});
|
|
42862
44358
|
return;
|
|
@@ -43507,9 +45003,9 @@ function ContinueKey({ onContinue }) {
|
|
|
43507
45003
|
init_providerConfig();
|
|
43508
45004
|
|
|
43509
45005
|
// src/cli/wizard/firstRun.ts
|
|
43510
|
-
import { existsSync as
|
|
45006
|
+
import { existsSync as existsSync38 } from "node:fs";
|
|
43511
45007
|
function shouldRunWizard(input) {
|
|
43512
|
-
const exists = input.exists ??
|
|
45008
|
+
const exists = input.exists ?? existsSync38;
|
|
43513
45009
|
if (input.hasResetConfigFlag) {
|
|
43514
45010
|
return { shouldRun: true, reason: "--reset-config flag forced wizard" };
|
|
43515
45011
|
}
|
|
@@ -43794,7 +45290,7 @@ init_keyStore();
|
|
|
43794
45290
|
init_providerConfig();
|
|
43795
45291
|
init_openai_compatible();
|
|
43796
45292
|
init_phase();
|
|
43797
|
-
import { readFileSync as
|
|
45293
|
+
import { readFileSync as readFileSync31 } from "node:fs";
|
|
43798
45294
|
function parseHeadlessFlags(argv) {
|
|
43799
45295
|
if (!argv.includes("--headless")) {
|
|
43800
45296
|
return { options: null };
|
|
@@ -43808,6 +45304,7 @@ function parseHeadlessFlags(argv) {
|
|
|
43808
45304
|
let provider;
|
|
43809
45305
|
let model;
|
|
43810
45306
|
let history2;
|
|
45307
|
+
let once = false;
|
|
43811
45308
|
for (let i = 0; i < argv.length; i++) {
|
|
43812
45309
|
const arg = argv[i];
|
|
43813
45310
|
if (arg === "--headless") continue;
|
|
@@ -43862,7 +45359,7 @@ function parseHeadlessFlags(argv) {
|
|
|
43862
45359
|
let raw = null;
|
|
43863
45360
|
if (arg === "--history-file") {
|
|
43864
45361
|
try {
|
|
43865
|
-
raw =
|
|
45362
|
+
raw = readFileSync31(next, "utf-8");
|
|
43866
45363
|
} catch {
|
|
43867
45364
|
raw = null;
|
|
43868
45365
|
}
|
|
@@ -43896,6 +45393,8 @@ function parseHeadlessFlags(argv) {
|
|
|
43896
45393
|
}
|
|
43897
45394
|
i++;
|
|
43898
45395
|
}
|
|
45396
|
+
} else if (arg === "--once") {
|
|
45397
|
+
once = true;
|
|
43899
45398
|
}
|
|
43900
45399
|
}
|
|
43901
45400
|
if (councilFlag && !modeExplicit) {
|
|
@@ -43918,7 +45417,8 @@ function parseHeadlessFlags(argv) {
|
|
|
43918
45417
|
useCouncil: mode === "council",
|
|
43919
45418
|
provider,
|
|
43920
45419
|
model,
|
|
43921
|
-
...history2 && history2.length > 0 ? { history: history2 } : {}
|
|
45420
|
+
...history2 && history2.length > 0 ? { history: history2 } : {},
|
|
45421
|
+
...once ? { once: true } : {}
|
|
43922
45422
|
}
|
|
43923
45423
|
};
|
|
43924
45424
|
}
|
|
@@ -43989,6 +45489,17 @@ function createStreamScrubber() {
|
|
|
43989
45489
|
|
|
43990
45490
|
// src/cli/runHeadless.ts
|
|
43991
45491
|
async function runHeadless(opts) {
|
|
45492
|
+
try {
|
|
45493
|
+
const { expandAtMentions: expandAtMentions2 } = await Promise.resolve().then(() => (init_atMentions(), atMentions_exports));
|
|
45494
|
+
const task = typeof opts.task === "string" ? opts.task : "";
|
|
45495
|
+
if (task.includes("@")) {
|
|
45496
|
+
const { text } = expandAtMentions2(task, process.cwd());
|
|
45497
|
+
if (text !== task) {
|
|
45498
|
+
opts = { ...opts, task: text };
|
|
45499
|
+
}
|
|
45500
|
+
}
|
|
45501
|
+
} catch {
|
|
45502
|
+
}
|
|
43992
45503
|
let crashed = false;
|
|
43993
45504
|
const handleFatal = (label, err) => {
|
|
43994
45505
|
if (crashed) return;
|
|
@@ -44601,6 +46112,22 @@ ${JSON.stringify({ deliverable: brief.deliverableThisMission, mvp: brief.sliceMv
|
|
|
44601
46112
|
}
|
|
44602
46113
|
let exitCode = 0;
|
|
44603
46114
|
let lastMissionAssistant = "";
|
|
46115
|
+
let lockAcquired = false;
|
|
46116
|
+
if (opts.once) {
|
|
46117
|
+
const { acquireLock: acquireLock2 } = await Promise.resolve().then(() => (init_triggerLock(), triggerLock_exports));
|
|
46118
|
+
const lockRes = await acquireLock2(projectRoot);
|
|
46119
|
+
if (!lockRes.acquired) {
|
|
46120
|
+
process.stderr.write(
|
|
46121
|
+
`[zelari-code --once] skip: another mission is running (pid ${lockRes.heldBy}).
|
|
46122
|
+
`
|
|
46123
|
+
);
|
|
46124
|
+
return 0;
|
|
46125
|
+
}
|
|
46126
|
+
lockAcquired = true;
|
|
46127
|
+
if (!process.env.ZELARI_MISSION_MAX_ITER) {
|
|
46128
|
+
process.env.ZELARI_MISSION_MAX_ITER = "1";
|
|
46129
|
+
}
|
|
46130
|
+
}
|
|
44604
46131
|
try {
|
|
44605
46132
|
const state2 = await runZelariMission2(missionTask, brief, {
|
|
44606
46133
|
projectRoot,
|
|
@@ -44819,6 +46346,10 @@ ${ragContext}` : slicePrompt;
|
|
|
44819
46346
|
);
|
|
44820
46347
|
return 2;
|
|
44821
46348
|
} finally {
|
|
46349
|
+
if (lockAcquired) {
|
|
46350
|
+
const { releaseLock: releaseLock2 } = await Promise.resolve().then(() => (init_triggerLock(), triggerLock_exports));
|
|
46351
|
+
await releaseLock2(projectRoot);
|
|
46352
|
+
}
|
|
44822
46353
|
await memory.close().catch(() => void 0);
|
|
44823
46354
|
}
|
|
44824
46355
|
if (opts.output === "json") {
|
|
@@ -44836,250 +46367,8 @@ ${ragContext}` : slicePrompt;
|
|
|
44836
46367
|
return exitCode;
|
|
44837
46368
|
}
|
|
44838
46369
|
|
|
44839
|
-
// src/cli/
|
|
44840
|
-
|
|
44841
|
-
init_providerConfig();
|
|
44842
|
-
init_updater();
|
|
44843
|
-
function wantsPrintConfig(argv) {
|
|
44844
|
-
return argv.includes("--print-config");
|
|
44845
|
-
}
|
|
44846
|
-
function wantsSetKey(argv) {
|
|
44847
|
-
return argv.includes("--set-key");
|
|
44848
|
-
}
|
|
44849
|
-
function wantsDiscoverModels(argv) {
|
|
44850
|
-
return argv.includes("--discover-models");
|
|
44851
|
-
}
|
|
44852
|
-
function parseSetConfigFlags(argv) {
|
|
44853
|
-
if (!argv.includes("--set-config")) {
|
|
44854
|
-
return { request: null };
|
|
44855
|
-
}
|
|
44856
|
-
let provider;
|
|
44857
|
-
let model;
|
|
44858
|
-
let endpoint;
|
|
44859
|
-
let endpointClear = false;
|
|
44860
|
-
for (let i = 0; i < argv.length; i++) {
|
|
44861
|
-
const arg = argv[i];
|
|
44862
|
-
if (arg === "--provider") {
|
|
44863
|
-
provider = argv[i + 1];
|
|
44864
|
-
i++;
|
|
44865
|
-
} else if (arg === "--model") {
|
|
44866
|
-
model = argv[i + 1];
|
|
44867
|
-
i++;
|
|
44868
|
-
} else if (arg === "--endpoint") {
|
|
44869
|
-
endpoint = argv[i + 1];
|
|
44870
|
-
i++;
|
|
44871
|
-
} else if (arg === "--endpoint-clear") {
|
|
44872
|
-
endpointClear = true;
|
|
44873
|
-
}
|
|
44874
|
-
}
|
|
44875
|
-
if (!provider && !model && !endpoint && !endpointClear) {
|
|
44876
|
-
return {
|
|
44877
|
-
request: null,
|
|
44878
|
-
error: "--set-config requires --provider, --model, --endpoint, and/or --endpoint-clear"
|
|
44879
|
-
};
|
|
44880
|
-
}
|
|
44881
|
-
if (provider !== void 0 && provider.trim().length === 0) {
|
|
44882
|
-
return { request: null, error: "--provider cannot be empty" };
|
|
44883
|
-
}
|
|
44884
|
-
if (model !== void 0 && model.trim().length === 0) {
|
|
44885
|
-
return { request: null, error: "--model cannot be empty" };
|
|
44886
|
-
}
|
|
44887
|
-
if (endpoint !== void 0 && endpoint.trim().length === 0) {
|
|
44888
|
-
return { request: null, error: "--endpoint cannot be empty" };
|
|
44889
|
-
}
|
|
44890
|
-
if (endpoint && endpointClear) {
|
|
44891
|
-
return { request: null, error: "--endpoint and --endpoint-clear conflict" };
|
|
44892
|
-
}
|
|
44893
|
-
return {
|
|
44894
|
-
request: {
|
|
44895
|
-
provider: provider?.trim(),
|
|
44896
|
-
model: model?.trim(),
|
|
44897
|
-
endpoint: endpoint?.trim(),
|
|
44898
|
-
endpointClear: endpointClear || void 0
|
|
44899
|
-
}
|
|
44900
|
-
};
|
|
44901
|
-
}
|
|
44902
|
-
function parseSetKeyFlags(argv) {
|
|
44903
|
-
if (!argv.includes("--set-key")) {
|
|
44904
|
-
return { request: null };
|
|
44905
|
-
}
|
|
44906
|
-
let provider;
|
|
44907
|
-
let key;
|
|
44908
|
-
for (let i = 0; i < argv.length; i++) {
|
|
44909
|
-
const arg = argv[i];
|
|
44910
|
-
if (arg === "--provider") {
|
|
44911
|
-
provider = argv[i + 1];
|
|
44912
|
-
i++;
|
|
44913
|
-
} else if (arg === "--key") {
|
|
44914
|
-
key = argv[i + 1];
|
|
44915
|
-
i++;
|
|
44916
|
-
}
|
|
44917
|
-
}
|
|
44918
|
-
if (!provider || provider.trim().length === 0) {
|
|
44919
|
-
return { request: null, error: "--set-key requires --provider <id>" };
|
|
44920
|
-
}
|
|
44921
|
-
if (!key || key.trim().length === 0) {
|
|
44922
|
-
return { request: null, error: "--set-key requires --key <secret>" };
|
|
44923
|
-
}
|
|
44924
|
-
return {
|
|
44925
|
-
request: {
|
|
44926
|
-
provider: provider.trim(),
|
|
44927
|
-
key: key.trim()
|
|
44928
|
-
}
|
|
44929
|
-
};
|
|
44930
|
-
}
|
|
44931
|
-
function parseDiscoverModelsFlags(argv) {
|
|
44932
|
-
if (!argv.includes("--discover-models")) {
|
|
44933
|
-
return { provider: null, present: false };
|
|
44934
|
-
}
|
|
44935
|
-
let provider;
|
|
44936
|
-
for (let i = 0; i < argv.length; i++) {
|
|
44937
|
-
if (argv[i] === "--provider") {
|
|
44938
|
-
provider = argv[i + 1];
|
|
44939
|
-
i++;
|
|
44940
|
-
}
|
|
44941
|
-
}
|
|
44942
|
-
return {
|
|
44943
|
-
present: true,
|
|
44944
|
-
provider: provider?.trim() || null
|
|
44945
|
-
};
|
|
44946
|
-
}
|
|
44947
|
-
function buildDesktopConfigSnapshot() {
|
|
44948
|
-
const config2 = getProviderConfig();
|
|
44949
|
-
const providers = PROVIDERS.map((p3) => {
|
|
44950
|
-
const cached2 = getCachedModels(p3.id);
|
|
44951
|
-
const models = cached2?.models.map((m) => m.id) ?? [];
|
|
44952
|
-
const defaultModel = config2.modelByProvider[p3.id] ?? "";
|
|
44953
|
-
if (defaultModel && !models.includes(defaultModel)) {
|
|
44954
|
-
models.unshift(defaultModel);
|
|
44955
|
-
}
|
|
44956
|
-
const custom2 = getCustomEndpoint(p3.id);
|
|
44957
|
-
const builtin = p3.baseUrl ?? null;
|
|
44958
|
-
return {
|
|
44959
|
-
id: p3.id,
|
|
44960
|
-
displayName: p3.displayName,
|
|
44961
|
-
hasKey: !!resolveApiKey(p3.id),
|
|
44962
|
-
envVar: p3.envVar,
|
|
44963
|
-
models,
|
|
44964
|
-
defaultModel,
|
|
44965
|
-
endpoint: custom2 ?? null,
|
|
44966
|
-
baseUrl: custom2 ?? builtin
|
|
44967
|
-
};
|
|
44968
|
-
});
|
|
44969
|
-
return {
|
|
44970
|
-
activeProviderId: config2.activeProviderId,
|
|
44971
|
-
modelByProvider: { ...config2.modelByProvider },
|
|
44972
|
-
providers,
|
|
44973
|
-
cliVersion: getCurrentVersion(),
|
|
44974
|
-
configPaths: {
|
|
44975
|
-
provider: getProviderConfigPath(),
|
|
44976
|
-
keys: getKeyStorePath()
|
|
44977
|
-
}
|
|
44978
|
-
};
|
|
44979
|
-
}
|
|
44980
|
-
function printDesktopConfig() {
|
|
44981
|
-
const snap = buildDesktopConfigSnapshot();
|
|
44982
|
-
process.stdout.write(JSON.stringify(snap, null, 2) + "\n");
|
|
44983
|
-
}
|
|
44984
|
-
function applySetConfig(req) {
|
|
44985
|
-
try {
|
|
44986
|
-
const config2 = getProviderConfig();
|
|
44987
|
-
let targetProvider = req.provider ?? config2.activeProviderId;
|
|
44988
|
-
if (req.provider) {
|
|
44989
|
-
const exists = PROVIDERS.some((p3) => p3.id === req.provider);
|
|
44990
|
-
if (!exists) {
|
|
44991
|
-
return {
|
|
44992
|
-
ok: false,
|
|
44993
|
-
error: `unknown provider '${req.provider}'. Available: ${PROVIDERS.map((p3) => p3.id).join(", ")}`
|
|
44994
|
-
};
|
|
44995
|
-
}
|
|
44996
|
-
setActiveProviderId(req.provider);
|
|
44997
|
-
targetProvider = req.provider;
|
|
44998
|
-
}
|
|
44999
|
-
if (req.endpointClear) {
|
|
45000
|
-
clearCustomEndpoint(targetProvider);
|
|
45001
|
-
}
|
|
45002
|
-
if (req.endpoint) {
|
|
45003
|
-
setCustomEndpoint(targetProvider, req.endpoint);
|
|
45004
|
-
}
|
|
45005
|
-
if (req.model) {
|
|
45006
|
-
setModelForProvider(targetProvider, req.model);
|
|
45007
|
-
}
|
|
45008
|
-
const after = getProviderConfig();
|
|
45009
|
-
const ep = getCustomEndpoint(after.activeProviderId);
|
|
45010
|
-
return {
|
|
45011
|
-
ok: true,
|
|
45012
|
-
message: `activeProvider=${after.activeProviderId} model=${after.modelByProvider[after.activeProviderId]}` + (ep ? ` endpoint=${ep}` : "")
|
|
45013
|
-
};
|
|
45014
|
-
} catch (err) {
|
|
45015
|
-
return {
|
|
45016
|
-
ok: false,
|
|
45017
|
-
error: err instanceof Error ? err.message : String(err)
|
|
45018
|
-
};
|
|
45019
|
-
}
|
|
45020
|
-
}
|
|
45021
|
-
function applySetKey(req) {
|
|
45022
|
-
try {
|
|
45023
|
-
const exists = PROVIDERS.some((p3) => p3.id === req.provider);
|
|
45024
|
-
if (!exists) {
|
|
45025
|
-
return {
|
|
45026
|
-
ok: false,
|
|
45027
|
-
error: `unknown provider '${req.provider}'. Available: ${PROVIDERS.map((p3) => p3.id).join(", ")}`
|
|
45028
|
-
};
|
|
45029
|
-
}
|
|
45030
|
-
setApiKey(req.provider, req.key);
|
|
45031
|
-
setActiveProviderId(req.provider);
|
|
45032
|
-
return {
|
|
45033
|
-
ok: true,
|
|
45034
|
-
provider: req.provider,
|
|
45035
|
-
masked: maskKey(req.key)
|
|
45036
|
-
};
|
|
45037
|
-
} catch (err) {
|
|
45038
|
-
return {
|
|
45039
|
-
ok: false,
|
|
45040
|
-
error: err instanceof Error ? err.message : String(err)
|
|
45041
|
-
};
|
|
45042
|
-
}
|
|
45043
|
-
}
|
|
45044
|
-
var DISCOVERABLE = [
|
|
45045
|
-
"grok",
|
|
45046
|
-
"glm",
|
|
45047
|
-
"minimax",
|
|
45048
|
-
"deepseek",
|
|
45049
|
-
"openai-compatible"
|
|
45050
|
-
];
|
|
45051
|
-
async function runDiscoverModels(providerArg) {
|
|
45052
|
-
const config2 = getProviderConfig();
|
|
45053
|
-
const providerId = (providerArg ?? config2.activeProviderId).trim();
|
|
45054
|
-
if (!DISCOVERABLE.includes(providerId)) {
|
|
45055
|
-
if (providerId === "custom") {
|
|
45056
|
-
} else {
|
|
45057
|
-
return {
|
|
45058
|
-
ok: false,
|
|
45059
|
-
error: `provider '${providerId}' is not discoverable. Use: ${DISCOVERABLE.join(", ")}`
|
|
45060
|
-
};
|
|
45061
|
-
}
|
|
45062
|
-
}
|
|
45063
|
-
const discoveryId = providerId === "custom" ? "openai-compatible" : providerId;
|
|
45064
|
-
try {
|
|
45065
|
-
const entry = await discoverModelsForProvider(discoveryId);
|
|
45066
|
-
return {
|
|
45067
|
-
ok: true,
|
|
45068
|
-
payload: {
|
|
45069
|
-
ok: true,
|
|
45070
|
-
provider: providerId,
|
|
45071
|
-
models: entry.models.map((m) => m.id),
|
|
45072
|
-
fetchedAt: entry.fetchedAt,
|
|
45073
|
-
baseUrl: entry.baseUrl
|
|
45074
|
-
}
|
|
45075
|
-
};
|
|
45076
|
-
} catch (err) {
|
|
45077
|
-
return {
|
|
45078
|
-
ok: false,
|
|
45079
|
-
error: err instanceof Error ? err.message : String(err)
|
|
45080
|
-
};
|
|
45081
|
-
}
|
|
45082
|
-
}
|
|
46370
|
+
// src/cli/main.ts
|
|
46371
|
+
init_desktopConfig();
|
|
45083
46372
|
|
|
45084
46373
|
// src/cli/plugins/cliFlags.ts
|
|
45085
46374
|
init_registry2();
|
|
@@ -45178,6 +46467,451 @@ init_skillsMd();
|
|
|
45178
46467
|
init_skills2();
|
|
45179
46468
|
init_updater();
|
|
45180
46469
|
init_mcpConfigIo();
|
|
46470
|
+
|
|
46471
|
+
// src/cli/skillConfigIo.ts
|
|
46472
|
+
init_skills2();
|
|
46473
|
+
init_skillsMd();
|
|
46474
|
+
import {
|
|
46475
|
+
existsSync as existsSync39,
|
|
46476
|
+
mkdirSync as mkdirSync16,
|
|
46477
|
+
readdirSync as readdirSync6,
|
|
46478
|
+
readFileSync as readFileSync32,
|
|
46479
|
+
rmSync as rmSync3,
|
|
46480
|
+
writeFileSync as writeFileSync20
|
|
46481
|
+
} from "node:fs";
|
|
46482
|
+
import { dirname as dirname9, join as join34 } from "node:path";
|
|
46483
|
+
import { homedir as homedir10 } from "node:os";
|
|
46484
|
+
|
|
46485
|
+
// src/cli/skillCategories.ts
|
|
46486
|
+
var CODING_CATEGORIES_LIST = [
|
|
46487
|
+
"plan",
|
|
46488
|
+
"refactor",
|
|
46489
|
+
"debug",
|
|
46490
|
+
"review",
|
|
46491
|
+
"test",
|
|
46492
|
+
"docs",
|
|
46493
|
+
"ops",
|
|
46494
|
+
"git",
|
|
46495
|
+
"db",
|
|
46496
|
+
"maint"
|
|
46497
|
+
];
|
|
46498
|
+
var CODING_CATEGORIES2 = new Set(CODING_CATEGORIES_LIST);
|
|
46499
|
+
|
|
46500
|
+
// src/cli/skillConfigIo.ts
|
|
46501
|
+
var NAME_RE = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
46502
|
+
var BUILTIN_SKILL_MODULES = [
|
|
46503
|
+
"@zelari/core/skills/builtin/debugging",
|
|
46504
|
+
"@zelari/core/skills/builtin/docs",
|
|
46505
|
+
"@zelari/core/skills/builtin/git-ops",
|
|
46506
|
+
"@zelari/core/skills/builtin/planning",
|
|
46507
|
+
"@zelari/core/skills/builtin/refactoring",
|
|
46508
|
+
"@zelari/core/skills/builtin/review",
|
|
46509
|
+
"@zelari/core/skills/builtin/testing",
|
|
46510
|
+
"@zelari/core/skills/builtin/schema-loop",
|
|
46511
|
+
"@zelari/core/skills/builtin/computer-use-cua"
|
|
46512
|
+
];
|
|
46513
|
+
var builtinsLoaded = false;
|
|
46514
|
+
function ensureBuiltinSkillsLoadedSync() {
|
|
46515
|
+
if (builtinsLoaded) return;
|
|
46516
|
+
for (const spec of BUILTIN_SKILL_MODULES) {
|
|
46517
|
+
try {
|
|
46518
|
+
require?.(spec);
|
|
46519
|
+
} catch {
|
|
46520
|
+
}
|
|
46521
|
+
}
|
|
46522
|
+
}
|
|
46523
|
+
function getUserSkillsDir() {
|
|
46524
|
+
return join34(homedir10(), ".zelari-code", "skills");
|
|
46525
|
+
}
|
|
46526
|
+
function getProjectSkillsDir(projectRoot) {
|
|
46527
|
+
return join34(projectRoot, ".zelari", "skills");
|
|
46528
|
+
}
|
|
46529
|
+
function skillFilePath(dir, name) {
|
|
46530
|
+
return join34(dir, name, "SKILL.md");
|
|
46531
|
+
}
|
|
46532
|
+
function classifyScope(skillPath, projectRoot) {
|
|
46533
|
+
const userDir = getUserSkillsDir().replace(/\\/g, "/");
|
|
46534
|
+
const norm = skillPath.replace(/\\/g, "/");
|
|
46535
|
+
if (norm.startsWith(userDir + "/") || norm === userDir) {
|
|
46536
|
+
return { scope: "user", writable: true };
|
|
46537
|
+
}
|
|
46538
|
+
if (projectRoot) {
|
|
46539
|
+
const projDir = getProjectSkillsDir(projectRoot).replace(/\\/g, "/");
|
|
46540
|
+
if (norm.startsWith(projDir + "/") || norm === projDir) {
|
|
46541
|
+
return { scope: "project", writable: true };
|
|
46542
|
+
}
|
|
46543
|
+
}
|
|
46544
|
+
return { scope: "compat", writable: false };
|
|
46545
|
+
}
|
|
46546
|
+
function entryFromParsed(parsed, projectRoot) {
|
|
46547
|
+
const { scope, writable } = classifyScope(parsed.sourcePath, projectRoot);
|
|
46548
|
+
return {
|
|
46549
|
+
id: parsed.name,
|
|
46550
|
+
name: parsed.name,
|
|
46551
|
+
description: parsed.description,
|
|
46552
|
+
category: parsed.category,
|
|
46553
|
+
estimatedCost: parsed.estimatedCost,
|
|
46554
|
+
requiredTools: parsed.requiredTools,
|
|
46555
|
+
scope,
|
|
46556
|
+
path: parsed.sourcePath,
|
|
46557
|
+
body: parsed.body,
|
|
46558
|
+
builtin: false,
|
|
46559
|
+
writable
|
|
46560
|
+
};
|
|
46561
|
+
}
|
|
46562
|
+
function entryFromBuiltin(skill) {
|
|
46563
|
+
return {
|
|
46564
|
+
id: skill.id,
|
|
46565
|
+
name: skill.name || skill.id,
|
|
46566
|
+
description: skill.description || "",
|
|
46567
|
+
category: skill.category,
|
|
46568
|
+
estimatedCost: skill.estimatedCost,
|
|
46569
|
+
requiredTools: skill.requiredTools,
|
|
46570
|
+
scope: "builtin",
|
|
46571
|
+
path: null,
|
|
46572
|
+
// Expose body so Desktop skill picker can expand like `/skill <id>`.
|
|
46573
|
+
body: skill.systemPromptFragment || void 0,
|
|
46574
|
+
builtin: true,
|
|
46575
|
+
writable: false
|
|
46576
|
+
};
|
|
46577
|
+
}
|
|
46578
|
+
function scanSkillsDir(dir, projectRoot, seen, out) {
|
|
46579
|
+
if (!existsSync39(dir)) return;
|
|
46580
|
+
let entries;
|
|
46581
|
+
try {
|
|
46582
|
+
entries = readdirSync6(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
46583
|
+
} catch {
|
|
46584
|
+
return;
|
|
46585
|
+
}
|
|
46586
|
+
for (const entry of entries) {
|
|
46587
|
+
const skillPath = skillFilePath(dir, entry);
|
|
46588
|
+
if (!existsSync39(skillPath)) continue;
|
|
46589
|
+
try {
|
|
46590
|
+
const parsed = parseSkillMd(readFileSync32(skillPath, "utf8"), skillPath);
|
|
46591
|
+
if (!parsed) continue;
|
|
46592
|
+
if (seen.has(parsed.name)) continue;
|
|
46593
|
+
seen.add(parsed.name);
|
|
46594
|
+
out.push(entryFromParsed(parsed, projectRoot));
|
|
46595
|
+
} catch {
|
|
46596
|
+
}
|
|
46597
|
+
}
|
|
46598
|
+
}
|
|
46599
|
+
function listSkillsSnapshot(projectRoot) {
|
|
46600
|
+
const root = projectRoot && projectRoot.trim() ? projectRoot.trim() : null;
|
|
46601
|
+
const userSkillsDir = getUserSkillsDir();
|
|
46602
|
+
const projectSkillsDir = root ? getProjectSkillsDir(root) : null;
|
|
46603
|
+
const skills = [];
|
|
46604
|
+
const seen = /* @__PURE__ */ new Set();
|
|
46605
|
+
if (root) {
|
|
46606
|
+
scanSkillsDir(join34(root, ".zelari", "skills"), root, seen, skills);
|
|
46607
|
+
scanSkillsDir(join34(root, ".claude", "skills"), root, seen, skills);
|
|
46608
|
+
scanSkillsDir(join34(root, ".opencode", "skills"), root, seen, skills);
|
|
46609
|
+
}
|
|
46610
|
+
scanSkillsDir(userSkillsDir, root, seen, skills);
|
|
46611
|
+
for (const s of listCodingSkills()) {
|
|
46612
|
+
if (seen.has(s.id)) continue;
|
|
46613
|
+
seen.add(s.id);
|
|
46614
|
+
if (s.builtin) {
|
|
46615
|
+
skills.push(entryFromBuiltin(s));
|
|
46616
|
+
}
|
|
46617
|
+
}
|
|
46618
|
+
skills.sort((a, b) => {
|
|
46619
|
+
const order = (s) => s === "project" ? 0 : s === "user" ? 1 : s === "compat" ? 2 : 3;
|
|
46620
|
+
const d = order(a.scope) - order(b.scope);
|
|
46621
|
+
if (d !== 0) return d;
|
|
46622
|
+
return a.id.localeCompare(b.id);
|
|
46623
|
+
});
|
|
46624
|
+
return { userSkillsDir, projectSkillsDir, skills };
|
|
46625
|
+
}
|
|
46626
|
+
function serializeSkillMd(opts) {
|
|
46627
|
+
const name = opts.name.trim().toLowerCase();
|
|
46628
|
+
const description = opts.description.trim();
|
|
46629
|
+
const body = opts.body.trim();
|
|
46630
|
+
const lines = ["---", `name: ${name}`, `description: ${description}`];
|
|
46631
|
+
const cat = (opts.category ?? "").trim().toLowerCase();
|
|
46632
|
+
if (cat && CODING_CATEGORIES2.has(cat)) {
|
|
46633
|
+
lines.push(`category: ${cat}`);
|
|
46634
|
+
}
|
|
46635
|
+
if (opts.tools && opts.tools.length > 0) {
|
|
46636
|
+
lines.push(`tools: [${opts.tools.map((t) => t.trim()).filter(Boolean).join(", ")}]`);
|
|
46637
|
+
}
|
|
46638
|
+
const cost = (opts.cost ?? "").trim().toLowerCase();
|
|
46639
|
+
if (cost === "low" || cost === "medium" || cost === "high") {
|
|
46640
|
+
lines.push(`cost: ${cost}`);
|
|
46641
|
+
}
|
|
46642
|
+
lines.push("---", "", body, "");
|
|
46643
|
+
return lines.join("\n");
|
|
46644
|
+
}
|
|
46645
|
+
function upsertSkill(opts) {
|
|
46646
|
+
const name = opts.name.trim().toLowerCase();
|
|
46647
|
+
if (!name || !NAME_RE.test(name)) {
|
|
46648
|
+
return {
|
|
46649
|
+
ok: false,
|
|
46650
|
+
error: "Invalid skill name (use lowercase letters, digits, hyphens; max 64 chars)"
|
|
46651
|
+
};
|
|
46652
|
+
}
|
|
46653
|
+
const description = opts.description.trim();
|
|
46654
|
+
if (!description) {
|
|
46655
|
+
return { ok: false, error: "description is required" };
|
|
46656
|
+
}
|
|
46657
|
+
const body = opts.body.trim();
|
|
46658
|
+
if (!body) {
|
|
46659
|
+
return { ok: false, error: "body is required" };
|
|
46660
|
+
}
|
|
46661
|
+
let dir;
|
|
46662
|
+
if (opts.scope === "user") {
|
|
46663
|
+
dir = getUserSkillsDir();
|
|
46664
|
+
} else {
|
|
46665
|
+
const root = opts.projectRoot?.trim();
|
|
46666
|
+
if (!root) {
|
|
46667
|
+
return {
|
|
46668
|
+
ok: false,
|
|
46669
|
+
error: "projectRoot required for project scope (Open Folder first)"
|
|
46670
|
+
};
|
|
46671
|
+
}
|
|
46672
|
+
dir = getProjectSkillsDir(root);
|
|
46673
|
+
}
|
|
46674
|
+
const path40 = skillFilePath(dir, name);
|
|
46675
|
+
const content = serializeSkillMd({
|
|
46676
|
+
name,
|
|
46677
|
+
description,
|
|
46678
|
+
body,
|
|
46679
|
+
category: opts.category,
|
|
46680
|
+
tools: opts.tools,
|
|
46681
|
+
cost: opts.cost
|
|
46682
|
+
});
|
|
46683
|
+
const parsed = parseSkillMd(content, path40);
|
|
46684
|
+
if (!parsed) {
|
|
46685
|
+
return { ok: false, error: "Generated SKILL.md failed validation" };
|
|
46686
|
+
}
|
|
46687
|
+
mkdirSync16(dirname9(path40), { recursive: true });
|
|
46688
|
+
writeFileSync20(path40, content, "utf8");
|
|
46689
|
+
return { ok: true, path: path40 };
|
|
46690
|
+
}
|
|
46691
|
+
function removeSkill(opts) {
|
|
46692
|
+
const name = opts.name.trim().toLowerCase();
|
|
46693
|
+
if (!name) {
|
|
46694
|
+
return { ok: false, error: "name is required" };
|
|
46695
|
+
}
|
|
46696
|
+
let dir;
|
|
46697
|
+
if (opts.scope === "user") {
|
|
46698
|
+
dir = getUserSkillsDir();
|
|
46699
|
+
} else {
|
|
46700
|
+
const root = opts.projectRoot?.trim();
|
|
46701
|
+
if (!root) {
|
|
46702
|
+
return { ok: false, error: "projectRoot required for project scope" };
|
|
46703
|
+
}
|
|
46704
|
+
dir = getProjectSkillsDir(root);
|
|
46705
|
+
}
|
|
46706
|
+
const skillDir = join34(dir, name);
|
|
46707
|
+
const path40 = skillFilePath(dir, name);
|
|
46708
|
+
if (!existsSync39(path40) && !existsSync39(skillDir)) {
|
|
46709
|
+
return { ok: false, error: `Skill "${name}" not found in ${dir}` };
|
|
46710
|
+
}
|
|
46711
|
+
try {
|
|
46712
|
+
rmSync3(skillDir, { recursive: true, force: true });
|
|
46713
|
+
} catch (err) {
|
|
46714
|
+
return {
|
|
46715
|
+
ok: false,
|
|
46716
|
+
error: err instanceof Error ? err.message : String(err)
|
|
46717
|
+
};
|
|
46718
|
+
}
|
|
46719
|
+
return { ok: true, path: path40 };
|
|
46720
|
+
}
|
|
46721
|
+
|
|
46722
|
+
// src/cli/generateSkillFromUrl.ts
|
|
46723
|
+
init_providerConfig();
|
|
46724
|
+
init_keyStore();
|
|
46725
|
+
init_openai_compatible();
|
|
46726
|
+
var MAX_PAGE_CHARS = 24e3;
|
|
46727
|
+
var FETCH_TIMEOUT_MS2 = 25e3;
|
|
46728
|
+
var LLM_TIMEOUT_MS = 9e4;
|
|
46729
|
+
var SYSTEM = `You convert web page content into a Zelari Code coding skill (SKILL.md style).
|
|
46730
|
+
|
|
46731
|
+
Return ONLY a single JSON object (no markdown fences) with keys:
|
|
46732
|
+
- name: string, kebab-case id (a-z0-9-hyphens, max 64, start with letter/digit)
|
|
46733
|
+
- description: string, one line, what the skill does
|
|
46734
|
+
- body: string, markdown instructions the agent should follow (steps, rules, checks)
|
|
46735
|
+
- category: one of ${CODING_CATEGORIES_LIST.join("|")} (optional)
|
|
46736
|
+
- tools: string[] of tool names the skill needs (optional, e.g. bash, read_file, write_file)
|
|
46737
|
+
- cost: "low" | "medium" | "high" (optional, default medium)
|
|
46738
|
+
|
|
46739
|
+
Rules:
|
|
46740
|
+
- Prefer actionable coding-agent instructions over marketing copy.
|
|
46741
|
+
- If the page is a skill/prompt already, adapt it cleanly to this schema.
|
|
46742
|
+
- If the page is docs/blog, distill a reusable workflow skill.
|
|
46743
|
+
- name must be unique-looking and descriptive (not "untitled" or "skill").
|
|
46744
|
+
- body must be non-empty markdown with clear steps.`;
|
|
46745
|
+
function stripHtml(html) {
|
|
46746
|
+
let t = html.replace(/<script[\s\S]*?<\/script>/gi, " ").replace(/<style[\s\S]*?<\/style>/gi, " ").replace(/<noscript[\s\S]*?<\/noscript>/gi, " ");
|
|
46747
|
+
t = t.replace(/<!--[\s\S]*?-->/g, " ");
|
|
46748
|
+
t = t.replace(/<[^>]+>/g, " ");
|
|
46749
|
+
t = t.replace(/ /g, " ").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'");
|
|
46750
|
+
t = t.replace(/[ \t]+\n/g, "\n").replace(/\n{3,}/g, "\n\n");
|
|
46751
|
+
t = t.replace(/[ \t]{2,}/g, " ").trim();
|
|
46752
|
+
return t;
|
|
46753
|
+
}
|
|
46754
|
+
async function fetchUrlText(url2) {
|
|
46755
|
+
const u = url2.trim();
|
|
46756
|
+
if (!/^https?:\/\//i.test(u)) {
|
|
46757
|
+
throw new Error("URL must start with http:// or https://");
|
|
46758
|
+
}
|
|
46759
|
+
const controller = new AbortController();
|
|
46760
|
+
const t = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS2);
|
|
46761
|
+
try {
|
|
46762
|
+
const res = await fetch(u, {
|
|
46763
|
+
signal: controller.signal,
|
|
46764
|
+
headers: {
|
|
46765
|
+
"user-agent": "zelari-code-skill-import/1.0",
|
|
46766
|
+
accept: "text/html,text/plain,application/json;q=0.9,*/*;q=0.8"
|
|
46767
|
+
},
|
|
46768
|
+
redirect: "follow"
|
|
46769
|
+
});
|
|
46770
|
+
if (!res.ok) {
|
|
46771
|
+
throw new Error(`Fetch failed HTTP ${res.status}`);
|
|
46772
|
+
}
|
|
46773
|
+
const ct = (res.headers.get("content-type") || "").toLowerCase();
|
|
46774
|
+
const raw = await res.text();
|
|
46775
|
+
if (ct.includes("json") || raw.trimStart().startsWith("{") || raw.trimStart().startsWith("[")) {
|
|
46776
|
+
return raw.slice(0, MAX_PAGE_CHARS);
|
|
46777
|
+
}
|
|
46778
|
+
if (ct.includes("html") || /<html[\s>]/i.test(raw) || /<body[\s>]/i.test(raw)) {
|
|
46779
|
+
return stripHtml(raw).slice(0, MAX_PAGE_CHARS);
|
|
46780
|
+
}
|
|
46781
|
+
return raw.slice(0, MAX_PAGE_CHARS);
|
|
46782
|
+
} finally {
|
|
46783
|
+
clearTimeout(t);
|
|
46784
|
+
}
|
|
46785
|
+
}
|
|
46786
|
+
function extractJsonObject(text) {
|
|
46787
|
+
const trimmed = text.trim();
|
|
46788
|
+
const fenced = /^```(?:json)?\s*([\s\S]*?)```$/i.exec(trimmed);
|
|
46789
|
+
const body = fenced ? fenced[1].trim() : trimmed;
|
|
46790
|
+
try {
|
|
46791
|
+
return JSON.parse(body);
|
|
46792
|
+
} catch {
|
|
46793
|
+
const start = body.indexOf("{");
|
|
46794
|
+
const end = body.lastIndexOf("}");
|
|
46795
|
+
if (start >= 0 && end > start) {
|
|
46796
|
+
return JSON.parse(body.slice(start, end + 1));
|
|
46797
|
+
}
|
|
46798
|
+
throw new Error("Model did not return valid JSON");
|
|
46799
|
+
}
|
|
46800
|
+
}
|
|
46801
|
+
function normalizeDraft(raw, sourceUrl, provider, model) {
|
|
46802
|
+
if (!raw || typeof raw !== "object") {
|
|
46803
|
+
throw new Error("Invalid skill draft object");
|
|
46804
|
+
}
|
|
46805
|
+
const o = raw;
|
|
46806
|
+
let name = String(o.name ?? "").trim().toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
|
|
46807
|
+
if (!name || !/^[a-z0-9][a-z0-9-]{0,63}$/.test(name)) {
|
|
46808
|
+
try {
|
|
46809
|
+
const path40 = new URL(sourceUrl).pathname.split("/").filter(Boolean).pop()?.replace(/\.[a-z0-9]+$/i, "").toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
|
|
46810
|
+
name = path40 && /^[a-z0-9]/.test(path40) ? path40 : "imported-skill";
|
|
46811
|
+
} catch {
|
|
46812
|
+
name = "imported-skill";
|
|
46813
|
+
}
|
|
46814
|
+
}
|
|
46815
|
+
const description = String(o.description ?? "").trim();
|
|
46816
|
+
const body = String(o.body ?? "").trim();
|
|
46817
|
+
if (!description) throw new Error("Model draft missing description");
|
|
46818
|
+
if (!body) throw new Error("Model draft missing body");
|
|
46819
|
+
const catRaw = String(o.category ?? "").trim().toLowerCase();
|
|
46820
|
+
const category = CODING_CATEGORIES_LIST.includes(catRaw) ? catRaw : void 0;
|
|
46821
|
+
let tools;
|
|
46822
|
+
if (Array.isArray(o.tools)) {
|
|
46823
|
+
tools = o.tools.map((t) => String(t).trim()).filter(Boolean);
|
|
46824
|
+
} else if (typeof o.tools === "string" && o.tools.trim()) {
|
|
46825
|
+
tools = o.tools.split(",").map((t) => t.trim()).filter(Boolean);
|
|
46826
|
+
}
|
|
46827
|
+
const costRaw = String(o.cost ?? "medium").trim().toLowerCase();
|
|
46828
|
+
const cost = costRaw === "low" || costRaw === "high" || costRaw === "medium" ? costRaw : "medium";
|
|
46829
|
+
return {
|
|
46830
|
+
name,
|
|
46831
|
+
description,
|
|
46832
|
+
body,
|
|
46833
|
+
category,
|
|
46834
|
+
tools,
|
|
46835
|
+
cost,
|
|
46836
|
+
sourceUrl,
|
|
46837
|
+
model,
|
|
46838
|
+
provider
|
|
46839
|
+
};
|
|
46840
|
+
}
|
|
46841
|
+
async function resolveLlm(opts) {
|
|
46842
|
+
const active = opts.provider?.trim() || getProviderConfig().activeProviderId;
|
|
46843
|
+
const meta3 = await resolveApiKeyWithMeta(active);
|
|
46844
|
+
if (!meta3?.apiKey) {
|
|
46845
|
+
throw new Error(
|
|
46846
|
+
`No API key for provider '${active}'. Save a key in Settings \u2192 Provider.`
|
|
46847
|
+
);
|
|
46848
|
+
}
|
|
46849
|
+
const baseUrl = resolveBaseUrl(active);
|
|
46850
|
+
if (!baseUrl) {
|
|
46851
|
+
throw new Error(
|
|
46852
|
+
`No base URL for provider '${active}'. Set a custom endpoint in Settings.`
|
|
46853
|
+
);
|
|
46854
|
+
}
|
|
46855
|
+
const model = opts.model?.trim() || getModelForProvider(active) || process.env.ZELARI_MODEL || "";
|
|
46856
|
+
if (!model) {
|
|
46857
|
+
throw new Error(`No model selected for provider '${active}'`);
|
|
46858
|
+
}
|
|
46859
|
+
return { provider: active, model, apiKey: meta3.apiKey, baseUrl };
|
|
46860
|
+
}
|
|
46861
|
+
async function generateSkillFromUrl(opts) {
|
|
46862
|
+
const page = await fetchUrlText(opts.url);
|
|
46863
|
+
if (!page.trim()) {
|
|
46864
|
+
throw new Error("Page content is empty after fetch");
|
|
46865
|
+
}
|
|
46866
|
+
const llm = await resolveLlm({
|
|
46867
|
+
provider: opts.provider,
|
|
46868
|
+
model: opts.model
|
|
46869
|
+
});
|
|
46870
|
+
const controller = new AbortController();
|
|
46871
|
+
const t = setTimeout(() => controller.abort(), LLM_TIMEOUT_MS);
|
|
46872
|
+
try {
|
|
46873
|
+
const url2 = `${llm.baseUrl.replace(/\/$/, "")}/chat/completions`;
|
|
46874
|
+
const res = await fetch(url2, {
|
|
46875
|
+
method: "POST",
|
|
46876
|
+
signal: controller.signal,
|
|
46877
|
+
headers: {
|
|
46878
|
+
"content-type": "application/json",
|
|
46879
|
+
authorization: `Bearer ${llm.apiKey}`
|
|
46880
|
+
},
|
|
46881
|
+
body: JSON.stringify({
|
|
46882
|
+
model: llm.model,
|
|
46883
|
+
temperature: 0.2,
|
|
46884
|
+
max_tokens: 4096,
|
|
46885
|
+
stream: false,
|
|
46886
|
+
messages: [
|
|
46887
|
+
{ role: "system", content: SYSTEM },
|
|
46888
|
+
{
|
|
46889
|
+
role: "user",
|
|
46890
|
+
content: `Source URL: ${opts.url.trim()}
|
|
46891
|
+
|
|
46892
|
+
Page content (truncated):
|
|
46893
|
+
${page}`
|
|
46894
|
+
}
|
|
46895
|
+
]
|
|
46896
|
+
})
|
|
46897
|
+
});
|
|
46898
|
+
if (!res.ok) {
|
|
46899
|
+
const errBody = await res.text().catch(() => "");
|
|
46900
|
+
throw new Error(
|
|
46901
|
+
`LLM HTTP ${res.status}${errBody ? `: ${errBody.slice(0, 200)}` : ""}`
|
|
46902
|
+
);
|
|
46903
|
+
}
|
|
46904
|
+
const json2 = await res.json();
|
|
46905
|
+
const text = json2.choices?.[0]?.message?.content?.trim();
|
|
46906
|
+
if (!text) throw new Error("Empty model response");
|
|
46907
|
+
const parsed = extractJsonObject(text);
|
|
46908
|
+
return normalizeDraft(parsed, opts.url.trim(), llm.provider, llm.model);
|
|
46909
|
+
} finally {
|
|
46910
|
+
clearTimeout(t);
|
|
46911
|
+
}
|
|
46912
|
+
}
|
|
46913
|
+
|
|
46914
|
+
// src/cli/main.ts
|
|
45181
46915
|
init_mcpPresets();
|
|
45182
46916
|
init_targets();
|
|
45183
46917
|
var VERSION = getCurrentVersion();
|
|
@@ -45217,7 +46951,7 @@ function runPreflight() {
|
|
|
45217
46951
|
}
|
|
45218
46952
|
async function backgroundUpdateCheck() {
|
|
45219
46953
|
if (process.env.ANATHEMA_DEV === "1") return;
|
|
45220
|
-
await new Promise((
|
|
46954
|
+
await new Promise((resolve3) => setTimeout(resolve3, 3e3));
|
|
45221
46955
|
try {
|
|
45222
46956
|
const { checkForUpdate: checkForUpdate2 } = await Promise.resolve().then(() => (init_updater(), updater_exports));
|
|
45223
46957
|
const info = await checkForUpdate2();
|
|
@@ -45255,6 +46989,35 @@ function pickRootComponent() {
|
|
|
45255
46989
|
void runPluginsInstall(argv).then((code) => process.exit(code));
|
|
45256
46990
|
return { kind: "done" };
|
|
45257
46991
|
}
|
|
46992
|
+
if (argv.includes("serve") || argv.includes("--serve")) {
|
|
46993
|
+
const { parseServeFlags: parseServeFlags2 } = (init_serve(), __toCommonJS(serve_exports));
|
|
46994
|
+
return { kind: "serve", serveOpts: parseServeFlags2(argv) ?? {} };
|
|
46995
|
+
}
|
|
46996
|
+
if (argv.includes("--generate-skill-from-url")) {
|
|
46997
|
+
void (async () => {
|
|
46998
|
+
try {
|
|
46999
|
+
const get = (flag) => {
|
|
47000
|
+
const i = argv.indexOf(flag);
|
|
47001
|
+
return i >= 0 && argv[i + 1] ? argv[i + 1] : void 0;
|
|
47002
|
+
};
|
|
47003
|
+
const url2 = get("--url");
|
|
47004
|
+
if (!url2) throw new Error("--url is required");
|
|
47005
|
+
const draft = await generateSkillFromUrl({
|
|
47006
|
+
url: url2,
|
|
47007
|
+
provider: get("--provider"),
|
|
47008
|
+
model: get("--model")
|
|
47009
|
+
});
|
|
47010
|
+
console.log(JSON.stringify({ ok: true, ...draft }, null, 2));
|
|
47011
|
+
process.exit(0);
|
|
47012
|
+
} catch (err) {
|
|
47013
|
+
console.error(
|
|
47014
|
+
`[zelari-code --generate-skill-from-url] ${err instanceof Error ? err.message : String(err)}`
|
|
47015
|
+
);
|
|
47016
|
+
process.exit(1);
|
|
47017
|
+
}
|
|
47018
|
+
})();
|
|
47019
|
+
return { kind: "done" };
|
|
47020
|
+
}
|
|
45258
47021
|
if (argv.includes("--doctor") || argv.includes("doctor")) {
|
|
45259
47022
|
const { runDoctor: runDoctor2 } = (init_doctor(), __toCommonJS(doctor_exports));
|
|
45260
47023
|
void runDoctor2().then((healthy) => process.exit(healthy ? 0 : 1));
|
|
@@ -45306,7 +47069,7 @@ function pickRootComponent() {
|
|
|
45306
47069
|
}
|
|
45307
47070
|
if (argv.includes("--help") || argv.includes("-h")) {
|
|
45308
47071
|
console.log(
|
|
45309
|
-
"zelari-code \u2014 AI Council coding agent CLI.\n\nUsage: zelari-code [options]\n\nOptions:\n --version, -v Print version and exit\n --help, -h Print this help and exit\n --doctor Diagnose install health (shim, bundle, PATH, deps,\n node/git/bash in the agent shell)\n --fix-path Add the npm global prefix to the user PATH\n (Windows only; fixes 'command not found' after install)\n --fix-budget Set recommended ZELARI_MAX_TOOL_LOOP_HARD=180,\n ZELARI_MAX_TOOL_LOOP_ITERATIONS=60, ZELARI_CONTEXT_LIMIT=400000\n at User scope (prevents the agent stopping mid-task)\n --skip-checks Skip the boot-time prerequisite check\n (alias for ZELARI_SKIP_PREFLIGHT=1)\n --no-wizard Skip the first-run wizard\n --reset-config Re-run the wizard (clears provider.json on commit)\n --headless Run a single task without mounting the TUI\n --task <text> Task prompt (required in headless mode)\n --output json|plain Output format (default: json)\n --mode agent|council|zelari Dispatch mode (default: agent)\n --council Alias for --mode council\n --phase plan|build Work phase (default: build)\n --provider <id> Provider override (default: active)\n --model <id> Model override (default: provider default)\n --print-config Print provider/model config as JSON (no secrets)\n --plugins-status JSON status of optional plugins (Playwright, eslint, \u2026)\n --plugins-install <id> Install plugin (playwright also fetches Chromium)\n --cwd <path> Workspace for -D installs (default: process.cwd())\n --set-config Persist provider/model/endpoint\n --provider <id> Set active provider\n --model <id> Set model for that provider\n --endpoint <url> Custom OpenAI-compatible base URL\n --endpoint-clear Remove custom endpoint override\n --set-key Store an API key (never printed back)\n --provider <id> Provider id (required)\n --key <secret> API key (required)\n --discover-models Refresh model list for a provider\n --provider <id> Provider (default: active)\n --print-mcp Print MCP server config (user + project)\n --cwd <path> Project root for .zelari/mcp.json\n --set-mcp Add/update an MCP server entry\n --name <id> Server name (required)\n --command <bin> Executable (required)\n --args <json> JSON array of args (optional)\n --scope user|project Default: user\n --enabled true|false Default: true\n --cwd <path> Required when scope=project\n --set-mcp-preset Install a named MCP preset (e.g. cua)\n --preset cua Cua Driver desktop computer-use (MCP)\n --scope user|project Default: user\n --cwd <path> Required when scope=project\n --remove-mcp Remove an MCP server entry\n --name <id> --scope user|project [--cwd <path>]\n --print-ssh-targets Print SSH deploy/monitor targets\n --set-ssh-target Upsert target (--json '{...}' or flags)\n --remove-ssh-target --id <id>\n --test-ssh-target --id <id> (BatchMode ssh true)\n --print-ssh-pubkey --path <private-or-.pub> (display public key)\n\nEnvironment:\n ZELARI_NO_WIZARD=1 Skip the first-run wizard\n ZELARI_SKIP_PREFLIGHT=1 Skip the boot prerequisite check\n ZELARI_NO_PLUGIN_PROMPT=1 Skip the boot plugin-install prompt\n ANATHEMA_DEV=1 Disable background update check + preflight\n"
|
|
47072
|
+
"zelari-code \u2014 AI Council coding agent CLI.\n\nUsage: zelari-code [options]\n\nOptions:\n --version, -v Print version and exit\n --help, -h Print this help and exit\n --doctor Diagnose install health (shim, bundle, PATH, deps,\n node/git/bash in the agent shell)\n --fix-path Add the npm global prefix to the user PATH\n (Windows only; fixes 'command not found' after install)\n --fix-budget Set recommended ZELARI_MAX_TOOL_LOOP_HARD=180,\n ZELARI_MAX_TOOL_LOOP_ITERATIONS=60, ZELARI_CONTEXT_LIMIT=400000\n at User scope (prevents the agent stopping mid-task)\n --skip-checks Skip the boot-time prerequisite check\n (alias for ZELARI_SKIP_PREFLIGHT=1)\n --no-wizard Skip the first-run wizard\n --reset-config Re-run the wizard (clears provider.json on commit)\n --headless Run a single task without mounting the TUI\n --task <text> Task prompt (required in headless mode)\n --output json|plain Output format (default: json)\n --mode agent|council|zelari Dispatch mode (default: agent)\n --council Alias for --mode council\n --phase plan|build Work phase (default: build)\n --provider <id> Provider override (default: active)\n --model <id> Model override (default: provider default)\n serve Companion host for Android/remote clients (Tailscale)\n --bind <ip> Listen address (default: 127.0.0.1; use Tailscale IP)\n --port <n> Port (default: 7421)\n --token <secret> Bearer token (default: ~/.zelari-code/companion.token)\n --project <path> Allowlisted project root (repeatable)\n --save-projects Persist --project list to companion.json\n --print-config Print provider/model config as JSON (no secrets)\n --plugins-status JSON status of optional plugins (Playwright, eslint, \u2026)\n --plugins-install <id> Install plugin (playwright also fetches Chromium)\n --cwd <path> Workspace for -D installs (default: process.cwd())\n --set-config Persist provider/model/endpoint\n --provider <id> Set active provider\n --model <id> Set model for that provider\n --endpoint <url> Custom OpenAI-compatible base URL\n --endpoint-clear Remove custom endpoint override\n --set-key Store an API key (never printed back)\n --provider <id> Provider id (required)\n --key <secret> API key (required)\n --discover-models Refresh model list for a provider\n --provider <id> Provider (default: active)\n --print-mcp Print MCP server config (user + project)\n --cwd <path> Project root for .zelari/mcp.json\n --set-mcp Add/update an MCP server entry\n --name <id> Server name (required)\n --command <bin> Executable (required)\n --args <json> JSON array of args (optional)\n --scope user|project Default: user\n --enabled true|false Default: true\n --cwd <path> Required when scope=project\n --set-mcp-preset Install a named MCP preset (e.g. cua)\n --preset cua Cua Driver desktop computer-use (MCP)\n --scope user|project Default: user\n --cwd <path> Required when scope=project\n --remove-mcp Remove an MCP server entry\n --name <id> --scope user|project [--cwd <path>]\n --print-skills Print skills (builtin + user + project)\n --cwd <path> Project root for .zelari/skills\n --set-skill Add/update a SKILL.md skill\n --name <id> Skill id (required)\n --description <t> One-line description (required)\n --body <text> Markdown instructions (required)\n --category <c> plan|refactor|debug|review|test|docs|ops|git|db|maint\n --tools <csv> Comma-separated required tools (optional)\n --cost <l> low|medium|high (default: medium)\n --scope user|project Default: user\n --cwd <path> Required when scope=project\n --remove-skill Remove a user/project SKILL.md\n --name <id> --scope user|project [--cwd <path>]\n --generate-skill-from-url Fetch URL + draft skill via model (JSON)\n --url <https://...> Required\n --provider <id> Override active provider\n --model <name> Override model for the selected provider\n --print-ssh-targets Print SSH deploy/monitor targets\n --set-ssh-target Upsert target (--json '{...}' or flags)\n --remove-ssh-target --id <id>\n --test-ssh-target --id <id> (BatchMode ssh true)\n --print-ssh-pubkey --path <private-or-.pub> (display public key)\n\nEnvironment:\n ZELARI_NO_WIZARD=1 Skip the first-run wizard\n ZELARI_SKIP_PREFLIGHT=1 Skip the boot prerequisite check\n ZELARI_NO_PLUGIN_PROMPT=1 Skip the boot plugin-install prompt\n ANATHEMA_DEV=1 Disable background update check + preflight\n"
|
|
45310
47073
|
);
|
|
45311
47074
|
process.exit(0);
|
|
45312
47075
|
}
|
|
@@ -45385,6 +47148,82 @@ function pickRootComponent() {
|
|
|
45385
47148
|
process.exit(1);
|
|
45386
47149
|
}
|
|
45387
47150
|
}
|
|
47151
|
+
if (argv.includes("--print-skills")) {
|
|
47152
|
+
try {
|
|
47153
|
+
const cwdIdx = argv.indexOf("--cwd");
|
|
47154
|
+
const cwd = cwdIdx >= 0 && argv[cwdIdx + 1] ? argv[cwdIdx + 1] : process.cwd();
|
|
47155
|
+
ensureBuiltinSkillsLoadedSync();
|
|
47156
|
+
const snap = listSkillsSnapshot(cwd);
|
|
47157
|
+
console.log(JSON.stringify(snap, null, 2));
|
|
47158
|
+
process.exit(0);
|
|
47159
|
+
} catch (err) {
|
|
47160
|
+
console.error(
|
|
47161
|
+
`[zelari-code --print-skills] ${err instanceof Error ? err.message : String(err)}`
|
|
47162
|
+
);
|
|
47163
|
+
process.exit(1);
|
|
47164
|
+
}
|
|
47165
|
+
}
|
|
47166
|
+
if (argv.includes("--set-skill")) {
|
|
47167
|
+
try {
|
|
47168
|
+
const get = (flag) => {
|
|
47169
|
+
const i = argv.indexOf(flag);
|
|
47170
|
+
return i >= 0 && argv[i + 1] ? argv[i + 1] : void 0;
|
|
47171
|
+
};
|
|
47172
|
+
const name = get("--name");
|
|
47173
|
+
const description = get("--description");
|
|
47174
|
+
const body = get("--body");
|
|
47175
|
+
const category = get("--category");
|
|
47176
|
+
const toolsRaw = get("--tools");
|
|
47177
|
+
const cost = get("--cost");
|
|
47178
|
+
const scopeRaw = get("--scope") ?? "user";
|
|
47179
|
+
const scope = scopeRaw === "project" ? "project" : "user";
|
|
47180
|
+
const cwd = get("--cwd") ?? process.cwd();
|
|
47181
|
+
if (!name) throw new Error("--name is required");
|
|
47182
|
+
if (!description) throw new Error("--description is required");
|
|
47183
|
+
if (!body) throw new Error("--body is required");
|
|
47184
|
+
const tools = toolsRaw ? toolsRaw.split(",").map((t) => t.trim()).filter(Boolean) : void 0;
|
|
47185
|
+
const result = upsertSkill({
|
|
47186
|
+
scope,
|
|
47187
|
+
name,
|
|
47188
|
+
description,
|
|
47189
|
+
body,
|
|
47190
|
+
category,
|
|
47191
|
+
tools,
|
|
47192
|
+
cost,
|
|
47193
|
+
projectRoot: cwd
|
|
47194
|
+
});
|
|
47195
|
+
if (!result.ok) throw new Error(result.error);
|
|
47196
|
+
console.log(JSON.stringify({ ok: true, path: result.path, name, scope }));
|
|
47197
|
+
process.exit(0);
|
|
47198
|
+
} catch (err) {
|
|
47199
|
+
console.error(
|
|
47200
|
+
`[zelari-code --set-skill] ${err instanceof Error ? err.message : String(err)}`
|
|
47201
|
+
);
|
|
47202
|
+
process.exit(1);
|
|
47203
|
+
}
|
|
47204
|
+
}
|
|
47205
|
+
if (argv.includes("--remove-skill")) {
|
|
47206
|
+
try {
|
|
47207
|
+
const get = (flag) => {
|
|
47208
|
+
const i = argv.indexOf(flag);
|
|
47209
|
+
return i >= 0 && argv[i + 1] ? argv[i + 1] : void 0;
|
|
47210
|
+
};
|
|
47211
|
+
const name = get("--name");
|
|
47212
|
+
const scopeRaw = get("--scope") ?? "user";
|
|
47213
|
+
const scope = scopeRaw === "project" ? "project" : "user";
|
|
47214
|
+
const cwd = get("--cwd") ?? process.cwd();
|
|
47215
|
+
if (!name) throw new Error("--name is required");
|
|
47216
|
+
const result = removeSkill({ scope, name, projectRoot: cwd });
|
|
47217
|
+
if (!result.ok) throw new Error(result.error);
|
|
47218
|
+
console.log(JSON.stringify({ ok: true, path: result.path, name, scope }));
|
|
47219
|
+
process.exit(0);
|
|
47220
|
+
} catch (err) {
|
|
47221
|
+
console.error(
|
|
47222
|
+
`[zelari-code --remove-skill] ${err instanceof Error ? err.message : String(err)}`
|
|
47223
|
+
);
|
|
47224
|
+
process.exit(1);
|
|
47225
|
+
}
|
|
47226
|
+
}
|
|
45388
47227
|
if (argv.includes("--set-mcp-preset")) {
|
|
45389
47228
|
try {
|
|
45390
47229
|
const get = (flag) => {
|
|
@@ -45657,6 +47496,15 @@ function loadUserSkills() {
|
|
|
45657
47496
|
function main() {
|
|
45658
47497
|
const picked = pickRootComponent();
|
|
45659
47498
|
if (picked.kind === "done") return;
|
|
47499
|
+
if (picked.kind === "serve") {
|
|
47500
|
+
void Promise.resolve().then(() => (init_serve(), serve_exports)).then(({ runCompanionServe: runCompanionServe2 }) => runCompanionServe2(picked.serveOpts ?? {})).then(() => process.exit(0)).catch((err) => {
|
|
47501
|
+
console.error(
|
|
47502
|
+
`[zelari-code serve] ${err instanceof Error ? err.message : String(err)}`
|
|
47503
|
+
);
|
|
47504
|
+
process.exit(1);
|
|
47505
|
+
});
|
|
47506
|
+
return;
|
|
47507
|
+
}
|
|
45660
47508
|
runPreflight();
|
|
45661
47509
|
loadUserSkills();
|
|
45662
47510
|
if (picked.kind === "headless") {
|
|
@@ -45670,7 +47518,7 @@ function main() {
|
|
|
45670
47518
|
closeMcpClients2();
|
|
45671
47519
|
} catch {
|
|
45672
47520
|
}
|
|
45673
|
-
await new Promise((
|
|
47521
|
+
await new Promise((resolve3) => setImmediate(resolve3));
|
|
45674
47522
|
process.exit(code);
|
|
45675
47523
|
}).catch(async (err) => {
|
|
45676
47524
|
console.error(
|