@chanlerdev/scorel 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -0
- package/dist/index.js +1126 -211
- package/dist/index.js.map +4 -4
- package/docs/CHANGELOG.md +94 -0
- package/docs/ROADMAP.md +20 -2
- package/docs/SHIP.md +9 -5
- 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/S0064-gui-product-intent-and-boundary.md +1 -1
- package/docs/spec/ship/S0073-provider-model-profile-contract.md +14 -7
- 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/docs/spec/ship/S0103-daemon-lifecycle-and-settings-resilience.md +61 -0
- package/docs/spec/ship/S0104-tool-result-artifacts.md +64 -0
- package/docs/spec/ship/S0105-cli-update-and-gui-release.md +128 -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, renderFullBashResult, writeBashArtifact, safeArtifactSegment, projectBashStreams, projectOutputStream, resolveDefaultShell, resolveRtkCommand, rtkRewriteResult, executableRewriteCommand, readRtkGain, rtkSavedTokenDelta, withRtkSavings, nonNegativeInteger, isRecord3, shellQuote, shellCommandArgs, userShell, truncate, sliceBytes, 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");
|
|
@@ -1868,30 +1914,61 @@ String: ${input.old_string}`
|
|
|
1868
1914
|
defineTool({
|
|
1869
1915
|
name: "Bash",
|
|
1870
1916
|
description: "Execute a shell command in the workspace with timeout and output truncation.",
|
|
1871
|
-
execute: async (
|
|
1917
|
+
execute: async (toolCallId, args, signal) => {
|
|
1872
1918
|
const input = parseBashArgs(args);
|
|
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 await bashResult({
|
|
1944
|
+
exitCode: 0,
|
|
1945
|
+
stdout: result.stdout,
|
|
1946
|
+
stderr: result.stderr,
|
|
1947
|
+
cwd: commandCwd,
|
|
1948
|
+
outputLimit,
|
|
1949
|
+
artifactDir: options.toolResultArtifacts?.dir,
|
|
1950
|
+
toolCallId,
|
|
1951
|
+
shell: defaultShell,
|
|
1952
|
+
command,
|
|
1953
|
+
rtk: withRtkSavings(rtkResult, rtkSavedTokens)
|
|
1954
|
+
});
|
|
1884
1955
|
} catch (cause) {
|
|
1885
1956
|
if (isTimeoutError(cause)) {
|
|
1886
1957
|
throw new Error(`Bash command timed out after ${timeoutMs}ms`);
|
|
1887
1958
|
}
|
|
1888
1959
|
if (isExecError(cause)) {
|
|
1889
|
-
|
|
1960
|
+
const rtkSavedTokens = rtk?.executable ? await rtkSavedTokenDelta(rtk.executable, commandCwd, rtkGainBefore) : void 0;
|
|
1961
|
+
return await bashResult({
|
|
1890
1962
|
exitCode: typeof cause.code === "number" ? cause.code : 1,
|
|
1891
1963
|
stdout: String(cause.stdout ?? ""),
|
|
1892
1964
|
stderr: String(cause.stderr ?? cause.message),
|
|
1893
1965
|
cwd: commandCwd,
|
|
1894
|
-
outputLimit
|
|
1966
|
+
outputLimit,
|
|
1967
|
+
artifactDir: options.toolResultArtifacts?.dir,
|
|
1968
|
+
toolCallId,
|
|
1969
|
+
shell: defaultShell,
|
|
1970
|
+
command,
|
|
1971
|
+
rtk: withRtkSavings(rtkResult, rtkSavedTokens)
|
|
1895
1972
|
});
|
|
1896
1973
|
}
|
|
1897
1974
|
throw cause;
|
|
@@ -1905,7 +1982,7 @@ String: ${input.old_string}`
|
|
|
1905
1982
|
const input = parseGlobArgs(args);
|
|
1906
1983
|
const limit = input.head_limit ?? DEFAULT_SEARCH_LIMIT;
|
|
1907
1984
|
const offset = input.offset ?? 0;
|
|
1908
|
-
const all = await runRipgrep(["--files", "--hidden", "--glob", input.pattern, ...vcsExcludes()], workspaceTarget(input.path), root, signal);
|
|
1985
|
+
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
1986
|
const selected = paginate(all, limit, offset);
|
|
1910
1987
|
const text = selected.items.map(toWorkspaceRelative(root)).join("\n");
|
|
1911
1988
|
return textResult(text || "No files found", {
|
|
@@ -2256,28 +2333,191 @@ ${filenames.join("\n")}`,
|
|
|
2256
2333
|
throw cause;
|
|
2257
2334
|
}
|
|
2258
2335
|
};
|
|
2259
|
-
bashResult = (input) => {
|
|
2260
|
-
const
|
|
2261
|
-
const
|
|
2262
|
-
|
|
2336
|
+
bashResult = async (input) => {
|
|
2337
|
+
const stdoutBytes = Buffer.byteLength(input.stdout);
|
|
2338
|
+
const stderrBytes = Buffer.byteLength(input.stderr);
|
|
2339
|
+
const fullResult = renderFullBashResult(input);
|
|
2340
|
+
const resultBytes = Buffer.byteLength(fullResult);
|
|
2341
|
+
const shouldArchive = Boolean(input.artifactDir) && resultBytes > input.outputLimit;
|
|
2342
|
+
const artifactPath = shouldArchive && input.artifactDir ? await writeBashArtifact(input.artifactDir, input.toolCallId, fullResult) : void 0;
|
|
2343
|
+
const projection = artifactPath ? projectBashStreams(input.stdout, input.stderr, input.outputLimit) : void 0;
|
|
2344
|
+
const stdout = projection?.stdout ?? truncate(input.stdout, input.outputLimit, "stdout");
|
|
2345
|
+
const stderr = projection?.stderr ?? truncate(input.stderr, input.outputLimit, "stderr");
|
|
2346
|
+
const text = artifactPath ? [
|
|
2347
|
+
`exitCode: ${input.exitCode}`,
|
|
2348
|
+
`cwd: ${input.cwd}`,
|
|
2349
|
+
`artifact: ${artifactPath}`,
|
|
2350
|
+
`resultBytes: ${resultBytes}`,
|
|
2351
|
+
`stdoutBytes: ${stdoutBytes}`,
|
|
2352
|
+
`stderrBytes: ${stderrBytes}`,
|
|
2353
|
+
...projection?.lines ?? []
|
|
2354
|
+
].join("\n") : `exitCode: ${input.exitCode}
|
|
2263
2355
|
cwd: ${input.cwd}
|
|
2264
2356
|
stdout:
|
|
2265
2357
|
${stdout}
|
|
2266
2358
|
stderr:
|
|
2267
|
-
${stderr}
|
|
2359
|
+
${stderr}`;
|
|
2360
|
+
return textResult(text, {
|
|
2268
2361
|
exitCode: input.exitCode,
|
|
2269
|
-
cwd: input.cwd
|
|
2362
|
+
cwd: input.cwd,
|
|
2363
|
+
...artifactPath ? {
|
|
2364
|
+
artifact: {
|
|
2365
|
+
path: artifactPath,
|
|
2366
|
+
resultBytes,
|
|
2367
|
+
stdoutBytes,
|
|
2368
|
+
stderrBytes
|
|
2369
|
+
}
|
|
2370
|
+
} : {},
|
|
2371
|
+
...input.shell ? { shell: input.shell } : {},
|
|
2372
|
+
...input.command ? { command: input.command } : {},
|
|
2373
|
+
...input.rtk ? {
|
|
2374
|
+
rtk: {
|
|
2375
|
+
...input.rtk,
|
|
2376
|
+
estimatedOutputTokens: estimateTokens(`${stdout}
|
|
2377
|
+
${stderr}`)
|
|
2378
|
+
}
|
|
2379
|
+
} : {}
|
|
2270
2380
|
});
|
|
2271
2381
|
};
|
|
2382
|
+
renderFullBashResult = (input) => `exitCode: ${input.exitCode}
|
|
2383
|
+
cwd: ${input.cwd}
|
|
2384
|
+
stdout:
|
|
2385
|
+
${input.stdout}
|
|
2386
|
+
stderr:
|
|
2387
|
+
${input.stderr}`;
|
|
2388
|
+
writeBashArtifact = async (artifactDir, toolCallId, content) => {
|
|
2389
|
+
const directory = resolve(artifactDir, safeArtifactSegment(toolCallId));
|
|
2390
|
+
await mkdir2(directory, { recursive: true });
|
|
2391
|
+
const path = resolve(directory, "result.txt");
|
|
2392
|
+
await writeFile2(path, content, { encoding: "utf8", mode: 384 });
|
|
2393
|
+
return path;
|
|
2394
|
+
};
|
|
2395
|
+
safeArtifactSegment = (value) => value.replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 120) || "tool_call";
|
|
2396
|
+
projectBashStreams = (stdout, stderr, maxBytes) => {
|
|
2397
|
+
const streams = [
|
|
2398
|
+
{ label: "stdout", value: stdout },
|
|
2399
|
+
{ label: "stderr", value: stderr }
|
|
2400
|
+
].filter((stream) => Buffer.byteLength(stream.value) > 0);
|
|
2401
|
+
if (streams.length === 0) {
|
|
2402
|
+
return { lines: ["stdout:", "", "stderr:", ""], stdout: "", stderr: "" };
|
|
2403
|
+
}
|
|
2404
|
+
const perStreamBudget = Math.max(1, Math.floor(maxBytes / streams.length));
|
|
2405
|
+
const projected = streams.map((stream) => projectOutputStream(stream.value, perStreamBudget, stream.label));
|
|
2406
|
+
const stdoutText = projected.find((stream) => stream.label === "stdout")?.text ?? "stdout:\n";
|
|
2407
|
+
const stderrText = projected.find((stream) => stream.label === "stderr")?.text ?? "stderr:\n";
|
|
2408
|
+
return {
|
|
2409
|
+
lines: projected.map((stream) => stream.text),
|
|
2410
|
+
stdout: stdoutText,
|
|
2411
|
+
stderr: stderrText
|
|
2412
|
+
};
|
|
2413
|
+
};
|
|
2414
|
+
projectOutputStream = (value, maxBytes, label) => {
|
|
2415
|
+
const bytes = Buffer.byteLength(value);
|
|
2416
|
+
if (bytes <= maxBytes) {
|
|
2417
|
+
return { label, text: `${label}:
|
|
2418
|
+
${value}` };
|
|
2419
|
+
}
|
|
2420
|
+
const headBytes = Math.max(1, Math.floor(maxBytes / 2));
|
|
2421
|
+
const tailBytes = Math.max(1, maxBytes - headBytes);
|
|
2422
|
+
return {
|
|
2423
|
+
label,
|
|
2424
|
+
text: [
|
|
2425
|
+
`${label} head:`,
|
|
2426
|
+
sliceBytes(value, 0, headBytes),
|
|
2427
|
+
`${label} tail:`,
|
|
2428
|
+
sliceBytes(value, Math.max(0, bytes - tailBytes), bytes),
|
|
2429
|
+
`[${label} archived: ${bytes} bytes; projection budget ${maxBytes} bytes]`
|
|
2430
|
+
].join("\n")
|
|
2431
|
+
};
|
|
2432
|
+
};
|
|
2433
|
+
resolveDefaultShell = (input) => {
|
|
2434
|
+
const shell = input || process.env.SHELL || userShell() || "/bin/sh";
|
|
2435
|
+
return shell.trim() || "/bin/sh";
|
|
2436
|
+
};
|
|
2437
|
+
resolveRtkCommand = async (rtk, command) => {
|
|
2438
|
+
if (rtk?.enabled !== true || typeof rtk.executable !== "string" || rtk.executable.length === 0) {
|
|
2439
|
+
return { applied: false };
|
|
2440
|
+
}
|
|
2441
|
+
try {
|
|
2442
|
+
const result = await execFileAsync(rtk.executable, ["rewrite", command], {
|
|
2443
|
+
timeout: 5e3,
|
|
2444
|
+
maxBuffer: 1024 * 1024
|
|
2445
|
+
});
|
|
2446
|
+
return rtkRewriteResult(result.stdout, rtk.executable);
|
|
2447
|
+
} catch (cause) {
|
|
2448
|
+
if (isExecError(cause) && typeof cause.stdout === "string") {
|
|
2449
|
+
return rtkRewriteResult(cause.stdout, rtk.executable);
|
|
2450
|
+
}
|
|
2451
|
+
return { applied: false };
|
|
2452
|
+
}
|
|
2453
|
+
};
|
|
2454
|
+
rtkRewriteResult = (stdout, executable) => {
|
|
2455
|
+
const rewrittenCommand = stdout.trim();
|
|
2456
|
+
return rewrittenCommand ? { applied: true, rewrittenCommand, executionCommand: executableRewriteCommand(rewrittenCommand, executable) } : { applied: false };
|
|
2457
|
+
};
|
|
2458
|
+
executableRewriteCommand = (command, executable) => command.replace(/^rtk(?=\s|$)/, shellQuote(executable));
|
|
2459
|
+
readRtkGain = async (rtkExecutable, cwd) => {
|
|
2460
|
+
try {
|
|
2461
|
+
const { stdout } = await execFileAsync(rtkExecutable, ["gain", "--project", "--format", "json"], {
|
|
2462
|
+
cwd,
|
|
2463
|
+
timeout: 5e3,
|
|
2464
|
+
maxBuffer: 5e6
|
|
2465
|
+
});
|
|
2466
|
+
const parsed = JSON.parse(stdout);
|
|
2467
|
+
if (!isRecord3(parsed) || !isRecord3(parsed.summary)) {
|
|
2468
|
+
return void 0;
|
|
2469
|
+
}
|
|
2470
|
+
return { savedTokens: nonNegativeInteger(parsed.summary.total_saved) };
|
|
2471
|
+
} catch {
|
|
2472
|
+
return void 0;
|
|
2473
|
+
}
|
|
2474
|
+
};
|
|
2475
|
+
rtkSavedTokenDelta = async (rtkExecutable, cwd, before) => {
|
|
2476
|
+
if (!before) {
|
|
2477
|
+
return void 0;
|
|
2478
|
+
}
|
|
2479
|
+
const after = await readRtkGain(rtkExecutable, cwd);
|
|
2480
|
+
if (!after) {
|
|
2481
|
+
return void 0;
|
|
2482
|
+
}
|
|
2483
|
+
return Math.max(0, after.savedTokens - before.savedTokens);
|
|
2484
|
+
};
|
|
2485
|
+
withRtkSavings = (rtk, savedTokens) => ({
|
|
2486
|
+
...rtk,
|
|
2487
|
+
...rtk.applied && savedTokens !== void 0 ? { estimatedSavedTokens: savedTokens } : {}
|
|
2488
|
+
});
|
|
2489
|
+
nonNegativeInteger = (value) => {
|
|
2490
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
2491
|
+
return 0;
|
|
2492
|
+
}
|
|
2493
|
+
return Math.floor(value);
|
|
2494
|
+
};
|
|
2495
|
+
isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2496
|
+
shellQuote = (value) => `'${value.replace(/'/g, "'\\''")}'`;
|
|
2497
|
+
shellCommandArgs = (shell, command) => {
|
|
2498
|
+
const name = basename2(shell).toLowerCase();
|
|
2499
|
+
if (name === "csh" || name === "tcsh" || name === "fish") {
|
|
2500
|
+
return ["-c", command];
|
|
2501
|
+
}
|
|
2502
|
+
return ["-lc", command];
|
|
2503
|
+
};
|
|
2504
|
+
userShell = () => {
|
|
2505
|
+
try {
|
|
2506
|
+
return userInfo().shell ?? void 0;
|
|
2507
|
+
} catch {
|
|
2508
|
+
return void 0;
|
|
2509
|
+
}
|
|
2510
|
+
};
|
|
2272
2511
|
truncate = (value, maxBytes, label) => {
|
|
2273
2512
|
const bytes = Buffer.byteLength(value);
|
|
2274
2513
|
if (bytes <= maxBytes) {
|
|
2275
2514
|
return value;
|
|
2276
2515
|
}
|
|
2277
|
-
const truncated =
|
|
2516
|
+
const truncated = sliceBytes(value, 0, maxBytes);
|
|
2278
2517
|
return `${truncated}
|
|
2279
2518
|
[${label} truncated: ${bytes} bytes > ${maxBytes} bytes]`;
|
|
2280
2519
|
};
|
|
2520
|
+
sliceBytes = (value, start, end) => Buffer.from(value).subarray(start, end).toString("utf8");
|
|
2281
2521
|
textResult = (text, details) => ({
|
|
2282
2522
|
content: [{ type: "text", text }],
|
|
2283
2523
|
details
|
|
@@ -2452,7 +2692,7 @@ var init_tools = __esm({
|
|
|
2452
2692
|
});
|
|
2453
2693
|
|
|
2454
2694
|
// packages/core/src/channel/index.ts
|
|
2455
|
-
var createSendChannelMessageTool, parseSendChannelMessageInput, parseAttachments, optionalString2,
|
|
2695
|
+
var createSendChannelMessageTool, parseSendChannelMessageInput, parseAttachments, optionalString2, isRecord4;
|
|
2456
2696
|
var init_channel = __esm({
|
|
2457
2697
|
"packages/core/src/channel/index.ts"() {
|
|
2458
2698
|
"use strict";
|
|
@@ -2470,7 +2710,7 @@ var init_channel = __esm({
|
|
|
2470
2710
|
}
|
|
2471
2711
|
});
|
|
2472
2712
|
parseSendChannelMessageInput = (value) => {
|
|
2473
|
-
if (!
|
|
2713
|
+
if (!isRecord4(value)) {
|
|
2474
2714
|
throw new Error("SendChannelMessage args must be an object");
|
|
2475
2715
|
}
|
|
2476
2716
|
const text = typeof value.text === "string" && value.text.trim().length > 0 ? value.text : void 0;
|
|
@@ -2499,7 +2739,7 @@ var init_channel = __esm({
|
|
|
2499
2739
|
throw new Error("SendChannelMessage.attachments must be an array");
|
|
2500
2740
|
}
|
|
2501
2741
|
return value.map((item, index) => {
|
|
2502
|
-
if (!
|
|
2742
|
+
if (!isRecord4(item)) {
|
|
2503
2743
|
throw new Error(`SendChannelMessage.attachments.${index} must be an object`);
|
|
2504
2744
|
}
|
|
2505
2745
|
if (item.type !== "image" && item.type !== "file") {
|
|
@@ -2528,14 +2768,14 @@ var init_channel = __esm({
|
|
|
2528
2768
|
}
|
|
2529
2769
|
return value;
|
|
2530
2770
|
};
|
|
2531
|
-
|
|
2771
|
+
isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2532
2772
|
}
|
|
2533
2773
|
});
|
|
2534
2774
|
|
|
2535
2775
|
// packages/core/src/extensions/index.ts
|
|
2536
2776
|
import { readFile as readFile5 } from "node:fs/promises";
|
|
2537
2777
|
import { dirname as dirname4, resolve as resolve2 } from "node:path";
|
|
2538
|
-
var loadExtensionManifest, parseExtensionManifest, requireString2, requireIdentifier2, requireKind, requireRelativePath, optionalRelativePaths,
|
|
2778
|
+
var loadExtensionManifest, parseExtensionManifest, requireString2, requireIdentifier2, requireKind, requireRelativePath, optionalRelativePaths, isRecord5;
|
|
2539
2779
|
var init_extensions = __esm({
|
|
2540
2780
|
"packages/core/src/extensions/index.ts"() {
|
|
2541
2781
|
"use strict";
|
|
@@ -2548,7 +2788,7 @@ var init_extensions = __esm({
|
|
|
2548
2788
|
const message = cause instanceof Error ? cause.message : String(cause);
|
|
2549
2789
|
throw new Error(`Invalid extension manifest JSON at ${manifestPath}: ${message}`);
|
|
2550
2790
|
}
|
|
2551
|
-
if (!
|
|
2791
|
+
if (!isRecord5(value)) {
|
|
2552
2792
|
throw new Error(`Extension manifest at ${manifestPath} must be an object`);
|
|
2553
2793
|
}
|
|
2554
2794
|
const rootDir = dirname4(resolve2(manifestPath));
|
|
@@ -2604,7 +2844,7 @@ var init_extensions = __esm({
|
|
|
2604
2844
|
}
|
|
2605
2845
|
return value.map((item, index) => requireRelativePath(item, `${name}.${index}`, manifestPath));
|
|
2606
2846
|
};
|
|
2607
|
-
|
|
2847
|
+
isRecord5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2608
2848
|
}
|
|
2609
2849
|
});
|
|
2610
2850
|
|
|
@@ -2613,7 +2853,7 @@ import { existsSync } from "node:fs";
|
|
|
2613
2853
|
import { readdir as readdir4, readFile as readFile6 } from "node:fs/promises";
|
|
2614
2854
|
import { homedir as homedir2, platform, release } from "node:os";
|
|
2615
2855
|
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,
|
|
2856
|
+
var BASELINE_PROMPT, buildInstructionSnapshot, renderSystemPrompt, section, discoverAgentsSources, projectAgentsPaths, findGitRoot, renderAgentsBlock, renderWorkspaceBlock, renderEnvironmentBlock, renderTimeBlock, isNodeErrorCode2;
|
|
2617
2857
|
var init_instructions = __esm({
|
|
2618
2858
|
"packages/core/src/instructions/index.ts"() {
|
|
2619
2859
|
"use strict";
|
|
@@ -2677,7 +2917,7 @@ var init_instructions = __esm({
|
|
|
2677
2917
|
content
|
|
2678
2918
|
});
|
|
2679
2919
|
} catch (cause) {
|
|
2680
|
-
if (!
|
|
2920
|
+
if (!isNodeErrorCode2(cause, "ENOENT") && !isNodeErrorCode2(cause, "ENOTDIR")) {
|
|
2681
2921
|
throw cause;
|
|
2682
2922
|
}
|
|
2683
2923
|
}
|
|
@@ -2740,7 +2980,7 @@ var init_instructions = __esm({
|
|
|
2740
2980
|
};
|
|
2741
2981
|
renderEnvironmentBlock = (env) => [`Platform: ${platform()} ${release()}`, `Shell: ${env.SHELL ?? "unknown"}`].join("\n");
|
|
2742
2982
|
renderTimeBlock = (timestamp) => [`Session started at: ${new Date(timestamp).toISOString()}`, `Timezone: ${Intl.DateTimeFormat().resolvedOptions().timeZone}`].join("\n");
|
|
2743
|
-
|
|
2983
|
+
isNodeErrorCode2 = (cause, code) => cause instanceof Error && "code" in cause && cause.code === code;
|
|
2744
2984
|
}
|
|
2745
2985
|
});
|
|
2746
2986
|
|
|
@@ -2748,7 +2988,7 @@ var init_instructions = __esm({
|
|
|
2748
2988
|
import { appendFile, mkdir as mkdir3, readFile as readFile7, writeFile as writeFile3 } from "node:fs/promises";
|
|
2749
2989
|
import { homedir as homedir3 } from "node:os";
|
|
2750
2990
|
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,
|
|
2991
|
+
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
2992
|
var init_memory = __esm({
|
|
2753
2993
|
"packages/core/src/memory/index.ts"() {
|
|
2754
2994
|
"use strict";
|
|
@@ -2950,7 +3190,7 @@ var init_memory = __esm({
|
|
|
2950
3190
|
try {
|
|
2951
3191
|
await writeFile3(path, content, { encoding: "utf8", flag: "wx", mode: 384 });
|
|
2952
3192
|
} catch (cause) {
|
|
2953
|
-
if (!
|
|
3193
|
+
if (!isNodeErrorCode3(cause, "EEXIST")) {
|
|
2954
3194
|
throw cause;
|
|
2955
3195
|
}
|
|
2956
3196
|
}
|
|
@@ -2959,7 +3199,7 @@ var init_memory = __esm({
|
|
|
2959
3199
|
try {
|
|
2960
3200
|
return await readFile7(path, "utf8");
|
|
2961
3201
|
} catch (cause) {
|
|
2962
|
-
if (
|
|
3202
|
+
if (isNodeErrorCode3(cause, "ENOENT")) {
|
|
2963
3203
|
return "";
|
|
2964
3204
|
}
|
|
2965
3205
|
throw cause;
|
|
@@ -2980,7 +3220,7 @@ var init_memory = __esm({
|
|
|
2980
3220
|
normalizeMarkdownFile = (value) => `${value.trimEnd()}
|
|
2981
3221
|
`;
|
|
2982
3222
|
parseAppendDailyInput = (value) => {
|
|
2983
|
-
if (!
|
|
3223
|
+
if (!isRecord6(value)) {
|
|
2984
3224
|
throw new Error("AppendDaily args must be an object");
|
|
2985
3225
|
}
|
|
2986
3226
|
const summary = requireString3(value.summary, "summary");
|
|
@@ -3046,19 +3286,19 @@ var init_memory = __esm({
|
|
|
3046
3286
|
optionalNumber2 = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
3047
3287
|
optionalString3 = (value) => typeof value === "string" && value.trim() ? value : void 0;
|
|
3048
3288
|
parseLastFailure = (value) => {
|
|
3049
|
-
if (!
|
|
3289
|
+
if (!isRecord6(value)) return void 0;
|
|
3050
3290
|
const at = optionalNumber2(value.at);
|
|
3051
3291
|
const message = optionalString3(value.message);
|
|
3052
3292
|
return at !== void 0 && message ? { at, message } : void 0;
|
|
3053
3293
|
};
|
|
3054
|
-
|
|
3294
|
+
isRecord6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3055
3295
|
safeProjectId = (projectId) => {
|
|
3056
3296
|
if (!/^[A-Za-z0-9_-]+$/.test(projectId)) {
|
|
3057
3297
|
throw new Error("projectId must contain only letters, numbers, underscores, or hyphens");
|
|
3058
3298
|
}
|
|
3059
3299
|
return projectId;
|
|
3060
3300
|
};
|
|
3061
|
-
|
|
3301
|
+
isNodeErrorCode3 = (cause, code) => cause instanceof Error && "code" in cause && cause.code === code;
|
|
3062
3302
|
}
|
|
3063
3303
|
});
|
|
3064
3304
|
|
|
@@ -3347,7 +3587,7 @@ var init_pi_ai = __esm({
|
|
|
3347
3587
|
});
|
|
3348
3588
|
|
|
3349
3589
|
// packages/core/src/runtime/index.ts
|
|
3350
|
-
var ScorelRuntime, normalizeAssistantMessage, isAssistantMessage, partialAssistantMessage;
|
|
3590
|
+
var ScorelRuntime, toolResultForContext, normalizeAssistantMessage, isAssistantMessage, partialAssistantMessage;
|
|
3351
3591
|
var init_runtime = __esm({
|
|
3352
3592
|
"packages/core/src/runtime/index.ts"() {
|
|
3353
3593
|
"use strict";
|
|
@@ -3496,7 +3736,7 @@ var init_runtime = __esm({
|
|
|
3496
3736
|
type: "tool_result",
|
|
3497
3737
|
toolCallId: toolCall.toolCallId,
|
|
3498
3738
|
toolName: toolCall.toolName,
|
|
3499
|
-
result,
|
|
3739
|
+
result: toolResultForContext(result),
|
|
3500
3740
|
isError
|
|
3501
3741
|
};
|
|
3502
3742
|
return {
|
|
@@ -3505,6 +3745,9 @@ var init_runtime = __esm({
|
|
|
3505
3745
|
};
|
|
3506
3746
|
}
|
|
3507
3747
|
};
|
|
3748
|
+
toolResultForContext = (result) => ({
|
|
3749
|
+
content: result.content
|
|
3750
|
+
});
|
|
3508
3751
|
normalizeAssistantMessage = (value, streamed, fallbackStopReason) => {
|
|
3509
3752
|
if (value) {
|
|
3510
3753
|
if (!isAssistantMessage(value)) {
|
|
@@ -3536,7 +3779,7 @@ var init_runtime = __esm({
|
|
|
3536
3779
|
import { appendFile as appendFile2, mkdir as mkdir4, readFile as readFile8, writeFile as writeFile4 } from "node:fs/promises";
|
|
3537
3780
|
import { dirname as dirname6, join as join7 } from "node:path";
|
|
3538
3781
|
function assertTreeEvent(value) {
|
|
3539
|
-
if (!
|
|
3782
|
+
if (!isRecord7(value)) {
|
|
3540
3783
|
throw new SessionStoreError("invalid_event", "Event must be an object");
|
|
3541
3784
|
}
|
|
3542
3785
|
if (value.type === "session_header") {
|
|
@@ -3548,7 +3791,7 @@ function assertTreeEvent(value) {
|
|
|
3548
3791
|
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
3792
|
throw new SessionStoreError("invalid_event", "Event is missing required base fields");
|
|
3550
3793
|
}
|
|
3551
|
-
if ((value.type === "user_message" || value.type === "assistant_message" || value.type === "tool_result") && !
|
|
3794
|
+
if ((value.type === "user_message" || value.type === "assistant_message" || value.type === "tool_result") && !isRecord7(value.message)) {
|
|
3552
3795
|
throw new SessionStoreError("invalid_event", "Message event is missing message payload");
|
|
3553
3796
|
}
|
|
3554
3797
|
if (value.type === "session_title_updated" && !isSessionTitleUpdated(value)) {
|
|
@@ -3573,7 +3816,7 @@ function assertTreeEvent(value) {
|
|
|
3573
3816
|
throw new SessionStoreError("invalid_event", "skill_index_delta is missing delta payload");
|
|
3574
3817
|
}
|
|
3575
3818
|
}
|
|
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,
|
|
3819
|
+
var SessionStoreError, SessionTree, JsonlSession, sessionFilePath, sessionLogFilePath, sessionArtifactsDirPath, 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
3820
|
var init_session = __esm({
|
|
3578
3821
|
"packages/core/src/session/index.ts"() {
|
|
3579
3822
|
"use strict";
|
|
@@ -3754,6 +3997,7 @@ var init_session = __esm({
|
|
|
3754
3997
|
};
|
|
3755
3998
|
sessionFilePath = (sessionsDir, sessionId) => join7(sessionsDir, `${sessionId}.jsonl`);
|
|
3756
3999
|
sessionLogFilePath = (sessionsDir, sessionId) => join7(sessionsDir, `${sessionId}.log`);
|
|
4000
|
+
sessionArtifactsDirPath = (sessionsDir, sessionId) => join7(sessionsDir, `${sessionId}.artifacts`);
|
|
3757
4001
|
createSession = async ({ sessionsDir, header }) => {
|
|
3758
4002
|
const validHeader = parseHeader(header);
|
|
3759
4003
|
await mkdir4(sessionsDir, { recursive: true });
|
|
@@ -3841,13 +4085,13 @@ var init_session = __esm({
|
|
|
3841
4085
|
}
|
|
3842
4086
|
};
|
|
3843
4087
|
parseHeader = (value) => {
|
|
3844
|
-
if (!
|
|
4088
|
+
if (!isRecord7(value)) {
|
|
3845
4089
|
throw new SessionStoreError("invalid_header", "Session header must be an object");
|
|
3846
4090
|
}
|
|
3847
4091
|
if (value.version !== 1 || typeof value.sessionId !== "string" || typeof value.deviceId !== "string") {
|
|
3848
4092
|
throw new SessionStoreError("invalid_header", "Session header is missing required identity fields");
|
|
3849
4093
|
}
|
|
3850
|
-
if (typeof value.createdAt !== "number" || !
|
|
4094
|
+
if (typeof value.createdAt !== "number" || !isRecord7(value.meta)) {
|
|
3851
4095
|
throw new SessionStoreError("invalid_header", "Session header is missing createdAt or meta");
|
|
3852
4096
|
}
|
|
3853
4097
|
if (typeof value.meta.projectId !== "string" || value.meta.projectId.length === 0) {
|
|
@@ -3861,7 +4105,7 @@ var init_session = __esm({
|
|
|
3861
4105
|
return value;
|
|
3862
4106
|
};
|
|
3863
4107
|
validateSessionMatch = (header, value) => {
|
|
3864
|
-
if (!
|
|
4108
|
+
if (!isRecord7(value) || typeof value.sessionId !== "string") {
|
|
3865
4109
|
throw new SessionStoreError("invalid_header", "Event must be an object with a sessionId");
|
|
3866
4110
|
}
|
|
3867
4111
|
if (value.sessionId !== header.sessionId) {
|
|
@@ -3870,24 +4114,24 @@ var init_session = __esm({
|
|
|
3870
4114
|
};
|
|
3871
4115
|
isConversationEvent = (event) => event.type === "user_message" || event.type === "assistant_message" || event.type === "tool_result" || event.type === "harness_item" || event.type === "compact";
|
|
3872
4116
|
isInstructionSnapshot = (value) => {
|
|
3873
|
-
if (!
|
|
4117
|
+
if (!isRecord7(value) || value.version !== 1 || typeof value.cwd !== "string" || !Array.isArray(value.sections)) {
|
|
3874
4118
|
return false;
|
|
3875
4119
|
}
|
|
3876
4120
|
return value.sections.every(
|
|
3877
|
-
(section2) =>
|
|
4121
|
+
(section2) => isRecord7(section2) && typeof section2.kind === "string" && typeof section2.frozenAt === "number" && typeof section2.renderedBlock === "string"
|
|
3878
4122
|
);
|
|
3879
4123
|
};
|
|
3880
|
-
isHarnessItem = (value) =>
|
|
4124
|
+
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
4125
|
isCompactEvent = (value) => typeof value.summary === "string" && typeof value.compactedThrough === "string" && typeof value.tokensBefore === "number" && typeof value.tokensAfter === "number" && typeof value.retainedEventCount === "number";
|
|
3882
4126
|
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) =>
|
|
4127
|
+
(item) => isRecord7(item) && typeof item.id === "string" && Array.isArray(item.content) && typeof item.createdAt === "number" && typeof item.updatedAt === "number" && typeof item.clientId === "string"
|
|
3884
4128
|
);
|
|
3885
|
-
isSessionTitleUpdated = (value) => typeof value.title === "string" && value.title.length > 0 && (value.source === "model" || value.source === "user") && (value.derivedFrom === void 0 ||
|
|
4129
|
+
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
4130
|
isSkillIndexSnapshot = (value) => (value.anchorEventId === null || typeof value.anchorEventId === "string") && Array.isArray(value.entries) && value.entries.every(isSkillIndexEntry);
|
|
3887
4131
|
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) =>
|
|
4132
|
+
(item) => isRecord7(item) && typeof item.name === "string" && typeof item.previousPath === "string"
|
|
3889
4133
|
);
|
|
3890
|
-
isSkillIndexEntry = (value) =>
|
|
4134
|
+
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
4135
|
appendHarnessItemToContext = (messages, event) => {
|
|
3892
4136
|
const reminder = renderSystemReminder(event.item.content);
|
|
3893
4137
|
const last = messages.at(-1);
|
|
@@ -3924,7 +4168,7 @@ ${reminder}` }]
|
|
|
3924
4168
|
}
|
|
3925
4169
|
return false;
|
|
3926
4170
|
};
|
|
3927
|
-
isToolResultWithContent = (value) =>
|
|
4171
|
+
isToolResultWithContent = (value) => isRecord7(value) && Array.isArray(value.content);
|
|
3928
4172
|
renderSystemReminder = (content) => `<system-reminder>
|
|
3929
4173
|
${content}
|
|
3930
4174
|
</system-reminder>`;
|
|
@@ -3948,21 +4192,20 @@ ${content}
|
|
|
3948
4192
|
cloneMessage = (message) => ({
|
|
3949
4193
|
...message,
|
|
3950
4194
|
content: message.content.map((block) => {
|
|
3951
|
-
if (block.type !== "tool_result" || !
|
|
4195
|
+
if (block.type !== "tool_result" || !isRecord7(block.result)) {
|
|
3952
4196
|
return { ...block };
|
|
3953
4197
|
}
|
|
3954
|
-
const content = Array.isArray(block.result.content) ? { content: block.result.content.map((item) =>
|
|
4198
|
+
const content = Array.isArray(block.result.content) ? { content: block.result.content.map((item) => isRecord7(item) ? { ...item } : item) } : {};
|
|
3955
4199
|
return {
|
|
3956
4200
|
...block,
|
|
3957
4201
|
result: {
|
|
3958
|
-
|
|
3959
|
-
...content
|
|
4202
|
+
content: content.content ?? []
|
|
3960
4203
|
}
|
|
3961
4204
|
};
|
|
3962
4205
|
}),
|
|
3963
4206
|
...message.meta ? { meta: { ...message.meta } } : {}
|
|
3964
4207
|
});
|
|
3965
|
-
|
|
4208
|
+
isRecord7 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3966
4209
|
}
|
|
3967
4210
|
});
|
|
3968
4211
|
|
|
@@ -3972,7 +4215,7 @@ import { existsSync as existsSync2 } from "node:fs";
|
|
|
3972
4215
|
import { readdir as readdir5, readFile as readFile9, stat as stat4 } from "node:fs/promises";
|
|
3973
4216
|
import { homedir as homedir4 } from "node:os";
|
|
3974
4217
|
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,
|
|
4218
|
+
var scanSkillIndex, diffSkillIndex, hasSkillIndexDelta, renderSkillListing, renderSkillDelta, createSkillTool, projectSkillRoots, readSkillEntry, parseSkillMetadata, firstParagraph, parseSkillArgs, findGitRoot2, isNodeErrorCode4;
|
|
3976
4219
|
var init_skills = __esm({
|
|
3977
4220
|
"packages/core/src/skills/index.ts"() {
|
|
3978
4221
|
"use strict";
|
|
@@ -3995,7 +4238,7 @@ var init_skills = __esm({
|
|
|
3995
4238
|
try {
|
|
3996
4239
|
children = await readdir5(root.path);
|
|
3997
4240
|
} catch (cause) {
|
|
3998
|
-
if (
|
|
4241
|
+
if (isNodeErrorCode4(cause, "ENOENT") || isNodeErrorCode4(cause, "ENOTDIR")) {
|
|
3999
4242
|
continue;
|
|
4000
4243
|
}
|
|
4001
4244
|
throw cause;
|
|
@@ -4108,7 +4351,7 @@ var init_skills = __esm({
|
|
|
4108
4351
|
try {
|
|
4109
4352
|
[fileStat, content] = await Promise.all([stat4(options.skillPath), readFile9(options.skillPath, "utf8")]);
|
|
4110
4353
|
} catch (cause) {
|
|
4111
|
-
if (
|
|
4354
|
+
if (isNodeErrorCode4(cause, "ENOENT") || isNodeErrorCode4(cause, "ENOTDIR")) {
|
|
4112
4355
|
return void 0;
|
|
4113
4356
|
}
|
|
4114
4357
|
throw cause;
|
|
@@ -4188,7 +4431,7 @@ var init_skills = __esm({
|
|
|
4188
4431
|
current = next;
|
|
4189
4432
|
}
|
|
4190
4433
|
};
|
|
4191
|
-
|
|
4434
|
+
isNodeErrorCode4 = (cause, code) => cause instanceof Error && "code" in cause && cause.code === code;
|
|
4192
4435
|
}
|
|
4193
4436
|
});
|
|
4194
4437
|
|
|
@@ -4534,12 +4777,15 @@ var init_host_client = __esm({
|
|
|
4534
4777
|
});
|
|
4535
4778
|
|
|
4536
4779
|
// packages/daemon/src/index.ts
|
|
4780
|
+
import { execFile as execFile2 } from "node:child_process";
|
|
4537
4781
|
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 {
|
|
4782
|
+
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";
|
|
4783
|
+
import { userInfo as userInfo2 } from "node:os";
|
|
4784
|
+
import { basename as basename3, dirname as dirname8, join as join10, resolve as resolve5 } from "node:path";
|
|
4540
4785
|
import { pathToFileURL } from "node:url";
|
|
4786
|
+
import { promisify as promisify2 } from "node:util";
|
|
4541
4787
|
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,
|
|
4788
|
+
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, parseQueuedModelSelection, imBindingKey, defaultBuiltinExtensionsDir, runtimeModuleDir, findBuiltinExtensionsDir, isSteerMessage, stripImCommandPrefix, isRecord8, parseMemoryUpdate, normalizeMarkdownFile2, sanitizeSessionTitle, shortStack, formatDiagnosticLine, formatDiagnosticValue;
|
|
4543
4789
|
var init_src4 = __esm({
|
|
4544
4790
|
"packages/daemon/src/index.ts"() {
|
|
4545
4791
|
"use strict";
|
|
@@ -4554,6 +4800,7 @@ var init_src4 = __esm({
|
|
|
4554
4800
|
daemonPackageName = "@scorel/daemon";
|
|
4555
4801
|
SESSION_MEMORY_COMPACT_WAIT_MS = 5e3;
|
|
4556
4802
|
AUTO_COMPACT_RETAINED_EVENTS = 8;
|
|
4803
|
+
execFileAsync2 = promisify2(execFile2);
|
|
4557
4804
|
localDaemonStateFile = (stateDir) => join10(stateDir, "daemon.json");
|
|
4558
4805
|
createLocalDaemonState = async (options) => {
|
|
4559
4806
|
const state = {
|
|
@@ -4765,9 +5012,10 @@ var init_src4 = __esm({
|
|
|
4765
5012
|
}
|
|
4766
5013
|
server.close((error) => error ? reject(error) : resolve7());
|
|
4767
5014
|
});
|
|
4768
|
-
createRealRuntime = (options) => {
|
|
5015
|
+
createRealRuntime = async (options) => {
|
|
4769
5016
|
const selection = resolveModelSelection(options.config, options.modelSelection);
|
|
4770
5017
|
const model = resolvePiAiModel(selection.config);
|
|
5018
|
+
const rtkExecutable = options.rtkExecutable ?? (options.config.runtime.tokenSavingRtk ? (await detectRtk()).executable : void 0);
|
|
4771
5019
|
const runtime = new ScorelRuntime({
|
|
4772
5020
|
provider: createPiAiProvider({
|
|
4773
5021
|
model,
|
|
@@ -4775,7 +5023,17 @@ var init_src4 = __esm({
|
|
|
4775
5023
|
})
|
|
4776
5024
|
});
|
|
4777
5025
|
if (options.includeTools !== false) {
|
|
4778
|
-
for (const tool of createCodingTools({
|
|
5026
|
+
for (const tool of createCodingTools({
|
|
5027
|
+
cwd: options.cwd,
|
|
5028
|
+
contextWindow: model.contextWindow,
|
|
5029
|
+
...options.sessionsDir && options.sessionId ? { toolResultArtifacts: { dir: sessionArtifactsDirPath(options.sessionsDir, options.sessionId) } } : {},
|
|
5030
|
+
tokenSaving: {
|
|
5031
|
+
rtk: {
|
|
5032
|
+
enabled: options.config.runtime.tokenSavingRtk,
|
|
5033
|
+
executable: rtkExecutable
|
|
5034
|
+
}
|
|
5035
|
+
}
|
|
5036
|
+
})) {
|
|
4779
5037
|
runtime.registerTool(tool);
|
|
4780
5038
|
}
|
|
4781
5039
|
}
|
|
@@ -4794,6 +5052,8 @@ var init_src4 = __esm({
|
|
|
4794
5052
|
#createRuntime;
|
|
4795
5053
|
#memoryHomeDir;
|
|
4796
5054
|
#onSessionListChanged;
|
|
5055
|
+
#idleShutdownMs;
|
|
5056
|
+
#onIdleShutdown;
|
|
4797
5057
|
#now;
|
|
4798
5058
|
#createId;
|
|
4799
5059
|
#sessions = /* @__PURE__ */ new Map();
|
|
@@ -4805,6 +5065,9 @@ var init_src4 = __esm({
|
|
|
4805
5065
|
#imExtensions = /* @__PURE__ */ new Map();
|
|
4806
5066
|
#imBindings = /* @__PURE__ */ new Map();
|
|
4807
5067
|
#registry;
|
|
5068
|
+
#runtimeStatsQueue = Promise.resolve();
|
|
5069
|
+
#idleShutdownTimer;
|
|
5070
|
+
#lastActiveWorkAt;
|
|
4808
5071
|
#started = false;
|
|
4809
5072
|
constructor(options) {
|
|
4810
5073
|
this.#sessionsDir = options.sessionsDir;
|
|
@@ -4819,8 +5082,11 @@ var init_src4 = __esm({
|
|
|
4819
5082
|
this.#createRuntime = options.createRuntime;
|
|
4820
5083
|
this.#memoryHomeDir = options.memoryHomeDir;
|
|
4821
5084
|
this.#onSessionListChanged = options.onSessionListChanged;
|
|
5085
|
+
this.#idleShutdownMs = options.idleShutdownMs;
|
|
5086
|
+
this.#onIdleShutdown = options.onIdleShutdown;
|
|
4822
5087
|
this.#now = options.now ?? Date.now;
|
|
4823
5088
|
this.#createId = options.createId ?? (() => crypto.randomUUID());
|
|
5089
|
+
this.#lastActiveWorkAt = this.#now();
|
|
4824
5090
|
this.#registry = new ProjectRegistry({
|
|
4825
5091
|
sessionsDir: this.#sessionsDir,
|
|
4826
5092
|
projectsPath: options.projectsPath,
|
|
@@ -4833,8 +5099,10 @@ var init_src4 = __esm({
|
|
|
4833
5099
|
await mkdir6(this.#scorelHomeDir, { recursive: true });
|
|
4834
5100
|
await this.#loadImBindings();
|
|
4835
5101
|
await this.#startEnabledImExtensions();
|
|
5102
|
+
this.#scheduleIdleShutdownCheck();
|
|
4836
5103
|
}
|
|
4837
5104
|
async shutdown() {
|
|
5105
|
+
this.#clearIdleShutdownTimer();
|
|
4838
5106
|
for (const schedule of this.#memoryDreams.values()) {
|
|
4839
5107
|
if (schedule.timer) {
|
|
4840
5108
|
clearTimeout(schedule.timer);
|
|
@@ -4849,9 +5117,11 @@ var init_src4 = __esm({
|
|
|
4849
5117
|
this.#assertStarted();
|
|
4850
5118
|
await this.#stopImExtensions();
|
|
4851
5119
|
await this.#startEnabledImExtensions();
|
|
5120
|
+
this.#scheduleIdleShutdownCheck();
|
|
4852
5121
|
}
|
|
4853
5122
|
connect(connection, sessionId) {
|
|
4854
5123
|
this.#assertStarted();
|
|
5124
|
+
this.#clearIdleShutdownTimer();
|
|
4855
5125
|
connection.sessionId = sessionId;
|
|
4856
5126
|
this.#connections.add(connection);
|
|
4857
5127
|
if (sessionId) {
|
|
@@ -4876,10 +5146,18 @@ var init_src4 = __esm({
|
|
|
4876
5146
|
});
|
|
4877
5147
|
}
|
|
4878
5148
|
this.#connections.delete(connection);
|
|
5149
|
+
this.#scheduleIdleShutdownCheck();
|
|
4879
5150
|
}
|
|
4880
5151
|
releaseSessionEventBuffer(sessionId) {
|
|
4881
5152
|
this.#events.delete(sessionId);
|
|
4882
5153
|
}
|
|
5154
|
+
activityStatus() {
|
|
5155
|
+
const activeWork = this.#hasActiveWork();
|
|
5156
|
+
if (activeWork) {
|
|
5157
|
+
this.#lastActiveWorkAt = this.#now();
|
|
5158
|
+
}
|
|
5159
|
+
return { activeWork, lastActiveWorkAt: this.#lastActiveWorkAt };
|
|
5160
|
+
}
|
|
4883
5161
|
async handleMessage(connection, message) {
|
|
4884
5162
|
this.#assertStarted();
|
|
4885
5163
|
try {
|
|
@@ -4896,6 +5174,8 @@ var init_src4 = __esm({
|
|
|
4896
5174
|
return;
|
|
4897
5175
|
}
|
|
4898
5176
|
throw cause;
|
|
5177
|
+
} finally {
|
|
5178
|
+
this.#scheduleIdleShutdownCheck();
|
|
4899
5179
|
}
|
|
4900
5180
|
}
|
|
4901
5181
|
async listDirectories(path) {
|
|
@@ -5004,7 +5284,7 @@ var init_src4 = __esm({
|
|
|
5004
5284
|
break;
|
|
5005
5285
|
}
|
|
5006
5286
|
case "get_memory_settings": {
|
|
5007
|
-
this.#respond(connection, message, { memory: await this.#
|
|
5287
|
+
this.#respond(connection, message, { memory: await this.#memorySettings(message.projectId) });
|
|
5008
5288
|
break;
|
|
5009
5289
|
}
|
|
5010
5290
|
case "get_memory_status": {
|
|
@@ -5015,6 +5295,14 @@ var init_src4 = __esm({
|
|
|
5015
5295
|
this.#respond(connection, message, { memory: await this.#handleUpsertMemorySettings(message) });
|
|
5016
5296
|
break;
|
|
5017
5297
|
}
|
|
5298
|
+
case "get_runtime_settings": {
|
|
5299
|
+
this.#respond(connection, message, { runtime: await this.#runtimeSettings(message.projectId) });
|
|
5300
|
+
break;
|
|
5301
|
+
}
|
|
5302
|
+
case "upsert_runtime_settings": {
|
|
5303
|
+
this.#respond(connection, message, { runtime: await this.#handleUpsertRuntimeSettings(message) });
|
|
5304
|
+
break;
|
|
5305
|
+
}
|
|
5018
5306
|
case "get_extension_settings": {
|
|
5019
5307
|
this.#respond(connection, message, { extension: await this.#extensionSettings(message.extensionId) });
|
|
5020
5308
|
break;
|
|
@@ -5043,6 +5331,39 @@ var init_src4 = __esm({
|
|
|
5043
5331
|
break;
|
|
5044
5332
|
}
|
|
5045
5333
|
}
|
|
5334
|
+
#scheduleIdleShutdownCheck() {
|
|
5335
|
+
this.#clearIdleShutdownTimer();
|
|
5336
|
+
if (!this.#shouldIdleShutdown()) {
|
|
5337
|
+
return;
|
|
5338
|
+
}
|
|
5339
|
+
this.#idleShutdownTimer = setTimeout(() => {
|
|
5340
|
+
this.#idleShutdownTimer = void 0;
|
|
5341
|
+
if (this.#shouldIdleShutdown()) {
|
|
5342
|
+
this.#onIdleShutdown?.();
|
|
5343
|
+
}
|
|
5344
|
+
}, this.#idleShutdownMs);
|
|
5345
|
+
}
|
|
5346
|
+
#clearIdleShutdownTimer() {
|
|
5347
|
+
if (!this.#idleShutdownTimer) {
|
|
5348
|
+
return;
|
|
5349
|
+
}
|
|
5350
|
+
clearTimeout(this.#idleShutdownTimer);
|
|
5351
|
+
this.#idleShutdownTimer = void 0;
|
|
5352
|
+
}
|
|
5353
|
+
#shouldIdleShutdown() {
|
|
5354
|
+
return this.#started && this.#idleShutdownMs !== void 0 && this.#idleShutdownMs > 0 && this.#connections.size === 0 && this.#imExtensions.size === 0 && !this.#hasActiveWork();
|
|
5355
|
+
}
|
|
5356
|
+
#hasActiveWork() {
|
|
5357
|
+
for (const lane of this.#sessions.values()) {
|
|
5358
|
+
if (lane.runtime.running) {
|
|
5359
|
+
return true;
|
|
5360
|
+
}
|
|
5361
|
+
if (lane.session.tree.controlState.queues.follow_up.length > 0 || lane.session.tree.controlState.queues.steer.length > 0) {
|
|
5362
|
+
return true;
|
|
5363
|
+
}
|
|
5364
|
+
}
|
|
5365
|
+
return false;
|
|
5366
|
+
}
|
|
5046
5367
|
async #handleCreateSession(connection, request) {
|
|
5047
5368
|
const sessionId = request.sessionId ?? asSessionId(`ses_${this.#createId()}`);
|
|
5048
5369
|
const project = await this.#resolveProject(sessionId, request.meta.projectId);
|
|
@@ -5059,7 +5380,7 @@ var init_src4 = __esm({
|
|
|
5059
5380
|
try {
|
|
5060
5381
|
lane = await this.#createLane(sessionId, request.meta, project);
|
|
5061
5382
|
} catch (cause) {
|
|
5062
|
-
if (!request.sessionId || !
|
|
5383
|
+
if (!request.sessionId || !isNodeErrorCode5(cause, "EEXIST")) {
|
|
5063
5384
|
throw cause;
|
|
5064
5385
|
}
|
|
5065
5386
|
lane = await this.#getLane(sessionId);
|
|
@@ -5125,6 +5446,7 @@ var init_src4 = __esm({
|
|
|
5125
5446
|
content: normalizeContent(request.content),
|
|
5126
5447
|
parentId: request.options?.parentId,
|
|
5127
5448
|
source: "user",
|
|
5449
|
+
modelSelection: request.options?.modelSelection,
|
|
5128
5450
|
channelContext: request.options?.channelContext ? runtimeChannelContextFromWire(request.options.channelContext) : void 0,
|
|
5129
5451
|
onComplete: (result) => this.#respond(connection, request, { ...result, status: "completed" })
|
|
5130
5452
|
});
|
|
@@ -5150,11 +5472,14 @@ var init_src4 = __esm({
|
|
|
5150
5472
|
});
|
|
5151
5473
|
}
|
|
5152
5474
|
async #runUserTurn(lane, clientId, input) {
|
|
5475
|
+
this.#lastActiveWorkAt = this.#now();
|
|
5153
5476
|
const sessionId = lane.session.header.sessionId;
|
|
5477
|
+
await this.#selectChatRuntime(lane, input.modelSelection);
|
|
5154
5478
|
await this.#appendDiagnostic(sessionId, "send_message_started", {
|
|
5155
5479
|
clientId,
|
|
5156
5480
|
activeLeafId: lane.session.activeLeafId,
|
|
5157
|
-
source: input.source
|
|
5481
|
+
source: input.source,
|
|
5482
|
+
selectedModelId: lane.selectedModel?.modelId
|
|
5158
5483
|
});
|
|
5159
5484
|
const instructionSnapshot = await this.#ensureInstructionSnapshot(lane, clientId);
|
|
5160
5485
|
await this.#syncSkillIndex(lane, clientId);
|
|
@@ -5358,7 +5683,7 @@ var init_src4 = __esm({
|
|
|
5358
5683
|
createdAt: now,
|
|
5359
5684
|
updatedAt: now,
|
|
5360
5685
|
clientId: connection.clientId,
|
|
5361
|
-
...request.options?.channelContext ? { data: { channelContext: request.options.channelContext } } : {}
|
|
5686
|
+
...request.options?.channelContext || request.options?.modelSelection ? { data: { channelContext: request.options.channelContext, modelSelection: request.options.modelSelection } } : {}
|
|
5362
5687
|
};
|
|
5363
5688
|
lane.followUpWaiters.set(item.id, { connection, request });
|
|
5364
5689
|
await this.#appendQueueRewrite(lane, "follow_up", [...lane.session.tree.controlState.queues.follow_up, item], {
|
|
@@ -5411,6 +5736,7 @@ var init_src4 = __esm({
|
|
|
5411
5736
|
parentId: lane.session.activeLeafId,
|
|
5412
5737
|
source: "follow_up",
|
|
5413
5738
|
queueItemId: item.id,
|
|
5739
|
+
modelSelection: parseQueuedModelSelection(item.data?.modelSelection),
|
|
5414
5740
|
channelContext: parseQueuedChannelContext(item.data?.channelContext),
|
|
5415
5741
|
onComplete: waiter ? (result) => this.#respond(waiter.connection, waiter.request, { ...result, status: "completed" }) : void 0
|
|
5416
5742
|
});
|
|
@@ -5578,6 +5904,18 @@ var init_src4 = __esm({
|
|
|
5578
5904
|
]
|
|
5579
5905
|
}
|
|
5580
5906
|
});
|
|
5907
|
+
const rtkSavings = rtkSavingsFromToolResult(rawEvent.result);
|
|
5908
|
+
if (rtkSavings) {
|
|
5909
|
+
await this.#recordRtkSavings({
|
|
5910
|
+
projectId: lane.project.projectId,
|
|
5911
|
+
sessionId: lane.session.header.sessionId,
|
|
5912
|
+
savings: rtkSavings
|
|
5913
|
+
}).catch(
|
|
5914
|
+
(cause) => this.#appendDiagnostic(lane.session.header.sessionId, "runtime_stats_update_failed", {
|
|
5915
|
+
message: cause instanceof Error ? cause.message : String(cause)
|
|
5916
|
+
})
|
|
5917
|
+
);
|
|
5918
|
+
}
|
|
5581
5919
|
state.parentId = toolResultId;
|
|
5582
5920
|
break;
|
|
5583
5921
|
}
|
|
@@ -6381,6 +6719,7 @@ var init_src4 = __esm({
|
|
|
6381
6719
|
session: loaded,
|
|
6382
6720
|
project,
|
|
6383
6721
|
runtime,
|
|
6722
|
+
...selectedModel ? { selectedModel } : {},
|
|
6384
6723
|
queue: Promise.resolve(),
|
|
6385
6724
|
appendQueue: Promise.resolve(),
|
|
6386
6725
|
followUpWaiters: /* @__PURE__ */ new Map()
|
|
@@ -6398,7 +6737,7 @@ var init_src4 = __esm({
|
|
|
6398
6737
|
await this.#getLane(sessionId);
|
|
6399
6738
|
return true;
|
|
6400
6739
|
} catch (cause) {
|
|
6401
|
-
if (
|
|
6740
|
+
if (isNodeErrorCode5(cause, "ENOENT")) {
|
|
6402
6741
|
return false;
|
|
6403
6742
|
}
|
|
6404
6743
|
throw cause;
|
|
@@ -6432,6 +6771,7 @@ var init_src4 = __esm({
|
|
|
6432
6771
|
session,
|
|
6433
6772
|
project,
|
|
6434
6773
|
runtime,
|
|
6774
|
+
...selectedModel ? { selectedModel } : {},
|
|
6435
6775
|
queue: Promise.resolve(),
|
|
6436
6776
|
appendQueue: Promise.resolve(),
|
|
6437
6777
|
followUpWaiters: /* @__PURE__ */ new Map()
|
|
@@ -6447,6 +6787,32 @@ var init_src4 = __esm({
|
|
|
6447
6787
|
})
|
|
6448
6788
|
);
|
|
6449
6789
|
}
|
|
6790
|
+
async #selectChatRuntime(lane, modelSelection) {
|
|
6791
|
+
if (!modelSelection) {
|
|
6792
|
+
return;
|
|
6793
|
+
}
|
|
6794
|
+
const selectedModel = await this.#selectedModelFromMeta(
|
|
6795
|
+
{ projectId: lane.project.projectId, modelSelection },
|
|
6796
|
+
lane.project
|
|
6797
|
+
);
|
|
6798
|
+
if (!selectedModel || lane.selectedModel?.modelId === selectedModel.modelId) {
|
|
6799
|
+
return;
|
|
6800
|
+
}
|
|
6801
|
+
lane.runtime = await this.#createRuntime({
|
|
6802
|
+
sessionId: lane.session.header.sessionId,
|
|
6803
|
+
project: lane.project,
|
|
6804
|
+
selectedModel,
|
|
6805
|
+
purpose: "chat"
|
|
6806
|
+
});
|
|
6807
|
+
lane.selectedModel = selectedModel;
|
|
6808
|
+
this.#registerLaneTools(lane);
|
|
6809
|
+
await this.#appendDiagnostic(lane.session.header.sessionId, "chat_model_selected", {
|
|
6810
|
+
projectId: lane.project.projectId,
|
|
6811
|
+
workDir: lane.project.workDir,
|
|
6812
|
+
selectedModelId: selectedModel.modelId,
|
|
6813
|
+
role: selectedModel.role
|
|
6814
|
+
});
|
|
6815
|
+
}
|
|
6450
6816
|
#syncChannelTool(lane, channelContext) {
|
|
6451
6817
|
if (!channelContext) {
|
|
6452
6818
|
lane.runtime.unregisterTool("SendChannelMessage");
|
|
@@ -6556,7 +6922,7 @@ var init_src4 = __esm({
|
|
|
6556
6922
|
try {
|
|
6557
6923
|
children = await readdir6(root);
|
|
6558
6924
|
} catch (cause) {
|
|
6559
|
-
if (
|
|
6925
|
+
if (isNodeErrorCode5(cause, "ENOENT") || isNodeErrorCode5(cause, "ENOTDIR")) {
|
|
6560
6926
|
continue;
|
|
6561
6927
|
}
|
|
6562
6928
|
throw cause;
|
|
@@ -6666,7 +7032,7 @@ var init_src4 = __esm({
|
|
|
6666
7032
|
this.#imBindings.set(imBindingKey(binding.extensionId, binding.externalConversationId), binding);
|
|
6667
7033
|
}
|
|
6668
7034
|
} catch (cause) {
|
|
6669
|
-
if (!
|
|
7035
|
+
if (!isNodeErrorCode5(cause, "ENOENT")) {
|
|
6670
7036
|
throw cause;
|
|
6671
7037
|
}
|
|
6672
7038
|
}
|
|
@@ -6680,9 +7046,13 @@ var init_src4 = __esm({
|
|
|
6680
7046
|
#imBindingsPath() {
|
|
6681
7047
|
return join10(this.#scorelHomeDir, "channels", "im-bindings.json");
|
|
6682
7048
|
}
|
|
6683
|
-
async #loadUserConfigProfile() {
|
|
7049
|
+
async #loadUserConfigProfile(options = {}) {
|
|
6684
7050
|
try {
|
|
6685
|
-
return await loadScorelConfigProfile({
|
|
7051
|
+
return await loadScorelConfigProfile({
|
|
7052
|
+
cwd: this.#userHomeDir,
|
|
7053
|
+
scorelHomeDir: this.#scorelHomeDir,
|
|
7054
|
+
includeSecrets: options.includeSecrets ?? false
|
|
7055
|
+
});
|
|
6686
7056
|
} catch (cause) {
|
|
6687
7057
|
if (isMissingConfigError(cause)) {
|
|
6688
7058
|
return void 0;
|
|
@@ -6690,16 +7060,24 @@ var init_src4 = __esm({
|
|
|
6690
7060
|
throw cause;
|
|
6691
7061
|
}
|
|
6692
7062
|
}
|
|
7063
|
+
#configWriteTarget() {
|
|
7064
|
+
return {
|
|
7065
|
+
configDir: this.#scorelHomeDir,
|
|
7066
|
+
configPath: join10(this.#scorelHomeDir, "config.toml"),
|
|
7067
|
+
workDir: this.#userHomeDir
|
|
7068
|
+
};
|
|
7069
|
+
}
|
|
6693
7070
|
async #listModels(projectId) {
|
|
6694
7071
|
let config;
|
|
6695
7072
|
try {
|
|
6696
|
-
config = await this.#configProfileForProject(projectId);
|
|
7073
|
+
config = projectId ? await this.#configProfileForProject(projectId) : await this.#loadUserConfigProfile();
|
|
6697
7074
|
} catch (cause) {
|
|
6698
7075
|
if (!isMissingConfigError(cause)) {
|
|
6699
7076
|
throw cause;
|
|
6700
7077
|
}
|
|
6701
7078
|
config = void 0;
|
|
6702
7079
|
}
|
|
7080
|
+
config ??= projectId ? void 0 : this.#modelProfile;
|
|
6703
7081
|
if (!config) {
|
|
6704
7082
|
return {
|
|
6705
7083
|
providers: [],
|
|
@@ -6722,19 +7100,18 @@ var init_src4 = __esm({
|
|
|
6722
7100
|
};
|
|
6723
7101
|
}
|
|
6724
7102
|
async #handleUpsertModelProfile(request) {
|
|
6725
|
-
const
|
|
6726
|
-
const configPath = join10(project.workDir, ".scorel", "config.toml");
|
|
7103
|
+
const target = this.#configWriteTarget();
|
|
6727
7104
|
let existingConfigText;
|
|
6728
7105
|
try {
|
|
6729
|
-
existingConfigText = await readFile11(configPath, "utf8");
|
|
7106
|
+
existingConfigText = await readFile11(target.configPath, "utf8");
|
|
6730
7107
|
} catch (cause) {
|
|
6731
|
-
if (!
|
|
7108
|
+
if (!isNodeErrorCode5(cause, "ENOENT")) {
|
|
6732
7109
|
throw cause;
|
|
6733
7110
|
}
|
|
6734
7111
|
}
|
|
6735
|
-
await mkdir6(
|
|
7112
|
+
await mkdir6(target.configDir, { recursive: true });
|
|
6736
7113
|
await writeFile6(
|
|
6737
|
-
configPath,
|
|
7114
|
+
target.configPath,
|
|
6738
7115
|
renderModelProfileConfig({
|
|
6739
7116
|
providerId: request.providerId,
|
|
6740
7117
|
providerType: request.providerType,
|
|
@@ -6761,38 +7138,41 @@ var init_src4 = __esm({
|
|
|
6761
7138
|
"utf8"
|
|
6762
7139
|
);
|
|
6763
7140
|
await this.#appendHostDiagnostic("model_profile_upserted", {
|
|
6764
|
-
projectId:
|
|
6765
|
-
|
|
7141
|
+
...request.projectId ? { ignoredProjectId: request.projectId } : {},
|
|
7142
|
+
scope: "device",
|
|
7143
|
+
workDir: target.workDir,
|
|
6766
7144
|
providerId: request.providerId,
|
|
6767
7145
|
modelId: request.modelId
|
|
6768
7146
|
});
|
|
6769
|
-
return this.#listModels(
|
|
7147
|
+
return this.#listModels();
|
|
6770
7148
|
}
|
|
6771
7149
|
async #handleRemoveModelProvider(request) {
|
|
6772
|
-
const
|
|
6773
|
-
const configPath = join10(project.workDir, ".scorel", "config.toml");
|
|
7150
|
+
const target = this.#configWriteTarget();
|
|
6774
7151
|
let existingConfigText;
|
|
6775
7152
|
try {
|
|
6776
|
-
existingConfigText = await readFile11(configPath, "utf8");
|
|
7153
|
+
existingConfigText = await readFile11(target.configPath, "utf8");
|
|
6777
7154
|
} catch (cause) {
|
|
6778
|
-
if (!
|
|
7155
|
+
if (!isNodeErrorCode5(cause, "ENOENT")) {
|
|
6779
7156
|
throw cause;
|
|
6780
7157
|
}
|
|
6781
7158
|
}
|
|
6782
|
-
await mkdir6(
|
|
7159
|
+
await mkdir6(target.configDir, { recursive: true });
|
|
6783
7160
|
await writeFile6(
|
|
6784
|
-
configPath,
|
|
7161
|
+
target.configPath,
|
|
6785
7162
|
renderModelProfileConfig({
|
|
6786
7163
|
removeProviderId: request.providerId,
|
|
6787
7164
|
existingConfigText
|
|
6788
7165
|
}),
|
|
6789
7166
|
"utf8"
|
|
6790
7167
|
);
|
|
6791
|
-
const profile = await this.#listModels(
|
|
7168
|
+
const profile = await this.#listModels();
|
|
6792
7169
|
return { ...profile, removed: true };
|
|
6793
7170
|
}
|
|
6794
7171
|
async #memorySettingsForProject(projectId) {
|
|
6795
|
-
|
|
7172
|
+
return this.#memorySettings(projectId);
|
|
7173
|
+
}
|
|
7174
|
+
async #memorySettings(projectId) {
|
|
7175
|
+
const config = await (projectId ? this.#configProfileForProject(projectId) : this.#loadUserConfigProfile()).catch((cause) => {
|
|
6796
7176
|
if (isMissingConfigError(cause)) {
|
|
6797
7177
|
return void 0;
|
|
6798
7178
|
}
|
|
@@ -6814,19 +7194,18 @@ var init_src4 = __esm({
|
|
|
6814
7194
|
}
|
|
6815
7195
|
}
|
|
6816
7196
|
async #handleUpsertMemorySettings(request) {
|
|
6817
|
-
const
|
|
6818
|
-
const configPath = join10(project.workDir, ".scorel", "config.toml");
|
|
7197
|
+
const target = this.#configWriteTarget();
|
|
6819
7198
|
let existingConfigText;
|
|
6820
7199
|
try {
|
|
6821
|
-
existingConfigText = await readFile11(configPath, "utf8");
|
|
7200
|
+
existingConfigText = await readFile11(target.configPath, "utf8");
|
|
6822
7201
|
} catch (cause) {
|
|
6823
|
-
if (!
|
|
7202
|
+
if (!isNodeErrorCode5(cause, "ENOENT")) {
|
|
6824
7203
|
throw cause;
|
|
6825
7204
|
}
|
|
6826
7205
|
}
|
|
6827
|
-
await mkdir6(
|
|
7206
|
+
await mkdir6(target.configDir, { recursive: true });
|
|
6828
7207
|
await writeFile6(
|
|
6829
|
-
configPath,
|
|
7208
|
+
target.configPath,
|
|
6830
7209
|
renderMemoryConfig({
|
|
6831
7210
|
enabled: request.enabled,
|
|
6832
7211
|
daily: request.daily,
|
|
@@ -6840,10 +7219,66 @@ var init_src4 = __esm({
|
|
|
6840
7219
|
"utf8"
|
|
6841
7220
|
);
|
|
6842
7221
|
await this.#appendHostDiagnostic("memory_settings_upserted", {
|
|
6843
|
-
projectId:
|
|
6844
|
-
|
|
7222
|
+
...request.projectId ? { ignoredProjectId: request.projectId } : {},
|
|
7223
|
+
scope: "device",
|
|
7224
|
+
workDir: target.workDir
|
|
7225
|
+
});
|
|
7226
|
+
return this.#memorySettings();
|
|
7227
|
+
}
|
|
7228
|
+
async #runtimeSettingsForProject(projectId, installStatus) {
|
|
7229
|
+
return this.#runtimeSettings(projectId, installStatus);
|
|
7230
|
+
}
|
|
7231
|
+
async #runtimeSettings(projectId, installStatus) {
|
|
7232
|
+
const config = await (projectId ? this.#configProfileForProject(projectId) : this.#loadUserConfigProfile()).catch((cause) => {
|
|
7233
|
+
if (isMissingConfigError(cause)) {
|
|
7234
|
+
return void 0;
|
|
7235
|
+
}
|
|
7236
|
+
throw cause;
|
|
7237
|
+
});
|
|
7238
|
+
const detected = await detectRtk();
|
|
7239
|
+
const savings = await readRuntimeStats(this.#runtimeStatsPath());
|
|
7240
|
+
return {
|
|
7241
|
+
tokenSavingRtk: config?.runtime.tokenSavingRtk ?? false,
|
|
7242
|
+
rtkAvailable: detected.available,
|
|
7243
|
+
...detected.executable ? { rtkExecutable: detected.executable } : {},
|
|
7244
|
+
...detected.version ? { rtkVersion: detected.version } : {},
|
|
7245
|
+
...installStatus?.installStatus ? { installStatus: installStatus.installStatus } : {},
|
|
7246
|
+
...installStatus?.installMessage ? { installMessage: installStatus.installMessage } : {},
|
|
7247
|
+
estimatedOutputTokens: savings.rtk.outputTokens,
|
|
7248
|
+
estimatedSavedTokens: savings.rtk.savedTokens
|
|
7249
|
+
};
|
|
7250
|
+
}
|
|
7251
|
+
async #handleUpsertRuntimeSettings(request) {
|
|
7252
|
+
const target = this.#configWriteTarget();
|
|
7253
|
+
let existingConfigText;
|
|
7254
|
+
try {
|
|
7255
|
+
existingConfigText = await readFile11(target.configPath, "utf8");
|
|
7256
|
+
} catch (cause) {
|
|
7257
|
+
if (!isNodeErrorCode5(cause, "ENOENT")) {
|
|
7258
|
+
throw cause;
|
|
7259
|
+
}
|
|
7260
|
+
}
|
|
7261
|
+
await mkdir6(target.configDir, { recursive: true });
|
|
7262
|
+
await writeFile6(
|
|
7263
|
+
target.configPath,
|
|
7264
|
+
renderRuntimeConfig({
|
|
7265
|
+
tokenSavingRtk: request.tokenSavingRtk,
|
|
7266
|
+
existingConfigText
|
|
7267
|
+
}),
|
|
7268
|
+
"utf8"
|
|
7269
|
+
);
|
|
7270
|
+
const installResult = request.tokenSavingRtk === true ? await ensureRtkAvailable() : { status: "idle" };
|
|
7271
|
+
await this.#appendHostDiagnostic("runtime_settings_upserted", {
|
|
7272
|
+
...request.projectId ? { ignoredProjectId: request.projectId } : {},
|
|
7273
|
+
scope: "device",
|
|
7274
|
+
workDir: target.workDir,
|
|
7275
|
+
tokenSavingRtk: request.tokenSavingRtk,
|
|
7276
|
+
installStatus: installResult.status
|
|
7277
|
+
});
|
|
7278
|
+
return this.#runtimeSettings(void 0, {
|
|
7279
|
+
installStatus: installResult.status,
|
|
7280
|
+
...installResult.message ? { installMessage: installResult.message } : {}
|
|
6845
7281
|
});
|
|
6846
|
-
return this.#memorySettingsForProject(project.projectId);
|
|
6847
7282
|
}
|
|
6848
7283
|
async #extensionSettings(extensionId) {
|
|
6849
7284
|
const config = await this.#loadUserConfigProfile().catch((cause) => {
|
|
@@ -6867,7 +7302,7 @@ var init_src4 = __esm({
|
|
|
6867
7302
|
try {
|
|
6868
7303
|
existingConfigText = await readFile11(configPath, "utf8");
|
|
6869
7304
|
} catch (cause) {
|
|
6870
|
-
if (!
|
|
7305
|
+
if (!isNodeErrorCode5(cause, "ENOENT")) {
|
|
6871
7306
|
throw cause;
|
|
6872
7307
|
}
|
|
6873
7308
|
}
|
|
@@ -6891,8 +7326,11 @@ var init_src4 = __esm({
|
|
|
6891
7326
|
return this.#extensionSettings(request.extensionId);
|
|
6892
7327
|
}
|
|
6893
7328
|
async #fetchProviderModels(projectId, providerId) {
|
|
6894
|
-
const
|
|
6895
|
-
|
|
7329
|
+
const config = projectId ? await loadScorelConfigProfile({
|
|
7330
|
+
cwd: (await this.#registry.require(projectId)).workDir,
|
|
7331
|
+
scorelHomeDir: this.#scorelHomeDir,
|
|
7332
|
+
includeSecrets: true
|
|
7333
|
+
}) : await this.#loadUserConfigProfile({ includeSecrets: true });
|
|
6896
7334
|
if (!config) {
|
|
6897
7335
|
throw new Error("Model profile config is not configured");
|
|
6898
7336
|
}
|
|
@@ -6935,9 +7373,10 @@ var init_src4 = __esm({
|
|
|
6935
7373
|
}
|
|
6936
7374
|
const persistedSelection = "selectedModel" in meta ? meta.selectedModel : void 0;
|
|
6937
7375
|
const requestedSelection = "modelSelection" in meta ? meta.modelSelection : void 0;
|
|
7376
|
+
const selectionInput = persistedSelection ? config.models[persistedSelection.modelId] ? { modelId: persistedSelection.modelId, role: persistedSelection.role } : persistedSelection.role ? { role: persistedSelection.role } : void 0 : requestedSelection;
|
|
6938
7377
|
const selection = resolveModelSelection(
|
|
6939
7378
|
config,
|
|
6940
|
-
|
|
7379
|
+
selectionInput
|
|
6941
7380
|
);
|
|
6942
7381
|
const model = resolvePiAiModel(selection.config);
|
|
6943
7382
|
return {
|
|
@@ -6977,7 +7416,7 @@ var init_src4 = __esm({
|
|
|
6977
7416
|
}
|
|
6978
7417
|
const project = await this.#registry.require(projectId);
|
|
6979
7418
|
try {
|
|
6980
|
-
return await loadScorelConfigProfile({ cwd: project.workDir });
|
|
7419
|
+
return await loadScorelConfigProfile({ cwd: project.workDir, scorelHomeDir: this.#scorelHomeDir });
|
|
6981
7420
|
} catch (cause) {
|
|
6982
7421
|
if (!isMissingConfigError(cause)) {
|
|
6983
7422
|
throw cause;
|
|
@@ -7018,6 +7457,20 @@ var init_src4 = __esm({
|
|
|
7018
7457
|
await appendFile3(join10(this.#sessionsDir, "host.log"), `${line}
|
|
7019
7458
|
`, "utf8");
|
|
7020
7459
|
}
|
|
7460
|
+
#runtimeStatsPath() {
|
|
7461
|
+
return join10(this.#scorelHomeDir, "runtime-stats.json");
|
|
7462
|
+
}
|
|
7463
|
+
async #recordRtkSavings(input) {
|
|
7464
|
+
const updateTask = this.#runtimeStatsQueue.then(async () => {
|
|
7465
|
+
const path = this.#runtimeStatsPath();
|
|
7466
|
+
const stats = await readRuntimeStats(path);
|
|
7467
|
+
addRtkSavings(stats, String(input.projectId), String(input.sessionId), input.savings);
|
|
7468
|
+
await writeRuntimeStats(path, stats);
|
|
7469
|
+
});
|
|
7470
|
+
this.#runtimeStatsQueue = updateTask.catch(() => {
|
|
7471
|
+
});
|
|
7472
|
+
await updateTask;
|
|
7473
|
+
}
|
|
7021
7474
|
async #resolveProject(sessionId, projectId) {
|
|
7022
7475
|
const project = await this.#registry.require(projectId);
|
|
7023
7476
|
await this.#appendDiagnostic(sessionId, "project_resolved", {
|
|
@@ -7073,7 +7526,7 @@ var init_src4 = __esm({
|
|
|
7073
7526
|
}
|
|
7074
7527
|
};
|
|
7075
7528
|
};
|
|
7076
|
-
|
|
7529
|
+
isNodeErrorCode5 = (cause, code) => cause instanceof Error && "code" in cause && cause.code === code;
|
|
7077
7530
|
wireErrorCode = (cause) => {
|
|
7078
7531
|
if (!(cause instanceof ProjectRegistryError)) {
|
|
7079
7532
|
return "internal_error";
|
|
@@ -7124,7 +7577,7 @@ var init_src4 = __esm({
|
|
|
7124
7577
|
return void 0;
|
|
7125
7578
|
}
|
|
7126
7579
|
const parsed = JSON.parse(text);
|
|
7127
|
-
if (!
|
|
7580
|
+
if (!isRecord8(parsed)) {
|
|
7128
7581
|
return void 0;
|
|
7129
7582
|
}
|
|
7130
7583
|
return {
|
|
@@ -7144,6 +7597,147 @@ var init_src4 = __esm({
|
|
|
7144
7597
|
dreamIdleMinutes: 60,
|
|
7145
7598
|
autoCompactThreshold: 0.8
|
|
7146
7599
|
});
|
|
7600
|
+
detectRtk = async () => {
|
|
7601
|
+
try {
|
|
7602
|
+
const shell = resolveDefaultShell2();
|
|
7603
|
+
const path = (await execFileAsync2(shell, shellCommandArgs2(shell, "command -v rtk"), { timeout: 5e3 })).stdout.trim();
|
|
7604
|
+
if (!path) {
|
|
7605
|
+
return { available: false };
|
|
7606
|
+
}
|
|
7607
|
+
const version = await execFileAsync2(path, ["--version"], { timeout: 5e3 }).then((result) => result.stdout.trim() || result.stderr.trim()).catch(() => void 0);
|
|
7608
|
+
return {
|
|
7609
|
+
available: true,
|
|
7610
|
+
executable: path,
|
|
7611
|
+
...version ? { version } : {}
|
|
7612
|
+
};
|
|
7613
|
+
} catch {
|
|
7614
|
+
return { available: false };
|
|
7615
|
+
}
|
|
7616
|
+
};
|
|
7617
|
+
ensureRtkAvailable = async () => {
|
|
7618
|
+
const existing = await detectRtk();
|
|
7619
|
+
if (existing.available) {
|
|
7620
|
+
return { status: "installed", message: existing.version ?? existing.executable };
|
|
7621
|
+
}
|
|
7622
|
+
const shell = resolveDefaultShell2();
|
|
7623
|
+
const brew = await execFileAsync2(shell, shellCommandArgs2(shell, "command -v brew"), { timeout: 5e3 }).then((result) => result.stdout.trim()).catch(() => "");
|
|
7624
|
+
if (!brew) {
|
|
7625
|
+
return { status: "failed", message: "Homebrew is not available; install RTK manually with `brew install rtk`." };
|
|
7626
|
+
}
|
|
7627
|
+
try {
|
|
7628
|
+
await execFileAsync2(brew, ["install", "rtk"], { timeout: 12e4, maxBuffer: 2e7 });
|
|
7629
|
+
const installed = await detectRtk();
|
|
7630
|
+
return installed.available ? { status: "installed", message: installed.version ?? installed.executable } : { status: "failed", message: "RTK install finished but `rtk` is still not on PATH." };
|
|
7631
|
+
} catch (cause) {
|
|
7632
|
+
const message = cause instanceof Error ? cause.message : String(cause);
|
|
7633
|
+
return { status: "failed", message };
|
|
7634
|
+
}
|
|
7635
|
+
};
|
|
7636
|
+
emptyRuntimeStats = () => ({
|
|
7637
|
+
version: 1,
|
|
7638
|
+
rtk: {
|
|
7639
|
+
outputTokens: 0,
|
|
7640
|
+
savedTokens: 0,
|
|
7641
|
+
byProject: {},
|
|
7642
|
+
bySession: {}
|
|
7643
|
+
}
|
|
7644
|
+
});
|
|
7645
|
+
readRuntimeStats = async (path) => {
|
|
7646
|
+
try {
|
|
7647
|
+
return parseRuntimeStats(JSON.parse(await readFile11(path, "utf8")));
|
|
7648
|
+
} catch (cause) {
|
|
7649
|
+
if (isNodeErrorCode5(cause, "ENOENT")) {
|
|
7650
|
+
return emptyRuntimeStats();
|
|
7651
|
+
}
|
|
7652
|
+
return emptyRuntimeStats();
|
|
7653
|
+
}
|
|
7654
|
+
};
|
|
7655
|
+
writeRuntimeStats = async (path, stats) => {
|
|
7656
|
+
await mkdir6(dirname8(path), { recursive: true });
|
|
7657
|
+
const tempPath = join10(dirname8(path), `.runtime-stats-${process.pid}-${Date.now()}.tmp`);
|
|
7658
|
+
try {
|
|
7659
|
+
await writeFile6(tempPath, `${JSON.stringify(stats, null, 2)}
|
|
7660
|
+
`, "utf8");
|
|
7661
|
+
await rename3(tempPath, path);
|
|
7662
|
+
} catch (cause) {
|
|
7663
|
+
await rm2(tempPath, { force: true }).catch(() => void 0);
|
|
7664
|
+
throw cause;
|
|
7665
|
+
}
|
|
7666
|
+
};
|
|
7667
|
+
parseRuntimeStats = (value) => {
|
|
7668
|
+
if (!isRecord8(value) || !isRecord8(value.rtk)) {
|
|
7669
|
+
return emptyRuntimeStats();
|
|
7670
|
+
}
|
|
7671
|
+
return {
|
|
7672
|
+
version: 1,
|
|
7673
|
+
rtk: {
|
|
7674
|
+
outputTokens: nonNegativeInteger2(value.rtk.outputTokens),
|
|
7675
|
+
savedTokens: nonNegativeInteger2(value.rtk.savedTokens),
|
|
7676
|
+
byProject: parseRuntimeStatsBuckets(value.rtk.byProject),
|
|
7677
|
+
bySession: parseRuntimeStatsBuckets(value.rtk.bySession)
|
|
7678
|
+
}
|
|
7679
|
+
};
|
|
7680
|
+
};
|
|
7681
|
+
parseRuntimeStatsBuckets = (value) => {
|
|
7682
|
+
if (!isRecord8(value)) {
|
|
7683
|
+
return {};
|
|
7684
|
+
}
|
|
7685
|
+
return Object.fromEntries(
|
|
7686
|
+
Object.entries(value).map(([key, bucket]) => [
|
|
7687
|
+
key,
|
|
7688
|
+
isRecord8(bucket) ? {
|
|
7689
|
+
outputTokens: nonNegativeInteger2(bucket.outputTokens),
|
|
7690
|
+
savedTokens: nonNegativeInteger2(bucket.savedTokens)
|
|
7691
|
+
} : { outputTokens: 0, savedTokens: 0 }
|
|
7692
|
+
])
|
|
7693
|
+
);
|
|
7694
|
+
};
|
|
7695
|
+
addRtkSavings = (stats, projectId, sessionId, savings) => {
|
|
7696
|
+
addRuntimeStatsBucket(stats.rtk, savings);
|
|
7697
|
+
stats.rtk.byProject[projectId] = addRuntimeStatsBucket(stats.rtk.byProject[projectId] ?? { outputTokens: 0, savedTokens: 0 }, savings);
|
|
7698
|
+
stats.rtk.bySession[sessionId] = addRuntimeStatsBucket(stats.rtk.bySession[sessionId] ?? { outputTokens: 0, savedTokens: 0 }, savings);
|
|
7699
|
+
};
|
|
7700
|
+
addRuntimeStatsBucket = (bucket, savings) => {
|
|
7701
|
+
bucket.outputTokens += savings.outputTokens;
|
|
7702
|
+
bucket.savedTokens += savings.savedTokens;
|
|
7703
|
+
return bucket;
|
|
7704
|
+
};
|
|
7705
|
+
rtkSavingsFromToolResult = (result) => {
|
|
7706
|
+
if (!isRecord8(result) || !isRecord8(result.details)) {
|
|
7707
|
+
return void 0;
|
|
7708
|
+
}
|
|
7709
|
+
const rtk = result.details.rtk;
|
|
7710
|
+
if (!isRecord8(rtk) || rtk.applied !== true) {
|
|
7711
|
+
return void 0;
|
|
7712
|
+
}
|
|
7713
|
+
const outputTokens = nonNegativeInteger2(rtk.estimatedOutputTokens);
|
|
7714
|
+
const savedTokens = nonNegativeInteger2(rtk.estimatedSavedTokens);
|
|
7715
|
+
return outputTokens > 0 || savedTokens > 0 ? { outputTokens, savedTokens } : void 0;
|
|
7716
|
+
};
|
|
7717
|
+
nonNegativeInteger2 = (value) => {
|
|
7718
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
7719
|
+
return 0;
|
|
7720
|
+
}
|
|
7721
|
+
return Math.floor(value);
|
|
7722
|
+
};
|
|
7723
|
+
resolveDefaultShell2 = () => {
|
|
7724
|
+
const shell = process.env.SHELL || userShell2() || "/bin/sh";
|
|
7725
|
+
return shell.trim() || "/bin/sh";
|
|
7726
|
+
};
|
|
7727
|
+
shellCommandArgs2 = (shell, command) => {
|
|
7728
|
+
const name = basename3(shell).toLowerCase();
|
|
7729
|
+
if (name === "csh" || name === "tcsh" || name === "fish") {
|
|
7730
|
+
return ["-c", command];
|
|
7731
|
+
}
|
|
7732
|
+
return ["-lc", command];
|
|
7733
|
+
};
|
|
7734
|
+
userShell2 = () => {
|
|
7735
|
+
try {
|
|
7736
|
+
return userInfo2().shell ?? void 0;
|
|
7737
|
+
} catch {
|
|
7738
|
+
return void 0;
|
|
7739
|
+
}
|
|
7740
|
+
};
|
|
7147
7741
|
runtimeChannelContextFromWire = (context) => ({
|
|
7148
7742
|
extensionId: context.channel,
|
|
7149
7743
|
channel: context.channel,
|
|
@@ -7158,7 +7752,7 @@ var init_src4 = __esm({
|
|
|
7158
7752
|
...context.data ? { data: context.data } : {}
|
|
7159
7753
|
});
|
|
7160
7754
|
parseQueuedChannelContext = (value) => {
|
|
7161
|
-
if (!
|
|
7755
|
+
if (!isRecord8(value)) {
|
|
7162
7756
|
return void 0;
|
|
7163
7757
|
}
|
|
7164
7758
|
if (typeof value.channel !== "string" || typeof value.externalConversationId !== "string") {
|
|
@@ -7170,9 +7764,22 @@ var init_src4 = __esm({
|
|
|
7170
7764
|
...typeof value.conversationType === "string" ? { conversationType: value.conversationType } : {},
|
|
7171
7765
|
...typeof value.senderDisplayName === "string" ? { senderDisplayName: value.senderDisplayName } : {},
|
|
7172
7766
|
...typeof value.mentionedBot === "boolean" ? { mentionedBot: value.mentionedBot } : {},
|
|
7173
|
-
...
|
|
7767
|
+
...isRecord8(value.data) ? { data: value.data } : {}
|
|
7174
7768
|
});
|
|
7175
7769
|
};
|
|
7770
|
+
parseQueuedModelSelection = (value) => {
|
|
7771
|
+
if (!isRecord8(value)) {
|
|
7772
|
+
return void 0;
|
|
7773
|
+
}
|
|
7774
|
+
const selection = {};
|
|
7775
|
+
if (typeof value.modelId === "string") {
|
|
7776
|
+
selection.modelId = value.modelId;
|
|
7777
|
+
}
|
|
7778
|
+
if (value.role === "primary" || value.role === "standard" || value.role === "auxiliary") {
|
|
7779
|
+
selection.role = value.role;
|
|
7780
|
+
}
|
|
7781
|
+
return selection.modelId || selection.role ? selection : void 0;
|
|
7782
|
+
};
|
|
7176
7783
|
imBindingKey = (extensionId, externalConversationId) => `${extensionId}:${externalConversationId}`;
|
|
7177
7784
|
defaultBuiltinExtensionsDir = () => findBuiltinExtensionsDir([
|
|
7178
7785
|
runtimeModuleDir(),
|
|
@@ -7203,7 +7810,7 @@ var init_src4 = __esm({
|
|
|
7203
7810
|
};
|
|
7204
7811
|
isSteerMessage = (text) => /^\/(?:steer|interrupt)\b/i.test(text.trim());
|
|
7205
7812
|
stripImCommandPrefix = (text) => text.trim().replace(/^\/(?:steer|interrupt)\s*/i, "").trim() || text;
|
|
7206
|
-
|
|
7813
|
+
isRecord8 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7207
7814
|
parseMemoryUpdate = (raw) => {
|
|
7208
7815
|
const text = raw.trim().replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/, "").trim();
|
|
7209
7816
|
if (!text) {
|
|
@@ -7308,27 +7915,139 @@ var init_relay_cli = __esm({
|
|
|
7308
7915
|
}
|
|
7309
7916
|
});
|
|
7310
7917
|
|
|
7918
|
+
// apps/cli/src/update-cli.ts
|
|
7919
|
+
import { execFile as execFileCallback } from "node:child_process";
|
|
7920
|
+
import { readFile as readFile12 } from "node:fs/promises";
|
|
7921
|
+
import { dirname as dirname9, join as join12 } from "node:path";
|
|
7922
|
+
import { fileURLToPath } from "node:url";
|
|
7923
|
+
import { promisify as promisify3 } from "node:util";
|
|
7924
|
+
var SCOREL_PACKAGE_NAME, AUTO_UPDATE_INTERVAL_MS, ACTIVE_WORK_STALE_MS, execFileAsync3, compareSemver, shouldRunAutoUpdate, createNpmPackageUpdater, readInstalledScorelVersion, runCliUpdate, writeUpdateUsage, parseSemver;
|
|
7925
|
+
var init_update_cli = __esm({
|
|
7926
|
+
"apps/cli/src/update-cli.ts"() {
|
|
7927
|
+
"use strict";
|
|
7928
|
+
SCOREL_PACKAGE_NAME = "@chanlerdev/scorel";
|
|
7929
|
+
AUTO_UPDATE_INTERVAL_MS = 60 * 60 * 1e3;
|
|
7930
|
+
ACTIVE_WORK_STALE_MS = 3 * 60 * 60 * 1e3;
|
|
7931
|
+
execFileAsync3 = promisify3(execFileCallback);
|
|
7932
|
+
compareSemver = (a, b) => {
|
|
7933
|
+
const left = parseSemver(a);
|
|
7934
|
+
const right = parseSemver(b);
|
|
7935
|
+
for (let index = 0; index < 3; index += 1) {
|
|
7936
|
+
const delta = left[index] - right[index];
|
|
7937
|
+
if (delta !== 0) return delta;
|
|
7938
|
+
}
|
|
7939
|
+
return 0;
|
|
7940
|
+
};
|
|
7941
|
+
shouldRunAutoUpdate = (activity) => !activity.activeWork || activity.now - activity.lastActiveWorkAt >= ACTIVE_WORK_STALE_MS;
|
|
7942
|
+
createNpmPackageUpdater = (options) => {
|
|
7943
|
+
const packageName = options.packageName ?? SCOREL_PACKAGE_NAME;
|
|
7944
|
+
const execFile3 = options.execFile ?? ((command, argv) => execFileAsync3(command, argv));
|
|
7945
|
+
return {
|
|
7946
|
+
async checkLatest() {
|
|
7947
|
+
const result = await execFile3("npm", ["view", packageName, "version"]);
|
|
7948
|
+
const latest = result.stdout.trim();
|
|
7949
|
+
if (!latest) {
|
|
7950
|
+
throw new Error(`npm did not return a latest version for ${packageName}`);
|
|
7951
|
+
}
|
|
7952
|
+
parseSemver(latest);
|
|
7953
|
+
return latest;
|
|
7954
|
+
},
|
|
7955
|
+
async update() {
|
|
7956
|
+
const latestVersion = await this.checkLatest();
|
|
7957
|
+
if (compareSemver(options.currentVersion, latestVersion) >= 0) {
|
|
7958
|
+
return { status: "current", currentVersion: options.currentVersion, latestVersion };
|
|
7959
|
+
}
|
|
7960
|
+
await execFile3("npm", ["install", "-g", `${packageName}@${latestVersion}`]);
|
|
7961
|
+
return { status: "updated", currentVersion: options.currentVersion, latestVersion };
|
|
7962
|
+
}
|
|
7963
|
+
};
|
|
7964
|
+
};
|
|
7965
|
+
readInstalledScorelVersion = async () => {
|
|
7966
|
+
const here = dirname9(fileURLToPath(import.meta.url));
|
|
7967
|
+
for (const candidate of [
|
|
7968
|
+
join12(here, "..", "package.json"),
|
|
7969
|
+
join12(here, "..", "..", "package.json"),
|
|
7970
|
+
join12(process.cwd(), "package.json")
|
|
7971
|
+
]) {
|
|
7972
|
+
try {
|
|
7973
|
+
const parsed = JSON.parse(await readFile12(candidate, "utf8"));
|
|
7974
|
+
if (typeof parsed.version === "string" && (parsed.name === SCOREL_PACKAGE_NAME || parsed.name === "@scorel/app-cli")) {
|
|
7975
|
+
return parsed.version;
|
|
7976
|
+
}
|
|
7977
|
+
} catch {
|
|
7978
|
+
}
|
|
7979
|
+
}
|
|
7980
|
+
return "0.0.0";
|
|
7981
|
+
};
|
|
7982
|
+
runCliUpdate = async (argv, io, options = {}) => {
|
|
7983
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
7984
|
+
writeUpdateUsage(io.output);
|
|
7985
|
+
return 0;
|
|
7986
|
+
}
|
|
7987
|
+
if (argv.length > 0) {
|
|
7988
|
+
writeUpdateUsage(io.error);
|
|
7989
|
+
return 1;
|
|
7990
|
+
}
|
|
7991
|
+
const currentVersion = options.currentVersion ?? await readInstalledScorelVersion();
|
|
7992
|
+
const updater = options.updater ?? createNpmPackageUpdater({ currentVersion });
|
|
7993
|
+
try {
|
|
7994
|
+
const result = await updater.update();
|
|
7995
|
+
if (result.status === "current") {
|
|
7996
|
+
io.output.write(`scorel is current (${result.currentVersion})
|
|
7997
|
+
`);
|
|
7998
|
+
} else {
|
|
7999
|
+
io.output.write(`updated scorel ${result.currentVersion} -> ${result.latestVersion}
|
|
8000
|
+
`);
|
|
8001
|
+
}
|
|
8002
|
+
return 0;
|
|
8003
|
+
} catch (cause) {
|
|
8004
|
+
io.error.write(`scorel update error: ${cause instanceof Error ? cause.message : String(cause)}
|
|
8005
|
+
`);
|
|
8006
|
+
return 1;
|
|
8007
|
+
}
|
|
8008
|
+
};
|
|
8009
|
+
writeUpdateUsage = (output) => {
|
|
8010
|
+
output.write("Usage: scorel update\n scorel upgrade\n");
|
|
8011
|
+
};
|
|
8012
|
+
parseSemver = (version) => {
|
|
8013
|
+
const match = /^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/.exec(version);
|
|
8014
|
+
if (!match) {
|
|
8015
|
+
throw new Error(`Invalid semver version: ${version}`);
|
|
8016
|
+
}
|
|
8017
|
+
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
8018
|
+
};
|
|
8019
|
+
}
|
|
8020
|
+
});
|
|
8021
|
+
|
|
7311
8022
|
// apps/cli/src/daemon-cli.ts
|
|
7312
8023
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
8024
|
+
import { spawn } from "node:child_process";
|
|
7313
8025
|
import { homedir as homedir6 } from "node:os";
|
|
7314
|
-
import { join as
|
|
7315
|
-
|
|
8026
|
+
import { dirname as dirname10, join as join13 } from "node:path";
|
|
8027
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
8028
|
+
var DEFAULT_HOST, DEFAULT_PORT, STOP_POLL_INTERVAL_MS, STOP_GRACE_MS, START_READY_TIMEOUT_MS, AUTO_STARTED_IDLE_SHUTDOWN_MS, FOREGROUND_IDLE_SHUTDOWN_MS, defaultStateDir2, isLoopbackHost, formatTimestamp, runCliDaemon, runStartCommand, runServeCommand, startAutoUpdateLoop, stopRunningDaemon, runStatusCommand, runStopCommand, runResetCommand, formatStatusLine, parseServeFlags, parseStatusFlags, requireValue2, sleep, waitForDaemonReady, detachBackgroundDaemon, nodeEntrypointArgs, writeDaemonUsage;
|
|
7316
8029
|
var init_daemon_cli = __esm({
|
|
7317
8030
|
"apps/cli/src/daemon-cli.ts"() {
|
|
7318
8031
|
"use strict";
|
|
7319
8032
|
init_src4();
|
|
7320
8033
|
init_relay_cli();
|
|
8034
|
+
init_update_cli();
|
|
7321
8035
|
DEFAULT_HOST = "127.0.0.1";
|
|
7322
8036
|
DEFAULT_PORT = 7777;
|
|
7323
8037
|
STOP_POLL_INTERVAL_MS = 200;
|
|
7324
8038
|
STOP_GRACE_MS = 5e3;
|
|
7325
|
-
|
|
8039
|
+
START_READY_TIMEOUT_MS = 1e4;
|
|
8040
|
+
AUTO_STARTED_IDLE_SHUTDOWN_MS = 15 * 60 * 1e3;
|
|
8041
|
+
FOREGROUND_IDLE_SHUTDOWN_MS = 0;
|
|
8042
|
+
defaultStateDir2 = () => join13(homedir6(), ".scorel");
|
|
7326
8043
|
isLoopbackHost = (host) => host === "127.0.0.1" || host === "::1" || host === "localhost";
|
|
7327
8044
|
formatTimestamp = (epochMs) => new Date(epochMs).toISOString();
|
|
7328
8045
|
runCliDaemon = async (argv, options) => {
|
|
7329
8046
|
const [command, ...rest] = argv;
|
|
7330
8047
|
const stateDir = options.stateDir ?? defaultStateDir2();
|
|
7331
8048
|
switch (command) {
|
|
8049
|
+
case "start":
|
|
8050
|
+
return runStartCommand(rest, { ...options, stateDir });
|
|
7332
8051
|
case "serve":
|
|
7333
8052
|
return runServeCommand(rest, { ...options, stateDir });
|
|
7334
8053
|
case "status":
|
|
@@ -7346,10 +8065,67 @@ var init_daemon_cli = __esm({
|
|
|
7346
8065
|
return 1;
|
|
7347
8066
|
}
|
|
7348
8067
|
};
|
|
8068
|
+
runStartCommand = async (argv, options) => {
|
|
8069
|
+
let flags;
|
|
8070
|
+
try {
|
|
8071
|
+
flags = parseServeFlags(argv, options.cwd ?? process.cwd(), options.env ?? process.env, FOREGROUND_IDLE_SHUTDOWN_MS);
|
|
8072
|
+
} catch (cause) {
|
|
8073
|
+
options.error.write(`scorel daemon start error: ${cause.message}
|
|
8074
|
+
`);
|
|
8075
|
+
return 1;
|
|
8076
|
+
}
|
|
8077
|
+
const readState = options.readState ?? ((stateDir) => readLocalDaemonState({ stateDir }));
|
|
8078
|
+
const existing = await readState(options.stateDir);
|
|
8079
|
+
if (existing && daemonStateLiveness(existing) === "running") {
|
|
8080
|
+
options.output.write(`scorel host already running url=${existing.wsUrl} pid=${existing.pid}
|
|
8081
|
+
`);
|
|
8082
|
+
return 0;
|
|
8083
|
+
}
|
|
8084
|
+
const cliEntrypoint = options.cliEntrypoint ?? fileURLToPath2(import.meta.url).replace(/daemon-cli\.ts$/, "index.ts");
|
|
8085
|
+
const child = (options.spawn ?? spawn)(process.execPath, [
|
|
8086
|
+
...nodeEntrypointArgs(cliEntrypoint),
|
|
8087
|
+
"host",
|
|
8088
|
+
"serve",
|
|
8089
|
+
"--host",
|
|
8090
|
+
flags.host,
|
|
8091
|
+
"--port",
|
|
8092
|
+
String(flags.port),
|
|
8093
|
+
"--cwd",
|
|
8094
|
+
flags.cwd,
|
|
8095
|
+
"--idle-timeout-ms",
|
|
8096
|
+
String(flags.idleShutdownMs),
|
|
8097
|
+
...flags.token ? ["--token", flags.token] : [],
|
|
8098
|
+
...flags.relayUrl ? ["--relay", flags.relayUrl] : ["--no-relay"],
|
|
8099
|
+
...flags.replace ? ["--replace"] : []
|
|
8100
|
+
], {
|
|
8101
|
+
cwd: dirname10(cliEntrypoint),
|
|
8102
|
+
env: { ...process.env, ...options.env ?? {} },
|
|
8103
|
+
detached: true,
|
|
8104
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
8105
|
+
});
|
|
8106
|
+
try {
|
|
8107
|
+
await waitForDaemonReady(child, options.daemonReadyTimeoutMs ?? START_READY_TIMEOUT_MS);
|
|
8108
|
+
} catch (cause) {
|
|
8109
|
+
options.error.write(`scorel daemon start error: ${cause.message}
|
|
8110
|
+
`);
|
|
8111
|
+
child.kill("SIGTERM");
|
|
8112
|
+
return 1;
|
|
8113
|
+
}
|
|
8114
|
+
const state = await readState(options.stateDir);
|
|
8115
|
+
if (!state || daemonStateLiveness(state) !== "running") {
|
|
8116
|
+
options.error.write("scorel daemon start error: daemon state missing after start\n");
|
|
8117
|
+
child.kill("SIGTERM");
|
|
8118
|
+
return 1;
|
|
8119
|
+
}
|
|
8120
|
+
detachBackgroundDaemon(child);
|
|
8121
|
+
options.output.write(`scorel host started url=${state.wsUrl} pid=${state.pid}
|
|
8122
|
+
`);
|
|
8123
|
+
return 0;
|
|
8124
|
+
};
|
|
7349
8125
|
runServeCommand = async (argv, options) => {
|
|
7350
8126
|
let flags;
|
|
7351
8127
|
try {
|
|
7352
|
-
flags = parseServeFlags(argv, options.cwd ?? process.cwd(), options.env ?? process.env);
|
|
8128
|
+
flags = parseServeFlags(argv, options.cwd ?? process.cwd(), options.env ?? process.env, FOREGROUND_IDLE_SHUTDOWN_MS);
|
|
7353
8129
|
} catch (cause) {
|
|
7354
8130
|
options.error.write(`scorel daemon serve error: ${cause.message}
|
|
7355
8131
|
`);
|
|
@@ -7373,22 +8149,45 @@ Use --replace to stop it and start a new one.
|
|
|
7373
8149
|
}
|
|
7374
8150
|
const token = flags.token ?? existing?.token ?? randomUUID4();
|
|
7375
8151
|
const identity = await loadOrCreateHostDeviceIdentity({ stateDir: options.stateDir });
|
|
8152
|
+
const configScope = { scorelHomeDir: options.stateDir };
|
|
8153
|
+
let signalReason = "natural";
|
|
8154
|
+
let resolveStopWaiter;
|
|
8155
|
+
let stopRequested = false;
|
|
8156
|
+
const requestStop = (reason) => {
|
|
8157
|
+
signalReason = reason;
|
|
8158
|
+
stopRequested = true;
|
|
8159
|
+
resolveStopWaiter?.();
|
|
8160
|
+
};
|
|
8161
|
+
const sessionsDir = options.sessionsDir ?? scorelSessionsDir(homedir6());
|
|
7376
8162
|
const daemon = new ScorelHost({
|
|
7377
|
-
sessionsDir
|
|
7378
|
-
projectsPath:
|
|
8163
|
+
sessionsDir,
|
|
8164
|
+
projectsPath: join13(options.stateDir, "projects.json"),
|
|
7379
8165
|
deviceId: identity.deviceId,
|
|
7380
8166
|
deviceDisplayName: identity.displayName,
|
|
7381
|
-
|
|
7382
|
-
|
|
7383
|
-
|
|
8167
|
+
idleShutdownMs: flags.idleShutdownMs,
|
|
8168
|
+
onIdleShutdown: () => requestStop("idle"),
|
|
8169
|
+
scorelHomeDir: options.stateDir,
|
|
8170
|
+
loadConfig: async ({ project }) => loadScorelConfig({ cwd: project.workDir, ...configScope }),
|
|
8171
|
+
loadConfigProfile: async ({ project }) => loadScorelConfigProfile({ cwd: project.workDir, ...configScope }),
|
|
8172
|
+
createRuntime: async ({ sessionId, project, selectedModel, purpose }) => createRealRuntime({
|
|
7384
8173
|
cwd: project.workDir,
|
|
7385
|
-
config: await loadScorelConfig({ cwd: project.workDir }),
|
|
8174
|
+
config: await loadScorelConfig({ cwd: project.workDir, ...configScope }),
|
|
8175
|
+
sessionsDir,
|
|
8176
|
+
sessionId,
|
|
7386
8177
|
modelSelection: selectedModel ? { modelId: selectedModel.modelId, role: selectedModel.role } : void 0,
|
|
7387
8178
|
includeTools: purpose === "chat"
|
|
7388
8179
|
})
|
|
7389
8180
|
});
|
|
7390
8181
|
await daemon.start();
|
|
7391
8182
|
await daemon.registerProject(flags.cwd);
|
|
8183
|
+
const autoUpdater = await startAutoUpdateLoop({
|
|
8184
|
+
host: daemon,
|
|
8185
|
+
requestStop,
|
|
8186
|
+
output: options.output,
|
|
8187
|
+
error: options.error,
|
|
8188
|
+
updater: options.packageUpdater,
|
|
8189
|
+
intervalMs: options.autoUpdateIntervalMs ?? AUTO_UPDATE_INTERVAL_MS
|
|
8190
|
+
});
|
|
7392
8191
|
const server = await startScorelHostWebSocketServer({
|
|
7393
8192
|
hostService: daemon,
|
|
7394
8193
|
host: flags.host,
|
|
@@ -7434,6 +8233,7 @@ Use --replace to stop it and start a new one.
|
|
|
7434
8233
|
}
|
|
7435
8234
|
const shutdown = async () => {
|
|
7436
8235
|
try {
|
|
8236
|
+
autoUpdater.stop();
|
|
7437
8237
|
relayClient?.close();
|
|
7438
8238
|
await server.close();
|
|
7439
8239
|
} finally {
|
|
@@ -7441,20 +8241,22 @@ Use --replace to stop it and start a new one.
|
|
|
7441
8241
|
await markDaemonStopped({ stateDir: options.stateDir, stoppedAt: Date.now() });
|
|
7442
8242
|
}
|
|
7443
8243
|
};
|
|
7444
|
-
let signalReason = "natural";
|
|
7445
8244
|
const signalHandlers = /* @__PURE__ */ new Map();
|
|
7446
8245
|
const stopWaiter = new Promise((resolve7) => {
|
|
8246
|
+
resolveStopWaiter = resolve7;
|
|
8247
|
+
if (stopRequested) {
|
|
8248
|
+
resolve7();
|
|
8249
|
+
return;
|
|
8250
|
+
}
|
|
7447
8251
|
if (options.serveSignal) {
|
|
7448
8252
|
if (options.serveSignal.aborted) {
|
|
7449
|
-
|
|
7450
|
-
resolve7();
|
|
8253
|
+
requestStop("abort");
|
|
7451
8254
|
return;
|
|
7452
8255
|
}
|
|
7453
8256
|
options.serveSignal.addEventListener(
|
|
7454
8257
|
"abort",
|
|
7455
8258
|
() => {
|
|
7456
|
-
|
|
7457
|
-
resolve7();
|
|
8259
|
+
requestStop("abort");
|
|
7458
8260
|
},
|
|
7459
8261
|
{ once: true }
|
|
7460
8262
|
);
|
|
@@ -7462,8 +8264,7 @@ Use --replace to stop it and start a new one.
|
|
|
7462
8264
|
}
|
|
7463
8265
|
const installSignal = (signal) => {
|
|
7464
8266
|
const handler = () => {
|
|
7465
|
-
|
|
7466
|
-
resolve7();
|
|
8267
|
+
requestStop(signal);
|
|
7467
8268
|
};
|
|
7468
8269
|
signalHandlers.set(signal, handler);
|
|
7469
8270
|
process.once(signal, handler);
|
|
@@ -7483,6 +8284,42 @@ Use --replace to stop it and start a new one.
|
|
|
7483
8284
|
`);
|
|
7484
8285
|
return 0;
|
|
7485
8286
|
};
|
|
8287
|
+
startAutoUpdateLoop = async (options) => {
|
|
8288
|
+
const updater = options.updater ?? createNpmPackageUpdater({ currentVersion: await readInstalledScorelVersion() });
|
|
8289
|
+
let timer;
|
|
8290
|
+
let running = false;
|
|
8291
|
+
const tick = async () => {
|
|
8292
|
+
if (running) return;
|
|
8293
|
+
running = true;
|
|
8294
|
+
try {
|
|
8295
|
+
const activity = options.host.activityStatus();
|
|
8296
|
+
if (!shouldRunAutoUpdate({ ...activity, now: Date.now() })) {
|
|
8297
|
+
return;
|
|
8298
|
+
}
|
|
8299
|
+
const result = await updater.update();
|
|
8300
|
+
if (result.status === "updated") {
|
|
8301
|
+
options.output.write(`scorel auto-updated ${result.currentVersion} -> ${result.latestVersion}; restarting host
|
|
8302
|
+
`);
|
|
8303
|
+
options.requestStop("auto-update");
|
|
8304
|
+
}
|
|
8305
|
+
} catch (cause) {
|
|
8306
|
+
options.error.write(`scorel auto-update error: ${cause instanceof Error ? cause.message : String(cause)}
|
|
8307
|
+
`);
|
|
8308
|
+
} finally {
|
|
8309
|
+
running = false;
|
|
8310
|
+
}
|
|
8311
|
+
};
|
|
8312
|
+
timer = setInterval(() => void tick(), options.intervalMs);
|
|
8313
|
+
timer.unref?.();
|
|
8314
|
+
return {
|
|
8315
|
+
stop() {
|
|
8316
|
+
if (timer) {
|
|
8317
|
+
clearInterval(timer);
|
|
8318
|
+
timer = void 0;
|
|
8319
|
+
}
|
|
8320
|
+
}
|
|
8321
|
+
};
|
|
8322
|
+
};
|
|
7486
8323
|
stopRunningDaemon = async (state, options) => {
|
|
7487
8324
|
try {
|
|
7488
8325
|
process.kill(state.pid, "SIGTERM");
|
|
@@ -7586,13 +8423,14 @@ Use --replace to stop it and start a new one.
|
|
|
7586
8423
|
const stoppedAt = state.stoppedAt !== null ? formatTimestamp(state.stoppedAt) : "unknown";
|
|
7587
8424
|
return `stopped url=${state.wsUrl} last-pid=${state.pid} stoppedAt=${stoppedAt} liveness=${liveness}`;
|
|
7588
8425
|
};
|
|
7589
|
-
parseServeFlags = (argv, defaultCwd, env) => {
|
|
8426
|
+
parseServeFlags = (argv, defaultCwd, env, defaultIdleShutdownMs) => {
|
|
7590
8427
|
let host = DEFAULT_HOST;
|
|
7591
8428
|
let port = DEFAULT_PORT;
|
|
7592
8429
|
let cwd = defaultCwd;
|
|
7593
8430
|
let token;
|
|
7594
8431
|
let relayUrl = resolveDefaultRelayUrl(env);
|
|
7595
8432
|
let replace = false;
|
|
8433
|
+
let idleShutdownMs = defaultIdleShutdownMs;
|
|
7596
8434
|
for (let index = 0; index < argv.length; index += 1) {
|
|
7597
8435
|
const arg = argv[index];
|
|
7598
8436
|
if (arg === "--host") {
|
|
@@ -7636,9 +8474,17 @@ Use --replace to stop it and start a new one.
|
|
|
7636
8474
|
replace = true;
|
|
7637
8475
|
continue;
|
|
7638
8476
|
}
|
|
8477
|
+
if (arg === "--idle-timeout-ms") {
|
|
8478
|
+
idleShutdownMs = Number(requireValue2(argv, index, "--idle-timeout-ms"));
|
|
8479
|
+
if (!Number.isInteger(idleShutdownMs) || idleShutdownMs < 0) {
|
|
8480
|
+
throw new Error("--idle-timeout-ms must be a non-negative integer");
|
|
8481
|
+
}
|
|
8482
|
+
index += 1;
|
|
8483
|
+
continue;
|
|
8484
|
+
}
|
|
7639
8485
|
throw new Error(`Unknown serve option: ${arg}`);
|
|
7640
8486
|
}
|
|
7641
|
-
return { host, port, token, cwd, relayUrl, replace };
|
|
8487
|
+
return { host, port, token, cwd, relayUrl, replace, idleShutdownMs };
|
|
7642
8488
|
};
|
|
7643
8489
|
parseStatusFlags = (argv) => {
|
|
7644
8490
|
let showToken = false;
|
|
@@ -7661,11 +8507,66 @@ Use --replace to stop it and start a new one.
|
|
|
7661
8507
|
sleep = (ms) => new Promise((resolve7) => {
|
|
7662
8508
|
setTimeout(resolve7, ms);
|
|
7663
8509
|
});
|
|
8510
|
+
waitForDaemonReady = (child, timeoutMs) => new Promise((resolveReady, rejectReady) => {
|
|
8511
|
+
if (!child.stdout) {
|
|
8512
|
+
rejectReady(new Error("daemon child has no stdout stream"));
|
|
8513
|
+
return;
|
|
8514
|
+
}
|
|
8515
|
+
let buffer = "";
|
|
8516
|
+
let stderrBuffer = "";
|
|
8517
|
+
let settled = false;
|
|
8518
|
+
const timer = setTimeout(() => {
|
|
8519
|
+
if (settled) return;
|
|
8520
|
+
settled = true;
|
|
8521
|
+
cleanup();
|
|
8522
|
+
rejectReady(new Error("timed out waiting for daemon ready line"));
|
|
8523
|
+
}, timeoutMs);
|
|
8524
|
+
const onData = (chunk) => {
|
|
8525
|
+
buffer += chunk.toString();
|
|
8526
|
+
if (!buffer.includes("\n")) return;
|
|
8527
|
+
if (buffer.includes("scorel daemon serving url=") || buffer.includes("scorel host serving url=")) {
|
|
8528
|
+
if (settled) return;
|
|
8529
|
+
settled = true;
|
|
8530
|
+
cleanup();
|
|
8531
|
+
resolveReady();
|
|
8532
|
+
}
|
|
8533
|
+
const newlineIndex = buffer.lastIndexOf("\n");
|
|
8534
|
+
buffer = newlineIndex >= 0 ? buffer.slice(newlineIndex + 1) : buffer;
|
|
8535
|
+
};
|
|
8536
|
+
const onStderr = (chunk) => {
|
|
8537
|
+
stderrBuffer += chunk.toString();
|
|
8538
|
+
};
|
|
8539
|
+
const onExit = (code) => {
|
|
8540
|
+
if (settled) return;
|
|
8541
|
+
settled = true;
|
|
8542
|
+
cleanup();
|
|
8543
|
+
const trimmed = stderrBuffer.trim();
|
|
8544
|
+
const detail = trimmed ? `: ${trimmed}` : "";
|
|
8545
|
+
rejectReady(new Error(`daemon exited before ready code=${code}${detail}`));
|
|
8546
|
+
};
|
|
8547
|
+
const cleanup = () => {
|
|
8548
|
+
clearTimeout(timer);
|
|
8549
|
+
child.stdout?.off("data", onData);
|
|
8550
|
+
child.stderr?.off("data", onStderr);
|
|
8551
|
+
child.off("exit", onExit);
|
|
8552
|
+
};
|
|
8553
|
+
child.stdout.on("data", onData);
|
|
8554
|
+
child.stderr?.on("data", onStderr);
|
|
8555
|
+
child.once("exit", onExit);
|
|
8556
|
+
});
|
|
8557
|
+
detachBackgroundDaemon = (child) => {
|
|
8558
|
+
child.stdout?.destroy();
|
|
8559
|
+
child.stderr?.destroy();
|
|
8560
|
+
child.unref();
|
|
8561
|
+
};
|
|
8562
|
+
nodeEntrypointArgs = (entrypoint) => entrypoint.endsWith(".ts") ? ["--import", "tsx", entrypoint] : [entrypoint];
|
|
7664
8563
|
writeDaemonUsage = (output) => {
|
|
7665
8564
|
output.write(
|
|
7666
8565
|
[
|
|
7667
8566
|
"Usage: scorel host serve [--host <h>] [--port <p>] [--token <t>] [--project <dir>]",
|
|
7668
|
-
" [--relay <relay-url> | --no-relay] [--replace]",
|
|
8567
|
+
" [--relay <relay-url> | --no-relay] [--replace] [--idle-timeout-ms <ms>]",
|
|
8568
|
+
" scorel host start [--host <h>] [--port <p>] [--token <t>] [--project <dir>]",
|
|
8569
|
+
" [--relay <relay-url> | --no-relay] [--replace] [--idle-timeout-ms <ms>]",
|
|
7669
8570
|
" scorel host status [--show-token]",
|
|
7670
8571
|
" scorel host stop",
|
|
7671
8572
|
" scorel host reset",
|
|
@@ -7866,8 +8767,8 @@ var init_routing = __esm({
|
|
|
7866
8767
|
});
|
|
7867
8768
|
|
|
7868
8769
|
// apps/relay/src/store.ts
|
|
7869
|
-
import { mkdir as mkdir7, readFile as
|
|
7870
|
-
import { join as
|
|
8770
|
+
import { mkdir as mkdir7, readFile as readFile13, writeFile as writeFile7 } from "node:fs/promises";
|
|
8771
|
+
import { join as join14 } from "node:path";
|
|
7871
8772
|
var FileRelayStore, emptyStoreFile;
|
|
7872
8773
|
var init_store = __esm({
|
|
7873
8774
|
"apps/relay/src/store.ts"() {
|
|
@@ -7877,7 +8778,7 @@ var init_store = __esm({
|
|
|
7877
8778
|
#now;
|
|
7878
8779
|
#queue = Promise.resolve();
|
|
7879
8780
|
constructor(options) {
|
|
7880
|
-
this.#filePath =
|
|
8781
|
+
this.#filePath = join14(options.dataDir, "relay-store.json");
|
|
7881
8782
|
this.#now = options.now ?? Date.now;
|
|
7882
8783
|
}
|
|
7883
8784
|
async upsertDevice(record) {
|
|
@@ -7920,7 +8821,7 @@ var init_store = __esm({
|
|
|
7920
8821
|
this.#queue = this.#queue.then(async () => {
|
|
7921
8822
|
const file = await this.#read();
|
|
7922
8823
|
mutator(file);
|
|
7923
|
-
await mkdir7(
|
|
8824
|
+
await mkdir7(join14(this.#filePath, ".."), { recursive: true });
|
|
7924
8825
|
await writeFile7(this.#filePath, `${JSON.stringify(file, null, 2)}
|
|
7925
8826
|
`);
|
|
7926
8827
|
});
|
|
@@ -7928,7 +8829,7 @@ var init_store = __esm({
|
|
|
7928
8829
|
}
|
|
7929
8830
|
async #read() {
|
|
7930
8831
|
try {
|
|
7931
|
-
const raw = JSON.parse(await
|
|
8832
|
+
const raw = JSON.parse(await readFile13(this.#filePath, "utf8"));
|
|
7932
8833
|
if (raw.version !== 1 || !Array.isArray(raw.devices) || !Array.isArray(raw.clients) || !Array.isArray(raw.bindings)) {
|
|
7933
8834
|
return emptyStoreFile();
|
|
7934
8835
|
}
|
|
@@ -8181,7 +9082,7 @@ var init_library = __esm({
|
|
|
8181
9082
|
|
|
8182
9083
|
// apps/cli/src/relay-server-cli.ts
|
|
8183
9084
|
import { homedir as homedir7 } from "node:os";
|
|
8184
|
-
import { join as
|
|
9085
|
+
import { join as join15 } from "node:path";
|
|
8185
9086
|
var DEFAULT_HOST2, DEFAULT_PORT2, runCliRelay, runRelayServe, parseRelayServeFlags, waitForStop, requireValue3, writeRelayUsage;
|
|
8186
9087
|
var init_relay_server_cli = __esm({
|
|
8187
9088
|
"apps/cli/src/relay-server-cli.ts"() {
|
|
@@ -8233,7 +9134,7 @@ var init_relay_server_cli = __esm({
|
|
|
8233
9134
|
parseRelayServeFlags = (argv) => {
|
|
8234
9135
|
let host = DEFAULT_HOST2;
|
|
8235
9136
|
let port = DEFAULT_PORT2;
|
|
8236
|
-
let dataDir =
|
|
9137
|
+
let dataDir = join15(homedir7(), ".scorel", "relay");
|
|
8237
9138
|
for (let index = 0; index < argv.length; index += 1) {
|
|
8238
9139
|
const arg = argv[index];
|
|
8239
9140
|
if (arg === "--host") {
|
|
@@ -8289,19 +9190,20 @@ var init_relay_server_cli = __esm({
|
|
|
8289
9190
|
});
|
|
8290
9191
|
|
|
8291
9192
|
// apps/cli/src/up-cli.ts
|
|
8292
|
-
import { spawn } from "node:child_process";
|
|
9193
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
8293
9194
|
import { homedir as homedir8 } from "node:os";
|
|
8294
|
-
import { join as
|
|
8295
|
-
import { fileURLToPath } from "node:url";
|
|
8296
|
-
var DEFAULT_DAEMON_PORT, DEFAULT_WEBUI_PORT, DEFAULT_DAEMON_READY_TIMEOUT_MS, defaultStateDir3, defaultAttachSigint, runCliUp, parseUpFlags, requireValue4,
|
|
9195
|
+
import { dirname as dirname11, join as join16 } from "node:path";
|
|
9196
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
9197
|
+
var DEFAULT_DAEMON_PORT, DEFAULT_WEBUI_PORT, DEFAULT_DAEMON_READY_TIMEOUT_MS, defaultStateDir3, defaultAttachSigint, runCliUp, parseUpFlags, requireValue4, waitForDaemonReady2, pipeWithPrefix, detachBackgroundDaemon2, nodeEntrypointArgs2, pipeStreamLines, once;
|
|
8297
9198
|
var init_up_cli = __esm({
|
|
8298
9199
|
"apps/cli/src/up-cli.ts"() {
|
|
8299
9200
|
"use strict";
|
|
8300
9201
|
init_src4();
|
|
9202
|
+
init_daemon_cli();
|
|
8301
9203
|
DEFAULT_DAEMON_PORT = 7777;
|
|
8302
9204
|
DEFAULT_WEBUI_PORT = 3e3;
|
|
8303
9205
|
DEFAULT_DAEMON_READY_TIMEOUT_MS = 1e4;
|
|
8304
|
-
defaultStateDir3 = () =>
|
|
9206
|
+
defaultStateDir3 = () => join16(homedir8(), ".scorel");
|
|
8305
9207
|
defaultAttachSigint = (listener) => {
|
|
8306
9208
|
process.on("SIGINT", listener);
|
|
8307
9209
|
return () => process.off("SIGINT", listener);
|
|
@@ -8316,8 +9218,8 @@ var init_up_cli = __esm({
|
|
|
8316
9218
|
return 1;
|
|
8317
9219
|
}
|
|
8318
9220
|
const stateDir = options.stateDir ?? defaultStateDir3();
|
|
8319
|
-
const cliEntrypoint = options.cliEntrypoint ??
|
|
8320
|
-
const spawnFn = options.spawn ??
|
|
9221
|
+
const cliEntrypoint = options.cliEntrypoint ?? fileURLToPath3(import.meta.url).replace(/up-cli\.ts$/, "index.ts");
|
|
9222
|
+
const spawnFn = options.spawn ?? spawn2;
|
|
8321
9223
|
const readState = options.readState ?? ((dir) => readLocalDaemonState({ stateDir: dir }));
|
|
8322
9224
|
const attachSigint = options.attachSigint ?? defaultAttachSigint;
|
|
8323
9225
|
const readyTimeout = options.daemonReadyTimeoutMs ?? DEFAULT_DAEMON_READY_TIMEOUT_MS;
|
|
@@ -8328,31 +9230,31 @@ var init_up_cli = __esm({
|
|
|
8328
9230
|
let daemonState = existingState;
|
|
8329
9231
|
if (!reuseDaemon) {
|
|
8330
9232
|
const daemonArgs = [
|
|
8331
|
-
|
|
8332
|
-
"tsx",
|
|
8333
|
-
cliEntrypoint,
|
|
9233
|
+
...nodeEntrypointArgs2(cliEntrypoint),
|
|
8334
9234
|
"daemon",
|
|
8335
9235
|
"serve",
|
|
8336
9236
|
"--port",
|
|
8337
9237
|
String(flags.daemonPort),
|
|
8338
9238
|
"--cwd",
|
|
8339
9239
|
flags.cwd,
|
|
9240
|
+
"--idle-timeout-ms",
|
|
9241
|
+
String(AUTO_STARTED_IDLE_SHUTDOWN_MS),
|
|
8340
9242
|
"--no-relay"
|
|
8341
9243
|
];
|
|
8342
9244
|
daemonChild = spawnFn(process.execPath, daemonArgs, {
|
|
8343
|
-
cwd:
|
|
9245
|
+
cwd: dirname11(cliEntrypoint),
|
|
8344
9246
|
env: { ...process.env },
|
|
9247
|
+
detached: true,
|
|
8345
9248
|
stdio: ["ignore", "pipe", "pipe"]
|
|
8346
9249
|
});
|
|
8347
9250
|
try {
|
|
8348
|
-
await
|
|
9251
|
+
await waitForDaemonReady2(daemonChild, readyTimeout);
|
|
8349
9252
|
} catch (cause) {
|
|
8350
9253
|
options.error.write(`scorel up error: ${cause.message}
|
|
8351
9254
|
`);
|
|
8352
9255
|
daemonChild.kill("SIGTERM");
|
|
8353
9256
|
return 1;
|
|
8354
9257
|
}
|
|
8355
|
-
pipeWithPrefix(daemonChild, "[daemon]", options.output, options.error);
|
|
8356
9258
|
daemonState = await readState(stateDir);
|
|
8357
9259
|
}
|
|
8358
9260
|
if (!daemonState) {
|
|
@@ -8360,16 +9262,17 @@ var init_up_cli = __esm({
|
|
|
8360
9262
|
daemonChild?.kill("SIGTERM");
|
|
8361
9263
|
return 1;
|
|
8362
9264
|
}
|
|
9265
|
+
if (daemonChild) {
|
|
9266
|
+
detachBackgroundDaemon2(daemonChild);
|
|
9267
|
+
}
|
|
8363
9268
|
const webuiArgs = [
|
|
8364
|
-
|
|
8365
|
-
"tsx",
|
|
8366
|
-
cliEntrypoint,
|
|
9269
|
+
...nodeEntrypointArgs2(cliEntrypoint),
|
|
8367
9270
|
"webui",
|
|
8368
9271
|
"--port",
|
|
8369
9272
|
String(flags.webuiPort)
|
|
8370
9273
|
];
|
|
8371
9274
|
const webuiChild = spawnFn(process.execPath, webuiArgs, {
|
|
8372
|
-
cwd:
|
|
9275
|
+
cwd: dirname11(cliEntrypoint),
|
|
8373
9276
|
env: { ...process.env },
|
|
8374
9277
|
stdio: ["ignore", "pipe", "pipe"]
|
|
8375
9278
|
});
|
|
@@ -8386,33 +9289,21 @@ var init_up_cli = __esm({
|
|
|
8386
9289
|
return;
|
|
8387
9290
|
}
|
|
8388
9291
|
shuttingDown = true;
|
|
8389
|
-
daemonChild?.kill("SIGTERM");
|
|
8390
9292
|
webuiChild.kill("SIGTERM");
|
|
8391
9293
|
});
|
|
8392
|
-
const daemonExit = daemonChild ? once(daemonChild) : Promise.resolve(0);
|
|
8393
9294
|
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
9295
|
const webuiDeathWatcher = webuiExit.then((code) => {
|
|
8404
9296
|
if (!shuttingDown) {
|
|
8405
9297
|
shuttingDown = true;
|
|
8406
9298
|
options.error.write(`scorel up webui exited code=${code}
|
|
8407
9299
|
`);
|
|
8408
|
-
daemonChild?.kill("SIGTERM");
|
|
8409
9300
|
}
|
|
8410
9301
|
return code;
|
|
8411
9302
|
});
|
|
8412
|
-
const
|
|
9303
|
+
const webuiCode = await webuiDeathWatcher;
|
|
8413
9304
|
detachSigint();
|
|
8414
9305
|
options.output.write("scorel up stopped\n");
|
|
8415
|
-
return
|
|
9306
|
+
return webuiCode === 0 ? 0 : 1;
|
|
8416
9307
|
};
|
|
8417
9308
|
parseUpFlags = (argv, defaultCwd) => {
|
|
8418
9309
|
let daemonPort = DEFAULT_DAEMON_PORT;
|
|
@@ -8452,7 +9343,7 @@ var init_up_cli = __esm({
|
|
|
8452
9343
|
}
|
|
8453
9344
|
return value;
|
|
8454
9345
|
};
|
|
8455
|
-
|
|
9346
|
+
waitForDaemonReady2 = (child, timeoutMs) => new Promise((resolveReady, rejectReady) => {
|
|
8456
9347
|
if (!child.stdout) {
|
|
8457
9348
|
rejectReady(new Error("daemon child has no stdout stream"));
|
|
8458
9349
|
return;
|
|
@@ -8509,6 +9400,12 @@ var init_up_cli = __esm({
|
|
|
8509
9400
|
pipeStreamLines(child.stderr, prefix, error);
|
|
8510
9401
|
}
|
|
8511
9402
|
};
|
|
9403
|
+
detachBackgroundDaemon2 = (child) => {
|
|
9404
|
+
child.stdout?.destroy();
|
|
9405
|
+
child.stderr?.destroy();
|
|
9406
|
+
child.unref();
|
|
9407
|
+
};
|
|
9408
|
+
nodeEntrypointArgs2 = (entrypoint) => entrypoint.endsWith(".ts") ? ["--import", "tsx", entrypoint] : [entrypoint];
|
|
8512
9409
|
pipeStreamLines = (stream, prefix, destination) => {
|
|
8513
9410
|
let buffer = "";
|
|
8514
9411
|
stream.setEncoding?.("utf8");
|
|
@@ -8538,10 +9435,10 @@ var init_up_cli = __esm({
|
|
|
8538
9435
|
});
|
|
8539
9436
|
|
|
8540
9437
|
// apps/cli/src/webui-cli.ts
|
|
8541
|
-
import { spawn as
|
|
9438
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
8542
9439
|
import { existsSync as existsSync4 } from "node:fs";
|
|
8543
|
-
import { dirname as
|
|
8544
|
-
import { fileURLToPath as
|
|
9440
|
+
import { dirname as dirname12, resolve as resolve6 } from "node:path";
|
|
9441
|
+
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
8545
9442
|
var DEFAULT_PORT3, DEFAULT_HOST3, runCliWebUi, findWebuiAppDir, buildWebUiSpawnPlan, parseWebUiFlags, requireValue5, waitForChildExit;
|
|
8546
9443
|
var init_webui_cli = __esm({
|
|
8547
9444
|
"apps/cli/src/webui-cli.ts"() {
|
|
@@ -8563,7 +9460,7 @@ var init_webui_cli = __esm({
|
|
|
8563
9460
|
return 1;
|
|
8564
9461
|
}
|
|
8565
9462
|
const plan = buildWebUiSpawnPlan(flags, webuiAppDir);
|
|
8566
|
-
const spawnFn = options.spawn ??
|
|
9463
|
+
const spawnFn = options.spawn ?? spawn3;
|
|
8567
9464
|
const child = spawnFn(plan.command, plan.argv, {
|
|
8568
9465
|
cwd: plan.cwd,
|
|
8569
9466
|
env: plan.env,
|
|
@@ -8572,7 +9469,7 @@ var init_webui_cli = __esm({
|
|
|
8572
9469
|
return await waitForChildExit(child, options);
|
|
8573
9470
|
};
|
|
8574
9471
|
findWebuiAppDir = () => {
|
|
8575
|
-
let cursor =
|
|
9472
|
+
let cursor = dirname12(fileURLToPath4(import.meta.url));
|
|
8576
9473
|
for (let depth = 0; depth < 8; depth += 1) {
|
|
8577
9474
|
const candidate = resolve6(cursor, "apps/webui/package.json");
|
|
8578
9475
|
if (existsSync4(candidate)) {
|
|
@@ -8661,11 +9558,11 @@ __export(index_exports, {
|
|
|
8661
9558
|
runCli: () => runCli
|
|
8662
9559
|
});
|
|
8663
9560
|
import { createHash as createHash3 } from "node:crypto";
|
|
8664
|
-
import { appendFile as appendFile4, mkdir as mkdir8, readFile as
|
|
9561
|
+
import { appendFile as appendFile4, mkdir as mkdir8, readFile as readFile14, realpath as realpath3, readdir as readdir7, writeFile as writeFile8 } from "node:fs/promises";
|
|
8665
9562
|
import { createInterface } from "node:readline/promises";
|
|
8666
9563
|
import { homedir as homedir9 } from "node:os";
|
|
8667
|
-
import { fileURLToPath as
|
|
8668
|
-
import { basename as
|
|
9564
|
+
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
9565
|
+
import { basename as basename4, dirname as dirname13, join as join17 } from "node:path";
|
|
8669
9566
|
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
9567
|
var init_index = __esm({
|
|
8671
9568
|
async "apps/cli/src/index.ts"() {
|
|
@@ -8678,13 +9575,19 @@ var init_index = __esm({
|
|
|
8678
9575
|
init_relay_server_cli();
|
|
8679
9576
|
init_up_cli();
|
|
8680
9577
|
init_webui_cli();
|
|
9578
|
+
init_update_cli();
|
|
8681
9579
|
cliAppName = "@scorel/app-cli";
|
|
8682
9580
|
cliClientDependency = clientPackageName;
|
|
8683
9581
|
cliDaemonDependency = daemonPackageName;
|
|
8684
9582
|
defaultSessionsDir = () => scorelSessionsDir(homedir9());
|
|
8685
|
-
defaultStateDir4 = () =>
|
|
9583
|
+
defaultStateDir4 = () => join17(homedir9(), ".scorel");
|
|
8686
9584
|
runCli = async (argv, io = { input: process.stdin, output: process.stdout, error: process.stderr }, runOptions = {}) => {
|
|
8687
9585
|
const [command, ...rest] = argv;
|
|
9586
|
+
if (command === "--version" || command === "-v" || command === "version") {
|
|
9587
|
+
io.output.write(`${await readInstalledScorelVersion()}
|
|
9588
|
+
`);
|
|
9589
|
+
return 0;
|
|
9590
|
+
}
|
|
8688
9591
|
if (!command || command === "chat") {
|
|
8689
9592
|
if (rest.includes("--help") || rest.includes("-h")) {
|
|
8690
9593
|
writeUsage(io.output);
|
|
@@ -8730,6 +9633,9 @@ var init_index = __esm({
|
|
|
8730
9633
|
error: io.error
|
|
8731
9634
|
});
|
|
8732
9635
|
}
|
|
9636
|
+
if (command === "update" || command === "upgrade") {
|
|
9637
|
+
return runCliUpdate(rest, { output: io.output, error: io.error });
|
|
9638
|
+
}
|
|
8733
9639
|
if (command === "attach") {
|
|
8734
9640
|
try {
|
|
8735
9641
|
return runAttach(parseAttachOptions(rest), {
|
|
@@ -8817,10 +9723,10 @@ var init_index = __esm({
|
|
|
8817
9723
|
}
|
|
8818
9724
|
};
|
|
8819
9725
|
runLogs = async (options, io) => {
|
|
8820
|
-
const filePath = options.attach ? await findAttachDiagnosticsFilePath(io.stateDir, options.sessionId, options.remoteUrl) :
|
|
9726
|
+
const filePath = options.attach ? await findAttachDiagnosticsFilePath(io.stateDir, options.sessionId, options.remoteUrl) : join17(io.sessionsDir, `${options.sessionId}.log`);
|
|
8821
9727
|
let content;
|
|
8822
9728
|
try {
|
|
8823
|
-
content = await
|
|
9729
|
+
content = await readFile14(filePath, "utf8");
|
|
8824
9730
|
} catch (cause) {
|
|
8825
9731
|
io.error.write(`scorel logs error: ${cause instanceof Error ? cause.message : String(cause)}
|
|
8826
9732
|
`);
|
|
@@ -8974,31 +9880,31 @@ var init_index = __esm({
|
|
|
8974
9880
|
};
|
|
8975
9881
|
attachCacheFilePath = (stateDir, scope, sessionId) => {
|
|
8976
9882
|
const scopeKey = createHash3("sha256").update(`${scope.kind}\0${scope.locator}`).digest("hex").slice(0, 24);
|
|
8977
|
-
return
|
|
9883
|
+
return join17(stateDir, "attach-cache", scopeKey, `${sessionId}.json`);
|
|
8978
9884
|
};
|
|
8979
9885
|
attachDiagnosticsFilePath = (stateDir, scope, sessionId) => {
|
|
8980
9886
|
const scopeKey = createHash3("sha256").update(`${scope.kind}\0${scope.locator}`).digest("hex").slice(0, 24);
|
|
8981
|
-
return
|
|
9887
|
+
return join17(stateDir, "attach-cache", scopeKey, `${sessionId}.log`);
|
|
8982
9888
|
};
|
|
8983
9889
|
findAttachDiagnosticsFilePath = async (stateDir, sessionId, _remoteUrl) => {
|
|
8984
|
-
const root =
|
|
9890
|
+
const root = join17(stateDir, "attach-cache");
|
|
8985
9891
|
const scopes = await readdir7(root).catch(() => []);
|
|
8986
9892
|
for (const scope of scopes) {
|
|
8987
|
-
const candidate =
|
|
9893
|
+
const candidate = join17(root, scope, `${sessionId}.log`);
|
|
8988
9894
|
try {
|
|
8989
|
-
await
|
|
9895
|
+
await readFile14(candidate, "utf8");
|
|
8990
9896
|
return candidate;
|
|
8991
9897
|
} catch {
|
|
8992
9898
|
continue;
|
|
8993
9899
|
}
|
|
8994
9900
|
}
|
|
8995
|
-
return
|
|
9901
|
+
return join17(root, "__missing__", `${sessionId}.log`);
|
|
8996
9902
|
};
|
|
8997
9903
|
stateDirFromSessionsDir = (sessionsDir) => {
|
|
8998
9904
|
if (!sessionsDir) {
|
|
8999
9905
|
return defaultStateDir4();
|
|
9000
9906
|
}
|
|
9001
|
-
return
|
|
9907
|
+
return basename4(sessionsDir) === "sessions" ? dirname13(sessionsDir) : sessionsDir;
|
|
9002
9908
|
};
|
|
9003
9909
|
AttachDiagnostics = class {
|
|
9004
9910
|
#stateDir;
|
|
@@ -9050,14 +9956,14 @@ var init_index = __esm({
|
|
|
9050
9956
|
}
|
|
9051
9957
|
const filePath = attachDiagnosticsFilePath(this.#stateDir, this.#scope, this.#sessionId);
|
|
9052
9958
|
this.#writes.push(
|
|
9053
|
-
mkdir8(
|
|
9959
|
+
mkdir8(dirname13(filePath), { recursive: true }).then(() => appendFile4(filePath, `${line}
|
|
9054
9960
|
`, "utf8"))
|
|
9055
9961
|
);
|
|
9056
9962
|
}
|
|
9057
9963
|
};
|
|
9058
9964
|
readAttachCache = async (stateDir, scope, sessionId) => {
|
|
9059
9965
|
try {
|
|
9060
|
-
const raw = JSON.parse(await
|
|
9966
|
+
const raw = JSON.parse(await readFile14(attachCacheFilePath(stateDir, scope, sessionId), "utf8"));
|
|
9061
9967
|
if (raw.version !== 1 || raw.sessionId !== String(sessionId) || raw.scope.kind !== scope.kind || raw.scope.locator !== scope.locator || !Array.isArray(raw.events)) {
|
|
9062
9968
|
return emptyAttachCacheSnapshot();
|
|
9063
9969
|
}
|
|
@@ -9080,7 +9986,7 @@ var init_index = __esm({
|
|
|
9080
9986
|
const filePath = attachCacheFilePath(stateDir, scope, sessionId);
|
|
9081
9987
|
const uniqueEvents = mergePersistentEvents(snapshot.events);
|
|
9082
9988
|
const transients = removeCompletedTransients(snapshot.transients, uniqueEvents);
|
|
9083
|
-
await mkdir8(
|
|
9989
|
+
await mkdir8(dirname13(filePath), { recursive: true });
|
|
9084
9990
|
await writeFile8(
|
|
9085
9991
|
filePath,
|
|
9086
9992
|
`${JSON.stringify({ version: 1, scope, sessionId: String(sessionId), events: uniqueEvents, transients }, null, 2)}
|
|
@@ -9217,17 +10123,21 @@ var init_index = __esm({
|
|
|
9217
10123
|
return { sessionId, tail, attach, remoteUrl };
|
|
9218
10124
|
};
|
|
9219
10125
|
runChat = async (options, io) => {
|
|
9220
|
-
const
|
|
9221
|
-
const
|
|
10126
|
+
const configScope = { scorelHomeDir: options.stateDir };
|
|
10127
|
+
const loadProjectConfig = async (project2) => options.config ?? await loadScorelConfig({ cwd: project2.workDir, ...configScope });
|
|
10128
|
+
const loadProjectConfigProfile = async (project2) => options.config ?? await loadScorelConfigProfile({ cwd: project2.workDir, ...configScope });
|
|
9222
10129
|
const daemon = new ScorelHost({
|
|
9223
10130
|
sessionsDir: options.sessionsDir,
|
|
9224
|
-
projectsPath:
|
|
10131
|
+
projectsPath: join17(options.stateDir, "projects.json"),
|
|
9225
10132
|
deviceId: asDeviceId("device_local"),
|
|
10133
|
+
scorelHomeDir: options.stateDir,
|
|
9226
10134
|
loadConfig: async ({ project: project2 }) => loadProjectConfig(project2),
|
|
9227
10135
|
loadConfigProfile: async ({ project: project2 }) => loadProjectConfigProfile(project2),
|
|
9228
|
-
createRuntime: async ({ project: project2, selectedModel, purpose }) => createRealRuntime({
|
|
10136
|
+
createRuntime: async ({ sessionId, project: project2, selectedModel, purpose }) => createRealRuntime({
|
|
9229
10137
|
cwd: project2.workDir,
|
|
9230
10138
|
config: await loadProjectConfig(project2),
|
|
10139
|
+
sessionsDir: options.sessionsDir,
|
|
10140
|
+
sessionId,
|
|
9231
10141
|
modelSelection: selectedModel ? { modelId: selectedModel.modelId, role: selectedModel.role } : void 0,
|
|
9232
10142
|
includeTools: purpose === "chat"
|
|
9233
10143
|
})
|
|
@@ -9356,8 +10266,10 @@ var init_index = __esm({
|
|
|
9356
10266
|
"Usage: scorel chat [--session <id>] [--cwd <dir>]",
|
|
9357
10267
|
" scorel [--session <id>] [--cwd <dir>]",
|
|
9358
10268
|
" scorel attach --session <id> --remote <ws-url> --token <token>",
|
|
10269
|
+
" scorel host start [--host <h>] [--port <p>] [--token <t>] [--project <dir>]",
|
|
10270
|
+
" [--relay <relay-url> | --no-relay] [--replace] [--idle-timeout-ms <ms>]",
|
|
9359
10271
|
" scorel host serve [--host <h>] [--port <p>] [--token <t>] [--project <dir>]",
|
|
9360
|
-
" [--relay <relay-url> | --no-relay] [--replace]",
|
|
10272
|
+
" [--relay <relay-url> | --no-relay] [--replace] [--idle-timeout-ms <ms>]",
|
|
9361
10273
|
" scorel host status [--show-token]",
|
|
9362
10274
|
" scorel host stop",
|
|
9363
10275
|
" scorel host reset",
|
|
@@ -9365,6 +10277,9 @@ var init_index = __esm({
|
|
|
9365
10277
|
" scorel relay serve [--host <h>] [--port <p>] [--data-dir <dir>]",
|
|
9366
10278
|
" scorel webui [--port <p>] [--host <h>]",
|
|
9367
10279
|
" scorel up [--daemon-port <p>] [--webui-port <p>] [--cwd <d>]",
|
|
10280
|
+
" scorel update",
|
|
10281
|
+
" scorel upgrade",
|
|
10282
|
+
" scorel version",
|
|
9368
10283
|
" scorel logs [--attach] --session <id> [--remote <ws-url>] [--tail <n>]",
|
|
9369
10284
|
" scorel project list",
|
|
9370
10285
|
" scorel project add <dir>",
|
|
@@ -9496,7 +10411,7 @@ ${text}
|
|
|
9496
10411
|
if (!process.argv[1]) return false;
|
|
9497
10412
|
const [argvPath, modulePath] = await Promise.all([
|
|
9498
10413
|
realpath3(process.argv[1]).catch(() => process.argv[1]),
|
|
9499
|
-
realpath3(
|
|
10414
|
+
realpath3(fileURLToPath5(import.meta.url)).catch(() => fileURLToPath5(import.meta.url))
|
|
9500
10415
|
]);
|
|
9501
10416
|
return argvPath === modulePath;
|
|
9502
10417
|
};
|