@chanlerdev/scorel 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +779 -173
- package/dist/index.js.map +3 -3
- package/docs/CHANGELOG.md +53 -0
- package/docs/ROADMAP.md +15 -2
- package/docs/SHIP.md +1 -1
- package/docs/spec/extensions.md +6 -5
- package/docs/spec/ship/S0060-relay-hosted-webui-e2e-validation.verification.md +1 -1
- package/docs/spec/ship/S0073-provider-model-profile-contract.md +6 -6
- package/docs/spec/ship/S0074-gui-model-provider-settings-split.md +1 -1
- package/docs/spec/ship/S0076-provider-modal-search-and-direct-key.md +1 -1
- package/docs/spec/ship/S0086-auto-compact-and-session-memory.md +1 -1
- package/docs/spec/ship/S0096-glob-stable-order.md +32 -0
- package/docs/spec/ship/S0097-rtk-token-saving-settings.md +61 -0
- package/docs/spec/ship/S0098-local-daemon-singleton-unified-state.md +96 -0
- package/docs/spec/ship/S0099-gui-connection-device-settings.md +85 -0
- package/docs/spec/ship/S0100-gui-provider-danger-zone.md +57 -0
- package/docs/spec/ship/S0101-gui-device-settings-polish.md +66 -0
- package/docs/spec/ship/S0102-device-only-config.md +58 -0
- package/package.json +4 -2
package/dist/index.js
CHANGED
|
@@ -261,7 +261,7 @@ var init_src2 = __esm({
|
|
|
261
261
|
this.#assertDaemonConnected();
|
|
262
262
|
return this.#request("remove_model_provider", input);
|
|
263
263
|
}
|
|
264
|
-
async getMemorySettings(input) {
|
|
264
|
+
async getMemorySettings(input = {}) {
|
|
265
265
|
this.#assertDaemonConnected();
|
|
266
266
|
return (await this.#request("get_memory_settings", input)).memory;
|
|
267
267
|
}
|
|
@@ -273,6 +273,14 @@ var init_src2 = __esm({
|
|
|
273
273
|
this.#assertDaemonConnected();
|
|
274
274
|
return (await this.#request("upsert_memory_settings", input)).memory;
|
|
275
275
|
}
|
|
276
|
+
async getRuntimeSettings(input = {}) {
|
|
277
|
+
this.#assertDaemonConnected();
|
|
278
|
+
return (await this.#request("get_runtime_settings", input)).runtime;
|
|
279
|
+
}
|
|
280
|
+
async upsertRuntimeSettings(input) {
|
|
281
|
+
this.#assertDaemonConnected();
|
|
282
|
+
return (await this.#request("upsert_runtime_settings", input)).runtime;
|
|
283
|
+
}
|
|
276
284
|
async getExtensionSettings(input) {
|
|
277
285
|
this.#assertDaemonConnected();
|
|
278
286
|
return (await this.#request("get_extension_settings", input)).extension;
|
|
@@ -807,7 +815,7 @@ var init_sessions = __esm({
|
|
|
807
815
|
// packages/core/src/config/index.ts
|
|
808
816
|
import { readFile as readFile3 } from "node:fs/promises";
|
|
809
817
|
import { join as join4 } from "node:path";
|
|
810
|
-
var SCOREL_CONFIG_SCHEMA, scorelUserRoot, scorelUserConfigPath, scorelSessionsDir,
|
|
818
|
+
var SCOREL_CONFIG_SCHEMA, scorelUserRoot, scorelUserConfigPath, scorelSessionsDir, loadScorelConfig, loadScorelConfigProfile, listProviderConnections, listAvailableModels, listProviderModels, resolveModelSelection, renderModelProfileConfig, removeProvider, renderMemoryConfig, renderRuntimeConfig, renderExtensionConfig, DEFAULT_MEMORY_CONFIG, DEFAULT_RUNTIME_CONFIG, loadMemory, loadRuntime, loadExtensions, loadProviders, loadProviderProfiles, loadProviderModels, loadAvailableModels, loadRoles, readConfigText, configPathForDevice, parseToml, parseEditableConfig, renderRawConfig, emptyRawConfig, stripComment, requireString, normalizeProviderName, requireProviderCredential, resolveProviderApiKey, providerCredentialSummary, requireNumber, requireNonNegativeNumber, requireCompactThreshold, requireBoolean, requireCustomApi, requireProviderType, requireSection, ensureSection, setConfigValue, assertKnownKey, setValue, parseTomlValue, stripTrailingSlashes, requireIdentifier, tomlString, renderTomlValue, isNodeErrorCode, requireModelRole, modelRoles;
|
|
811
819
|
var init_config = __esm({
|
|
812
820
|
"packages/core/src/config/index.ts"() {
|
|
813
821
|
"use strict";
|
|
@@ -815,8 +823,7 @@ var init_config = __esm({
|
|
|
815
823
|
fixedPaths: {
|
|
816
824
|
userRoot: "~/.scorel",
|
|
817
825
|
userConfig: "~/.scorel/config.toml",
|
|
818
|
-
sessionsDir: "~/.scorel/sessions"
|
|
819
|
-
projectConfig: ".scorel/config.toml"
|
|
826
|
+
sessionsDir: "~/.scorel/sessions"
|
|
820
827
|
},
|
|
821
828
|
sections: {
|
|
822
829
|
root: {
|
|
@@ -837,6 +844,9 @@ var init_config = __esm({
|
|
|
837
844
|
memory: {
|
|
838
845
|
keys: ["enabled", "daily", "sessionMemory", "autoDream", "promoteRoot", "dreamIdleMinutes", "autoCompactThreshold"]
|
|
839
846
|
},
|
|
847
|
+
runtime: {
|
|
848
|
+
keys: ["tokenSavingRtk"]
|
|
849
|
+
},
|
|
840
850
|
extension: {
|
|
841
851
|
keys: ["enabled", "kind"]
|
|
842
852
|
},
|
|
@@ -848,7 +858,6 @@ var init_config = __esm({
|
|
|
848
858
|
scorelUserRoot = (homeDir) => join4(homeDir, ".scorel");
|
|
849
859
|
scorelUserConfigPath = (homeDir) => join4(scorelUserRoot(homeDir), "config.toml");
|
|
850
860
|
scorelSessionsDir = (homeDir) => join4(scorelUserRoot(homeDir), "sessions");
|
|
851
|
-
scorelProjectConfigPath = (cwd) => join4(cwd, ".scorel", "config.toml");
|
|
852
861
|
loadScorelConfig = async (options) => {
|
|
853
862
|
const env = options.env ?? process.env;
|
|
854
863
|
const raw = parseToml(await readConfigText(options));
|
|
@@ -862,6 +871,7 @@ var init_config = __esm({
|
|
|
862
871
|
models,
|
|
863
872
|
modelProfile: { roles },
|
|
864
873
|
memory: loadMemory(raw),
|
|
874
|
+
runtime: loadRuntime(raw),
|
|
865
875
|
extensions: loadExtensions(raw)
|
|
866
876
|
};
|
|
867
877
|
};
|
|
@@ -878,6 +888,7 @@ var init_config = __esm({
|
|
|
878
888
|
models,
|
|
879
889
|
modelProfile: { roles },
|
|
880
890
|
memory: loadMemory(raw),
|
|
891
|
+
runtime: loadRuntime(raw),
|
|
881
892
|
extensions: loadExtensions(raw)
|
|
882
893
|
};
|
|
883
894
|
};
|
|
@@ -1134,6 +1145,14 @@ var init_config = __esm({
|
|
|
1134
1145
|
};
|
|
1135
1146
|
return renderRawConfig(raw);
|
|
1136
1147
|
};
|
|
1148
|
+
renderRuntimeConfig = (input) => {
|
|
1149
|
+
const raw = parseEditableConfig(input.existingConfigText);
|
|
1150
|
+
raw.runtime = {
|
|
1151
|
+
...loadRuntime(raw),
|
|
1152
|
+
...input.tokenSavingRtk !== void 0 ? { tokenSavingRtk: requireBoolean(input.tokenSavingRtk, "runtime.tokenSavingRtk") } : {}
|
|
1153
|
+
};
|
|
1154
|
+
return renderRawConfig(raw);
|
|
1155
|
+
};
|
|
1137
1156
|
renderExtensionConfig = (input) => {
|
|
1138
1157
|
const raw = parseEditableConfig(input.existingConfigText);
|
|
1139
1158
|
const extensionId = requireIdentifier(input.extensionId, "extensionId");
|
|
@@ -1165,6 +1184,9 @@ var init_config = __esm({
|
|
|
1165
1184
|
dreamIdleMinutes: 60,
|
|
1166
1185
|
autoCompactThreshold: 0.8
|
|
1167
1186
|
};
|
|
1187
|
+
DEFAULT_RUNTIME_CONFIG = {
|
|
1188
|
+
tokenSavingRtk: false
|
|
1189
|
+
};
|
|
1168
1190
|
loadMemory = (raw) => ({
|
|
1169
1191
|
enabled: raw.memory?.enabled ?? DEFAULT_MEMORY_CONFIG.enabled,
|
|
1170
1192
|
daily: raw.memory?.daily ?? DEFAULT_MEMORY_CONFIG.daily,
|
|
@@ -1174,6 +1196,9 @@ var init_config = __esm({
|
|
|
1174
1196
|
dreamIdleMinutes: requireNonNegativeNumber(raw.memory?.dreamIdleMinutes ?? DEFAULT_MEMORY_CONFIG.dreamIdleMinutes, "memory.dreamIdleMinutes"),
|
|
1175
1197
|
autoCompactThreshold: requireCompactThreshold(raw.memory?.autoCompactThreshold ?? DEFAULT_MEMORY_CONFIG.autoCompactThreshold)
|
|
1176
1198
|
});
|
|
1199
|
+
loadRuntime = (raw) => ({
|
|
1200
|
+
tokenSavingRtk: raw.runtime?.tokenSavingRtk ?? DEFAULT_RUNTIME_CONFIG.tokenSavingRtk
|
|
1201
|
+
});
|
|
1177
1202
|
loadExtensions = (raw) => {
|
|
1178
1203
|
const extensions = {};
|
|
1179
1204
|
for (const [extensionId, extension] of Object.entries(raw.extensions)) {
|
|
@@ -1336,21 +1361,25 @@ var init_config = __esm({
|
|
|
1336
1361
|
};
|
|
1337
1362
|
};
|
|
1338
1363
|
readConfigText = async (options) => {
|
|
1339
|
-
const
|
|
1364
|
+
const userPath = configPathForDevice(options);
|
|
1340
1365
|
try {
|
|
1341
|
-
return await readFile3(
|
|
1342
|
-
} catch {
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
throw new Error(`Scorel config not found: ${projectPath}`);
|
|
1346
|
-
}
|
|
1347
|
-
const userPath = scorelUserConfigPath(home);
|
|
1348
|
-
try {
|
|
1349
|
-
return await readFile3(userPath, "utf8");
|
|
1350
|
-
} catch {
|
|
1351
|
-
throw new Error(`Scorel config not found: ${projectPath} or ${userPath}`);
|
|
1366
|
+
return await readFile3(userPath, "utf8");
|
|
1367
|
+
} catch (cause) {
|
|
1368
|
+
if (isNodeErrorCode(cause, "ENOENT")) {
|
|
1369
|
+
throw new Error(`Scorel config not found: ${userPath}`);
|
|
1352
1370
|
}
|
|
1371
|
+
throw cause;
|
|
1372
|
+
}
|
|
1373
|
+
};
|
|
1374
|
+
configPathForDevice = (options) => {
|
|
1375
|
+
if (options.scorelHomeDir) {
|
|
1376
|
+
return join4(options.scorelHomeDir, "config.toml");
|
|
1377
|
+
}
|
|
1378
|
+
const home = options.homeDir ?? process.env.HOME;
|
|
1379
|
+
if (!home) {
|
|
1380
|
+
throw new Error("Scorel config not found: HOME is not set");
|
|
1353
1381
|
}
|
|
1382
|
+
return scorelUserConfigPath(home);
|
|
1354
1383
|
};
|
|
1355
1384
|
parseToml = (text) => {
|
|
1356
1385
|
const result = emptyRawConfig();
|
|
@@ -1453,6 +1482,12 @@ var init_config = __esm({
|
|
|
1453
1482
|
lines.push(`autoCompactThreshold = ${memory.autoCompactThreshold}`);
|
|
1454
1483
|
lines.push("");
|
|
1455
1484
|
}
|
|
1485
|
+
if (raw.runtime) {
|
|
1486
|
+
const runtime = loadRuntime(raw);
|
|
1487
|
+
lines.push("[runtime]");
|
|
1488
|
+
lines.push(`tokenSavingRtk = ${runtime.tokenSavingRtk}`);
|
|
1489
|
+
lines.push("");
|
|
1490
|
+
}
|
|
1456
1491
|
for (const [extensionId, extension] of Object.entries(raw.extensions).sort(([left], [right]) => left.localeCompare(right))) {
|
|
1457
1492
|
lines.push(`[extensions.${extensionId}]`);
|
|
1458
1493
|
lines.push(`enabled = ${extension.enabled === true}`);
|
|
@@ -1584,6 +1619,9 @@ var init_config = __esm({
|
|
|
1584
1619
|
if (section2 === "memory") {
|
|
1585
1620
|
return { kind: "memory" };
|
|
1586
1621
|
}
|
|
1622
|
+
if (section2 === "runtime") {
|
|
1623
|
+
return { kind: "runtime" };
|
|
1624
|
+
}
|
|
1587
1625
|
const extensionConfigMatch = /^extensions\.([A-Za-z0-9_-]+)\.config$/.exec(section2);
|
|
1588
1626
|
if (extensionConfigMatch?.[1]) {
|
|
1589
1627
|
return { kind: "extensionConfig", id: extensionConfigMatch[1] };
|
|
@@ -1606,6 +1644,8 @@ var init_config = __esm({
|
|
|
1606
1644
|
config.modelProfile.roles ??= {};
|
|
1607
1645
|
} else if (section2.kind === "memory") {
|
|
1608
1646
|
config.memory ??= {};
|
|
1647
|
+
} else if (section2.kind === "runtime") {
|
|
1648
|
+
config.runtime ??= {};
|
|
1609
1649
|
} else if (section2.kind === "extension") {
|
|
1610
1650
|
config.extensions[section2.id] ??= {};
|
|
1611
1651
|
} else if (section2.kind === "extensionConfig") {
|
|
@@ -1631,6 +1671,9 @@ var init_config = __esm({
|
|
|
1631
1671
|
} else if (section2.kind === "memory") {
|
|
1632
1672
|
config.memory ??= {};
|
|
1633
1673
|
setValue(config.memory, key, value);
|
|
1674
|
+
} else if (section2.kind === "runtime") {
|
|
1675
|
+
config.runtime ??= {};
|
|
1676
|
+
setValue(config.runtime, key, value);
|
|
1634
1677
|
} else if (section2.kind === "extension") {
|
|
1635
1678
|
config.extensions[section2.id] ??= {};
|
|
1636
1679
|
setValue(config.extensions[section2.id], key, value);
|
|
@@ -1684,6 +1727,7 @@ var init_config = __esm({
|
|
|
1684
1727
|
};
|
|
1685
1728
|
tomlString = (value) => `"${value.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
|
|
1686
1729
|
renderTomlValue = (value) => typeof value === "string" ? tomlString(value) : String(value);
|
|
1730
|
+
isNodeErrorCode = (cause, code) => typeof cause === "object" && cause !== null && "code" in cause && cause.code === code;
|
|
1687
1731
|
requireModelRole = (value, role, models) => {
|
|
1688
1732
|
const modelId = requireString(value, `model_profile.roles.${role}`);
|
|
1689
1733
|
if (!models[modelId]) {
|
|
@@ -1699,9 +1743,10 @@ var init_config = __esm({
|
|
|
1699
1743
|
import { createHash, randomUUID as randomUUID2 } from "node:crypto";
|
|
1700
1744
|
import { execFile } from "node:child_process";
|
|
1701
1745
|
import { mkdir as mkdir2, readFile as readFile4, rename as rename2, rm, stat as stat3, writeFile as writeFile2 } from "node:fs/promises";
|
|
1702
|
-
import {
|
|
1746
|
+
import { userInfo } from "node:os";
|
|
1747
|
+
import { basename as basename2, dirname as dirname3, extname, isAbsolute, relative, resolve } from "node:path";
|
|
1703
1748
|
import { promisify } from "node:util";
|
|
1704
|
-
var execFileAsync, DEFAULT_SEARCH_LIMIT, DEFAULT_GREP_LIMIT, DEFAULT_READ_LIMIT, DEFAULT_CONTEXT_WINDOW, READ_TOKEN_BUDGET_RATIO, FULL_READ_TOKEN_BUDGET_RATIO, createCodingTools, parseReadArgs, parseWriteArgs, parseEditArgs, parseBashArgs, parseGlobArgs, parseGrepArgs, parseTodoWriteArgs, parseTodoItem, expectRecord, expectPath, expectString, optionalString, optionalNumber, optionalBoolean, snapshotFile, sameSnapshot, exists, isWithin, linesOf, IMAGE_EXTENSIONS, DOCUMENT_EXTENSIONS, BINARY_EXTENSIONS, assertReadableFileKind, assertTextBuffer, selectCompleteLinesWithinBudget, estimateTokens, renderReadLines, readTokenBudget, completeRanges, hasCompleteCoverage, mergeRanges, countOccurrences, atomicWriteFile, bashResult, truncate, textResult, byteLength, isTimeoutError, isExecError, runRipgrep, splitOutput, vcsExcludes, grepArgs, splitGlobPatterns, paginate, toWorkspaceRelative, relativizeGrepLine, relativizeCountLine, sortPathsByMtime, formatPaginatedText, formatLimitSuffix, parseCountLines;
|
|
1749
|
+
var execFileAsync, DEFAULT_SEARCH_LIMIT, DEFAULT_GREP_LIMIT, DEFAULT_READ_LIMIT, DEFAULT_CONTEXT_WINDOW, READ_TOKEN_BUDGET_RATIO, FULL_READ_TOKEN_BUDGET_RATIO, createCodingTools, parseReadArgs, parseWriteArgs, parseEditArgs, parseBashArgs, parseGlobArgs, parseGrepArgs, parseTodoWriteArgs, parseTodoItem, expectRecord, expectPath, expectString, optionalString, optionalNumber, optionalBoolean, snapshotFile, sameSnapshot, exists, isWithin, linesOf, IMAGE_EXTENSIONS, DOCUMENT_EXTENSIONS, BINARY_EXTENSIONS, assertReadableFileKind, assertTextBuffer, selectCompleteLinesWithinBudget, estimateTokens, renderReadLines, readTokenBudget, completeRanges, hasCompleteCoverage, mergeRanges, countOccurrences, atomicWriteFile, bashResult, resolveDefaultShell, resolveRtkCommand, rtkRewriteResult, executableRewriteCommand, readRtkGain, rtkSavedTokenDelta, withRtkSavings, nonNegativeInteger, isRecord3, shellQuote, shellCommandArgs, userShell, truncate, textResult, byteLength, isTimeoutError, isExecError, runRipgrep, splitOutput, vcsExcludes, grepArgs, splitGlobPatterns, paginate, toWorkspaceRelative, relativizeGrepLine, relativizeCountLine, sortPathsByMtime, formatPaginatedText, formatLimitSuffix, parseCountLines;
|
|
1705
1750
|
var init_coding_tools = __esm({
|
|
1706
1751
|
"packages/core/src/tools/coding-tools.ts"() {
|
|
1707
1752
|
"use strict";
|
|
@@ -1721,6 +1766,7 @@ var init_coding_tools = __esm({
|
|
|
1721
1766
|
const maxOutputBytes = options.maxOutputBytes ?? 16e3;
|
|
1722
1767
|
const normalReadTokens = options.maxReadTokens ?? readTokenBudget(options.contextWindow, READ_TOKEN_BUDGET_RATIO);
|
|
1723
1768
|
const fullReadTokens = options.maxReadTokens ?? readTokenBudget(options.contextWindow, FULL_READ_TOKEN_BUDGET_RATIO);
|
|
1769
|
+
const defaultShell = resolveDefaultShell(options.defaultShell);
|
|
1724
1770
|
const resolveWorkspacePath = (input) => {
|
|
1725
1771
|
if (input.length === 0) {
|
|
1726
1772
|
throw new Error("path must not be empty");
|
|
@@ -1873,25 +1919,52 @@ String: ${input.old_string}`
|
|
|
1873
1919
|
const commandCwd = input.cwd ? resolveWorkspacePath(input.cwd) : root;
|
|
1874
1920
|
const timeoutMs = Math.min(input.timeoutMs ?? defaultTimeoutMs, maxTimeoutMs);
|
|
1875
1921
|
const outputLimit = input.maxOutputBytes ?? maxOutputBytes;
|
|
1922
|
+
const rtk = options.tokenSaving?.rtk;
|
|
1923
|
+
const rtkCommand = await resolveRtkCommand(rtk, input.command);
|
|
1924
|
+
const command = rtkCommand.rewrittenCommand ?? input.command;
|
|
1925
|
+
const executionCommand = rtkCommand.executionCommand ?? input.command;
|
|
1926
|
+
const executable = defaultShell;
|
|
1927
|
+
const argv = shellCommandArgs(defaultShell, executionCommand);
|
|
1928
|
+
const rtkGainBefore = rtkCommand.applied && rtk?.executable ? await readRtkGain(rtk.executable, commandCwd) : void 0;
|
|
1929
|
+
const rtkResult = {
|
|
1930
|
+
enabled: rtk?.enabled === true,
|
|
1931
|
+
applied: rtkCommand.applied,
|
|
1932
|
+
...rtk?.executable ? { executable: rtk.executable } : {},
|
|
1933
|
+
...rtkCommand.rewrittenCommand ? { rewrittenCommand: rtkCommand.rewrittenCommand } : {}
|
|
1934
|
+
};
|
|
1876
1935
|
try {
|
|
1877
|
-
const result = await execFileAsync(
|
|
1936
|
+
const result = await execFileAsync(executable, argv, {
|
|
1878
1937
|
cwd: commandCwd,
|
|
1879
1938
|
timeout: timeoutMs,
|
|
1880
1939
|
signal,
|
|
1881
1940
|
maxBuffer: Math.max(outputLimit * 4, 1024 * 1024)
|
|
1882
1941
|
});
|
|
1883
|
-
|
|
1942
|
+
const rtkSavedTokens = rtk?.executable ? await rtkSavedTokenDelta(rtk.executable, commandCwd, rtkGainBefore) : void 0;
|
|
1943
|
+
return bashResult({
|
|
1944
|
+
exitCode: 0,
|
|
1945
|
+
stdout: result.stdout,
|
|
1946
|
+
stderr: result.stderr,
|
|
1947
|
+
cwd: commandCwd,
|
|
1948
|
+
outputLimit,
|
|
1949
|
+
shell: defaultShell,
|
|
1950
|
+
command,
|
|
1951
|
+
rtk: withRtkSavings(rtkResult, rtkSavedTokens)
|
|
1952
|
+
});
|
|
1884
1953
|
} catch (cause) {
|
|
1885
1954
|
if (isTimeoutError(cause)) {
|
|
1886
1955
|
throw new Error(`Bash command timed out after ${timeoutMs}ms`);
|
|
1887
1956
|
}
|
|
1888
1957
|
if (isExecError(cause)) {
|
|
1958
|
+
const rtkSavedTokens = rtk?.executable ? await rtkSavedTokenDelta(rtk.executable, commandCwd, rtkGainBefore) : void 0;
|
|
1889
1959
|
return bashResult({
|
|
1890
1960
|
exitCode: typeof cause.code === "number" ? cause.code : 1,
|
|
1891
1961
|
stdout: String(cause.stdout ?? ""),
|
|
1892
1962
|
stderr: String(cause.stderr ?? cause.message),
|
|
1893
1963
|
cwd: commandCwd,
|
|
1894
|
-
outputLimit
|
|
1964
|
+
outputLimit,
|
|
1965
|
+
shell: defaultShell,
|
|
1966
|
+
command,
|
|
1967
|
+
rtk: withRtkSavings(rtkResult, rtkSavedTokens)
|
|
1895
1968
|
});
|
|
1896
1969
|
}
|
|
1897
1970
|
throw cause;
|
|
@@ -1905,7 +1978,7 @@ String: ${input.old_string}`
|
|
|
1905
1978
|
const input = parseGlobArgs(args);
|
|
1906
1979
|
const limit = input.head_limit ?? DEFAULT_SEARCH_LIMIT;
|
|
1907
1980
|
const offset = input.offset ?? 0;
|
|
1908
|
-
const all = await runRipgrep(["--files", "--hidden", "--glob", input.pattern, ...vcsExcludes()], workspaceTarget(input.path), root, signal);
|
|
1981
|
+
const all = (await runRipgrep(["--files", "--hidden", "--glob", input.pattern, ...vcsExcludes()], workspaceTarget(input.path), root, signal)).sort((left, right) => toWorkspaceRelative(root)(left).localeCompare(toWorkspaceRelative(root)(right)));
|
|
1909
1982
|
const selected = paginate(all, limit, offset);
|
|
1910
1983
|
const text = selected.items.map(toWorkspaceRelative(root)).join("\n");
|
|
1911
1984
|
return textResult(text || "No files found", {
|
|
@@ -2266,9 +2339,96 @@ ${stdout}
|
|
|
2266
2339
|
stderr:
|
|
2267
2340
|
${stderr}`, {
|
|
2268
2341
|
exitCode: input.exitCode,
|
|
2269
|
-
cwd: input.cwd
|
|
2342
|
+
cwd: input.cwd,
|
|
2343
|
+
...input.shell ? { shell: input.shell } : {},
|
|
2344
|
+
...input.command ? { command: input.command } : {},
|
|
2345
|
+
...input.rtk ? {
|
|
2346
|
+
rtk: {
|
|
2347
|
+
...input.rtk,
|
|
2348
|
+
estimatedOutputTokens: estimateTokens(`${stdout}
|
|
2349
|
+
${stderr}`)
|
|
2350
|
+
}
|
|
2351
|
+
} : {}
|
|
2270
2352
|
});
|
|
2271
2353
|
};
|
|
2354
|
+
resolveDefaultShell = (input) => {
|
|
2355
|
+
const shell = input || process.env.SHELL || userShell() || "/bin/sh";
|
|
2356
|
+
return shell.trim() || "/bin/sh";
|
|
2357
|
+
};
|
|
2358
|
+
resolveRtkCommand = async (rtk, command) => {
|
|
2359
|
+
if (rtk?.enabled !== true || typeof rtk.executable !== "string" || rtk.executable.length === 0) {
|
|
2360
|
+
return { applied: false };
|
|
2361
|
+
}
|
|
2362
|
+
try {
|
|
2363
|
+
const result = await execFileAsync(rtk.executable, ["rewrite", command], {
|
|
2364
|
+
timeout: 5e3,
|
|
2365
|
+
maxBuffer: 1024 * 1024
|
|
2366
|
+
});
|
|
2367
|
+
return rtkRewriteResult(result.stdout, rtk.executable);
|
|
2368
|
+
} catch (cause) {
|
|
2369
|
+
if (isExecError(cause) && typeof cause.stdout === "string") {
|
|
2370
|
+
return rtkRewriteResult(cause.stdout, rtk.executable);
|
|
2371
|
+
}
|
|
2372
|
+
return { applied: false };
|
|
2373
|
+
}
|
|
2374
|
+
};
|
|
2375
|
+
rtkRewriteResult = (stdout, executable) => {
|
|
2376
|
+
const rewrittenCommand = stdout.trim();
|
|
2377
|
+
return rewrittenCommand ? { applied: true, rewrittenCommand, executionCommand: executableRewriteCommand(rewrittenCommand, executable) } : { applied: false };
|
|
2378
|
+
};
|
|
2379
|
+
executableRewriteCommand = (command, executable) => command.replace(/^rtk(?=\s|$)/, shellQuote(executable));
|
|
2380
|
+
readRtkGain = async (rtkExecutable, cwd) => {
|
|
2381
|
+
try {
|
|
2382
|
+
const { stdout } = await execFileAsync(rtkExecutable, ["gain", "--project", "--format", "json"], {
|
|
2383
|
+
cwd,
|
|
2384
|
+
timeout: 5e3,
|
|
2385
|
+
maxBuffer: 5e6
|
|
2386
|
+
});
|
|
2387
|
+
const parsed = JSON.parse(stdout);
|
|
2388
|
+
if (!isRecord3(parsed) || !isRecord3(parsed.summary)) {
|
|
2389
|
+
return void 0;
|
|
2390
|
+
}
|
|
2391
|
+
return { savedTokens: nonNegativeInteger(parsed.summary.total_saved) };
|
|
2392
|
+
} catch {
|
|
2393
|
+
return void 0;
|
|
2394
|
+
}
|
|
2395
|
+
};
|
|
2396
|
+
rtkSavedTokenDelta = async (rtkExecutable, cwd, before) => {
|
|
2397
|
+
if (!before) {
|
|
2398
|
+
return void 0;
|
|
2399
|
+
}
|
|
2400
|
+
const after = await readRtkGain(rtkExecutable, cwd);
|
|
2401
|
+
if (!after) {
|
|
2402
|
+
return void 0;
|
|
2403
|
+
}
|
|
2404
|
+
return Math.max(0, after.savedTokens - before.savedTokens);
|
|
2405
|
+
};
|
|
2406
|
+
withRtkSavings = (rtk, savedTokens) => ({
|
|
2407
|
+
...rtk,
|
|
2408
|
+
...rtk.applied && savedTokens !== void 0 ? { estimatedSavedTokens: savedTokens } : {}
|
|
2409
|
+
});
|
|
2410
|
+
nonNegativeInteger = (value) => {
|
|
2411
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
2412
|
+
return 0;
|
|
2413
|
+
}
|
|
2414
|
+
return Math.floor(value);
|
|
2415
|
+
};
|
|
2416
|
+
isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2417
|
+
shellQuote = (value) => `'${value.replace(/'/g, "'\\''")}'`;
|
|
2418
|
+
shellCommandArgs = (shell, command) => {
|
|
2419
|
+
const name = basename2(shell).toLowerCase();
|
|
2420
|
+
if (name === "csh" || name === "tcsh" || name === "fish") {
|
|
2421
|
+
return ["-c", command];
|
|
2422
|
+
}
|
|
2423
|
+
return ["-lc", command];
|
|
2424
|
+
};
|
|
2425
|
+
userShell = () => {
|
|
2426
|
+
try {
|
|
2427
|
+
return userInfo().shell ?? void 0;
|
|
2428
|
+
} catch {
|
|
2429
|
+
return void 0;
|
|
2430
|
+
}
|
|
2431
|
+
};
|
|
2272
2432
|
truncate = (value, maxBytes, label) => {
|
|
2273
2433
|
const bytes = Buffer.byteLength(value);
|
|
2274
2434
|
if (bytes <= maxBytes) {
|
|
@@ -2452,7 +2612,7 @@ var init_tools = __esm({
|
|
|
2452
2612
|
});
|
|
2453
2613
|
|
|
2454
2614
|
// packages/core/src/channel/index.ts
|
|
2455
|
-
var createSendChannelMessageTool, parseSendChannelMessageInput, parseAttachments, optionalString2,
|
|
2615
|
+
var createSendChannelMessageTool, parseSendChannelMessageInput, parseAttachments, optionalString2, isRecord4;
|
|
2456
2616
|
var init_channel = __esm({
|
|
2457
2617
|
"packages/core/src/channel/index.ts"() {
|
|
2458
2618
|
"use strict";
|
|
@@ -2470,7 +2630,7 @@ var init_channel = __esm({
|
|
|
2470
2630
|
}
|
|
2471
2631
|
});
|
|
2472
2632
|
parseSendChannelMessageInput = (value) => {
|
|
2473
|
-
if (!
|
|
2633
|
+
if (!isRecord4(value)) {
|
|
2474
2634
|
throw new Error("SendChannelMessage args must be an object");
|
|
2475
2635
|
}
|
|
2476
2636
|
const text = typeof value.text === "string" && value.text.trim().length > 0 ? value.text : void 0;
|
|
@@ -2499,7 +2659,7 @@ var init_channel = __esm({
|
|
|
2499
2659
|
throw new Error("SendChannelMessage.attachments must be an array");
|
|
2500
2660
|
}
|
|
2501
2661
|
return value.map((item, index) => {
|
|
2502
|
-
if (!
|
|
2662
|
+
if (!isRecord4(item)) {
|
|
2503
2663
|
throw new Error(`SendChannelMessage.attachments.${index} must be an object`);
|
|
2504
2664
|
}
|
|
2505
2665
|
if (item.type !== "image" && item.type !== "file") {
|
|
@@ -2528,14 +2688,14 @@ var init_channel = __esm({
|
|
|
2528
2688
|
}
|
|
2529
2689
|
return value;
|
|
2530
2690
|
};
|
|
2531
|
-
|
|
2691
|
+
isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2532
2692
|
}
|
|
2533
2693
|
});
|
|
2534
2694
|
|
|
2535
2695
|
// packages/core/src/extensions/index.ts
|
|
2536
2696
|
import { readFile as readFile5 } from "node:fs/promises";
|
|
2537
2697
|
import { dirname as dirname4, resolve as resolve2 } from "node:path";
|
|
2538
|
-
var loadExtensionManifest, parseExtensionManifest, requireString2, requireIdentifier2, requireKind, requireRelativePath, optionalRelativePaths,
|
|
2698
|
+
var loadExtensionManifest, parseExtensionManifest, requireString2, requireIdentifier2, requireKind, requireRelativePath, optionalRelativePaths, isRecord5;
|
|
2539
2699
|
var init_extensions = __esm({
|
|
2540
2700
|
"packages/core/src/extensions/index.ts"() {
|
|
2541
2701
|
"use strict";
|
|
@@ -2548,7 +2708,7 @@ var init_extensions = __esm({
|
|
|
2548
2708
|
const message = cause instanceof Error ? cause.message : String(cause);
|
|
2549
2709
|
throw new Error(`Invalid extension manifest JSON at ${manifestPath}: ${message}`);
|
|
2550
2710
|
}
|
|
2551
|
-
if (!
|
|
2711
|
+
if (!isRecord5(value)) {
|
|
2552
2712
|
throw new Error(`Extension manifest at ${manifestPath} must be an object`);
|
|
2553
2713
|
}
|
|
2554
2714
|
const rootDir = dirname4(resolve2(manifestPath));
|
|
@@ -2604,7 +2764,7 @@ var init_extensions = __esm({
|
|
|
2604
2764
|
}
|
|
2605
2765
|
return value.map((item, index) => requireRelativePath(item, `${name}.${index}`, manifestPath));
|
|
2606
2766
|
};
|
|
2607
|
-
|
|
2767
|
+
isRecord5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2608
2768
|
}
|
|
2609
2769
|
});
|
|
2610
2770
|
|
|
@@ -2613,7 +2773,7 @@ import { existsSync } from "node:fs";
|
|
|
2613
2773
|
import { readdir as readdir4, readFile as readFile6 } from "node:fs/promises";
|
|
2614
2774
|
import { homedir as homedir2, platform, release } from "node:os";
|
|
2615
2775
|
import { dirname as dirname5, join as join5, resolve as resolve3 } from "node:path";
|
|
2616
|
-
var BASELINE_PROMPT, buildInstructionSnapshot, renderSystemPrompt, section, discoverAgentsSources, projectAgentsPaths, findGitRoot, renderAgentsBlock, renderWorkspaceBlock, renderEnvironmentBlock, renderTimeBlock,
|
|
2776
|
+
var BASELINE_PROMPT, buildInstructionSnapshot, renderSystemPrompt, section, discoverAgentsSources, projectAgentsPaths, findGitRoot, renderAgentsBlock, renderWorkspaceBlock, renderEnvironmentBlock, renderTimeBlock, isNodeErrorCode2;
|
|
2617
2777
|
var init_instructions = __esm({
|
|
2618
2778
|
"packages/core/src/instructions/index.ts"() {
|
|
2619
2779
|
"use strict";
|
|
@@ -2677,7 +2837,7 @@ var init_instructions = __esm({
|
|
|
2677
2837
|
content
|
|
2678
2838
|
});
|
|
2679
2839
|
} catch (cause) {
|
|
2680
|
-
if (!
|
|
2840
|
+
if (!isNodeErrorCode2(cause, "ENOENT") && !isNodeErrorCode2(cause, "ENOTDIR")) {
|
|
2681
2841
|
throw cause;
|
|
2682
2842
|
}
|
|
2683
2843
|
}
|
|
@@ -2740,7 +2900,7 @@ var init_instructions = __esm({
|
|
|
2740
2900
|
};
|
|
2741
2901
|
renderEnvironmentBlock = (env) => [`Platform: ${platform()} ${release()}`, `Shell: ${env.SHELL ?? "unknown"}`].join("\n");
|
|
2742
2902
|
renderTimeBlock = (timestamp) => [`Session started at: ${new Date(timestamp).toISOString()}`, `Timezone: ${Intl.DateTimeFormat().resolvedOptions().timeZone}`].join("\n");
|
|
2743
|
-
|
|
2903
|
+
isNodeErrorCode2 = (cause, code) => cause instanceof Error && "code" in cause && cause.code === code;
|
|
2744
2904
|
}
|
|
2745
2905
|
});
|
|
2746
2906
|
|
|
@@ -2748,7 +2908,7 @@ var init_instructions = __esm({
|
|
|
2748
2908
|
import { appendFile, mkdir as mkdir3, readFile as readFile7, writeFile as writeFile3 } from "node:fs/promises";
|
|
2749
2909
|
import { homedir as homedir3 } from "node:os";
|
|
2750
2910
|
import { join as join6 } from "node:path";
|
|
2751
|
-
var memoryDate, scorelMemoryPaths, scorelSessionMemoryPaths, buildMemoryContext, renderMemoryHarness, appendDailyEntry, createAppendDailyTool, renderDailyEntry, readMemoryDreamState, writeMemoryDreamState, readSessionMemory, writeSessionMemory, renderSessionMemory, ensureMemoryFiles, ensureFile, readOptional, trimForContext, compactLine, renderList, renderBullets, normalizeMarkdownFile, parseAppendDailyInput, validateAppendDailyInput, isLowSignalSummary, containsNormalizedDailyEntry, normalizeDailyText, requireString3, optionalStringArray, optionalNumber2, optionalString3, parseLastFailure,
|
|
2911
|
+
var memoryDate, scorelMemoryPaths, scorelSessionMemoryPaths, buildMemoryContext, renderMemoryHarness, appendDailyEntry, createAppendDailyTool, renderDailyEntry, readMemoryDreamState, writeMemoryDreamState, readSessionMemory, writeSessionMemory, renderSessionMemory, ensureMemoryFiles, ensureFile, readOptional, trimForContext, compactLine, renderList, renderBullets, normalizeMarkdownFile, parseAppendDailyInput, validateAppendDailyInput, isLowSignalSummary, containsNormalizedDailyEntry, normalizeDailyText, requireString3, optionalStringArray, optionalNumber2, optionalString3, parseLastFailure, isRecord6, safeProjectId, isNodeErrorCode3;
|
|
2752
2912
|
var init_memory = __esm({
|
|
2753
2913
|
"packages/core/src/memory/index.ts"() {
|
|
2754
2914
|
"use strict";
|
|
@@ -2950,7 +3110,7 @@ var init_memory = __esm({
|
|
|
2950
3110
|
try {
|
|
2951
3111
|
await writeFile3(path, content, { encoding: "utf8", flag: "wx", mode: 384 });
|
|
2952
3112
|
} catch (cause) {
|
|
2953
|
-
if (!
|
|
3113
|
+
if (!isNodeErrorCode3(cause, "EEXIST")) {
|
|
2954
3114
|
throw cause;
|
|
2955
3115
|
}
|
|
2956
3116
|
}
|
|
@@ -2959,7 +3119,7 @@ var init_memory = __esm({
|
|
|
2959
3119
|
try {
|
|
2960
3120
|
return await readFile7(path, "utf8");
|
|
2961
3121
|
} catch (cause) {
|
|
2962
|
-
if (
|
|
3122
|
+
if (isNodeErrorCode3(cause, "ENOENT")) {
|
|
2963
3123
|
return "";
|
|
2964
3124
|
}
|
|
2965
3125
|
throw cause;
|
|
@@ -2980,7 +3140,7 @@ var init_memory = __esm({
|
|
|
2980
3140
|
normalizeMarkdownFile = (value) => `${value.trimEnd()}
|
|
2981
3141
|
`;
|
|
2982
3142
|
parseAppendDailyInput = (value) => {
|
|
2983
|
-
if (!
|
|
3143
|
+
if (!isRecord6(value)) {
|
|
2984
3144
|
throw new Error("AppendDaily args must be an object");
|
|
2985
3145
|
}
|
|
2986
3146
|
const summary = requireString3(value.summary, "summary");
|
|
@@ -3046,19 +3206,19 @@ var init_memory = __esm({
|
|
|
3046
3206
|
optionalNumber2 = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
3047
3207
|
optionalString3 = (value) => typeof value === "string" && value.trim() ? value : void 0;
|
|
3048
3208
|
parseLastFailure = (value) => {
|
|
3049
|
-
if (!
|
|
3209
|
+
if (!isRecord6(value)) return void 0;
|
|
3050
3210
|
const at = optionalNumber2(value.at);
|
|
3051
3211
|
const message = optionalString3(value.message);
|
|
3052
3212
|
return at !== void 0 && message ? { at, message } : void 0;
|
|
3053
3213
|
};
|
|
3054
|
-
|
|
3214
|
+
isRecord6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3055
3215
|
safeProjectId = (projectId) => {
|
|
3056
3216
|
if (!/^[A-Za-z0-9_-]+$/.test(projectId)) {
|
|
3057
3217
|
throw new Error("projectId must contain only letters, numbers, underscores, or hyphens");
|
|
3058
3218
|
}
|
|
3059
3219
|
return projectId;
|
|
3060
3220
|
};
|
|
3061
|
-
|
|
3221
|
+
isNodeErrorCode3 = (cause, code) => cause instanceof Error && "code" in cause && cause.code === code;
|
|
3062
3222
|
}
|
|
3063
3223
|
});
|
|
3064
3224
|
|
|
@@ -3347,7 +3507,7 @@ var init_pi_ai = __esm({
|
|
|
3347
3507
|
});
|
|
3348
3508
|
|
|
3349
3509
|
// packages/core/src/runtime/index.ts
|
|
3350
|
-
var ScorelRuntime, normalizeAssistantMessage, isAssistantMessage, partialAssistantMessage;
|
|
3510
|
+
var ScorelRuntime, toolResultForContext, normalizeAssistantMessage, isAssistantMessage, partialAssistantMessage;
|
|
3351
3511
|
var init_runtime = __esm({
|
|
3352
3512
|
"packages/core/src/runtime/index.ts"() {
|
|
3353
3513
|
"use strict";
|
|
@@ -3496,7 +3656,7 @@ var init_runtime = __esm({
|
|
|
3496
3656
|
type: "tool_result",
|
|
3497
3657
|
toolCallId: toolCall.toolCallId,
|
|
3498
3658
|
toolName: toolCall.toolName,
|
|
3499
|
-
result,
|
|
3659
|
+
result: toolResultForContext(result),
|
|
3500
3660
|
isError
|
|
3501
3661
|
};
|
|
3502
3662
|
return {
|
|
@@ -3505,6 +3665,9 @@ var init_runtime = __esm({
|
|
|
3505
3665
|
};
|
|
3506
3666
|
}
|
|
3507
3667
|
};
|
|
3668
|
+
toolResultForContext = (result) => ({
|
|
3669
|
+
content: result.content
|
|
3670
|
+
});
|
|
3508
3671
|
normalizeAssistantMessage = (value, streamed, fallbackStopReason) => {
|
|
3509
3672
|
if (value) {
|
|
3510
3673
|
if (!isAssistantMessage(value)) {
|
|
@@ -3536,7 +3699,7 @@ var init_runtime = __esm({
|
|
|
3536
3699
|
import { appendFile as appendFile2, mkdir as mkdir4, readFile as readFile8, writeFile as writeFile4 } from "node:fs/promises";
|
|
3537
3700
|
import { dirname as dirname6, join as join7 } from "node:path";
|
|
3538
3701
|
function assertTreeEvent(value) {
|
|
3539
|
-
if (!
|
|
3702
|
+
if (!isRecord7(value)) {
|
|
3540
3703
|
throw new SessionStoreError("invalid_event", "Event must be an object");
|
|
3541
3704
|
}
|
|
3542
3705
|
if (value.type === "session_header") {
|
|
@@ -3548,7 +3711,7 @@ function assertTreeEvent(value) {
|
|
|
3548
3711
|
if (typeof value.id !== "string" || value.parentId !== null && typeof value.parentId !== "string" || typeof value.seq !== "number" || typeof value.clientId !== "string" || typeof value.ts !== "number") {
|
|
3549
3712
|
throw new SessionStoreError("invalid_event", "Event is missing required base fields");
|
|
3550
3713
|
}
|
|
3551
|
-
if ((value.type === "user_message" || value.type === "assistant_message" || value.type === "tool_result") && !
|
|
3714
|
+
if ((value.type === "user_message" || value.type === "assistant_message" || value.type === "tool_result") && !isRecord7(value.message)) {
|
|
3552
3715
|
throw new SessionStoreError("invalid_event", "Message event is missing message payload");
|
|
3553
3716
|
}
|
|
3554
3717
|
if (value.type === "session_title_updated" && !isSessionTitleUpdated(value)) {
|
|
@@ -3573,7 +3736,7 @@ function assertTreeEvent(value) {
|
|
|
3573
3736
|
throw new SessionStoreError("invalid_event", "skill_index_delta is missing delta payload");
|
|
3574
3737
|
}
|
|
3575
3738
|
}
|
|
3576
|
-
var SessionStoreError, SessionTree, JsonlSession, sessionFilePath, sessionLogFilePath, createSession, loadSession, buildContext, retainedMessagesBeforeCompact, isRetainedContextStart, parseJsonLine, parseHeader, parseSessionEvent, validateSessionMatch, isConversationEvent, isInstructionSnapshot, isHarnessItem, isCompactEvent, isQueueUpdate, isSessionTitleUpdated, isSkillIndexSnapshot, isSkillIndexDelta, isSkillIndexEntry, appendHarnessItemToContext, appendReminderToToolResult, isToolResultWithContent, renderSystemReminder, compactSummaryMessage, cloneMessage,
|
|
3739
|
+
var SessionStoreError, SessionTree, JsonlSession, sessionFilePath, sessionLogFilePath, createSession, loadSession, buildContext, retainedMessagesBeforeCompact, isRetainedContextStart, parseJsonLine, parseHeader, parseSessionEvent, validateSessionMatch, isConversationEvent, isInstructionSnapshot, isHarnessItem, isCompactEvent, isQueueUpdate, isSessionTitleUpdated, isSkillIndexSnapshot, isSkillIndexDelta, isSkillIndexEntry, appendHarnessItemToContext, appendReminderToToolResult, isToolResultWithContent, renderSystemReminder, compactSummaryMessage, cloneMessage, isRecord7;
|
|
3577
3740
|
var init_session = __esm({
|
|
3578
3741
|
"packages/core/src/session/index.ts"() {
|
|
3579
3742
|
"use strict";
|
|
@@ -3841,13 +4004,13 @@ var init_session = __esm({
|
|
|
3841
4004
|
}
|
|
3842
4005
|
};
|
|
3843
4006
|
parseHeader = (value) => {
|
|
3844
|
-
if (!
|
|
4007
|
+
if (!isRecord7(value)) {
|
|
3845
4008
|
throw new SessionStoreError("invalid_header", "Session header must be an object");
|
|
3846
4009
|
}
|
|
3847
4010
|
if (value.version !== 1 || typeof value.sessionId !== "string" || typeof value.deviceId !== "string") {
|
|
3848
4011
|
throw new SessionStoreError("invalid_header", "Session header is missing required identity fields");
|
|
3849
4012
|
}
|
|
3850
|
-
if (typeof value.createdAt !== "number" || !
|
|
4013
|
+
if (typeof value.createdAt !== "number" || !isRecord7(value.meta)) {
|
|
3851
4014
|
throw new SessionStoreError("invalid_header", "Session header is missing createdAt or meta");
|
|
3852
4015
|
}
|
|
3853
4016
|
if (typeof value.meta.projectId !== "string" || value.meta.projectId.length === 0) {
|
|
@@ -3861,7 +4024,7 @@ var init_session = __esm({
|
|
|
3861
4024
|
return value;
|
|
3862
4025
|
};
|
|
3863
4026
|
validateSessionMatch = (header, value) => {
|
|
3864
|
-
if (!
|
|
4027
|
+
if (!isRecord7(value) || typeof value.sessionId !== "string") {
|
|
3865
4028
|
throw new SessionStoreError("invalid_header", "Event must be an object with a sessionId");
|
|
3866
4029
|
}
|
|
3867
4030
|
if (value.sessionId !== header.sessionId) {
|
|
@@ -3870,24 +4033,24 @@ var init_session = __esm({
|
|
|
3870
4033
|
};
|
|
3871
4034
|
isConversationEvent = (event) => event.type === "user_message" || event.type === "assistant_message" || event.type === "tool_result" || event.type === "harness_item" || event.type === "compact";
|
|
3872
4035
|
isInstructionSnapshot = (value) => {
|
|
3873
|
-
if (!
|
|
4036
|
+
if (!isRecord7(value) || value.version !== 1 || typeof value.cwd !== "string" || !Array.isArray(value.sections)) {
|
|
3874
4037
|
return false;
|
|
3875
4038
|
}
|
|
3876
4039
|
return value.sections.every(
|
|
3877
|
-
(section2) =>
|
|
4040
|
+
(section2) => isRecord7(section2) && typeof section2.kind === "string" && typeof section2.frozenAt === "number" && typeof section2.renderedBlock === "string"
|
|
3878
4041
|
);
|
|
3879
4042
|
};
|
|
3880
|
-
isHarnessItem = (value) =>
|
|
4043
|
+
isHarnessItem = (value) => isRecord7(value) && typeof value.kind === "string" && typeof value.origin === "string" && typeof value.content === "string" && (value.visibility === "display" || value.visibility === "hidden" || value.visibility === "compact");
|
|
3881
4044
|
isCompactEvent = (value) => typeof value.summary === "string" && typeof value.compactedThrough === "string" && typeof value.tokensBefore === "number" && typeof value.tokensAfter === "number" && typeof value.retainedEventCount === "number";
|
|
3882
4045
|
isQueueUpdate = (value) => (value.queue === "follow_up" || value.queue === "steer") && value.operation === "rewrite" && Array.isArray(value.items) && (value.anchorEventId === null || typeof value.anchorEventId === "string") && value.items.every(
|
|
3883
|
-
(item) =>
|
|
4046
|
+
(item) => isRecord7(item) && typeof item.id === "string" && Array.isArray(item.content) && typeof item.createdAt === "number" && typeof item.updatedAt === "number" && typeof item.clientId === "string"
|
|
3884
4047
|
);
|
|
3885
|
-
isSessionTitleUpdated = (value) => typeof value.title === "string" && value.title.length > 0 && (value.source === "model" || value.source === "user") && (value.derivedFrom === void 0 ||
|
|
4048
|
+
isSessionTitleUpdated = (value) => typeof value.title === "string" && value.title.length > 0 && (value.source === "model" || value.source === "user") && (value.derivedFrom === void 0 || isRecord7(value.derivedFrom) && typeof value.derivedFrom.eventId === "string" && typeof value.derivedFrom.seq === "number");
|
|
3886
4049
|
isSkillIndexSnapshot = (value) => (value.anchorEventId === null || typeof value.anchorEventId === "string") && Array.isArray(value.entries) && value.entries.every(isSkillIndexEntry);
|
|
3887
4050
|
isSkillIndexDelta = (value) => (value.anchorEventId === null || typeof value.anchorEventId === "string") && Array.isArray(value.added) && Array.isArray(value.changed) && Array.isArray(value.removed) && value.added.every(isSkillIndexEntry) && value.changed.every(isSkillIndexEntry) && value.removed.every(
|
|
3888
|
-
(item) =>
|
|
4051
|
+
(item) => isRecord7(item) && typeof item.name === "string" && typeof item.previousPath === "string"
|
|
3889
4052
|
);
|
|
3890
|
-
isSkillIndexEntry = (value) =>
|
|
4053
|
+
isSkillIndexEntry = (value) => isRecord7(value) && typeof value.name === "string" && typeof value.path === "string" && (value.scope === "user" || value.scope === "project" || value.scope === "extension") && typeof value.description === "string" && typeof value.mtimeMs === "number" && typeof value.size === "number" && typeof value.contentHash === "string" && typeof value.priority === "number";
|
|
3891
4054
|
appendHarnessItemToContext = (messages, event) => {
|
|
3892
4055
|
const reminder = renderSystemReminder(event.item.content);
|
|
3893
4056
|
const last = messages.at(-1);
|
|
@@ -3924,7 +4087,7 @@ ${reminder}` }]
|
|
|
3924
4087
|
}
|
|
3925
4088
|
return false;
|
|
3926
4089
|
};
|
|
3927
|
-
isToolResultWithContent = (value) =>
|
|
4090
|
+
isToolResultWithContent = (value) => isRecord7(value) && Array.isArray(value.content);
|
|
3928
4091
|
renderSystemReminder = (content) => `<system-reminder>
|
|
3929
4092
|
${content}
|
|
3930
4093
|
</system-reminder>`;
|
|
@@ -3948,21 +4111,20 @@ ${content}
|
|
|
3948
4111
|
cloneMessage = (message) => ({
|
|
3949
4112
|
...message,
|
|
3950
4113
|
content: message.content.map((block) => {
|
|
3951
|
-
if (block.type !== "tool_result" || !
|
|
4114
|
+
if (block.type !== "tool_result" || !isRecord7(block.result)) {
|
|
3952
4115
|
return { ...block };
|
|
3953
4116
|
}
|
|
3954
|
-
const content = Array.isArray(block.result.content) ? { content: block.result.content.map((item) =>
|
|
4117
|
+
const content = Array.isArray(block.result.content) ? { content: block.result.content.map((item) => isRecord7(item) ? { ...item } : item) } : {};
|
|
3955
4118
|
return {
|
|
3956
4119
|
...block,
|
|
3957
4120
|
result: {
|
|
3958
|
-
|
|
3959
|
-
...content
|
|
4121
|
+
content: content.content ?? []
|
|
3960
4122
|
}
|
|
3961
4123
|
};
|
|
3962
4124
|
}),
|
|
3963
4125
|
...message.meta ? { meta: { ...message.meta } } : {}
|
|
3964
4126
|
});
|
|
3965
|
-
|
|
4127
|
+
isRecord7 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3966
4128
|
}
|
|
3967
4129
|
});
|
|
3968
4130
|
|
|
@@ -3972,7 +4134,7 @@ import { existsSync as existsSync2 } from "node:fs";
|
|
|
3972
4134
|
import { readdir as readdir5, readFile as readFile9, stat as stat4 } from "node:fs/promises";
|
|
3973
4135
|
import { homedir as homedir4 } from "node:os";
|
|
3974
4136
|
import { dirname as dirname7, join as join8, resolve as resolve4 } from "node:path";
|
|
3975
|
-
var scanSkillIndex, diffSkillIndex, hasSkillIndexDelta, renderSkillListing, renderSkillDelta, createSkillTool, projectSkillRoots, readSkillEntry, parseSkillMetadata, firstParagraph, parseSkillArgs, findGitRoot2,
|
|
4137
|
+
var scanSkillIndex, diffSkillIndex, hasSkillIndexDelta, renderSkillListing, renderSkillDelta, createSkillTool, projectSkillRoots, readSkillEntry, parseSkillMetadata, firstParagraph, parseSkillArgs, findGitRoot2, isNodeErrorCode4;
|
|
3976
4138
|
var init_skills = __esm({
|
|
3977
4139
|
"packages/core/src/skills/index.ts"() {
|
|
3978
4140
|
"use strict";
|
|
@@ -3995,7 +4157,7 @@ var init_skills = __esm({
|
|
|
3995
4157
|
try {
|
|
3996
4158
|
children = await readdir5(root.path);
|
|
3997
4159
|
} catch (cause) {
|
|
3998
|
-
if (
|
|
4160
|
+
if (isNodeErrorCode4(cause, "ENOENT") || isNodeErrorCode4(cause, "ENOTDIR")) {
|
|
3999
4161
|
continue;
|
|
4000
4162
|
}
|
|
4001
4163
|
throw cause;
|
|
@@ -4108,7 +4270,7 @@ var init_skills = __esm({
|
|
|
4108
4270
|
try {
|
|
4109
4271
|
[fileStat, content] = await Promise.all([stat4(options.skillPath), readFile9(options.skillPath, "utf8")]);
|
|
4110
4272
|
} catch (cause) {
|
|
4111
|
-
if (
|
|
4273
|
+
if (isNodeErrorCode4(cause, "ENOENT") || isNodeErrorCode4(cause, "ENOTDIR")) {
|
|
4112
4274
|
return void 0;
|
|
4113
4275
|
}
|
|
4114
4276
|
throw cause;
|
|
@@ -4188,7 +4350,7 @@ var init_skills = __esm({
|
|
|
4188
4350
|
current = next;
|
|
4189
4351
|
}
|
|
4190
4352
|
};
|
|
4191
|
-
|
|
4353
|
+
isNodeErrorCode4 = (cause, code) => cause instanceof Error && "code" in cause && cause.code === code;
|
|
4192
4354
|
}
|
|
4193
4355
|
});
|
|
4194
4356
|
|
|
@@ -4534,12 +4696,15 @@ var init_host_client = __esm({
|
|
|
4534
4696
|
});
|
|
4535
4697
|
|
|
4536
4698
|
// packages/daemon/src/index.ts
|
|
4699
|
+
import { execFile as execFile2 } from "node:child_process";
|
|
4537
4700
|
import { existsSync as existsSync3 } from "node:fs";
|
|
4538
|
-
import { appendFile as appendFile3, mkdir as mkdir6, readFile as readFile11, readdir as readdir6, rm as rm2, writeFile as writeFile6 } from "node:fs/promises";
|
|
4539
|
-
import {
|
|
4701
|
+
import { appendFile as appendFile3, mkdir as mkdir6, readFile as readFile11, readdir as readdir6, rename as rename3, rm as rm2, writeFile as writeFile6 } from "node:fs/promises";
|
|
4702
|
+
import { userInfo as userInfo2 } from "node:os";
|
|
4703
|
+
import { basename as basename3, dirname as dirname8, join as join10, resolve as resolve5 } from "node:path";
|
|
4540
4704
|
import { pathToFileURL } from "node:url";
|
|
4705
|
+
import { promisify as promisify2 } from "node:util";
|
|
4541
4706
|
import { WebSocketServer } from "ws";
|
|
4542
|
-
var daemonPackageName, SESSION_MEMORY_COMPACT_WAIT_MS, AUTO_COMPACT_RETAINED_EVENTS, localDaemonStateFile, createLocalDaemonState, readLocalDaemonState, removeLocalDaemonState, markDaemonStopped, daemonStateLiveness, defaultIsPidAlive, startRemoteDaemonWebSocketServer, startScorelHostWebSocketServer, closeWebSocketServer, createRealRuntime, ScorelHost, isMissingConfigError, createEmbeddedTransport,
|
|
4707
|
+
var daemonPackageName, SESSION_MEMORY_COMPACT_WAIT_MS, AUTO_COMPACT_RETAINED_EVENTS, execFileAsync2, localDaemonStateFile, createLocalDaemonState, readLocalDaemonState, removeLocalDaemonState, markDaemonStopped, daemonStateLiveness, defaultIsPidAlive, startRemoteDaemonWebSocketServer, startScorelHostWebSocketServer, closeWebSocketServer, createRealRuntime, ScorelHost, isMissingConfigError, createEmbeddedTransport, isNodeErrorCode5, wireErrorCode, hasContinuousCoverage, countContentBlocks, normalizeContent, inputText, assistantText, messageText, estimateScorelMessagesTokens, estimateTextTokens, compactLine2, parseSessionMemoryJson, stringArray, disabledMemorySettings, detectRtk, ensureRtkAvailable, emptyRuntimeStats, readRuntimeStats, writeRuntimeStats, parseRuntimeStats, parseRuntimeStatsBuckets, addRtkSavings, addRuntimeStatsBucket, rtkSavingsFromToolResult, nonNegativeInteger2, resolveDefaultShell2, shellCommandArgs2, userShell2, runtimeChannelContextFromWire, parseQueuedChannelContext, imBindingKey, defaultBuiltinExtensionsDir, runtimeModuleDir, findBuiltinExtensionsDir, isSteerMessage, stripImCommandPrefix, isRecord8, parseMemoryUpdate, normalizeMarkdownFile2, sanitizeSessionTitle, shortStack, formatDiagnosticLine, formatDiagnosticValue;
|
|
4543
4708
|
var init_src4 = __esm({
|
|
4544
4709
|
"packages/daemon/src/index.ts"() {
|
|
4545
4710
|
"use strict";
|
|
@@ -4554,6 +4719,7 @@ var init_src4 = __esm({
|
|
|
4554
4719
|
daemonPackageName = "@scorel/daemon";
|
|
4555
4720
|
SESSION_MEMORY_COMPACT_WAIT_MS = 5e3;
|
|
4556
4721
|
AUTO_COMPACT_RETAINED_EVENTS = 8;
|
|
4722
|
+
execFileAsync2 = promisify2(execFile2);
|
|
4557
4723
|
localDaemonStateFile = (stateDir) => join10(stateDir, "daemon.json");
|
|
4558
4724
|
createLocalDaemonState = async (options) => {
|
|
4559
4725
|
const state = {
|
|
@@ -4765,9 +4931,10 @@ var init_src4 = __esm({
|
|
|
4765
4931
|
}
|
|
4766
4932
|
server.close((error) => error ? reject(error) : resolve7());
|
|
4767
4933
|
});
|
|
4768
|
-
createRealRuntime = (options) => {
|
|
4934
|
+
createRealRuntime = async (options) => {
|
|
4769
4935
|
const selection = resolveModelSelection(options.config, options.modelSelection);
|
|
4770
4936
|
const model = resolvePiAiModel(selection.config);
|
|
4937
|
+
const rtkExecutable = options.rtkExecutable ?? (options.config.runtime.tokenSavingRtk ? (await detectRtk()).executable : void 0);
|
|
4771
4938
|
const runtime = new ScorelRuntime({
|
|
4772
4939
|
provider: createPiAiProvider({
|
|
4773
4940
|
model,
|
|
@@ -4775,7 +4942,16 @@ var init_src4 = __esm({
|
|
|
4775
4942
|
})
|
|
4776
4943
|
});
|
|
4777
4944
|
if (options.includeTools !== false) {
|
|
4778
|
-
for (const tool of createCodingTools({
|
|
4945
|
+
for (const tool of createCodingTools({
|
|
4946
|
+
cwd: options.cwd,
|
|
4947
|
+
contextWindow: model.contextWindow,
|
|
4948
|
+
tokenSaving: {
|
|
4949
|
+
rtk: {
|
|
4950
|
+
enabled: options.config.runtime.tokenSavingRtk,
|
|
4951
|
+
executable: rtkExecutable
|
|
4952
|
+
}
|
|
4953
|
+
}
|
|
4954
|
+
})) {
|
|
4779
4955
|
runtime.registerTool(tool);
|
|
4780
4956
|
}
|
|
4781
4957
|
}
|
|
@@ -4794,6 +4970,8 @@ var init_src4 = __esm({
|
|
|
4794
4970
|
#createRuntime;
|
|
4795
4971
|
#memoryHomeDir;
|
|
4796
4972
|
#onSessionListChanged;
|
|
4973
|
+
#idleShutdownMs;
|
|
4974
|
+
#onIdleShutdown;
|
|
4797
4975
|
#now;
|
|
4798
4976
|
#createId;
|
|
4799
4977
|
#sessions = /* @__PURE__ */ new Map();
|
|
@@ -4805,6 +4983,8 @@ var init_src4 = __esm({
|
|
|
4805
4983
|
#imExtensions = /* @__PURE__ */ new Map();
|
|
4806
4984
|
#imBindings = /* @__PURE__ */ new Map();
|
|
4807
4985
|
#registry;
|
|
4986
|
+
#runtimeStatsQueue = Promise.resolve();
|
|
4987
|
+
#idleShutdownTimer;
|
|
4808
4988
|
#started = false;
|
|
4809
4989
|
constructor(options) {
|
|
4810
4990
|
this.#sessionsDir = options.sessionsDir;
|
|
@@ -4819,6 +4999,8 @@ var init_src4 = __esm({
|
|
|
4819
4999
|
this.#createRuntime = options.createRuntime;
|
|
4820
5000
|
this.#memoryHomeDir = options.memoryHomeDir;
|
|
4821
5001
|
this.#onSessionListChanged = options.onSessionListChanged;
|
|
5002
|
+
this.#idleShutdownMs = options.idleShutdownMs;
|
|
5003
|
+
this.#onIdleShutdown = options.onIdleShutdown;
|
|
4822
5004
|
this.#now = options.now ?? Date.now;
|
|
4823
5005
|
this.#createId = options.createId ?? (() => crypto.randomUUID());
|
|
4824
5006
|
this.#registry = new ProjectRegistry({
|
|
@@ -4833,8 +5015,10 @@ var init_src4 = __esm({
|
|
|
4833
5015
|
await mkdir6(this.#scorelHomeDir, { recursive: true });
|
|
4834
5016
|
await this.#loadImBindings();
|
|
4835
5017
|
await this.#startEnabledImExtensions();
|
|
5018
|
+
this.#scheduleIdleShutdownCheck();
|
|
4836
5019
|
}
|
|
4837
5020
|
async shutdown() {
|
|
5021
|
+
this.#clearIdleShutdownTimer();
|
|
4838
5022
|
for (const schedule of this.#memoryDreams.values()) {
|
|
4839
5023
|
if (schedule.timer) {
|
|
4840
5024
|
clearTimeout(schedule.timer);
|
|
@@ -4849,9 +5033,11 @@ var init_src4 = __esm({
|
|
|
4849
5033
|
this.#assertStarted();
|
|
4850
5034
|
await this.#stopImExtensions();
|
|
4851
5035
|
await this.#startEnabledImExtensions();
|
|
5036
|
+
this.#scheduleIdleShutdownCheck();
|
|
4852
5037
|
}
|
|
4853
5038
|
connect(connection, sessionId) {
|
|
4854
5039
|
this.#assertStarted();
|
|
5040
|
+
this.#clearIdleShutdownTimer();
|
|
4855
5041
|
connection.sessionId = sessionId;
|
|
4856
5042
|
this.#connections.add(connection);
|
|
4857
5043
|
if (sessionId) {
|
|
@@ -4876,6 +5062,7 @@ var init_src4 = __esm({
|
|
|
4876
5062
|
});
|
|
4877
5063
|
}
|
|
4878
5064
|
this.#connections.delete(connection);
|
|
5065
|
+
this.#scheduleIdleShutdownCheck();
|
|
4879
5066
|
}
|
|
4880
5067
|
releaseSessionEventBuffer(sessionId) {
|
|
4881
5068
|
this.#events.delete(sessionId);
|
|
@@ -4896,6 +5083,8 @@ var init_src4 = __esm({
|
|
|
4896
5083
|
return;
|
|
4897
5084
|
}
|
|
4898
5085
|
throw cause;
|
|
5086
|
+
} finally {
|
|
5087
|
+
this.#scheduleIdleShutdownCheck();
|
|
4899
5088
|
}
|
|
4900
5089
|
}
|
|
4901
5090
|
async listDirectories(path) {
|
|
@@ -5004,7 +5193,7 @@ var init_src4 = __esm({
|
|
|
5004
5193
|
break;
|
|
5005
5194
|
}
|
|
5006
5195
|
case "get_memory_settings": {
|
|
5007
|
-
this.#respond(connection, message, { memory: await this.#
|
|
5196
|
+
this.#respond(connection, message, { memory: await this.#memorySettings(message.projectId) });
|
|
5008
5197
|
break;
|
|
5009
5198
|
}
|
|
5010
5199
|
case "get_memory_status": {
|
|
@@ -5015,6 +5204,14 @@ var init_src4 = __esm({
|
|
|
5015
5204
|
this.#respond(connection, message, { memory: await this.#handleUpsertMemorySettings(message) });
|
|
5016
5205
|
break;
|
|
5017
5206
|
}
|
|
5207
|
+
case "get_runtime_settings": {
|
|
5208
|
+
this.#respond(connection, message, { runtime: await this.#runtimeSettings(message.projectId) });
|
|
5209
|
+
break;
|
|
5210
|
+
}
|
|
5211
|
+
case "upsert_runtime_settings": {
|
|
5212
|
+
this.#respond(connection, message, { runtime: await this.#handleUpsertRuntimeSettings(message) });
|
|
5213
|
+
break;
|
|
5214
|
+
}
|
|
5018
5215
|
case "get_extension_settings": {
|
|
5019
5216
|
this.#respond(connection, message, { extension: await this.#extensionSettings(message.extensionId) });
|
|
5020
5217
|
break;
|
|
@@ -5043,6 +5240,39 @@ var init_src4 = __esm({
|
|
|
5043
5240
|
break;
|
|
5044
5241
|
}
|
|
5045
5242
|
}
|
|
5243
|
+
#scheduleIdleShutdownCheck() {
|
|
5244
|
+
this.#clearIdleShutdownTimer();
|
|
5245
|
+
if (!this.#shouldIdleShutdown()) {
|
|
5246
|
+
return;
|
|
5247
|
+
}
|
|
5248
|
+
this.#idleShutdownTimer = setTimeout(() => {
|
|
5249
|
+
this.#idleShutdownTimer = void 0;
|
|
5250
|
+
if (this.#shouldIdleShutdown()) {
|
|
5251
|
+
this.#onIdleShutdown?.();
|
|
5252
|
+
}
|
|
5253
|
+
}, this.#idleShutdownMs);
|
|
5254
|
+
}
|
|
5255
|
+
#clearIdleShutdownTimer() {
|
|
5256
|
+
if (!this.#idleShutdownTimer) {
|
|
5257
|
+
return;
|
|
5258
|
+
}
|
|
5259
|
+
clearTimeout(this.#idleShutdownTimer);
|
|
5260
|
+
this.#idleShutdownTimer = void 0;
|
|
5261
|
+
}
|
|
5262
|
+
#shouldIdleShutdown() {
|
|
5263
|
+
return this.#started && this.#idleShutdownMs !== void 0 && this.#idleShutdownMs > 0 && this.#connections.size === 0 && this.#imExtensions.size === 0 && !this.#hasActiveWork();
|
|
5264
|
+
}
|
|
5265
|
+
#hasActiveWork() {
|
|
5266
|
+
for (const lane of this.#sessions.values()) {
|
|
5267
|
+
if (lane.runtime.running) {
|
|
5268
|
+
return true;
|
|
5269
|
+
}
|
|
5270
|
+
if (lane.session.tree.controlState.queues.follow_up.length > 0 || lane.session.tree.controlState.queues.steer.length > 0) {
|
|
5271
|
+
return true;
|
|
5272
|
+
}
|
|
5273
|
+
}
|
|
5274
|
+
return false;
|
|
5275
|
+
}
|
|
5046
5276
|
async #handleCreateSession(connection, request) {
|
|
5047
5277
|
const sessionId = request.sessionId ?? asSessionId(`ses_${this.#createId()}`);
|
|
5048
5278
|
const project = await this.#resolveProject(sessionId, request.meta.projectId);
|
|
@@ -5059,7 +5289,7 @@ var init_src4 = __esm({
|
|
|
5059
5289
|
try {
|
|
5060
5290
|
lane = await this.#createLane(sessionId, request.meta, project);
|
|
5061
5291
|
} catch (cause) {
|
|
5062
|
-
if (!request.sessionId || !
|
|
5292
|
+
if (!request.sessionId || !isNodeErrorCode5(cause, "EEXIST")) {
|
|
5063
5293
|
throw cause;
|
|
5064
5294
|
}
|
|
5065
5295
|
lane = await this.#getLane(sessionId);
|
|
@@ -5578,6 +5808,18 @@ var init_src4 = __esm({
|
|
|
5578
5808
|
]
|
|
5579
5809
|
}
|
|
5580
5810
|
});
|
|
5811
|
+
const rtkSavings = rtkSavingsFromToolResult(rawEvent.result);
|
|
5812
|
+
if (rtkSavings) {
|
|
5813
|
+
await this.#recordRtkSavings({
|
|
5814
|
+
projectId: lane.project.projectId,
|
|
5815
|
+
sessionId: lane.session.header.sessionId,
|
|
5816
|
+
savings: rtkSavings
|
|
5817
|
+
}).catch(
|
|
5818
|
+
(cause) => this.#appendDiagnostic(lane.session.header.sessionId, "runtime_stats_update_failed", {
|
|
5819
|
+
message: cause instanceof Error ? cause.message : String(cause)
|
|
5820
|
+
})
|
|
5821
|
+
);
|
|
5822
|
+
}
|
|
5581
5823
|
state.parentId = toolResultId;
|
|
5582
5824
|
break;
|
|
5583
5825
|
}
|
|
@@ -6398,7 +6640,7 @@ var init_src4 = __esm({
|
|
|
6398
6640
|
await this.#getLane(sessionId);
|
|
6399
6641
|
return true;
|
|
6400
6642
|
} catch (cause) {
|
|
6401
|
-
if (
|
|
6643
|
+
if (isNodeErrorCode5(cause, "ENOENT")) {
|
|
6402
6644
|
return false;
|
|
6403
6645
|
}
|
|
6404
6646
|
throw cause;
|
|
@@ -6556,7 +6798,7 @@ var init_src4 = __esm({
|
|
|
6556
6798
|
try {
|
|
6557
6799
|
children = await readdir6(root);
|
|
6558
6800
|
} catch (cause) {
|
|
6559
|
-
if (
|
|
6801
|
+
if (isNodeErrorCode5(cause, "ENOENT") || isNodeErrorCode5(cause, "ENOTDIR")) {
|
|
6560
6802
|
continue;
|
|
6561
6803
|
}
|
|
6562
6804
|
throw cause;
|
|
@@ -6666,7 +6908,7 @@ var init_src4 = __esm({
|
|
|
6666
6908
|
this.#imBindings.set(imBindingKey(binding.extensionId, binding.externalConversationId), binding);
|
|
6667
6909
|
}
|
|
6668
6910
|
} catch (cause) {
|
|
6669
|
-
if (!
|
|
6911
|
+
if (!isNodeErrorCode5(cause, "ENOENT")) {
|
|
6670
6912
|
throw cause;
|
|
6671
6913
|
}
|
|
6672
6914
|
}
|
|
@@ -6680,9 +6922,13 @@ var init_src4 = __esm({
|
|
|
6680
6922
|
#imBindingsPath() {
|
|
6681
6923
|
return join10(this.#scorelHomeDir, "channels", "im-bindings.json");
|
|
6682
6924
|
}
|
|
6683
|
-
async #loadUserConfigProfile() {
|
|
6925
|
+
async #loadUserConfigProfile(options = {}) {
|
|
6684
6926
|
try {
|
|
6685
|
-
return await loadScorelConfigProfile({
|
|
6927
|
+
return await loadScorelConfigProfile({
|
|
6928
|
+
cwd: this.#userHomeDir,
|
|
6929
|
+
scorelHomeDir: this.#scorelHomeDir,
|
|
6930
|
+
includeSecrets: options.includeSecrets ?? false
|
|
6931
|
+
});
|
|
6686
6932
|
} catch (cause) {
|
|
6687
6933
|
if (isMissingConfigError(cause)) {
|
|
6688
6934
|
return void 0;
|
|
@@ -6690,16 +6936,24 @@ var init_src4 = __esm({
|
|
|
6690
6936
|
throw cause;
|
|
6691
6937
|
}
|
|
6692
6938
|
}
|
|
6939
|
+
#configWriteTarget() {
|
|
6940
|
+
return {
|
|
6941
|
+
configDir: this.#scorelHomeDir,
|
|
6942
|
+
configPath: join10(this.#scorelHomeDir, "config.toml"),
|
|
6943
|
+
workDir: this.#userHomeDir
|
|
6944
|
+
};
|
|
6945
|
+
}
|
|
6693
6946
|
async #listModels(projectId) {
|
|
6694
6947
|
let config;
|
|
6695
6948
|
try {
|
|
6696
|
-
config = await this.#configProfileForProject(projectId);
|
|
6949
|
+
config = projectId ? await this.#configProfileForProject(projectId) : await this.#loadUserConfigProfile();
|
|
6697
6950
|
} catch (cause) {
|
|
6698
6951
|
if (!isMissingConfigError(cause)) {
|
|
6699
6952
|
throw cause;
|
|
6700
6953
|
}
|
|
6701
6954
|
config = void 0;
|
|
6702
6955
|
}
|
|
6956
|
+
config ??= projectId ? void 0 : this.#modelProfile;
|
|
6703
6957
|
if (!config) {
|
|
6704
6958
|
return {
|
|
6705
6959
|
providers: [],
|
|
@@ -6722,19 +6976,18 @@ var init_src4 = __esm({
|
|
|
6722
6976
|
};
|
|
6723
6977
|
}
|
|
6724
6978
|
async #handleUpsertModelProfile(request) {
|
|
6725
|
-
const
|
|
6726
|
-
const configPath = join10(project.workDir, ".scorel", "config.toml");
|
|
6979
|
+
const target = this.#configWriteTarget();
|
|
6727
6980
|
let existingConfigText;
|
|
6728
6981
|
try {
|
|
6729
|
-
existingConfigText = await readFile11(configPath, "utf8");
|
|
6982
|
+
existingConfigText = await readFile11(target.configPath, "utf8");
|
|
6730
6983
|
} catch (cause) {
|
|
6731
|
-
if (!
|
|
6984
|
+
if (!isNodeErrorCode5(cause, "ENOENT")) {
|
|
6732
6985
|
throw cause;
|
|
6733
6986
|
}
|
|
6734
6987
|
}
|
|
6735
|
-
await mkdir6(
|
|
6988
|
+
await mkdir6(target.configDir, { recursive: true });
|
|
6736
6989
|
await writeFile6(
|
|
6737
|
-
configPath,
|
|
6990
|
+
target.configPath,
|
|
6738
6991
|
renderModelProfileConfig({
|
|
6739
6992
|
providerId: request.providerId,
|
|
6740
6993
|
providerType: request.providerType,
|
|
@@ -6761,38 +7014,41 @@ var init_src4 = __esm({
|
|
|
6761
7014
|
"utf8"
|
|
6762
7015
|
);
|
|
6763
7016
|
await this.#appendHostDiagnostic("model_profile_upserted", {
|
|
6764
|
-
projectId:
|
|
6765
|
-
|
|
7017
|
+
...request.projectId ? { ignoredProjectId: request.projectId } : {},
|
|
7018
|
+
scope: "device",
|
|
7019
|
+
workDir: target.workDir,
|
|
6766
7020
|
providerId: request.providerId,
|
|
6767
7021
|
modelId: request.modelId
|
|
6768
7022
|
});
|
|
6769
|
-
return this.#listModels(
|
|
7023
|
+
return this.#listModels();
|
|
6770
7024
|
}
|
|
6771
7025
|
async #handleRemoveModelProvider(request) {
|
|
6772
|
-
const
|
|
6773
|
-
const configPath = join10(project.workDir, ".scorel", "config.toml");
|
|
7026
|
+
const target = this.#configWriteTarget();
|
|
6774
7027
|
let existingConfigText;
|
|
6775
7028
|
try {
|
|
6776
|
-
existingConfigText = await readFile11(configPath, "utf8");
|
|
7029
|
+
existingConfigText = await readFile11(target.configPath, "utf8");
|
|
6777
7030
|
} catch (cause) {
|
|
6778
|
-
if (!
|
|
7031
|
+
if (!isNodeErrorCode5(cause, "ENOENT")) {
|
|
6779
7032
|
throw cause;
|
|
6780
7033
|
}
|
|
6781
7034
|
}
|
|
6782
|
-
await mkdir6(
|
|
7035
|
+
await mkdir6(target.configDir, { recursive: true });
|
|
6783
7036
|
await writeFile6(
|
|
6784
|
-
configPath,
|
|
7037
|
+
target.configPath,
|
|
6785
7038
|
renderModelProfileConfig({
|
|
6786
7039
|
removeProviderId: request.providerId,
|
|
6787
7040
|
existingConfigText
|
|
6788
7041
|
}),
|
|
6789
7042
|
"utf8"
|
|
6790
7043
|
);
|
|
6791
|
-
const profile = await this.#listModels(
|
|
7044
|
+
const profile = await this.#listModels();
|
|
6792
7045
|
return { ...profile, removed: true };
|
|
6793
7046
|
}
|
|
6794
7047
|
async #memorySettingsForProject(projectId) {
|
|
6795
|
-
|
|
7048
|
+
return this.#memorySettings(projectId);
|
|
7049
|
+
}
|
|
7050
|
+
async #memorySettings(projectId) {
|
|
7051
|
+
const config = await (projectId ? this.#configProfileForProject(projectId) : this.#loadUserConfigProfile()).catch((cause) => {
|
|
6796
7052
|
if (isMissingConfigError(cause)) {
|
|
6797
7053
|
return void 0;
|
|
6798
7054
|
}
|
|
@@ -6814,19 +7070,18 @@ var init_src4 = __esm({
|
|
|
6814
7070
|
}
|
|
6815
7071
|
}
|
|
6816
7072
|
async #handleUpsertMemorySettings(request) {
|
|
6817
|
-
const
|
|
6818
|
-
const configPath = join10(project.workDir, ".scorel", "config.toml");
|
|
7073
|
+
const target = this.#configWriteTarget();
|
|
6819
7074
|
let existingConfigText;
|
|
6820
7075
|
try {
|
|
6821
|
-
existingConfigText = await readFile11(configPath, "utf8");
|
|
7076
|
+
existingConfigText = await readFile11(target.configPath, "utf8");
|
|
6822
7077
|
} catch (cause) {
|
|
6823
|
-
if (!
|
|
7078
|
+
if (!isNodeErrorCode5(cause, "ENOENT")) {
|
|
6824
7079
|
throw cause;
|
|
6825
7080
|
}
|
|
6826
7081
|
}
|
|
6827
|
-
await mkdir6(
|
|
7082
|
+
await mkdir6(target.configDir, { recursive: true });
|
|
6828
7083
|
await writeFile6(
|
|
6829
|
-
configPath,
|
|
7084
|
+
target.configPath,
|
|
6830
7085
|
renderMemoryConfig({
|
|
6831
7086
|
enabled: request.enabled,
|
|
6832
7087
|
daily: request.daily,
|
|
@@ -6840,10 +7095,66 @@ var init_src4 = __esm({
|
|
|
6840
7095
|
"utf8"
|
|
6841
7096
|
);
|
|
6842
7097
|
await this.#appendHostDiagnostic("memory_settings_upserted", {
|
|
6843
|
-
projectId:
|
|
6844
|
-
|
|
7098
|
+
...request.projectId ? { ignoredProjectId: request.projectId } : {},
|
|
7099
|
+
scope: "device",
|
|
7100
|
+
workDir: target.workDir
|
|
7101
|
+
});
|
|
7102
|
+
return this.#memorySettings();
|
|
7103
|
+
}
|
|
7104
|
+
async #runtimeSettingsForProject(projectId, installStatus) {
|
|
7105
|
+
return this.#runtimeSettings(projectId, installStatus);
|
|
7106
|
+
}
|
|
7107
|
+
async #runtimeSettings(projectId, installStatus) {
|
|
7108
|
+
const config = await (projectId ? this.#configProfileForProject(projectId) : this.#loadUserConfigProfile()).catch((cause) => {
|
|
7109
|
+
if (isMissingConfigError(cause)) {
|
|
7110
|
+
return void 0;
|
|
7111
|
+
}
|
|
7112
|
+
throw cause;
|
|
7113
|
+
});
|
|
7114
|
+
const detected = await detectRtk();
|
|
7115
|
+
const savings = await readRuntimeStats(this.#runtimeStatsPath());
|
|
7116
|
+
return {
|
|
7117
|
+
tokenSavingRtk: config?.runtime.tokenSavingRtk ?? false,
|
|
7118
|
+
rtkAvailable: detected.available,
|
|
7119
|
+
...detected.executable ? { rtkExecutable: detected.executable } : {},
|
|
7120
|
+
...detected.version ? { rtkVersion: detected.version } : {},
|
|
7121
|
+
...installStatus?.installStatus ? { installStatus: installStatus.installStatus } : {},
|
|
7122
|
+
...installStatus?.installMessage ? { installMessage: installStatus.installMessage } : {},
|
|
7123
|
+
estimatedOutputTokens: savings.rtk.outputTokens,
|
|
7124
|
+
estimatedSavedTokens: savings.rtk.savedTokens
|
|
7125
|
+
};
|
|
7126
|
+
}
|
|
7127
|
+
async #handleUpsertRuntimeSettings(request) {
|
|
7128
|
+
const target = this.#configWriteTarget();
|
|
7129
|
+
let existingConfigText;
|
|
7130
|
+
try {
|
|
7131
|
+
existingConfigText = await readFile11(target.configPath, "utf8");
|
|
7132
|
+
} catch (cause) {
|
|
7133
|
+
if (!isNodeErrorCode5(cause, "ENOENT")) {
|
|
7134
|
+
throw cause;
|
|
7135
|
+
}
|
|
7136
|
+
}
|
|
7137
|
+
await mkdir6(target.configDir, { recursive: true });
|
|
7138
|
+
await writeFile6(
|
|
7139
|
+
target.configPath,
|
|
7140
|
+
renderRuntimeConfig({
|
|
7141
|
+
tokenSavingRtk: request.tokenSavingRtk,
|
|
7142
|
+
existingConfigText
|
|
7143
|
+
}),
|
|
7144
|
+
"utf8"
|
|
7145
|
+
);
|
|
7146
|
+
const installResult = request.tokenSavingRtk === true ? await ensureRtkAvailable() : { status: "idle" };
|
|
7147
|
+
await this.#appendHostDiagnostic("runtime_settings_upserted", {
|
|
7148
|
+
...request.projectId ? { ignoredProjectId: request.projectId } : {},
|
|
7149
|
+
scope: "device",
|
|
7150
|
+
workDir: target.workDir,
|
|
7151
|
+
tokenSavingRtk: request.tokenSavingRtk,
|
|
7152
|
+
installStatus: installResult.status
|
|
7153
|
+
});
|
|
7154
|
+
return this.#runtimeSettings(void 0, {
|
|
7155
|
+
installStatus: installResult.status,
|
|
7156
|
+
...installResult.message ? { installMessage: installResult.message } : {}
|
|
6845
7157
|
});
|
|
6846
|
-
return this.#memorySettingsForProject(project.projectId);
|
|
6847
7158
|
}
|
|
6848
7159
|
async #extensionSettings(extensionId) {
|
|
6849
7160
|
const config = await this.#loadUserConfigProfile().catch((cause) => {
|
|
@@ -6867,7 +7178,7 @@ var init_src4 = __esm({
|
|
|
6867
7178
|
try {
|
|
6868
7179
|
existingConfigText = await readFile11(configPath, "utf8");
|
|
6869
7180
|
} catch (cause) {
|
|
6870
|
-
if (!
|
|
7181
|
+
if (!isNodeErrorCode5(cause, "ENOENT")) {
|
|
6871
7182
|
throw cause;
|
|
6872
7183
|
}
|
|
6873
7184
|
}
|
|
@@ -6891,8 +7202,11 @@ var init_src4 = __esm({
|
|
|
6891
7202
|
return this.#extensionSettings(request.extensionId);
|
|
6892
7203
|
}
|
|
6893
7204
|
async #fetchProviderModels(projectId, providerId) {
|
|
6894
|
-
const
|
|
6895
|
-
|
|
7205
|
+
const config = projectId ? await loadScorelConfigProfile({
|
|
7206
|
+
cwd: (await this.#registry.require(projectId)).workDir,
|
|
7207
|
+
scorelHomeDir: this.#scorelHomeDir,
|
|
7208
|
+
includeSecrets: true
|
|
7209
|
+
}) : await this.#loadUserConfigProfile({ includeSecrets: true });
|
|
6896
7210
|
if (!config) {
|
|
6897
7211
|
throw new Error("Model profile config is not configured");
|
|
6898
7212
|
}
|
|
@@ -6977,7 +7291,7 @@ var init_src4 = __esm({
|
|
|
6977
7291
|
}
|
|
6978
7292
|
const project = await this.#registry.require(projectId);
|
|
6979
7293
|
try {
|
|
6980
|
-
return await loadScorelConfigProfile({ cwd: project.workDir });
|
|
7294
|
+
return await loadScorelConfigProfile({ cwd: project.workDir, scorelHomeDir: this.#scorelHomeDir });
|
|
6981
7295
|
} catch (cause) {
|
|
6982
7296
|
if (!isMissingConfigError(cause)) {
|
|
6983
7297
|
throw cause;
|
|
@@ -7018,6 +7332,20 @@ var init_src4 = __esm({
|
|
|
7018
7332
|
await appendFile3(join10(this.#sessionsDir, "host.log"), `${line}
|
|
7019
7333
|
`, "utf8");
|
|
7020
7334
|
}
|
|
7335
|
+
#runtimeStatsPath() {
|
|
7336
|
+
return join10(this.#scorelHomeDir, "runtime-stats.json");
|
|
7337
|
+
}
|
|
7338
|
+
async #recordRtkSavings(input) {
|
|
7339
|
+
const updateTask = this.#runtimeStatsQueue.then(async () => {
|
|
7340
|
+
const path = this.#runtimeStatsPath();
|
|
7341
|
+
const stats = await readRuntimeStats(path);
|
|
7342
|
+
addRtkSavings(stats, String(input.projectId), String(input.sessionId), input.savings);
|
|
7343
|
+
await writeRuntimeStats(path, stats);
|
|
7344
|
+
});
|
|
7345
|
+
this.#runtimeStatsQueue = updateTask.catch(() => {
|
|
7346
|
+
});
|
|
7347
|
+
await updateTask;
|
|
7348
|
+
}
|
|
7021
7349
|
async #resolveProject(sessionId, projectId) {
|
|
7022
7350
|
const project = await this.#registry.require(projectId);
|
|
7023
7351
|
await this.#appendDiagnostic(sessionId, "project_resolved", {
|
|
@@ -7073,7 +7401,7 @@ var init_src4 = __esm({
|
|
|
7073
7401
|
}
|
|
7074
7402
|
};
|
|
7075
7403
|
};
|
|
7076
|
-
|
|
7404
|
+
isNodeErrorCode5 = (cause, code) => cause instanceof Error && "code" in cause && cause.code === code;
|
|
7077
7405
|
wireErrorCode = (cause) => {
|
|
7078
7406
|
if (!(cause instanceof ProjectRegistryError)) {
|
|
7079
7407
|
return "internal_error";
|
|
@@ -7124,7 +7452,7 @@ var init_src4 = __esm({
|
|
|
7124
7452
|
return void 0;
|
|
7125
7453
|
}
|
|
7126
7454
|
const parsed = JSON.parse(text);
|
|
7127
|
-
if (!
|
|
7455
|
+
if (!isRecord8(parsed)) {
|
|
7128
7456
|
return void 0;
|
|
7129
7457
|
}
|
|
7130
7458
|
return {
|
|
@@ -7144,6 +7472,147 @@ var init_src4 = __esm({
|
|
|
7144
7472
|
dreamIdleMinutes: 60,
|
|
7145
7473
|
autoCompactThreshold: 0.8
|
|
7146
7474
|
});
|
|
7475
|
+
detectRtk = async () => {
|
|
7476
|
+
try {
|
|
7477
|
+
const shell = resolveDefaultShell2();
|
|
7478
|
+
const path = (await execFileAsync2(shell, shellCommandArgs2(shell, "command -v rtk"), { timeout: 5e3 })).stdout.trim();
|
|
7479
|
+
if (!path) {
|
|
7480
|
+
return { available: false };
|
|
7481
|
+
}
|
|
7482
|
+
const version = await execFileAsync2(path, ["--version"], { timeout: 5e3 }).then((result) => result.stdout.trim() || result.stderr.trim()).catch(() => void 0);
|
|
7483
|
+
return {
|
|
7484
|
+
available: true,
|
|
7485
|
+
executable: path,
|
|
7486
|
+
...version ? { version } : {}
|
|
7487
|
+
};
|
|
7488
|
+
} catch {
|
|
7489
|
+
return { available: false };
|
|
7490
|
+
}
|
|
7491
|
+
};
|
|
7492
|
+
ensureRtkAvailable = async () => {
|
|
7493
|
+
const existing = await detectRtk();
|
|
7494
|
+
if (existing.available) {
|
|
7495
|
+
return { status: "installed", message: existing.version ?? existing.executable };
|
|
7496
|
+
}
|
|
7497
|
+
const shell = resolveDefaultShell2();
|
|
7498
|
+
const brew = await execFileAsync2(shell, shellCommandArgs2(shell, "command -v brew"), { timeout: 5e3 }).then((result) => result.stdout.trim()).catch(() => "");
|
|
7499
|
+
if (!brew) {
|
|
7500
|
+
return { status: "failed", message: "Homebrew is not available; install RTK manually with `brew install rtk`." };
|
|
7501
|
+
}
|
|
7502
|
+
try {
|
|
7503
|
+
await execFileAsync2(brew, ["install", "rtk"], { timeout: 12e4, maxBuffer: 2e7 });
|
|
7504
|
+
const installed = await detectRtk();
|
|
7505
|
+
return installed.available ? { status: "installed", message: installed.version ?? installed.executable } : { status: "failed", message: "RTK install finished but `rtk` is still not on PATH." };
|
|
7506
|
+
} catch (cause) {
|
|
7507
|
+
const message = cause instanceof Error ? cause.message : String(cause);
|
|
7508
|
+
return { status: "failed", message };
|
|
7509
|
+
}
|
|
7510
|
+
};
|
|
7511
|
+
emptyRuntimeStats = () => ({
|
|
7512
|
+
version: 1,
|
|
7513
|
+
rtk: {
|
|
7514
|
+
outputTokens: 0,
|
|
7515
|
+
savedTokens: 0,
|
|
7516
|
+
byProject: {},
|
|
7517
|
+
bySession: {}
|
|
7518
|
+
}
|
|
7519
|
+
});
|
|
7520
|
+
readRuntimeStats = async (path) => {
|
|
7521
|
+
try {
|
|
7522
|
+
return parseRuntimeStats(JSON.parse(await readFile11(path, "utf8")));
|
|
7523
|
+
} catch (cause) {
|
|
7524
|
+
if (isNodeErrorCode5(cause, "ENOENT")) {
|
|
7525
|
+
return emptyRuntimeStats();
|
|
7526
|
+
}
|
|
7527
|
+
return emptyRuntimeStats();
|
|
7528
|
+
}
|
|
7529
|
+
};
|
|
7530
|
+
writeRuntimeStats = async (path, stats) => {
|
|
7531
|
+
await mkdir6(dirname8(path), { recursive: true });
|
|
7532
|
+
const tempPath = join10(dirname8(path), `.runtime-stats-${process.pid}-${Date.now()}.tmp`);
|
|
7533
|
+
try {
|
|
7534
|
+
await writeFile6(tempPath, `${JSON.stringify(stats, null, 2)}
|
|
7535
|
+
`, "utf8");
|
|
7536
|
+
await rename3(tempPath, path);
|
|
7537
|
+
} catch (cause) {
|
|
7538
|
+
await rm2(tempPath, { force: true }).catch(() => void 0);
|
|
7539
|
+
throw cause;
|
|
7540
|
+
}
|
|
7541
|
+
};
|
|
7542
|
+
parseRuntimeStats = (value) => {
|
|
7543
|
+
if (!isRecord8(value) || !isRecord8(value.rtk)) {
|
|
7544
|
+
return emptyRuntimeStats();
|
|
7545
|
+
}
|
|
7546
|
+
return {
|
|
7547
|
+
version: 1,
|
|
7548
|
+
rtk: {
|
|
7549
|
+
outputTokens: nonNegativeInteger2(value.rtk.outputTokens),
|
|
7550
|
+
savedTokens: nonNegativeInteger2(value.rtk.savedTokens),
|
|
7551
|
+
byProject: parseRuntimeStatsBuckets(value.rtk.byProject),
|
|
7552
|
+
bySession: parseRuntimeStatsBuckets(value.rtk.bySession)
|
|
7553
|
+
}
|
|
7554
|
+
};
|
|
7555
|
+
};
|
|
7556
|
+
parseRuntimeStatsBuckets = (value) => {
|
|
7557
|
+
if (!isRecord8(value)) {
|
|
7558
|
+
return {};
|
|
7559
|
+
}
|
|
7560
|
+
return Object.fromEntries(
|
|
7561
|
+
Object.entries(value).map(([key, bucket]) => [
|
|
7562
|
+
key,
|
|
7563
|
+
isRecord8(bucket) ? {
|
|
7564
|
+
outputTokens: nonNegativeInteger2(bucket.outputTokens),
|
|
7565
|
+
savedTokens: nonNegativeInteger2(bucket.savedTokens)
|
|
7566
|
+
} : { outputTokens: 0, savedTokens: 0 }
|
|
7567
|
+
])
|
|
7568
|
+
);
|
|
7569
|
+
};
|
|
7570
|
+
addRtkSavings = (stats, projectId, sessionId, savings) => {
|
|
7571
|
+
addRuntimeStatsBucket(stats.rtk, savings);
|
|
7572
|
+
stats.rtk.byProject[projectId] = addRuntimeStatsBucket(stats.rtk.byProject[projectId] ?? { outputTokens: 0, savedTokens: 0 }, savings);
|
|
7573
|
+
stats.rtk.bySession[sessionId] = addRuntimeStatsBucket(stats.rtk.bySession[sessionId] ?? { outputTokens: 0, savedTokens: 0 }, savings);
|
|
7574
|
+
};
|
|
7575
|
+
addRuntimeStatsBucket = (bucket, savings) => {
|
|
7576
|
+
bucket.outputTokens += savings.outputTokens;
|
|
7577
|
+
bucket.savedTokens += savings.savedTokens;
|
|
7578
|
+
return bucket;
|
|
7579
|
+
};
|
|
7580
|
+
rtkSavingsFromToolResult = (result) => {
|
|
7581
|
+
if (!isRecord8(result) || !isRecord8(result.details)) {
|
|
7582
|
+
return void 0;
|
|
7583
|
+
}
|
|
7584
|
+
const rtk = result.details.rtk;
|
|
7585
|
+
if (!isRecord8(rtk) || rtk.applied !== true) {
|
|
7586
|
+
return void 0;
|
|
7587
|
+
}
|
|
7588
|
+
const outputTokens = nonNegativeInteger2(rtk.estimatedOutputTokens);
|
|
7589
|
+
const savedTokens = nonNegativeInteger2(rtk.estimatedSavedTokens);
|
|
7590
|
+
return outputTokens > 0 || savedTokens > 0 ? { outputTokens, savedTokens } : void 0;
|
|
7591
|
+
};
|
|
7592
|
+
nonNegativeInteger2 = (value) => {
|
|
7593
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
7594
|
+
return 0;
|
|
7595
|
+
}
|
|
7596
|
+
return Math.floor(value);
|
|
7597
|
+
};
|
|
7598
|
+
resolveDefaultShell2 = () => {
|
|
7599
|
+
const shell = process.env.SHELL || userShell2() || "/bin/sh";
|
|
7600
|
+
return shell.trim() || "/bin/sh";
|
|
7601
|
+
};
|
|
7602
|
+
shellCommandArgs2 = (shell, command) => {
|
|
7603
|
+
const name = basename3(shell).toLowerCase();
|
|
7604
|
+
if (name === "csh" || name === "tcsh" || name === "fish") {
|
|
7605
|
+
return ["-c", command];
|
|
7606
|
+
}
|
|
7607
|
+
return ["-lc", command];
|
|
7608
|
+
};
|
|
7609
|
+
userShell2 = () => {
|
|
7610
|
+
try {
|
|
7611
|
+
return userInfo2().shell ?? void 0;
|
|
7612
|
+
} catch {
|
|
7613
|
+
return void 0;
|
|
7614
|
+
}
|
|
7615
|
+
};
|
|
7147
7616
|
runtimeChannelContextFromWire = (context) => ({
|
|
7148
7617
|
extensionId: context.channel,
|
|
7149
7618
|
channel: context.channel,
|
|
@@ -7158,7 +7627,7 @@ var init_src4 = __esm({
|
|
|
7158
7627
|
...context.data ? { data: context.data } : {}
|
|
7159
7628
|
});
|
|
7160
7629
|
parseQueuedChannelContext = (value) => {
|
|
7161
|
-
if (!
|
|
7630
|
+
if (!isRecord8(value)) {
|
|
7162
7631
|
return void 0;
|
|
7163
7632
|
}
|
|
7164
7633
|
if (typeof value.channel !== "string" || typeof value.externalConversationId !== "string") {
|
|
@@ -7170,7 +7639,7 @@ var init_src4 = __esm({
|
|
|
7170
7639
|
...typeof value.conversationType === "string" ? { conversationType: value.conversationType } : {},
|
|
7171
7640
|
...typeof value.senderDisplayName === "string" ? { senderDisplayName: value.senderDisplayName } : {},
|
|
7172
7641
|
...typeof value.mentionedBot === "boolean" ? { mentionedBot: value.mentionedBot } : {},
|
|
7173
|
-
...
|
|
7642
|
+
...isRecord8(value.data) ? { data: value.data } : {}
|
|
7174
7643
|
});
|
|
7175
7644
|
};
|
|
7176
7645
|
imBindingKey = (extensionId, externalConversationId) => `${extensionId}:${externalConversationId}`;
|
|
@@ -7203,7 +7672,7 @@ var init_src4 = __esm({
|
|
|
7203
7672
|
};
|
|
7204
7673
|
isSteerMessage = (text) => /^\/(?:steer|interrupt)\b/i.test(text.trim());
|
|
7205
7674
|
stripImCommandPrefix = (text) => text.trim().replace(/^\/(?:steer|interrupt)\s*/i, "").trim() || text;
|
|
7206
|
-
|
|
7675
|
+
isRecord8 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7207
7676
|
parseMemoryUpdate = (raw) => {
|
|
7208
7677
|
const text = raw.trim().replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/, "").trim();
|
|
7209
7678
|
if (!text) {
|
|
@@ -7310,9 +7779,11 @@ var init_relay_cli = __esm({
|
|
|
7310
7779
|
|
|
7311
7780
|
// apps/cli/src/daemon-cli.ts
|
|
7312
7781
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
7782
|
+
import { spawn } from "node:child_process";
|
|
7313
7783
|
import { homedir as homedir6 } from "node:os";
|
|
7314
|
-
import { join as join12 } from "node:path";
|
|
7315
|
-
|
|
7784
|
+
import { dirname as dirname9, join as join12 } from "node:path";
|
|
7785
|
+
import { fileURLToPath } from "node:url";
|
|
7786
|
+
var DEFAULT_HOST, DEFAULT_PORT, STOP_POLL_INTERVAL_MS, STOP_GRACE_MS, START_READY_TIMEOUT_MS, DEFAULT_IDLE_SHUTDOWN_MS, defaultStateDir2, isLoopbackHost, formatTimestamp, runCliDaemon, runStartCommand, runServeCommand, stopRunningDaemon, runStatusCommand, runStopCommand, runResetCommand, formatStatusLine, parseServeFlags, parseStatusFlags, requireValue2, sleep, waitForDaemonReady, detachBackgroundDaemon, nodeEntrypointArgs, writeDaemonUsage;
|
|
7316
7787
|
var init_daemon_cli = __esm({
|
|
7317
7788
|
"apps/cli/src/daemon-cli.ts"() {
|
|
7318
7789
|
"use strict";
|
|
@@ -7322,6 +7793,8 @@ var init_daemon_cli = __esm({
|
|
|
7322
7793
|
DEFAULT_PORT = 7777;
|
|
7323
7794
|
STOP_POLL_INTERVAL_MS = 200;
|
|
7324
7795
|
STOP_GRACE_MS = 5e3;
|
|
7796
|
+
START_READY_TIMEOUT_MS = 1e4;
|
|
7797
|
+
DEFAULT_IDLE_SHUTDOWN_MS = 15 * 60 * 1e3;
|
|
7325
7798
|
defaultStateDir2 = () => join12(homedir6(), ".scorel");
|
|
7326
7799
|
isLoopbackHost = (host) => host === "127.0.0.1" || host === "::1" || host === "localhost";
|
|
7327
7800
|
formatTimestamp = (epochMs) => new Date(epochMs).toISOString();
|
|
@@ -7329,6 +7802,8 @@ var init_daemon_cli = __esm({
|
|
|
7329
7802
|
const [command, ...rest] = argv;
|
|
7330
7803
|
const stateDir = options.stateDir ?? defaultStateDir2();
|
|
7331
7804
|
switch (command) {
|
|
7805
|
+
case "start":
|
|
7806
|
+
return runStartCommand(rest, { ...options, stateDir });
|
|
7332
7807
|
case "serve":
|
|
7333
7808
|
return runServeCommand(rest, { ...options, stateDir });
|
|
7334
7809
|
case "status":
|
|
@@ -7346,6 +7821,63 @@ var init_daemon_cli = __esm({
|
|
|
7346
7821
|
return 1;
|
|
7347
7822
|
}
|
|
7348
7823
|
};
|
|
7824
|
+
runStartCommand = async (argv, options) => {
|
|
7825
|
+
let flags;
|
|
7826
|
+
try {
|
|
7827
|
+
flags = parseServeFlags(argv, options.cwd ?? process.cwd(), options.env ?? process.env);
|
|
7828
|
+
} catch (cause) {
|
|
7829
|
+
options.error.write(`scorel daemon start error: ${cause.message}
|
|
7830
|
+
`);
|
|
7831
|
+
return 1;
|
|
7832
|
+
}
|
|
7833
|
+
const readState = options.readState ?? ((stateDir) => readLocalDaemonState({ stateDir }));
|
|
7834
|
+
const existing = await readState(options.stateDir);
|
|
7835
|
+
if (existing && daemonStateLiveness(existing) === "running") {
|
|
7836
|
+
options.output.write(`scorel host already running url=${existing.wsUrl} pid=${existing.pid}
|
|
7837
|
+
`);
|
|
7838
|
+
return 0;
|
|
7839
|
+
}
|
|
7840
|
+
const cliEntrypoint = options.cliEntrypoint ?? fileURLToPath(import.meta.url).replace(/daemon-cli\.ts$/, "index.ts");
|
|
7841
|
+
const child = (options.spawn ?? spawn)(process.execPath, [
|
|
7842
|
+
...nodeEntrypointArgs(cliEntrypoint),
|
|
7843
|
+
"host",
|
|
7844
|
+
"serve",
|
|
7845
|
+
"--host",
|
|
7846
|
+
flags.host,
|
|
7847
|
+
"--port",
|
|
7848
|
+
String(flags.port),
|
|
7849
|
+
"--cwd",
|
|
7850
|
+
flags.cwd,
|
|
7851
|
+
"--idle-timeout-ms",
|
|
7852
|
+
String(flags.idleShutdownMs),
|
|
7853
|
+
...flags.token ? ["--token", flags.token] : [],
|
|
7854
|
+
...flags.relayUrl ? ["--relay", flags.relayUrl] : ["--no-relay"],
|
|
7855
|
+
...flags.replace ? ["--replace"] : []
|
|
7856
|
+
], {
|
|
7857
|
+
cwd: dirname9(cliEntrypoint),
|
|
7858
|
+
env: { ...process.env, ...options.env ?? {} },
|
|
7859
|
+
detached: true,
|
|
7860
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
7861
|
+
});
|
|
7862
|
+
try {
|
|
7863
|
+
await waitForDaemonReady(child, options.daemonReadyTimeoutMs ?? START_READY_TIMEOUT_MS);
|
|
7864
|
+
} catch (cause) {
|
|
7865
|
+
options.error.write(`scorel daemon start error: ${cause.message}
|
|
7866
|
+
`);
|
|
7867
|
+
child.kill("SIGTERM");
|
|
7868
|
+
return 1;
|
|
7869
|
+
}
|
|
7870
|
+
const state = await readState(options.stateDir);
|
|
7871
|
+
if (!state || daemonStateLiveness(state) !== "running") {
|
|
7872
|
+
options.error.write("scorel daemon start error: daemon state missing after start\n");
|
|
7873
|
+
child.kill("SIGTERM");
|
|
7874
|
+
return 1;
|
|
7875
|
+
}
|
|
7876
|
+
detachBackgroundDaemon(child);
|
|
7877
|
+
options.output.write(`scorel host started url=${state.wsUrl} pid=${state.pid}
|
|
7878
|
+
`);
|
|
7879
|
+
return 0;
|
|
7880
|
+
};
|
|
7349
7881
|
runServeCommand = async (argv, options) => {
|
|
7350
7882
|
let flags;
|
|
7351
7883
|
try {
|
|
@@ -7373,16 +7905,28 @@ Use --replace to stop it and start a new one.
|
|
|
7373
7905
|
}
|
|
7374
7906
|
const token = flags.token ?? existing?.token ?? randomUUID4();
|
|
7375
7907
|
const identity = await loadOrCreateHostDeviceIdentity({ stateDir: options.stateDir });
|
|
7908
|
+
const configScope = { scorelHomeDir: options.stateDir };
|
|
7909
|
+
let signalReason = "natural";
|
|
7910
|
+
let resolveStopWaiter;
|
|
7911
|
+
let stopRequested = false;
|
|
7912
|
+
const requestStop = (reason) => {
|
|
7913
|
+
signalReason = reason;
|
|
7914
|
+
stopRequested = true;
|
|
7915
|
+
resolveStopWaiter?.();
|
|
7916
|
+
};
|
|
7376
7917
|
const daemon = new ScorelHost({
|
|
7377
7918
|
sessionsDir: options.sessionsDir ?? scorelSessionsDir(homedir6()),
|
|
7378
7919
|
projectsPath: join12(options.stateDir, "projects.json"),
|
|
7379
7920
|
deviceId: identity.deviceId,
|
|
7380
7921
|
deviceDisplayName: identity.displayName,
|
|
7381
|
-
|
|
7382
|
-
|
|
7922
|
+
idleShutdownMs: flags.idleShutdownMs,
|
|
7923
|
+
onIdleShutdown: () => requestStop("idle"),
|
|
7924
|
+
scorelHomeDir: options.stateDir,
|
|
7925
|
+
loadConfig: async ({ project }) => loadScorelConfig({ cwd: project.workDir, ...configScope }),
|
|
7926
|
+
loadConfigProfile: async ({ project }) => loadScorelConfigProfile({ cwd: project.workDir, ...configScope }),
|
|
7383
7927
|
createRuntime: async ({ project, selectedModel, purpose }) => createRealRuntime({
|
|
7384
7928
|
cwd: project.workDir,
|
|
7385
|
-
config: await loadScorelConfig({ cwd: project.workDir }),
|
|
7929
|
+
config: await loadScorelConfig({ cwd: project.workDir, ...configScope }),
|
|
7386
7930
|
modelSelection: selectedModel ? { modelId: selectedModel.modelId, role: selectedModel.role } : void 0,
|
|
7387
7931
|
includeTools: purpose === "chat"
|
|
7388
7932
|
})
|
|
@@ -7441,20 +7985,22 @@ Use --replace to stop it and start a new one.
|
|
|
7441
7985
|
await markDaemonStopped({ stateDir: options.stateDir, stoppedAt: Date.now() });
|
|
7442
7986
|
}
|
|
7443
7987
|
};
|
|
7444
|
-
let signalReason = "natural";
|
|
7445
7988
|
const signalHandlers = /* @__PURE__ */ new Map();
|
|
7446
7989
|
const stopWaiter = new Promise((resolve7) => {
|
|
7990
|
+
resolveStopWaiter = resolve7;
|
|
7991
|
+
if (stopRequested) {
|
|
7992
|
+
resolve7();
|
|
7993
|
+
return;
|
|
7994
|
+
}
|
|
7447
7995
|
if (options.serveSignal) {
|
|
7448
7996
|
if (options.serveSignal.aborted) {
|
|
7449
|
-
|
|
7450
|
-
resolve7();
|
|
7997
|
+
requestStop("abort");
|
|
7451
7998
|
return;
|
|
7452
7999
|
}
|
|
7453
8000
|
options.serveSignal.addEventListener(
|
|
7454
8001
|
"abort",
|
|
7455
8002
|
() => {
|
|
7456
|
-
|
|
7457
|
-
resolve7();
|
|
8003
|
+
requestStop("abort");
|
|
7458
8004
|
},
|
|
7459
8005
|
{ once: true }
|
|
7460
8006
|
);
|
|
@@ -7462,8 +8008,7 @@ Use --replace to stop it and start a new one.
|
|
|
7462
8008
|
}
|
|
7463
8009
|
const installSignal = (signal) => {
|
|
7464
8010
|
const handler = () => {
|
|
7465
|
-
|
|
7466
|
-
resolve7();
|
|
8011
|
+
requestStop(signal);
|
|
7467
8012
|
};
|
|
7468
8013
|
signalHandlers.set(signal, handler);
|
|
7469
8014
|
process.once(signal, handler);
|
|
@@ -7593,6 +8138,7 @@ Use --replace to stop it and start a new one.
|
|
|
7593
8138
|
let token;
|
|
7594
8139
|
let relayUrl = resolveDefaultRelayUrl(env);
|
|
7595
8140
|
let replace = false;
|
|
8141
|
+
let idleShutdownMs = DEFAULT_IDLE_SHUTDOWN_MS;
|
|
7596
8142
|
for (let index = 0; index < argv.length; index += 1) {
|
|
7597
8143
|
const arg = argv[index];
|
|
7598
8144
|
if (arg === "--host") {
|
|
@@ -7636,9 +8182,17 @@ Use --replace to stop it and start a new one.
|
|
|
7636
8182
|
replace = true;
|
|
7637
8183
|
continue;
|
|
7638
8184
|
}
|
|
8185
|
+
if (arg === "--idle-timeout-ms") {
|
|
8186
|
+
idleShutdownMs = Number(requireValue2(argv, index, "--idle-timeout-ms"));
|
|
8187
|
+
if (!Number.isInteger(idleShutdownMs) || idleShutdownMs < 0) {
|
|
8188
|
+
throw new Error("--idle-timeout-ms must be a non-negative integer");
|
|
8189
|
+
}
|
|
8190
|
+
index += 1;
|
|
8191
|
+
continue;
|
|
8192
|
+
}
|
|
7639
8193
|
throw new Error(`Unknown serve option: ${arg}`);
|
|
7640
8194
|
}
|
|
7641
|
-
return { host, port, token, cwd, relayUrl, replace };
|
|
8195
|
+
return { host, port, token, cwd, relayUrl, replace, idleShutdownMs };
|
|
7642
8196
|
};
|
|
7643
8197
|
parseStatusFlags = (argv) => {
|
|
7644
8198
|
let showToken = false;
|
|
@@ -7661,11 +8215,66 @@ Use --replace to stop it and start a new one.
|
|
|
7661
8215
|
sleep = (ms) => new Promise((resolve7) => {
|
|
7662
8216
|
setTimeout(resolve7, ms);
|
|
7663
8217
|
});
|
|
8218
|
+
waitForDaemonReady = (child, timeoutMs) => new Promise((resolveReady, rejectReady) => {
|
|
8219
|
+
if (!child.stdout) {
|
|
8220
|
+
rejectReady(new Error("daemon child has no stdout stream"));
|
|
8221
|
+
return;
|
|
8222
|
+
}
|
|
8223
|
+
let buffer = "";
|
|
8224
|
+
let stderrBuffer = "";
|
|
8225
|
+
let settled = false;
|
|
8226
|
+
const timer = setTimeout(() => {
|
|
8227
|
+
if (settled) return;
|
|
8228
|
+
settled = true;
|
|
8229
|
+
cleanup();
|
|
8230
|
+
rejectReady(new Error("timed out waiting for daemon ready line"));
|
|
8231
|
+
}, timeoutMs);
|
|
8232
|
+
const onData = (chunk) => {
|
|
8233
|
+
buffer += chunk.toString();
|
|
8234
|
+
if (!buffer.includes("\n")) return;
|
|
8235
|
+
if (buffer.includes("scorel daemon serving url=") || buffer.includes("scorel host serving url=")) {
|
|
8236
|
+
if (settled) return;
|
|
8237
|
+
settled = true;
|
|
8238
|
+
cleanup();
|
|
8239
|
+
resolveReady();
|
|
8240
|
+
}
|
|
8241
|
+
const newlineIndex = buffer.lastIndexOf("\n");
|
|
8242
|
+
buffer = newlineIndex >= 0 ? buffer.slice(newlineIndex + 1) : buffer;
|
|
8243
|
+
};
|
|
8244
|
+
const onStderr = (chunk) => {
|
|
8245
|
+
stderrBuffer += chunk.toString();
|
|
8246
|
+
};
|
|
8247
|
+
const onExit = (code) => {
|
|
8248
|
+
if (settled) return;
|
|
8249
|
+
settled = true;
|
|
8250
|
+
cleanup();
|
|
8251
|
+
const trimmed = stderrBuffer.trim();
|
|
8252
|
+
const detail = trimmed ? `: ${trimmed}` : "";
|
|
8253
|
+
rejectReady(new Error(`daemon exited before ready code=${code}${detail}`));
|
|
8254
|
+
};
|
|
8255
|
+
const cleanup = () => {
|
|
8256
|
+
clearTimeout(timer);
|
|
8257
|
+
child.stdout?.off("data", onData);
|
|
8258
|
+
child.stderr?.off("data", onStderr);
|
|
8259
|
+
child.off("exit", onExit);
|
|
8260
|
+
};
|
|
8261
|
+
child.stdout.on("data", onData);
|
|
8262
|
+
child.stderr?.on("data", onStderr);
|
|
8263
|
+
child.once("exit", onExit);
|
|
8264
|
+
});
|
|
8265
|
+
detachBackgroundDaemon = (child) => {
|
|
8266
|
+
child.stdout?.destroy();
|
|
8267
|
+
child.stderr?.destroy();
|
|
8268
|
+
child.unref();
|
|
8269
|
+
};
|
|
8270
|
+
nodeEntrypointArgs = (entrypoint) => entrypoint.endsWith(".ts") ? ["--import", "tsx", entrypoint] : [entrypoint];
|
|
7664
8271
|
writeDaemonUsage = (output) => {
|
|
7665
8272
|
output.write(
|
|
7666
8273
|
[
|
|
7667
8274
|
"Usage: scorel host serve [--host <h>] [--port <p>] [--token <t>] [--project <dir>]",
|
|
7668
|
-
" [--relay <relay-url> | --no-relay] [--replace]",
|
|
8275
|
+
" [--relay <relay-url> | --no-relay] [--replace] [--idle-timeout-ms <ms>]",
|
|
8276
|
+
" scorel host start [--host <h>] [--port <p>] [--token <t>] [--project <dir>]",
|
|
8277
|
+
" [--relay <relay-url> | --no-relay] [--replace] [--idle-timeout-ms <ms>]",
|
|
7669
8278
|
" scorel host status [--show-token]",
|
|
7670
8279
|
" scorel host stop",
|
|
7671
8280
|
" scorel host reset",
|
|
@@ -8289,11 +8898,11 @@ var init_relay_server_cli = __esm({
|
|
|
8289
8898
|
});
|
|
8290
8899
|
|
|
8291
8900
|
// apps/cli/src/up-cli.ts
|
|
8292
|
-
import { spawn } from "node:child_process";
|
|
8901
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
8293
8902
|
import { homedir as homedir8 } from "node:os";
|
|
8294
|
-
import { join as join15 } from "node:path";
|
|
8295
|
-
import { fileURLToPath } from "node:url";
|
|
8296
|
-
var DEFAULT_DAEMON_PORT, DEFAULT_WEBUI_PORT, DEFAULT_DAEMON_READY_TIMEOUT_MS, defaultStateDir3, defaultAttachSigint, runCliUp, parseUpFlags, requireValue4,
|
|
8903
|
+
import { dirname as dirname10, join as join15 } from "node:path";
|
|
8904
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
8905
|
+
var DEFAULT_DAEMON_PORT, DEFAULT_WEBUI_PORT, DEFAULT_DAEMON_READY_TIMEOUT_MS, defaultStateDir3, defaultAttachSigint, runCliUp, parseUpFlags, requireValue4, waitForDaemonReady2, pipeWithPrefix, detachBackgroundDaemon2, nodeEntrypointArgs2, pipeStreamLines, once;
|
|
8297
8906
|
var init_up_cli = __esm({
|
|
8298
8907
|
"apps/cli/src/up-cli.ts"() {
|
|
8299
8908
|
"use strict";
|
|
@@ -8316,8 +8925,8 @@ var init_up_cli = __esm({
|
|
|
8316
8925
|
return 1;
|
|
8317
8926
|
}
|
|
8318
8927
|
const stateDir = options.stateDir ?? defaultStateDir3();
|
|
8319
|
-
const cliEntrypoint = options.cliEntrypoint ??
|
|
8320
|
-
const spawnFn = options.spawn ??
|
|
8928
|
+
const cliEntrypoint = options.cliEntrypoint ?? fileURLToPath2(import.meta.url).replace(/up-cli\.ts$/, "index.ts");
|
|
8929
|
+
const spawnFn = options.spawn ?? spawn2;
|
|
8321
8930
|
const readState = options.readState ?? ((dir) => readLocalDaemonState({ stateDir: dir }));
|
|
8322
8931
|
const attachSigint = options.attachSigint ?? defaultAttachSigint;
|
|
8323
8932
|
const readyTimeout = options.daemonReadyTimeoutMs ?? DEFAULT_DAEMON_READY_TIMEOUT_MS;
|
|
@@ -8328,9 +8937,7 @@ var init_up_cli = __esm({
|
|
|
8328
8937
|
let daemonState = existingState;
|
|
8329
8938
|
if (!reuseDaemon) {
|
|
8330
8939
|
const daemonArgs = [
|
|
8331
|
-
|
|
8332
|
-
"tsx",
|
|
8333
|
-
cliEntrypoint,
|
|
8940
|
+
...nodeEntrypointArgs2(cliEntrypoint),
|
|
8334
8941
|
"daemon",
|
|
8335
8942
|
"serve",
|
|
8336
8943
|
"--port",
|
|
@@ -8340,19 +8947,19 @@ var init_up_cli = __esm({
|
|
|
8340
8947
|
"--no-relay"
|
|
8341
8948
|
];
|
|
8342
8949
|
daemonChild = spawnFn(process.execPath, daemonArgs, {
|
|
8343
|
-
cwd:
|
|
8950
|
+
cwd: dirname10(cliEntrypoint),
|
|
8344
8951
|
env: { ...process.env },
|
|
8952
|
+
detached: true,
|
|
8345
8953
|
stdio: ["ignore", "pipe", "pipe"]
|
|
8346
8954
|
});
|
|
8347
8955
|
try {
|
|
8348
|
-
await
|
|
8956
|
+
await waitForDaemonReady2(daemonChild, readyTimeout);
|
|
8349
8957
|
} catch (cause) {
|
|
8350
8958
|
options.error.write(`scorel up error: ${cause.message}
|
|
8351
8959
|
`);
|
|
8352
8960
|
daemonChild.kill("SIGTERM");
|
|
8353
8961
|
return 1;
|
|
8354
8962
|
}
|
|
8355
|
-
pipeWithPrefix(daemonChild, "[daemon]", options.output, options.error);
|
|
8356
8963
|
daemonState = await readState(stateDir);
|
|
8357
8964
|
}
|
|
8358
8965
|
if (!daemonState) {
|
|
@@ -8360,16 +8967,17 @@ var init_up_cli = __esm({
|
|
|
8360
8967
|
daemonChild?.kill("SIGTERM");
|
|
8361
8968
|
return 1;
|
|
8362
8969
|
}
|
|
8970
|
+
if (daemonChild) {
|
|
8971
|
+
detachBackgroundDaemon2(daemonChild);
|
|
8972
|
+
}
|
|
8363
8973
|
const webuiArgs = [
|
|
8364
|
-
|
|
8365
|
-
"tsx",
|
|
8366
|
-
cliEntrypoint,
|
|
8974
|
+
...nodeEntrypointArgs2(cliEntrypoint),
|
|
8367
8975
|
"webui",
|
|
8368
8976
|
"--port",
|
|
8369
8977
|
String(flags.webuiPort)
|
|
8370
8978
|
];
|
|
8371
8979
|
const webuiChild = spawnFn(process.execPath, webuiArgs, {
|
|
8372
|
-
cwd:
|
|
8980
|
+
cwd: dirname10(cliEntrypoint),
|
|
8373
8981
|
env: { ...process.env },
|
|
8374
8982
|
stdio: ["ignore", "pipe", "pipe"]
|
|
8375
8983
|
});
|
|
@@ -8386,33 +8994,21 @@ var init_up_cli = __esm({
|
|
|
8386
8994
|
return;
|
|
8387
8995
|
}
|
|
8388
8996
|
shuttingDown = true;
|
|
8389
|
-
daemonChild?.kill("SIGTERM");
|
|
8390
8997
|
webuiChild.kill("SIGTERM");
|
|
8391
8998
|
});
|
|
8392
|
-
const daemonExit = daemonChild ? once(daemonChild) : Promise.resolve(0);
|
|
8393
8999
|
const webuiExit = once(webuiChild);
|
|
8394
|
-
const daemonDeathWatcher = daemonChild ? daemonExit.then((code) => {
|
|
8395
|
-
if (!shuttingDown) {
|
|
8396
|
-
shuttingDown = true;
|
|
8397
|
-
options.error.write(`scorel up daemon exited code=${code}
|
|
8398
|
-
`);
|
|
8399
|
-
webuiChild.kill("SIGTERM");
|
|
8400
|
-
}
|
|
8401
|
-
return code;
|
|
8402
|
-
}) : Promise.resolve(0);
|
|
8403
9000
|
const webuiDeathWatcher = webuiExit.then((code) => {
|
|
8404
9001
|
if (!shuttingDown) {
|
|
8405
9002
|
shuttingDown = true;
|
|
8406
9003
|
options.error.write(`scorel up webui exited code=${code}
|
|
8407
9004
|
`);
|
|
8408
|
-
daemonChild?.kill("SIGTERM");
|
|
8409
9005
|
}
|
|
8410
9006
|
return code;
|
|
8411
9007
|
});
|
|
8412
|
-
const
|
|
9008
|
+
const webuiCode = await webuiDeathWatcher;
|
|
8413
9009
|
detachSigint();
|
|
8414
9010
|
options.output.write("scorel up stopped\n");
|
|
8415
|
-
return
|
|
9011
|
+
return webuiCode === 0 ? 0 : 1;
|
|
8416
9012
|
};
|
|
8417
9013
|
parseUpFlags = (argv, defaultCwd) => {
|
|
8418
9014
|
let daemonPort = DEFAULT_DAEMON_PORT;
|
|
@@ -8452,7 +9048,7 @@ var init_up_cli = __esm({
|
|
|
8452
9048
|
}
|
|
8453
9049
|
return value;
|
|
8454
9050
|
};
|
|
8455
|
-
|
|
9051
|
+
waitForDaemonReady2 = (child, timeoutMs) => new Promise((resolveReady, rejectReady) => {
|
|
8456
9052
|
if (!child.stdout) {
|
|
8457
9053
|
rejectReady(new Error("daemon child has no stdout stream"));
|
|
8458
9054
|
return;
|
|
@@ -8509,6 +9105,12 @@ var init_up_cli = __esm({
|
|
|
8509
9105
|
pipeStreamLines(child.stderr, prefix, error);
|
|
8510
9106
|
}
|
|
8511
9107
|
};
|
|
9108
|
+
detachBackgroundDaemon2 = (child) => {
|
|
9109
|
+
child.stdout?.destroy();
|
|
9110
|
+
child.stderr?.destroy();
|
|
9111
|
+
child.unref();
|
|
9112
|
+
};
|
|
9113
|
+
nodeEntrypointArgs2 = (entrypoint) => entrypoint.endsWith(".ts") ? ["--import", "tsx", entrypoint] : [entrypoint];
|
|
8512
9114
|
pipeStreamLines = (stream, prefix, destination) => {
|
|
8513
9115
|
let buffer = "";
|
|
8514
9116
|
stream.setEncoding?.("utf8");
|
|
@@ -8538,10 +9140,10 @@ var init_up_cli = __esm({
|
|
|
8538
9140
|
});
|
|
8539
9141
|
|
|
8540
9142
|
// apps/cli/src/webui-cli.ts
|
|
8541
|
-
import { spawn as
|
|
9143
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
8542
9144
|
import { existsSync as existsSync4 } from "node:fs";
|
|
8543
|
-
import { dirname as
|
|
8544
|
-
import { fileURLToPath as
|
|
9145
|
+
import { dirname as dirname11, resolve as resolve6 } from "node:path";
|
|
9146
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
8545
9147
|
var DEFAULT_PORT3, DEFAULT_HOST3, runCliWebUi, findWebuiAppDir, buildWebUiSpawnPlan, parseWebUiFlags, requireValue5, waitForChildExit;
|
|
8546
9148
|
var init_webui_cli = __esm({
|
|
8547
9149
|
"apps/cli/src/webui-cli.ts"() {
|
|
@@ -8563,7 +9165,7 @@ var init_webui_cli = __esm({
|
|
|
8563
9165
|
return 1;
|
|
8564
9166
|
}
|
|
8565
9167
|
const plan = buildWebUiSpawnPlan(flags, webuiAppDir);
|
|
8566
|
-
const spawnFn = options.spawn ??
|
|
9168
|
+
const spawnFn = options.spawn ?? spawn3;
|
|
8567
9169
|
const child = spawnFn(plan.command, plan.argv, {
|
|
8568
9170
|
cwd: plan.cwd,
|
|
8569
9171
|
env: plan.env,
|
|
@@ -8572,7 +9174,7 @@ var init_webui_cli = __esm({
|
|
|
8572
9174
|
return await waitForChildExit(child, options);
|
|
8573
9175
|
};
|
|
8574
9176
|
findWebuiAppDir = () => {
|
|
8575
|
-
let cursor =
|
|
9177
|
+
let cursor = dirname11(fileURLToPath3(import.meta.url));
|
|
8576
9178
|
for (let depth = 0; depth < 8; depth += 1) {
|
|
8577
9179
|
const candidate = resolve6(cursor, "apps/webui/package.json");
|
|
8578
9180
|
if (existsSync4(candidate)) {
|
|
@@ -8664,8 +9266,8 @@ import { createHash as createHash3 } from "node:crypto";
|
|
|
8664
9266
|
import { appendFile as appendFile4, mkdir as mkdir8, readFile as readFile13, realpath as realpath3, readdir as readdir7, writeFile as writeFile8 } from "node:fs/promises";
|
|
8665
9267
|
import { createInterface } from "node:readline/promises";
|
|
8666
9268
|
import { homedir as homedir9 } from "node:os";
|
|
8667
|
-
import { fileURLToPath as
|
|
8668
|
-
import { basename as
|
|
9269
|
+
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
9270
|
+
import { basename as basename4, dirname as dirname12, join as join16 } from "node:path";
|
|
8669
9271
|
var cliAppName, cliClientDependency, cliDaemonDependency, defaultSessionsDir, defaultStateDir4, runCli, runProject, runLogs, runAttach, attachCacheScope, attachCacheFilePath, attachDiagnosticsFilePath, findAttachDiagnosticsFilePath, stateDirFromSessionsDir, AttachDiagnostics, readAttachCache, writeAttachCache, emptyAttachCacheSnapshot, mergePersistentEvents, highestSeq, highestCachedStreamSeq, updateAttachCacheSnapshot, removeCompletedTransients, isCachedTransientMessage, AsyncInputQueue, parseAttachOptions, parseLogsOptions, runChat, createSigintHandler, loadOrCreateSession, parseChatOptions, requireValue6, promptIfInteractive, writeUsage, writeProjectUsage, writeEventError, writeToolResult, redactDiagnosticFields, formatDiagnosticLine2, formatDiagnosticValue2, AttachEventRenderer, blocksToText, isCliEntrypoint;
|
|
8670
9272
|
var init_index = __esm({
|
|
8671
9273
|
async "apps/cli/src/index.ts"() {
|
|
@@ -8998,7 +9600,7 @@ var init_index = __esm({
|
|
|
8998
9600
|
if (!sessionsDir) {
|
|
8999
9601
|
return defaultStateDir4();
|
|
9000
9602
|
}
|
|
9001
|
-
return
|
|
9603
|
+
return basename4(sessionsDir) === "sessions" ? dirname12(sessionsDir) : sessionsDir;
|
|
9002
9604
|
};
|
|
9003
9605
|
AttachDiagnostics = class {
|
|
9004
9606
|
#stateDir;
|
|
@@ -9050,7 +9652,7 @@ var init_index = __esm({
|
|
|
9050
9652
|
}
|
|
9051
9653
|
const filePath = attachDiagnosticsFilePath(this.#stateDir, this.#scope, this.#sessionId);
|
|
9052
9654
|
this.#writes.push(
|
|
9053
|
-
mkdir8(
|
|
9655
|
+
mkdir8(dirname12(filePath), { recursive: true }).then(() => appendFile4(filePath, `${line}
|
|
9054
9656
|
`, "utf8"))
|
|
9055
9657
|
);
|
|
9056
9658
|
}
|
|
@@ -9080,7 +9682,7 @@ var init_index = __esm({
|
|
|
9080
9682
|
const filePath = attachCacheFilePath(stateDir, scope, sessionId);
|
|
9081
9683
|
const uniqueEvents = mergePersistentEvents(snapshot.events);
|
|
9082
9684
|
const transients = removeCompletedTransients(snapshot.transients, uniqueEvents);
|
|
9083
|
-
await mkdir8(
|
|
9685
|
+
await mkdir8(dirname12(filePath), { recursive: true });
|
|
9084
9686
|
await writeFile8(
|
|
9085
9687
|
filePath,
|
|
9086
9688
|
`${JSON.stringify({ version: 1, scope, sessionId: String(sessionId), events: uniqueEvents, transients }, null, 2)}
|
|
@@ -9217,12 +9819,14 @@ var init_index = __esm({
|
|
|
9217
9819
|
return { sessionId, tail, attach, remoteUrl };
|
|
9218
9820
|
};
|
|
9219
9821
|
runChat = async (options, io) => {
|
|
9220
|
-
const
|
|
9221
|
-
const
|
|
9822
|
+
const configScope = { scorelHomeDir: options.stateDir };
|
|
9823
|
+
const loadProjectConfig = async (project2) => options.config ?? await loadScorelConfig({ cwd: project2.workDir, ...configScope });
|
|
9824
|
+
const loadProjectConfigProfile = async (project2) => options.config ?? await loadScorelConfigProfile({ cwd: project2.workDir, ...configScope });
|
|
9222
9825
|
const daemon = new ScorelHost({
|
|
9223
9826
|
sessionsDir: options.sessionsDir,
|
|
9224
9827
|
projectsPath: join16(options.stateDir, "projects.json"),
|
|
9225
9828
|
deviceId: asDeviceId("device_local"),
|
|
9829
|
+
scorelHomeDir: options.stateDir,
|
|
9226
9830
|
loadConfig: async ({ project: project2 }) => loadProjectConfig(project2),
|
|
9227
9831
|
loadConfigProfile: async ({ project: project2 }) => loadProjectConfigProfile(project2),
|
|
9228
9832
|
createRuntime: async ({ project: project2, selectedModel, purpose }) => createRealRuntime({
|
|
@@ -9356,8 +9960,10 @@ var init_index = __esm({
|
|
|
9356
9960
|
"Usage: scorel chat [--session <id>] [--cwd <dir>]",
|
|
9357
9961
|
" scorel [--session <id>] [--cwd <dir>]",
|
|
9358
9962
|
" scorel attach --session <id> --remote <ws-url> --token <token>",
|
|
9963
|
+
" scorel host start [--host <h>] [--port <p>] [--token <t>] [--project <dir>]",
|
|
9964
|
+
" [--relay <relay-url> | --no-relay] [--replace] [--idle-timeout-ms <ms>]",
|
|
9359
9965
|
" scorel host serve [--host <h>] [--port <p>] [--token <t>] [--project <dir>]",
|
|
9360
|
-
" [--relay <relay-url> | --no-relay] [--replace]",
|
|
9966
|
+
" [--relay <relay-url> | --no-relay] [--replace] [--idle-timeout-ms <ms>]",
|
|
9361
9967
|
" scorel host status [--show-token]",
|
|
9362
9968
|
" scorel host stop",
|
|
9363
9969
|
" scorel host reset",
|
|
@@ -9496,7 +10102,7 @@ ${text}
|
|
|
9496
10102
|
if (!process.argv[1]) return false;
|
|
9497
10103
|
const [argvPath, modulePath] = await Promise.all([
|
|
9498
10104
|
realpath3(process.argv[1]).catch(() => process.argv[1]),
|
|
9499
|
-
realpath3(
|
|
10105
|
+
realpath3(fileURLToPath4(import.meta.url)).catch(() => fileURLToPath4(import.meta.url))
|
|
9500
10106
|
]);
|
|
9501
10107
|
return argvPath === modulePath;
|
|
9502
10108
|
};
|