@chanlerdev/scorel 0.0.4 → 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 +373 -64
- package/dist/index.js.map +4 -4
- package/docs/CHANGELOG.md +41 -0
- package/docs/ROADMAP.md +5 -0
- package/docs/SHIP.md +9 -5
- package/docs/spec/ship/S0064-gui-product-intent-and-boundary.md +1 -1
- package/docs/spec/ship/S0073-provider-model-profile-contract.md +8 -1
- 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 +1 -1
package/dist/index.js
CHANGED
|
@@ -1746,7 +1746,7 @@ import { mkdir as mkdir2, readFile as readFile4, rename as rename2, rm, stat as
|
|
|
1746
1746
|
import { userInfo } from "node:os";
|
|
1747
1747
|
import { basename as basename2, dirname as dirname3, extname, isAbsolute, relative, resolve } from "node:path";
|
|
1748
1748
|
import { promisify } from "node:util";
|
|
1749
|
-
var execFileAsync, DEFAULT_SEARCH_LIMIT, DEFAULT_GREP_LIMIT, DEFAULT_READ_LIMIT, DEFAULT_CONTEXT_WINDOW, READ_TOKEN_BUDGET_RATIO, FULL_READ_TOKEN_BUDGET_RATIO, createCodingTools, parseReadArgs, parseWriteArgs, parseEditArgs, parseBashArgs, parseGlobArgs, parseGrepArgs, parseTodoWriteArgs, parseTodoItem, expectRecord, expectPath, expectString, optionalString, optionalNumber, optionalBoolean, snapshotFile, sameSnapshot, exists, isWithin, linesOf, IMAGE_EXTENSIONS, DOCUMENT_EXTENSIONS, BINARY_EXTENSIONS, assertReadableFileKind, assertTextBuffer, selectCompleteLinesWithinBudget, estimateTokens, renderReadLines, readTokenBudget, completeRanges, hasCompleteCoverage, mergeRanges, countOccurrences, atomicWriteFile, bashResult, resolveDefaultShell, resolveRtkCommand, rtkRewriteResult, executableRewriteCommand, readRtkGain, rtkSavedTokenDelta, withRtkSavings, nonNegativeInteger, isRecord3, shellQuote, shellCommandArgs, userShell, truncate, textResult, byteLength, isTimeoutError, isExecError, runRipgrep, splitOutput, vcsExcludes, grepArgs, splitGlobPatterns, paginate, toWorkspaceRelative, relativizeGrepLine, relativizeCountLine, sortPathsByMtime, formatPaginatedText, formatLimitSuffix, parseCountLines;
|
|
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;
|
|
1750
1750
|
var init_coding_tools = __esm({
|
|
1751
1751
|
"packages/core/src/tools/coding-tools.ts"() {
|
|
1752
1752
|
"use strict";
|
|
@@ -1914,7 +1914,7 @@ String: ${input.old_string}`
|
|
|
1914
1914
|
defineTool({
|
|
1915
1915
|
name: "Bash",
|
|
1916
1916
|
description: "Execute a shell command in the workspace with timeout and output truncation.",
|
|
1917
|
-
execute: async (
|
|
1917
|
+
execute: async (toolCallId, args, signal) => {
|
|
1918
1918
|
const input = parseBashArgs(args);
|
|
1919
1919
|
const commandCwd = input.cwd ? resolveWorkspacePath(input.cwd) : root;
|
|
1920
1920
|
const timeoutMs = Math.min(input.timeoutMs ?? defaultTimeoutMs, maxTimeoutMs);
|
|
@@ -1940,12 +1940,14 @@ String: ${input.old_string}`
|
|
|
1940
1940
|
maxBuffer: Math.max(outputLimit * 4, 1024 * 1024)
|
|
1941
1941
|
});
|
|
1942
1942
|
const rtkSavedTokens = rtk?.executable ? await rtkSavedTokenDelta(rtk.executable, commandCwd, rtkGainBefore) : void 0;
|
|
1943
|
-
return bashResult({
|
|
1943
|
+
return await bashResult({
|
|
1944
1944
|
exitCode: 0,
|
|
1945
1945
|
stdout: result.stdout,
|
|
1946
1946
|
stderr: result.stderr,
|
|
1947
1947
|
cwd: commandCwd,
|
|
1948
1948
|
outputLimit,
|
|
1949
|
+
artifactDir: options.toolResultArtifacts?.dir,
|
|
1950
|
+
toolCallId,
|
|
1949
1951
|
shell: defaultShell,
|
|
1950
1952
|
command,
|
|
1951
1953
|
rtk: withRtkSavings(rtkResult, rtkSavedTokens)
|
|
@@ -1956,12 +1958,14 @@ String: ${input.old_string}`
|
|
|
1956
1958
|
}
|
|
1957
1959
|
if (isExecError(cause)) {
|
|
1958
1960
|
const rtkSavedTokens = rtk?.executable ? await rtkSavedTokenDelta(rtk.executable, commandCwd, rtkGainBefore) : void 0;
|
|
1959
|
-
return bashResult({
|
|
1961
|
+
return await bashResult({
|
|
1960
1962
|
exitCode: typeof cause.code === "number" ? cause.code : 1,
|
|
1961
1963
|
stdout: String(cause.stdout ?? ""),
|
|
1962
1964
|
stderr: String(cause.stderr ?? cause.message),
|
|
1963
1965
|
cwd: commandCwd,
|
|
1964
1966
|
outputLimit,
|
|
1967
|
+
artifactDir: options.toolResultArtifacts?.dir,
|
|
1968
|
+
toolCallId,
|
|
1965
1969
|
shell: defaultShell,
|
|
1966
1970
|
command,
|
|
1967
1971
|
rtk: withRtkSavings(rtkResult, rtkSavedTokens)
|
|
@@ -2329,17 +2333,41 @@ ${filenames.join("\n")}`,
|
|
|
2329
2333
|
throw cause;
|
|
2330
2334
|
}
|
|
2331
2335
|
};
|
|
2332
|
-
bashResult = (input) => {
|
|
2333
|
-
const
|
|
2334
|
-
const
|
|
2335
|
-
|
|
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}
|
|
2336
2355
|
cwd: ${input.cwd}
|
|
2337
2356
|
stdout:
|
|
2338
2357
|
${stdout}
|
|
2339
2358
|
stderr:
|
|
2340
|
-
${stderr}
|
|
2359
|
+
${stderr}`;
|
|
2360
|
+
return textResult(text, {
|
|
2341
2361
|
exitCode: input.exitCode,
|
|
2342
2362
|
cwd: input.cwd,
|
|
2363
|
+
...artifactPath ? {
|
|
2364
|
+
artifact: {
|
|
2365
|
+
path: artifactPath,
|
|
2366
|
+
resultBytes,
|
|
2367
|
+
stdoutBytes,
|
|
2368
|
+
stderrBytes
|
|
2369
|
+
}
|
|
2370
|
+
} : {},
|
|
2343
2371
|
...input.shell ? { shell: input.shell } : {},
|
|
2344
2372
|
...input.command ? { command: input.command } : {},
|
|
2345
2373
|
...input.rtk ? {
|
|
@@ -2351,6 +2379,57 @@ ${stderr}`)
|
|
|
2351
2379
|
} : {}
|
|
2352
2380
|
});
|
|
2353
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
|
+
};
|
|
2354
2433
|
resolveDefaultShell = (input) => {
|
|
2355
2434
|
const shell = input || process.env.SHELL || userShell() || "/bin/sh";
|
|
2356
2435
|
return shell.trim() || "/bin/sh";
|
|
@@ -2434,10 +2513,11 @@ ${stderr}`)
|
|
|
2434
2513
|
if (bytes <= maxBytes) {
|
|
2435
2514
|
return value;
|
|
2436
2515
|
}
|
|
2437
|
-
const truncated =
|
|
2516
|
+
const truncated = sliceBytes(value, 0, maxBytes);
|
|
2438
2517
|
return `${truncated}
|
|
2439
2518
|
[${label} truncated: ${bytes} bytes > ${maxBytes} bytes]`;
|
|
2440
2519
|
};
|
|
2520
|
+
sliceBytes = (value, start, end) => Buffer.from(value).subarray(start, end).toString("utf8");
|
|
2441
2521
|
textResult = (text, details) => ({
|
|
2442
2522
|
content: [{ type: "text", text }],
|
|
2443
2523
|
details
|
|
@@ -3736,7 +3816,7 @@ function assertTreeEvent(value) {
|
|
|
3736
3816
|
throw new SessionStoreError("invalid_event", "skill_index_delta is missing delta payload");
|
|
3737
3817
|
}
|
|
3738
3818
|
}
|
|
3739
|
-
var SessionStoreError, SessionTree, JsonlSession, sessionFilePath, sessionLogFilePath, createSession, loadSession, buildContext, retainedMessagesBeforeCompact, isRetainedContextStart, parseJsonLine, parseHeader, parseSessionEvent, validateSessionMatch, isConversationEvent, isInstructionSnapshot, isHarnessItem, isCompactEvent, isQueueUpdate, isSessionTitleUpdated, isSkillIndexSnapshot, isSkillIndexDelta, isSkillIndexEntry, appendHarnessItemToContext, appendReminderToToolResult, isToolResultWithContent, renderSystemReminder, compactSummaryMessage, cloneMessage, isRecord7;
|
|
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;
|
|
3740
3820
|
var init_session = __esm({
|
|
3741
3821
|
"packages/core/src/session/index.ts"() {
|
|
3742
3822
|
"use strict";
|
|
@@ -3917,6 +3997,7 @@ var init_session = __esm({
|
|
|
3917
3997
|
};
|
|
3918
3998
|
sessionFilePath = (sessionsDir, sessionId) => join7(sessionsDir, `${sessionId}.jsonl`);
|
|
3919
3999
|
sessionLogFilePath = (sessionsDir, sessionId) => join7(sessionsDir, `${sessionId}.log`);
|
|
4000
|
+
sessionArtifactsDirPath = (sessionsDir, sessionId) => join7(sessionsDir, `${sessionId}.artifacts`);
|
|
3920
4001
|
createSession = async ({ sessionsDir, header }) => {
|
|
3921
4002
|
const validHeader = parseHeader(header);
|
|
3922
4003
|
await mkdir4(sessionsDir, { recursive: true });
|
|
@@ -4704,7 +4785,7 @@ import { basename as basename3, dirname as dirname8, join as join10, resolve as
|
|
|
4704
4785
|
import { pathToFileURL } from "node:url";
|
|
4705
4786
|
import { promisify as promisify2 } from "node:util";
|
|
4706
4787
|
import { WebSocketServer } from "ws";
|
|
4707
|
-
var daemonPackageName, SESSION_MEMORY_COMPACT_WAIT_MS, AUTO_COMPACT_RETAINED_EVENTS, execFileAsync2, localDaemonStateFile, createLocalDaemonState, readLocalDaemonState, removeLocalDaemonState, markDaemonStopped, daemonStateLiveness, defaultIsPidAlive, startRemoteDaemonWebSocketServer, startScorelHostWebSocketServer, closeWebSocketServer, createRealRuntime, ScorelHost, isMissingConfigError, createEmbeddedTransport, isNodeErrorCode5, wireErrorCode, hasContinuousCoverage, countContentBlocks, normalizeContent, inputText, assistantText, messageText, estimateScorelMessagesTokens, estimateTextTokens, compactLine2, parseSessionMemoryJson, stringArray, disabledMemorySettings, detectRtk, ensureRtkAvailable, emptyRuntimeStats, readRuntimeStats, writeRuntimeStats, parseRuntimeStats, parseRuntimeStatsBuckets, addRtkSavings, addRuntimeStatsBucket, rtkSavingsFromToolResult, nonNegativeInteger2, resolveDefaultShell2, shellCommandArgs2, userShell2, runtimeChannelContextFromWire, parseQueuedChannelContext, imBindingKey, defaultBuiltinExtensionsDir, runtimeModuleDir, findBuiltinExtensionsDir, isSteerMessage, stripImCommandPrefix, isRecord8, parseMemoryUpdate, normalizeMarkdownFile2, sanitizeSessionTitle, shortStack, formatDiagnosticLine, formatDiagnosticValue;
|
|
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;
|
|
4708
4789
|
var init_src4 = __esm({
|
|
4709
4790
|
"packages/daemon/src/index.ts"() {
|
|
4710
4791
|
"use strict";
|
|
@@ -4945,6 +5026,7 @@ var init_src4 = __esm({
|
|
|
4945
5026
|
for (const tool of createCodingTools({
|
|
4946
5027
|
cwd: options.cwd,
|
|
4947
5028
|
contextWindow: model.contextWindow,
|
|
5029
|
+
...options.sessionsDir && options.sessionId ? { toolResultArtifacts: { dir: sessionArtifactsDirPath(options.sessionsDir, options.sessionId) } } : {},
|
|
4948
5030
|
tokenSaving: {
|
|
4949
5031
|
rtk: {
|
|
4950
5032
|
enabled: options.config.runtime.tokenSavingRtk,
|
|
@@ -4985,6 +5067,7 @@ var init_src4 = __esm({
|
|
|
4985
5067
|
#registry;
|
|
4986
5068
|
#runtimeStatsQueue = Promise.resolve();
|
|
4987
5069
|
#idleShutdownTimer;
|
|
5070
|
+
#lastActiveWorkAt;
|
|
4988
5071
|
#started = false;
|
|
4989
5072
|
constructor(options) {
|
|
4990
5073
|
this.#sessionsDir = options.sessionsDir;
|
|
@@ -5003,6 +5086,7 @@ var init_src4 = __esm({
|
|
|
5003
5086
|
this.#onIdleShutdown = options.onIdleShutdown;
|
|
5004
5087
|
this.#now = options.now ?? Date.now;
|
|
5005
5088
|
this.#createId = options.createId ?? (() => crypto.randomUUID());
|
|
5089
|
+
this.#lastActiveWorkAt = this.#now();
|
|
5006
5090
|
this.#registry = new ProjectRegistry({
|
|
5007
5091
|
sessionsDir: this.#sessionsDir,
|
|
5008
5092
|
projectsPath: options.projectsPath,
|
|
@@ -5067,6 +5151,13 @@ var init_src4 = __esm({
|
|
|
5067
5151
|
releaseSessionEventBuffer(sessionId) {
|
|
5068
5152
|
this.#events.delete(sessionId);
|
|
5069
5153
|
}
|
|
5154
|
+
activityStatus() {
|
|
5155
|
+
const activeWork = this.#hasActiveWork();
|
|
5156
|
+
if (activeWork) {
|
|
5157
|
+
this.#lastActiveWorkAt = this.#now();
|
|
5158
|
+
}
|
|
5159
|
+
return { activeWork, lastActiveWorkAt: this.#lastActiveWorkAt };
|
|
5160
|
+
}
|
|
5070
5161
|
async handleMessage(connection, message) {
|
|
5071
5162
|
this.#assertStarted();
|
|
5072
5163
|
try {
|
|
@@ -5355,6 +5446,7 @@ var init_src4 = __esm({
|
|
|
5355
5446
|
content: normalizeContent(request.content),
|
|
5356
5447
|
parentId: request.options?.parentId,
|
|
5357
5448
|
source: "user",
|
|
5449
|
+
modelSelection: request.options?.modelSelection,
|
|
5358
5450
|
channelContext: request.options?.channelContext ? runtimeChannelContextFromWire(request.options.channelContext) : void 0,
|
|
5359
5451
|
onComplete: (result) => this.#respond(connection, request, { ...result, status: "completed" })
|
|
5360
5452
|
});
|
|
@@ -5380,11 +5472,14 @@ var init_src4 = __esm({
|
|
|
5380
5472
|
});
|
|
5381
5473
|
}
|
|
5382
5474
|
async #runUserTurn(lane, clientId, input) {
|
|
5475
|
+
this.#lastActiveWorkAt = this.#now();
|
|
5383
5476
|
const sessionId = lane.session.header.sessionId;
|
|
5477
|
+
await this.#selectChatRuntime(lane, input.modelSelection);
|
|
5384
5478
|
await this.#appendDiagnostic(sessionId, "send_message_started", {
|
|
5385
5479
|
clientId,
|
|
5386
5480
|
activeLeafId: lane.session.activeLeafId,
|
|
5387
|
-
source: input.source
|
|
5481
|
+
source: input.source,
|
|
5482
|
+
selectedModelId: lane.selectedModel?.modelId
|
|
5388
5483
|
});
|
|
5389
5484
|
const instructionSnapshot = await this.#ensureInstructionSnapshot(lane, clientId);
|
|
5390
5485
|
await this.#syncSkillIndex(lane, clientId);
|
|
@@ -5588,7 +5683,7 @@ var init_src4 = __esm({
|
|
|
5588
5683
|
createdAt: now,
|
|
5589
5684
|
updatedAt: now,
|
|
5590
5685
|
clientId: connection.clientId,
|
|
5591
|
-
...request.options?.channelContext ? { data: { channelContext: request.options.channelContext } } : {}
|
|
5686
|
+
...request.options?.channelContext || request.options?.modelSelection ? { data: { channelContext: request.options.channelContext, modelSelection: request.options.modelSelection } } : {}
|
|
5592
5687
|
};
|
|
5593
5688
|
lane.followUpWaiters.set(item.id, { connection, request });
|
|
5594
5689
|
await this.#appendQueueRewrite(lane, "follow_up", [...lane.session.tree.controlState.queues.follow_up, item], {
|
|
@@ -5641,6 +5736,7 @@ var init_src4 = __esm({
|
|
|
5641
5736
|
parentId: lane.session.activeLeafId,
|
|
5642
5737
|
source: "follow_up",
|
|
5643
5738
|
queueItemId: item.id,
|
|
5739
|
+
modelSelection: parseQueuedModelSelection(item.data?.modelSelection),
|
|
5644
5740
|
channelContext: parseQueuedChannelContext(item.data?.channelContext),
|
|
5645
5741
|
onComplete: waiter ? (result) => this.#respond(waiter.connection, waiter.request, { ...result, status: "completed" }) : void 0
|
|
5646
5742
|
});
|
|
@@ -6623,6 +6719,7 @@ var init_src4 = __esm({
|
|
|
6623
6719
|
session: loaded,
|
|
6624
6720
|
project,
|
|
6625
6721
|
runtime,
|
|
6722
|
+
...selectedModel ? { selectedModel } : {},
|
|
6626
6723
|
queue: Promise.resolve(),
|
|
6627
6724
|
appendQueue: Promise.resolve(),
|
|
6628
6725
|
followUpWaiters: /* @__PURE__ */ new Map()
|
|
@@ -6674,6 +6771,7 @@ var init_src4 = __esm({
|
|
|
6674
6771
|
session,
|
|
6675
6772
|
project,
|
|
6676
6773
|
runtime,
|
|
6774
|
+
...selectedModel ? { selectedModel } : {},
|
|
6677
6775
|
queue: Promise.resolve(),
|
|
6678
6776
|
appendQueue: Promise.resolve(),
|
|
6679
6777
|
followUpWaiters: /* @__PURE__ */ new Map()
|
|
@@ -6689,6 +6787,32 @@ var init_src4 = __esm({
|
|
|
6689
6787
|
})
|
|
6690
6788
|
);
|
|
6691
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
|
+
}
|
|
6692
6816
|
#syncChannelTool(lane, channelContext) {
|
|
6693
6817
|
if (!channelContext) {
|
|
6694
6818
|
lane.runtime.unregisterTool("SendChannelMessage");
|
|
@@ -7249,9 +7373,10 @@ var init_src4 = __esm({
|
|
|
7249
7373
|
}
|
|
7250
7374
|
const persistedSelection = "selectedModel" in meta ? meta.selectedModel : void 0;
|
|
7251
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;
|
|
7252
7377
|
const selection = resolveModelSelection(
|
|
7253
7378
|
config,
|
|
7254
|
-
|
|
7379
|
+
selectionInput
|
|
7255
7380
|
);
|
|
7256
7381
|
const model = resolvePiAiModel(selection.config);
|
|
7257
7382
|
return {
|
|
@@ -7642,6 +7767,19 @@ var init_src4 = __esm({
|
|
|
7642
7767
|
...isRecord8(value.data) ? { data: value.data } : {}
|
|
7643
7768
|
});
|
|
7644
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
|
+
};
|
|
7645
7783
|
imBindingKey = (extensionId, externalConversationId) => `${extensionId}:${externalConversationId}`;
|
|
7646
7784
|
defaultBuiltinExtensionsDir = () => findBuiltinExtensionsDir([
|
|
7647
7785
|
runtimeModuleDir(),
|
|
@@ -7777,25 +7915,131 @@ var init_relay_cli = __esm({
|
|
|
7777
7915
|
}
|
|
7778
7916
|
});
|
|
7779
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
|
+
|
|
7780
8022
|
// apps/cli/src/daemon-cli.ts
|
|
7781
8023
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
7782
8024
|
import { spawn } from "node:child_process";
|
|
7783
8025
|
import { homedir as homedir6 } from "node:os";
|
|
7784
|
-
import { dirname as
|
|
7785
|
-
import { fileURLToPath } from "node:url";
|
|
7786
|
-
var DEFAULT_HOST, DEFAULT_PORT, STOP_POLL_INTERVAL_MS, STOP_GRACE_MS, START_READY_TIMEOUT_MS,
|
|
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;
|
|
7787
8029
|
var init_daemon_cli = __esm({
|
|
7788
8030
|
"apps/cli/src/daemon-cli.ts"() {
|
|
7789
8031
|
"use strict";
|
|
7790
8032
|
init_src4();
|
|
7791
8033
|
init_relay_cli();
|
|
8034
|
+
init_update_cli();
|
|
7792
8035
|
DEFAULT_HOST = "127.0.0.1";
|
|
7793
8036
|
DEFAULT_PORT = 7777;
|
|
7794
8037
|
STOP_POLL_INTERVAL_MS = 200;
|
|
7795
8038
|
STOP_GRACE_MS = 5e3;
|
|
7796
8039
|
START_READY_TIMEOUT_MS = 1e4;
|
|
7797
|
-
|
|
7798
|
-
|
|
8040
|
+
AUTO_STARTED_IDLE_SHUTDOWN_MS = 15 * 60 * 1e3;
|
|
8041
|
+
FOREGROUND_IDLE_SHUTDOWN_MS = 0;
|
|
8042
|
+
defaultStateDir2 = () => join13(homedir6(), ".scorel");
|
|
7799
8043
|
isLoopbackHost = (host) => host === "127.0.0.1" || host === "::1" || host === "localhost";
|
|
7800
8044
|
formatTimestamp = (epochMs) => new Date(epochMs).toISOString();
|
|
7801
8045
|
runCliDaemon = async (argv, options) => {
|
|
@@ -7824,7 +8068,7 @@ var init_daemon_cli = __esm({
|
|
|
7824
8068
|
runStartCommand = async (argv, options) => {
|
|
7825
8069
|
let flags;
|
|
7826
8070
|
try {
|
|
7827
|
-
flags = parseServeFlags(argv, options.cwd ?? process.cwd(), options.env ?? process.env);
|
|
8071
|
+
flags = parseServeFlags(argv, options.cwd ?? process.cwd(), options.env ?? process.env, FOREGROUND_IDLE_SHUTDOWN_MS);
|
|
7828
8072
|
} catch (cause) {
|
|
7829
8073
|
options.error.write(`scorel daemon start error: ${cause.message}
|
|
7830
8074
|
`);
|
|
@@ -7837,7 +8081,7 @@ var init_daemon_cli = __esm({
|
|
|
7837
8081
|
`);
|
|
7838
8082
|
return 0;
|
|
7839
8083
|
}
|
|
7840
|
-
const cliEntrypoint = options.cliEntrypoint ??
|
|
8084
|
+
const cliEntrypoint = options.cliEntrypoint ?? fileURLToPath2(import.meta.url).replace(/daemon-cli\.ts$/, "index.ts");
|
|
7841
8085
|
const child = (options.spawn ?? spawn)(process.execPath, [
|
|
7842
8086
|
...nodeEntrypointArgs(cliEntrypoint),
|
|
7843
8087
|
"host",
|
|
@@ -7854,7 +8098,7 @@ var init_daemon_cli = __esm({
|
|
|
7854
8098
|
...flags.relayUrl ? ["--relay", flags.relayUrl] : ["--no-relay"],
|
|
7855
8099
|
...flags.replace ? ["--replace"] : []
|
|
7856
8100
|
], {
|
|
7857
|
-
cwd:
|
|
8101
|
+
cwd: dirname10(cliEntrypoint),
|
|
7858
8102
|
env: { ...process.env, ...options.env ?? {} },
|
|
7859
8103
|
detached: true,
|
|
7860
8104
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -7881,7 +8125,7 @@ var init_daemon_cli = __esm({
|
|
|
7881
8125
|
runServeCommand = async (argv, options) => {
|
|
7882
8126
|
let flags;
|
|
7883
8127
|
try {
|
|
7884
|
-
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);
|
|
7885
8129
|
} catch (cause) {
|
|
7886
8130
|
options.error.write(`scorel daemon serve error: ${cause.message}
|
|
7887
8131
|
`);
|
|
@@ -7914,9 +8158,10 @@ Use --replace to stop it and start a new one.
|
|
|
7914
8158
|
stopRequested = true;
|
|
7915
8159
|
resolveStopWaiter?.();
|
|
7916
8160
|
};
|
|
8161
|
+
const sessionsDir = options.sessionsDir ?? scorelSessionsDir(homedir6());
|
|
7917
8162
|
const daemon = new ScorelHost({
|
|
7918
|
-
sessionsDir
|
|
7919
|
-
projectsPath:
|
|
8163
|
+
sessionsDir,
|
|
8164
|
+
projectsPath: join13(options.stateDir, "projects.json"),
|
|
7920
8165
|
deviceId: identity.deviceId,
|
|
7921
8166
|
deviceDisplayName: identity.displayName,
|
|
7922
8167
|
idleShutdownMs: flags.idleShutdownMs,
|
|
@@ -7924,15 +8169,25 @@ Use --replace to stop it and start a new one.
|
|
|
7924
8169
|
scorelHomeDir: options.stateDir,
|
|
7925
8170
|
loadConfig: async ({ project }) => loadScorelConfig({ cwd: project.workDir, ...configScope }),
|
|
7926
8171
|
loadConfigProfile: async ({ project }) => loadScorelConfigProfile({ cwd: project.workDir, ...configScope }),
|
|
7927
|
-
createRuntime: async ({ project, selectedModel, purpose }) => createRealRuntime({
|
|
8172
|
+
createRuntime: async ({ sessionId, project, selectedModel, purpose }) => createRealRuntime({
|
|
7928
8173
|
cwd: project.workDir,
|
|
7929
8174
|
config: await loadScorelConfig({ cwd: project.workDir, ...configScope }),
|
|
8175
|
+
sessionsDir,
|
|
8176
|
+
sessionId,
|
|
7930
8177
|
modelSelection: selectedModel ? { modelId: selectedModel.modelId, role: selectedModel.role } : void 0,
|
|
7931
8178
|
includeTools: purpose === "chat"
|
|
7932
8179
|
})
|
|
7933
8180
|
});
|
|
7934
8181
|
await daemon.start();
|
|
7935
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
|
+
});
|
|
7936
8191
|
const server = await startScorelHostWebSocketServer({
|
|
7937
8192
|
hostService: daemon,
|
|
7938
8193
|
host: flags.host,
|
|
@@ -7978,6 +8233,7 @@ Use --replace to stop it and start a new one.
|
|
|
7978
8233
|
}
|
|
7979
8234
|
const shutdown = async () => {
|
|
7980
8235
|
try {
|
|
8236
|
+
autoUpdater.stop();
|
|
7981
8237
|
relayClient?.close();
|
|
7982
8238
|
await server.close();
|
|
7983
8239
|
} finally {
|
|
@@ -8028,6 +8284,42 @@ Use --replace to stop it and start a new one.
|
|
|
8028
8284
|
`);
|
|
8029
8285
|
return 0;
|
|
8030
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
|
+
};
|
|
8031
8323
|
stopRunningDaemon = async (state, options) => {
|
|
8032
8324
|
try {
|
|
8033
8325
|
process.kill(state.pid, "SIGTERM");
|
|
@@ -8131,14 +8423,14 @@ Use --replace to stop it and start a new one.
|
|
|
8131
8423
|
const stoppedAt = state.stoppedAt !== null ? formatTimestamp(state.stoppedAt) : "unknown";
|
|
8132
8424
|
return `stopped url=${state.wsUrl} last-pid=${state.pid} stoppedAt=${stoppedAt} liveness=${liveness}`;
|
|
8133
8425
|
};
|
|
8134
|
-
parseServeFlags = (argv, defaultCwd, env) => {
|
|
8426
|
+
parseServeFlags = (argv, defaultCwd, env, defaultIdleShutdownMs) => {
|
|
8135
8427
|
let host = DEFAULT_HOST;
|
|
8136
8428
|
let port = DEFAULT_PORT;
|
|
8137
8429
|
let cwd = defaultCwd;
|
|
8138
8430
|
let token;
|
|
8139
8431
|
let relayUrl = resolveDefaultRelayUrl(env);
|
|
8140
8432
|
let replace = false;
|
|
8141
|
-
let idleShutdownMs =
|
|
8433
|
+
let idleShutdownMs = defaultIdleShutdownMs;
|
|
8142
8434
|
for (let index = 0; index < argv.length; index += 1) {
|
|
8143
8435
|
const arg = argv[index];
|
|
8144
8436
|
if (arg === "--host") {
|
|
@@ -8475,8 +8767,8 @@ var init_routing = __esm({
|
|
|
8475
8767
|
});
|
|
8476
8768
|
|
|
8477
8769
|
// apps/relay/src/store.ts
|
|
8478
|
-
import { mkdir as mkdir7, readFile as
|
|
8479
|
-
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";
|
|
8480
8772
|
var FileRelayStore, emptyStoreFile;
|
|
8481
8773
|
var init_store = __esm({
|
|
8482
8774
|
"apps/relay/src/store.ts"() {
|
|
@@ -8486,7 +8778,7 @@ var init_store = __esm({
|
|
|
8486
8778
|
#now;
|
|
8487
8779
|
#queue = Promise.resolve();
|
|
8488
8780
|
constructor(options) {
|
|
8489
|
-
this.#filePath =
|
|
8781
|
+
this.#filePath = join14(options.dataDir, "relay-store.json");
|
|
8490
8782
|
this.#now = options.now ?? Date.now;
|
|
8491
8783
|
}
|
|
8492
8784
|
async upsertDevice(record) {
|
|
@@ -8529,7 +8821,7 @@ var init_store = __esm({
|
|
|
8529
8821
|
this.#queue = this.#queue.then(async () => {
|
|
8530
8822
|
const file = await this.#read();
|
|
8531
8823
|
mutator(file);
|
|
8532
|
-
await mkdir7(
|
|
8824
|
+
await mkdir7(join14(this.#filePath, ".."), { recursive: true });
|
|
8533
8825
|
await writeFile7(this.#filePath, `${JSON.stringify(file, null, 2)}
|
|
8534
8826
|
`);
|
|
8535
8827
|
});
|
|
@@ -8537,7 +8829,7 @@ var init_store = __esm({
|
|
|
8537
8829
|
}
|
|
8538
8830
|
async #read() {
|
|
8539
8831
|
try {
|
|
8540
|
-
const raw = JSON.parse(await
|
|
8832
|
+
const raw = JSON.parse(await readFile13(this.#filePath, "utf8"));
|
|
8541
8833
|
if (raw.version !== 1 || !Array.isArray(raw.devices) || !Array.isArray(raw.clients) || !Array.isArray(raw.bindings)) {
|
|
8542
8834
|
return emptyStoreFile();
|
|
8543
8835
|
}
|
|
@@ -8790,7 +9082,7 @@ var init_library = __esm({
|
|
|
8790
9082
|
|
|
8791
9083
|
// apps/cli/src/relay-server-cli.ts
|
|
8792
9084
|
import { homedir as homedir7 } from "node:os";
|
|
8793
|
-
import { join as
|
|
9085
|
+
import { join as join15 } from "node:path";
|
|
8794
9086
|
var DEFAULT_HOST2, DEFAULT_PORT2, runCliRelay, runRelayServe, parseRelayServeFlags, waitForStop, requireValue3, writeRelayUsage;
|
|
8795
9087
|
var init_relay_server_cli = __esm({
|
|
8796
9088
|
"apps/cli/src/relay-server-cli.ts"() {
|
|
@@ -8842,7 +9134,7 @@ var init_relay_server_cli = __esm({
|
|
|
8842
9134
|
parseRelayServeFlags = (argv) => {
|
|
8843
9135
|
let host = DEFAULT_HOST2;
|
|
8844
9136
|
let port = DEFAULT_PORT2;
|
|
8845
|
-
let dataDir =
|
|
9137
|
+
let dataDir = join15(homedir7(), ".scorel", "relay");
|
|
8846
9138
|
for (let index = 0; index < argv.length; index += 1) {
|
|
8847
9139
|
const arg = argv[index];
|
|
8848
9140
|
if (arg === "--host") {
|
|
@@ -8900,17 +9192,18 @@ var init_relay_server_cli = __esm({
|
|
|
8900
9192
|
// apps/cli/src/up-cli.ts
|
|
8901
9193
|
import { spawn as spawn2 } from "node:child_process";
|
|
8902
9194
|
import { homedir as homedir8 } from "node:os";
|
|
8903
|
-
import { dirname as
|
|
8904
|
-
import { fileURLToPath as
|
|
9195
|
+
import { dirname as dirname11, join as join16 } from "node:path";
|
|
9196
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
8905
9197
|
var DEFAULT_DAEMON_PORT, DEFAULT_WEBUI_PORT, DEFAULT_DAEMON_READY_TIMEOUT_MS, defaultStateDir3, defaultAttachSigint, runCliUp, parseUpFlags, requireValue4, waitForDaemonReady2, pipeWithPrefix, detachBackgroundDaemon2, nodeEntrypointArgs2, pipeStreamLines, once;
|
|
8906
9198
|
var init_up_cli = __esm({
|
|
8907
9199
|
"apps/cli/src/up-cli.ts"() {
|
|
8908
9200
|
"use strict";
|
|
8909
9201
|
init_src4();
|
|
9202
|
+
init_daemon_cli();
|
|
8910
9203
|
DEFAULT_DAEMON_PORT = 7777;
|
|
8911
9204
|
DEFAULT_WEBUI_PORT = 3e3;
|
|
8912
9205
|
DEFAULT_DAEMON_READY_TIMEOUT_MS = 1e4;
|
|
8913
|
-
defaultStateDir3 = () =>
|
|
9206
|
+
defaultStateDir3 = () => join16(homedir8(), ".scorel");
|
|
8914
9207
|
defaultAttachSigint = (listener) => {
|
|
8915
9208
|
process.on("SIGINT", listener);
|
|
8916
9209
|
return () => process.off("SIGINT", listener);
|
|
@@ -8925,7 +9218,7 @@ var init_up_cli = __esm({
|
|
|
8925
9218
|
return 1;
|
|
8926
9219
|
}
|
|
8927
9220
|
const stateDir = options.stateDir ?? defaultStateDir3();
|
|
8928
|
-
const cliEntrypoint = options.cliEntrypoint ??
|
|
9221
|
+
const cliEntrypoint = options.cliEntrypoint ?? fileURLToPath3(import.meta.url).replace(/up-cli\.ts$/, "index.ts");
|
|
8929
9222
|
const spawnFn = options.spawn ?? spawn2;
|
|
8930
9223
|
const readState = options.readState ?? ((dir) => readLocalDaemonState({ stateDir: dir }));
|
|
8931
9224
|
const attachSigint = options.attachSigint ?? defaultAttachSigint;
|
|
@@ -8944,10 +9237,12 @@ var init_up_cli = __esm({
|
|
|
8944
9237
|
String(flags.daemonPort),
|
|
8945
9238
|
"--cwd",
|
|
8946
9239
|
flags.cwd,
|
|
9240
|
+
"--idle-timeout-ms",
|
|
9241
|
+
String(AUTO_STARTED_IDLE_SHUTDOWN_MS),
|
|
8947
9242
|
"--no-relay"
|
|
8948
9243
|
];
|
|
8949
9244
|
daemonChild = spawnFn(process.execPath, daemonArgs, {
|
|
8950
|
-
cwd:
|
|
9245
|
+
cwd: dirname11(cliEntrypoint),
|
|
8951
9246
|
env: { ...process.env },
|
|
8952
9247
|
detached: true,
|
|
8953
9248
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -8977,7 +9272,7 @@ var init_up_cli = __esm({
|
|
|
8977
9272
|
String(flags.webuiPort)
|
|
8978
9273
|
];
|
|
8979
9274
|
const webuiChild = spawnFn(process.execPath, webuiArgs, {
|
|
8980
|
-
cwd:
|
|
9275
|
+
cwd: dirname11(cliEntrypoint),
|
|
8981
9276
|
env: { ...process.env },
|
|
8982
9277
|
stdio: ["ignore", "pipe", "pipe"]
|
|
8983
9278
|
});
|
|
@@ -9142,8 +9437,8 @@ var init_up_cli = __esm({
|
|
|
9142
9437
|
// apps/cli/src/webui-cli.ts
|
|
9143
9438
|
import { spawn as spawn3 } from "node:child_process";
|
|
9144
9439
|
import { existsSync as existsSync4 } from "node:fs";
|
|
9145
|
-
import { dirname as
|
|
9146
|
-
import { fileURLToPath as
|
|
9440
|
+
import { dirname as dirname12, resolve as resolve6 } from "node:path";
|
|
9441
|
+
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
9147
9442
|
var DEFAULT_PORT3, DEFAULT_HOST3, runCliWebUi, findWebuiAppDir, buildWebUiSpawnPlan, parseWebUiFlags, requireValue5, waitForChildExit;
|
|
9148
9443
|
var init_webui_cli = __esm({
|
|
9149
9444
|
"apps/cli/src/webui-cli.ts"() {
|
|
@@ -9174,7 +9469,7 @@ var init_webui_cli = __esm({
|
|
|
9174
9469
|
return await waitForChildExit(child, options);
|
|
9175
9470
|
};
|
|
9176
9471
|
findWebuiAppDir = () => {
|
|
9177
|
-
let cursor =
|
|
9472
|
+
let cursor = dirname12(fileURLToPath4(import.meta.url));
|
|
9178
9473
|
for (let depth = 0; depth < 8; depth += 1) {
|
|
9179
9474
|
const candidate = resolve6(cursor, "apps/webui/package.json");
|
|
9180
9475
|
if (existsSync4(candidate)) {
|
|
@@ -9263,11 +9558,11 @@ __export(index_exports, {
|
|
|
9263
9558
|
runCli: () => runCli
|
|
9264
9559
|
});
|
|
9265
9560
|
import { createHash as createHash3 } from "node:crypto";
|
|
9266
|
-
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";
|
|
9267
9562
|
import { createInterface } from "node:readline/promises";
|
|
9268
9563
|
import { homedir as homedir9 } from "node:os";
|
|
9269
|
-
import { fileURLToPath as
|
|
9270
|
-
import { basename as basename4, dirname as
|
|
9564
|
+
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
9565
|
+
import { basename as basename4, dirname as dirname13, join as join17 } from "node:path";
|
|
9271
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;
|
|
9272
9567
|
var init_index = __esm({
|
|
9273
9568
|
async "apps/cli/src/index.ts"() {
|
|
@@ -9280,13 +9575,19 @@ var init_index = __esm({
|
|
|
9280
9575
|
init_relay_server_cli();
|
|
9281
9576
|
init_up_cli();
|
|
9282
9577
|
init_webui_cli();
|
|
9578
|
+
init_update_cli();
|
|
9283
9579
|
cliAppName = "@scorel/app-cli";
|
|
9284
9580
|
cliClientDependency = clientPackageName;
|
|
9285
9581
|
cliDaemonDependency = daemonPackageName;
|
|
9286
9582
|
defaultSessionsDir = () => scorelSessionsDir(homedir9());
|
|
9287
|
-
defaultStateDir4 = () =>
|
|
9583
|
+
defaultStateDir4 = () => join17(homedir9(), ".scorel");
|
|
9288
9584
|
runCli = async (argv, io = { input: process.stdin, output: process.stdout, error: process.stderr }, runOptions = {}) => {
|
|
9289
9585
|
const [command, ...rest] = argv;
|
|
9586
|
+
if (command === "--version" || command === "-v" || command === "version") {
|
|
9587
|
+
io.output.write(`${await readInstalledScorelVersion()}
|
|
9588
|
+
`);
|
|
9589
|
+
return 0;
|
|
9590
|
+
}
|
|
9290
9591
|
if (!command || command === "chat") {
|
|
9291
9592
|
if (rest.includes("--help") || rest.includes("-h")) {
|
|
9292
9593
|
writeUsage(io.output);
|
|
@@ -9332,6 +9633,9 @@ var init_index = __esm({
|
|
|
9332
9633
|
error: io.error
|
|
9333
9634
|
});
|
|
9334
9635
|
}
|
|
9636
|
+
if (command === "update" || command === "upgrade") {
|
|
9637
|
+
return runCliUpdate(rest, { output: io.output, error: io.error });
|
|
9638
|
+
}
|
|
9335
9639
|
if (command === "attach") {
|
|
9336
9640
|
try {
|
|
9337
9641
|
return runAttach(parseAttachOptions(rest), {
|
|
@@ -9419,10 +9723,10 @@ var init_index = __esm({
|
|
|
9419
9723
|
}
|
|
9420
9724
|
};
|
|
9421
9725
|
runLogs = async (options, io) => {
|
|
9422
|
-
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`);
|
|
9423
9727
|
let content;
|
|
9424
9728
|
try {
|
|
9425
|
-
content = await
|
|
9729
|
+
content = await readFile14(filePath, "utf8");
|
|
9426
9730
|
} catch (cause) {
|
|
9427
9731
|
io.error.write(`scorel logs error: ${cause instanceof Error ? cause.message : String(cause)}
|
|
9428
9732
|
`);
|
|
@@ -9576,31 +9880,31 @@ var init_index = __esm({
|
|
|
9576
9880
|
};
|
|
9577
9881
|
attachCacheFilePath = (stateDir, scope, sessionId) => {
|
|
9578
9882
|
const scopeKey = createHash3("sha256").update(`${scope.kind}\0${scope.locator}`).digest("hex").slice(0, 24);
|
|
9579
|
-
return
|
|
9883
|
+
return join17(stateDir, "attach-cache", scopeKey, `${sessionId}.json`);
|
|
9580
9884
|
};
|
|
9581
9885
|
attachDiagnosticsFilePath = (stateDir, scope, sessionId) => {
|
|
9582
9886
|
const scopeKey = createHash3("sha256").update(`${scope.kind}\0${scope.locator}`).digest("hex").slice(0, 24);
|
|
9583
|
-
return
|
|
9887
|
+
return join17(stateDir, "attach-cache", scopeKey, `${sessionId}.log`);
|
|
9584
9888
|
};
|
|
9585
9889
|
findAttachDiagnosticsFilePath = async (stateDir, sessionId, _remoteUrl) => {
|
|
9586
|
-
const root =
|
|
9890
|
+
const root = join17(stateDir, "attach-cache");
|
|
9587
9891
|
const scopes = await readdir7(root).catch(() => []);
|
|
9588
9892
|
for (const scope of scopes) {
|
|
9589
|
-
const candidate =
|
|
9893
|
+
const candidate = join17(root, scope, `${sessionId}.log`);
|
|
9590
9894
|
try {
|
|
9591
|
-
await
|
|
9895
|
+
await readFile14(candidate, "utf8");
|
|
9592
9896
|
return candidate;
|
|
9593
9897
|
} catch {
|
|
9594
9898
|
continue;
|
|
9595
9899
|
}
|
|
9596
9900
|
}
|
|
9597
|
-
return
|
|
9901
|
+
return join17(root, "__missing__", `${sessionId}.log`);
|
|
9598
9902
|
};
|
|
9599
9903
|
stateDirFromSessionsDir = (sessionsDir) => {
|
|
9600
9904
|
if (!sessionsDir) {
|
|
9601
9905
|
return defaultStateDir4();
|
|
9602
9906
|
}
|
|
9603
|
-
return basename4(sessionsDir) === "sessions" ?
|
|
9907
|
+
return basename4(sessionsDir) === "sessions" ? dirname13(sessionsDir) : sessionsDir;
|
|
9604
9908
|
};
|
|
9605
9909
|
AttachDiagnostics = class {
|
|
9606
9910
|
#stateDir;
|
|
@@ -9652,14 +9956,14 @@ var init_index = __esm({
|
|
|
9652
9956
|
}
|
|
9653
9957
|
const filePath = attachDiagnosticsFilePath(this.#stateDir, this.#scope, this.#sessionId);
|
|
9654
9958
|
this.#writes.push(
|
|
9655
|
-
mkdir8(
|
|
9959
|
+
mkdir8(dirname13(filePath), { recursive: true }).then(() => appendFile4(filePath, `${line}
|
|
9656
9960
|
`, "utf8"))
|
|
9657
9961
|
);
|
|
9658
9962
|
}
|
|
9659
9963
|
};
|
|
9660
9964
|
readAttachCache = async (stateDir, scope, sessionId) => {
|
|
9661
9965
|
try {
|
|
9662
|
-
const raw = JSON.parse(await
|
|
9966
|
+
const raw = JSON.parse(await readFile14(attachCacheFilePath(stateDir, scope, sessionId), "utf8"));
|
|
9663
9967
|
if (raw.version !== 1 || raw.sessionId !== String(sessionId) || raw.scope.kind !== scope.kind || raw.scope.locator !== scope.locator || !Array.isArray(raw.events)) {
|
|
9664
9968
|
return emptyAttachCacheSnapshot();
|
|
9665
9969
|
}
|
|
@@ -9682,7 +9986,7 @@ var init_index = __esm({
|
|
|
9682
9986
|
const filePath = attachCacheFilePath(stateDir, scope, sessionId);
|
|
9683
9987
|
const uniqueEvents = mergePersistentEvents(snapshot.events);
|
|
9684
9988
|
const transients = removeCompletedTransients(snapshot.transients, uniqueEvents);
|
|
9685
|
-
await mkdir8(
|
|
9989
|
+
await mkdir8(dirname13(filePath), { recursive: true });
|
|
9686
9990
|
await writeFile8(
|
|
9687
9991
|
filePath,
|
|
9688
9992
|
`${JSON.stringify({ version: 1, scope, sessionId: String(sessionId), events: uniqueEvents, transients }, null, 2)}
|
|
@@ -9824,14 +10128,16 @@ var init_index = __esm({
|
|
|
9824
10128
|
const loadProjectConfigProfile = async (project2) => options.config ?? await loadScorelConfigProfile({ cwd: project2.workDir, ...configScope });
|
|
9825
10129
|
const daemon = new ScorelHost({
|
|
9826
10130
|
sessionsDir: options.sessionsDir,
|
|
9827
|
-
projectsPath:
|
|
10131
|
+
projectsPath: join17(options.stateDir, "projects.json"),
|
|
9828
10132
|
deviceId: asDeviceId("device_local"),
|
|
9829
10133
|
scorelHomeDir: options.stateDir,
|
|
9830
10134
|
loadConfig: async ({ project: project2 }) => loadProjectConfig(project2),
|
|
9831
10135
|
loadConfigProfile: async ({ project: project2 }) => loadProjectConfigProfile(project2),
|
|
9832
|
-
createRuntime: async ({ project: project2, selectedModel, purpose }) => createRealRuntime({
|
|
10136
|
+
createRuntime: async ({ sessionId, project: project2, selectedModel, purpose }) => createRealRuntime({
|
|
9833
10137
|
cwd: project2.workDir,
|
|
9834
10138
|
config: await loadProjectConfig(project2),
|
|
10139
|
+
sessionsDir: options.sessionsDir,
|
|
10140
|
+
sessionId,
|
|
9835
10141
|
modelSelection: selectedModel ? { modelId: selectedModel.modelId, role: selectedModel.role } : void 0,
|
|
9836
10142
|
includeTools: purpose === "chat"
|
|
9837
10143
|
})
|
|
@@ -9971,6 +10277,9 @@ var init_index = __esm({
|
|
|
9971
10277
|
" scorel relay serve [--host <h>] [--port <p>] [--data-dir <dir>]",
|
|
9972
10278
|
" scorel webui [--port <p>] [--host <h>]",
|
|
9973
10279
|
" scorel up [--daemon-port <p>] [--webui-port <p>] [--cwd <d>]",
|
|
10280
|
+
" scorel update",
|
|
10281
|
+
" scorel upgrade",
|
|
10282
|
+
" scorel version",
|
|
9974
10283
|
" scorel logs [--attach] --session <id> [--remote <ws-url>] [--tail <n>]",
|
|
9975
10284
|
" scorel project list",
|
|
9976
10285
|
" scorel project add <dir>",
|
|
@@ -10102,7 +10411,7 @@ ${text}
|
|
|
10102
10411
|
if (!process.argv[1]) return false;
|
|
10103
10412
|
const [argvPath, modulePath] = await Promise.all([
|
|
10104
10413
|
realpath3(process.argv[1]).catch(() => process.argv[1]),
|
|
10105
|
-
realpath3(
|
|
10414
|
+
realpath3(fileURLToPath5(import.meta.url)).catch(() => fileURLToPath5(import.meta.url))
|
|
10106
10415
|
]);
|
|
10107
10416
|
return argvPath === modulePath;
|
|
10108
10417
|
};
|