@slock-ai/daemon 0.52.1 → 0.52.2-play.20260521182209
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-WGO5H7XX.js → chunk-GNXY3BNV.js} +697 -116
- package/dist/cli/index.js +151 -0
- package/dist/core.js +6 -2
- package/dist/drivers/piSdkRunner.js +96 -0
- package/dist/index.js +5 -3
- package/package.json +2 -1
|
@@ -7,11 +7,11 @@ import {
|
|
|
7
7
|
} from "./chunk-KNMCE6WB.js";
|
|
8
8
|
|
|
9
9
|
// src/core.ts
|
|
10
|
-
import
|
|
10
|
+
import path17 from "path";
|
|
11
11
|
import os8 from "os";
|
|
12
12
|
import { createRequire } from "module";
|
|
13
13
|
import { accessSync } from "fs";
|
|
14
|
-
import { fileURLToPath } from "url";
|
|
14
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
15
15
|
|
|
16
16
|
// ../shared/src/tracing/index.ts
|
|
17
17
|
var DEFAULT_TRACE_FLAGS = "00";
|
|
@@ -723,6 +723,7 @@ var SERVER_CAPABILITY_MATRIX = {
|
|
|
723
723
|
var RUNTIMES = [
|
|
724
724
|
{ id: "claude", displayName: "Claude Code", binary: "claude", supported: true },
|
|
725
725
|
{ id: "codex", displayName: "Codex CLI", binary: "codex", supported: true },
|
|
726
|
+
{ id: "pi", displayName: "Pi", binary: "pi", supported: true },
|
|
726
727
|
{ id: "kimi", displayName: "Kimi CLI", binary: "kimi", supported: true },
|
|
727
728
|
{ id: "copilot", displayName: "Copilot CLI", binary: "copilot", supported: true },
|
|
728
729
|
{ id: "cursor", displayName: "Cursor CLI", binary: "cursor-agent", supported: true },
|
|
@@ -764,10 +765,10 @@ var DISPLAY_PLAN_CONFIG = {
|
|
|
764
765
|
};
|
|
765
766
|
|
|
766
767
|
// src/agentProcessManager.ts
|
|
767
|
-
import { mkdirSync as
|
|
768
|
+
import { mkdirSync as mkdirSync5, readdirSync as readdirSync2, statSync as statSync2, writeFileSync as writeFileSync8 } from "fs";
|
|
768
769
|
import { mkdir, writeFile, access, readdir as readdir2, stat as stat2, readFile, rm as rm2 } from "fs/promises";
|
|
769
770
|
import { createHash as createHash2 } from "crypto";
|
|
770
|
-
import
|
|
771
|
+
import path13 from "path";
|
|
771
772
|
import os6 from "os";
|
|
772
773
|
|
|
773
774
|
// src/drivers/claude.ts
|
|
@@ -823,6 +824,7 @@ function buildPrompt(config, variant, opts) {
|
|
|
823
824
|
"- Always communicate through `slock` CLI commands. This is your only output channel.",
|
|
824
825
|
...opts.extraCriticalRules,
|
|
825
826
|
"- Use only the provided `slock` CLI commands for messaging.",
|
|
827
|
+
"- Do not combine multiple `slock` CLI commands in one shell command. Run one `slock` command per tool call, read its output, then decide the next command.",
|
|
826
828
|
"- Always claim a task via `slock task claim` before starting work on it. If the claim fails, move on to a different task."
|
|
827
829
|
] : [
|
|
828
830
|
`- Always communicate through ${sendCmd}. This is your only output channel.`,
|
|
@@ -871,13 +873,15 @@ Use the \`slock\` CLI for chat / task / attachment operations. The daemon inject
|
|
|
871
873
|
17. **\`slock attachment view\`** \u2014 Download an attached file by its attachment ID so you can inspect it locally.
|
|
872
874
|
18. **\`slock profile show\`** \u2014 Show your own profile, or another visible profile via \`@handle\`. Mirrors the canonical Slock profile view.
|
|
873
875
|
19. **\`slock profile update\`** \u2014 Update your own profile. Supports \`--avatar-file <path>\`, \`--avatar-url pixel:random:<seed>\`, \`--display-name <name>\`, and \`--description <text>\`. Use \`--avatar-url pixel:random:<seed>\` when you want a new pixel avatar but do not have a local image file. Values must be non-empty. Provide at least one flag per call; multiple flags can be combined.
|
|
874
|
-
20. **\`slock
|
|
875
|
-
21. **\`slock
|
|
876
|
-
22. **\`slock reminder
|
|
877
|
-
23. **\`slock reminder
|
|
878
|
-
24. **\`slock reminder
|
|
879
|
-
25. **\`slock reminder
|
|
880
|
-
26. **\`slock
|
|
876
|
+
20. **\`slock integration list\`** \u2014 List registered third-party services and this agent's active Slock Agent Logins.
|
|
877
|
+
21. **\`slock integration login\`** \u2014 Provision or reuse this agent's login for a registered third-party service.
|
|
878
|
+
22. **\`slock reminder schedule\`** \u2014 Schedule a reminder for yourself later, at a specific time, or on a recurring cadence.
|
|
879
|
+
23. **\`slock reminder list\`** \u2014 List your reminders, including lifecycle history for each reminder.
|
|
880
|
+
24. **\`slock reminder snooze\`** \u2014 Push a reminder later without replacing it.
|
|
881
|
+
25. **\`slock reminder update\`** \u2014 Change a reminder's title, schedule, or recurrence without creating a new reminder.
|
|
882
|
+
26. **\`slock reminder cancel\`** \u2014 Cancel one of your reminders by ID.
|
|
883
|
+
27. **\`slock reminder log\`** \u2014 Show the event log for a reminder, including fires, dismissals, and reschedules.
|
|
884
|
+
28. **\`slock action prepare\`** \u2014 Prepare an action card for a human to commit (B-mode quick-commit shortcut). Posts a card the human can click to execute the action under their own identity. Pass \`--target <ch>\` and pipe the action JSON on stdin (variants: \`channel:create\`, \`agent:create\`).
|
|
881
885
|
|
|
882
886
|
The CLI prints human-readable canonical text on success (matching the format you see in received messages and history). On failure it prints JSON to stderr:
|
|
883
887
|
- failure \u2192 stderr \`{"ok":false,"code":"...","message":"..."}\` with non-zero exit
|
|
@@ -978,6 +982,11 @@ Each channel has a **name** and optionally a **description** that define its pur
|
|
|
978
982
|
- **Reply in context** \u2014 always respond in the channel/thread the message came from.
|
|
979
983
|
- **Stay on topic** \u2014 when proactively sharing results or updates, post in the channel most relevant to the work. Don't scatter messages across unrelated channels.
|
|
980
984
|
- If unsure where something belongs, call ${serverInfoCmd} to review channel descriptions.`;
|
|
985
|
+
const thirdPartyIntegrationsSection = isCli ? `### Third-party integrations
|
|
986
|
+
|
|
987
|
+
If a registered third-party service requires login, use Slock Agent Login through the CLI instead of asking the human to copy tokens or complete human OAuth for you. If a human asks you to sign into, open, use, or fetch identity from a third-party app, first run \`slock integration list\` and match the app to a registered service before browsing the app. Use \`slock integration login --service <service>\` to provision or reuse your agent login for that service. When the command returns \`Agent login ready\` or \`Already logged in\`, the agent-side login is ready. If the output includes an app URL, open that URL as the service-provided third-party app surface; it should look like the service's normal Login with Slock callback and not require you to understand Slock's internal grant/request protocol. Do not crawl third-party routes looking for a session before trying the registered-service login path. Do not open the human \`Login with Slock\` browser flow, use internal request IDs as OAuth callback codes, or call third-party exchange endpoints unless a human explicitly asks you to debug that server-to-server protocol. If the service or human asks for your Slock Agent identity card, use \`slock profile show\`. Third-party pages may show \`Login with Slock\`; for agent-facing access, prefer the registered service / Slock Agent Login path.` : `### Third-party integrations
|
|
988
|
+
|
|
989
|
+
If a registered third-party service requires login, use Slock Agent Login through the available registered-service interface instead of asking the human to copy tokens or complete human OAuth for you. If a human asks you to sign into, open, use, or fetch identity from a third-party app, first inspect the registered-service interface and match the app to a registered service before browsing the app. Once the registered-service interface reports the agent login is ready, the agent-side login is ready. If that interface provides an app URL, use it as the service-provided third-party app surface; it should look like the service's normal Login with Slock callback and not require you to understand Slock's internal grant/request protocol. Do not crawl third-party routes looking for a session before trying the registered-service login path. Do not open the human \`Login with Slock\` browser flow or treat internal request IDs as OAuth callback codes unless a human explicitly asks you to debug that server-to-server protocol. If the service or human asks for your Slock Agent identity card, use your Slock profile view. Third-party pages may show \`Login with Slock\`; for agent-facing access, prefer the registered service / Slock Agent Login path.`;
|
|
981
990
|
const readingHistorySection = isCli ? `### Reading history
|
|
982
991
|
|
|
983
992
|
\`slock message read --channel "#channel-name"\` or \`slock message read --channel dm:@peer-name\` or \`slock message read --channel "#channel:shortid"\`
|
|
@@ -1149,6 +1158,8 @@ ${discoverySection}
|
|
|
1149
1158
|
|
|
1150
1159
|
${channelAwarenessSection}
|
|
1151
1160
|
|
|
1161
|
+
${thirdPartyIntegrationsSection}
|
|
1162
|
+
|
|
1152
1163
|
${readingHistorySection}
|
|
1153
1164
|
|
|
1154
1165
|
${historicalReferenceSection}
|
|
@@ -1180,6 +1191,17 @@ Keep the user informed. They cannot see your internal reasoning, so:
|
|
|
1180
1191
|
- For multi-step work, send short progress updates (e.g. "Working on step 2/3\u2026").
|
|
1181
1192
|
- When done, summarize the result.
|
|
1182
1193
|
- Keep updates concise \u2014 one or two sentences. Don't flood the chat.
|
|
1194
|
+
- For long answers where users need the conclusion first but details still matter, put the conclusion and next action outside any collapse, then use sanitized HTML details blocks for optional depth:
|
|
1195
|
+
|
|
1196
|
+
\`\`\`html
|
|
1197
|
+
<details>
|
|
1198
|
+
<summary>Evidence, logs, or edge cases</summary>
|
|
1199
|
+
|
|
1200
|
+
Detailed notes go here.
|
|
1201
|
+
</details>
|
|
1202
|
+
\`\`\`
|
|
1203
|
+
|
|
1204
|
+
Do not hide the main recommendation, blocker, or required action inside \`<details>\`; only fold supporting evidence, logs, alternatives, or extended rationale.
|
|
1183
1205
|
|
|
1184
1206
|
### Conversation etiquette
|
|
1185
1207
|
|
|
@@ -1348,6 +1370,19 @@ function listLegacySlockStatePaths(slockHome = resolveSlockHome(), homeDir = os.
|
|
|
1348
1370
|
return candidates.filter((candidate) => existsSync(candidate.path));
|
|
1349
1371
|
}
|
|
1350
1372
|
|
|
1373
|
+
// src/authEnv.ts
|
|
1374
|
+
var DAEMON_API_KEY_ENV = "SLOCK_MACHINE_API_KEY";
|
|
1375
|
+
var SLOCK_AGENT_TOKEN_ENV = "SLOCK_AGENT_TOKEN";
|
|
1376
|
+
function scrubDaemonAuthEnv(env) {
|
|
1377
|
+
delete env[DAEMON_API_KEY_ENV];
|
|
1378
|
+
return env;
|
|
1379
|
+
}
|
|
1380
|
+
function scrubDaemonChildEnv(env) {
|
|
1381
|
+
delete env[DAEMON_API_KEY_ENV];
|
|
1382
|
+
delete env[SLOCK_AGENT_TOKEN_ENV];
|
|
1383
|
+
return env;
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1351
1386
|
// src/agentCredentialProxy.ts
|
|
1352
1387
|
import { randomBytes } from "crypto";
|
|
1353
1388
|
import http from "http";
|
|
@@ -1810,6 +1845,18 @@ function consumeVisibleResponse(registration, targetUrl, sendTarget, responseTex
|
|
|
1810
1845
|
});
|
|
1811
1846
|
return;
|
|
1812
1847
|
}
|
|
1848
|
+
if (targetUrl.pathname === "/internal/agent-api/send" && parsed.state === "sent") {
|
|
1849
|
+
const messageSeq2 = typeof parsed.messageSeq === "number" && Number.isFinite(parsed.messageSeq) ? Math.floor(parsed.messageSeq) : void 0;
|
|
1850
|
+
if (sendTarget && messageSeq2 && messageSeq2 > 0) {
|
|
1851
|
+
coordinator.consumeVisibleMessages({
|
|
1852
|
+
target: sendTarget,
|
|
1853
|
+
messages: normalizeVisibleMessages([{ seq: messageSeq2, id: parsed.messageId }], sendTarget),
|
|
1854
|
+
boundarySeq: messageSeq2,
|
|
1855
|
+
source: "agent_api_send_commit"
|
|
1856
|
+
});
|
|
1857
|
+
}
|
|
1858
|
+
return;
|
|
1859
|
+
}
|
|
1813
1860
|
if (targetUrl.pathname === "/internal/agent-api/events" && Array.isArray(parsed.events)) {
|
|
1814
1861
|
const messages = normalizeVisibleMessages(parsed.events);
|
|
1815
1862
|
coordinator.consumeVisibleMessages({ messages, source: "agent_api_events" });
|
|
@@ -1861,6 +1908,14 @@ function unregisterAgentCredentialProxyForLaunch(input) {
|
|
|
1861
1908
|
var shellSingleQuote = (value) => `'${value.replace(/'/g, `'\\''`)}'`;
|
|
1862
1909
|
var DEFAULT_ACTIVE_CAPABILITIES = "send,read,mentions,tasks,reactions,server,channels";
|
|
1863
1910
|
var safePathPart = (value) => value.replace(/[^a-zA-Z0-9_.-]/g, "_");
|
|
1911
|
+
function windowsUtf8Env() {
|
|
1912
|
+
return {
|
|
1913
|
+
PYTHONIOENCODING: "utf-8",
|
|
1914
|
+
PYTHONUTF8: "1",
|
|
1915
|
+
LANG: "C.UTF-8",
|
|
1916
|
+
LC_ALL: "C.UTF-8"
|
|
1917
|
+
};
|
|
1918
|
+
}
|
|
1864
1919
|
function runtimeContextEnv(config) {
|
|
1865
1920
|
const ctx = config.runtimeContext;
|
|
1866
1921
|
if (!ctx) return {};
|
|
@@ -1919,9 +1974,17 @@ ${posixCredentialPrefix}exec ${shellSingleQuote(process.execPath)} ${shellSingle
|
|
|
1919
1974
|
set "SLOCK_AGENT_PROXY_TOKEN_FILE=${agentCredentialProxyTokenFile}"\r
|
|
1920
1975
|
set "SLOCK_AGENT_ACTIVE_CAPABILITIES=${DEFAULT_ACTIVE_CAPABILITIES}"\r
|
|
1921
1976
|
` : "";
|
|
1922
|
-
const cmdBody =
|
|
1923
|
-
|
|
1924
|
-
|
|
1977
|
+
const cmdBody = [
|
|
1978
|
+
"@echo off",
|
|
1979
|
+
"set PYTHONIOENCODING=utf-8",
|
|
1980
|
+
"set PYTHONUTF8=1",
|
|
1981
|
+
"set LANG=C.UTF-8",
|
|
1982
|
+
"set LC_ALL=C.UTF-8",
|
|
1983
|
+
"chcp 65001 >NUL 2>NUL",
|
|
1984
|
+
cmdCredentialLine.trimEnd(),
|
|
1985
|
+
`"${process.execPath}" "${ctx.slockCliPath}" %*`,
|
|
1986
|
+
""
|
|
1987
|
+
].filter((line) => line.length > 0).join("\r\n") + "\r\n";
|
|
1925
1988
|
writeFileSync(cmdWrapper, cmdBody);
|
|
1926
1989
|
}
|
|
1927
1990
|
const wrapperPath = platform === "win32" ? path2.join(slockDir, "slock.cmd") : posixWrapper;
|
|
@@ -1930,6 +1993,7 @@ ${cmdCredentialLine}"${process.execPath}" "${ctx.slockCliPath}" %*\r
|
|
|
1930
1993
|
FORCE_COLOR: "0",
|
|
1931
1994
|
...ctx.config.envVars || {},
|
|
1932
1995
|
...extraEnv,
|
|
1996
|
+
...platform === "win32" ? windowsUtf8Env() : {},
|
|
1933
1997
|
...runtimeContextEnv(ctx.config),
|
|
1934
1998
|
[SLOCK_HOME_ENV]: slockHome,
|
|
1935
1999
|
SLOCK_AGENT_ID: ctx.agentId,
|
|
@@ -1938,7 +2002,7 @@ ${cmdCredentialLine}"${process.execPath}" "${ctx.slockCliPath}" %*\r
|
|
|
1938
2002
|
...agentCredentialProxy ? {} : { SLOCK_AGENT_TOKEN_FILE: tokenFile },
|
|
1939
2003
|
PATH: `${slockDir}${path2.delimiter}${process.env.PATH ?? ""}`
|
|
1940
2004
|
};
|
|
1941
|
-
|
|
2005
|
+
scrubDaemonChildEnv(spawnEnv);
|
|
1942
2006
|
delete spawnEnv.SLOCK_AGENT_CREDENTIAL_KEY;
|
|
1943
2007
|
delete spawnEnv.SLOCK_AGENT_CREDENTIAL_KEY_FILE;
|
|
1944
2008
|
delete spawnEnv.SLOCK_AGENT_PROXY_URL;
|
|
@@ -1985,7 +2049,7 @@ function resolveCommandOnWindows(command, env, execFileSyncFn) {
|
|
|
1985
2049
|
}
|
|
1986
2050
|
function resolveCommandOnPath(command, deps = {}) {
|
|
1987
2051
|
const platform = deps.platform ?? process.platform;
|
|
1988
|
-
const env = deps.env ?? process.env;
|
|
2052
|
+
const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
|
|
1989
2053
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
|
|
1990
2054
|
if (platform === "win32") {
|
|
1991
2055
|
return resolveCommandOnWindows(command, env, execFileSyncFn);
|
|
@@ -2010,7 +2074,7 @@ function firstExistingPath(candidates, deps = {}) {
|
|
|
2010
2074
|
return null;
|
|
2011
2075
|
}
|
|
2012
2076
|
function readCommandVersion(command, args = [], deps = {}) {
|
|
2013
|
-
const env = deps.env ?? process.env;
|
|
2077
|
+
const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
|
|
2014
2078
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
|
|
2015
2079
|
try {
|
|
2016
2080
|
const output = normalizeExecOutput(execFileSyncFn(command, [...args, "--version"], {
|
|
@@ -3306,7 +3370,7 @@ function detectCursorModels(runCommand = runCursorModelsCommand) {
|
|
|
3306
3370
|
}
|
|
3307
3371
|
function runCursorModelsCommand() {
|
|
3308
3372
|
return spawnSync("cursor-agent", ["models"], {
|
|
3309
|
-
env: { ...process.env, FORCE_COLOR: "0", NO_COLOR: "1" },
|
|
3373
|
+
env: scrubDaemonChildEnv({ ...process.env, FORCE_COLOR: "0", NO_COLOR: "1" }),
|
|
3310
3374
|
encoding: "utf8",
|
|
3311
3375
|
timeout: 5e3
|
|
3312
3376
|
});
|
|
@@ -3353,7 +3417,7 @@ function resolveGeminiSpawn(commandArgs, deps = {}) {
|
|
|
3353
3417
|
}
|
|
3354
3418
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync3;
|
|
3355
3419
|
const existsSyncFn = deps.existsSyncFn ?? existsSync6;
|
|
3356
|
-
const env = deps.env ?? process.env;
|
|
3420
|
+
const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
|
|
3357
3421
|
const winPath = path8.win32;
|
|
3358
3422
|
let geminiEntry = null;
|
|
3359
3423
|
try {
|
|
@@ -3527,13 +3591,16 @@ var GeminiDriver = class {
|
|
|
3527
3591
|
// src/drivers/kimi.ts
|
|
3528
3592
|
import { randomUUID } from "crypto";
|
|
3529
3593
|
import { spawn as spawn6 } from "child_process";
|
|
3530
|
-
import { existsSync as existsSync7, readFileSync as readFileSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
3594
|
+
import { chmodSync, existsSync as existsSync7, readFileSync as readFileSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
3531
3595
|
import os4 from "os";
|
|
3532
3596
|
import path9 from "path";
|
|
3533
3597
|
var KIMI_WIRE_PROTOCOL_VERSION = "1.3";
|
|
3534
3598
|
var KIMI_SYSTEM_PROMPT_FILE = ".slock-kimi-system.md";
|
|
3535
3599
|
var KIMI_AGENT_FILE = ".slock-kimi-agent.yaml";
|
|
3536
3600
|
var KIMI_MCP_FILE = ".slock-kimi-mcp.json";
|
|
3601
|
+
var KIMI_GENERATED_CONFIG_FILE = ".slock-kimi-config.toml";
|
|
3602
|
+
var SLOCK_KIMI_CONFIG_CONTENT_ENV = "SLOCK_KIMI_CONFIG_CONTENT";
|
|
3603
|
+
var SLOCK_KIMI_CONFIG_FILE_ENV = "SLOCK_KIMI_CONFIG_FILE";
|
|
3537
3604
|
function parseToolArguments(raw) {
|
|
3538
3605
|
if (typeof raw !== "string") return raw;
|
|
3539
3606
|
try {
|
|
@@ -3542,6 +3609,73 @@ function parseToolArguments(raw) {
|
|
|
3542
3609
|
return raw;
|
|
3543
3610
|
}
|
|
3544
3611
|
}
|
|
3612
|
+
function readKimiConfigSource(home = os4.homedir(), env = process.env) {
|
|
3613
|
+
const inlineConfig = env[SLOCK_KIMI_CONFIG_CONTENT_ENV];
|
|
3614
|
+
if (inlineConfig && inlineConfig.trim()) {
|
|
3615
|
+
return {
|
|
3616
|
+
raw: inlineConfig,
|
|
3617
|
+
explicitPath: null,
|
|
3618
|
+
sourcePath: SLOCK_KIMI_CONFIG_CONTENT_ENV
|
|
3619
|
+
};
|
|
3620
|
+
}
|
|
3621
|
+
const explicitPath = env[SLOCK_KIMI_CONFIG_FILE_ENV];
|
|
3622
|
+
const configPath = explicitPath && explicitPath.trim() ? explicitPath : path9.join(home, ".kimi", "config.toml");
|
|
3623
|
+
try {
|
|
3624
|
+
return {
|
|
3625
|
+
raw: readFileSync3(configPath, "utf8"),
|
|
3626
|
+
explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
|
|
3627
|
+
sourcePath: configPath
|
|
3628
|
+
};
|
|
3629
|
+
} catch {
|
|
3630
|
+
return {
|
|
3631
|
+
raw: null,
|
|
3632
|
+
explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
|
|
3633
|
+
sourcePath: configPath
|
|
3634
|
+
};
|
|
3635
|
+
}
|
|
3636
|
+
}
|
|
3637
|
+
function buildKimiSpawnEnv(env = process.env) {
|
|
3638
|
+
const spawnEnv = { ...env, FORCE_COLOR: "0", NO_COLOR: "1" };
|
|
3639
|
+
delete spawnEnv[SLOCK_KIMI_CONFIG_CONTENT_ENV];
|
|
3640
|
+
delete spawnEnv[SLOCK_KIMI_CONFIG_FILE_ENV];
|
|
3641
|
+
return scrubDaemonChildEnv(spawnEnv);
|
|
3642
|
+
}
|
|
3643
|
+
function buildKimiEffectiveEnv(ctx, overrideEnv) {
|
|
3644
|
+
return {
|
|
3645
|
+
...process.env,
|
|
3646
|
+
...ctx.config.envVars || {},
|
|
3647
|
+
...overrideEnv || {}
|
|
3648
|
+
};
|
|
3649
|
+
}
|
|
3650
|
+
function buildKimiLaunchOptions(ctx, opts = {}) {
|
|
3651
|
+
const env = buildKimiEffectiveEnv(ctx, opts.env);
|
|
3652
|
+
const source = readKimiConfigSource(opts.home ?? os4.homedir(), env);
|
|
3653
|
+
const args = [];
|
|
3654
|
+
let configFilePath = null;
|
|
3655
|
+
let configContent = null;
|
|
3656
|
+
if (source.explicitPath) {
|
|
3657
|
+
configFilePath = source.explicitPath;
|
|
3658
|
+
} else if (source.raw !== null && source.sourcePath === SLOCK_KIMI_CONFIG_CONTENT_ENV) {
|
|
3659
|
+
configFilePath = path9.join(ctx.workingDirectory, KIMI_GENERATED_CONFIG_FILE);
|
|
3660
|
+
configContent = source.raw;
|
|
3661
|
+
if (opts.writeGeneratedConfig !== false) {
|
|
3662
|
+
writeFileSync6(configFilePath, source.raw, { encoding: "utf8", mode: 384 });
|
|
3663
|
+
chmodSync(configFilePath, 384);
|
|
3664
|
+
}
|
|
3665
|
+
}
|
|
3666
|
+
if (configFilePath) {
|
|
3667
|
+
args.push("--config-file", configFilePath);
|
|
3668
|
+
}
|
|
3669
|
+
if (ctx.config.model && ctx.config.model !== "default") {
|
|
3670
|
+
args.push("--model", ctx.config.model);
|
|
3671
|
+
}
|
|
3672
|
+
return {
|
|
3673
|
+
args,
|
|
3674
|
+
env: buildKimiSpawnEnv(env),
|
|
3675
|
+
configFilePath,
|
|
3676
|
+
configContent
|
|
3677
|
+
};
|
|
3678
|
+
}
|
|
3545
3679
|
function resolveKimiSpawn(commandArgs, deps = {}) {
|
|
3546
3680
|
return {
|
|
3547
3681
|
command: resolveCommandOnPath("kimi", deps) ?? "kimi",
|
|
@@ -3565,7 +3699,25 @@ var KimiDriver = class {
|
|
|
3565
3699
|
};
|
|
3566
3700
|
model = {
|
|
3567
3701
|
detectedModelsVerifiedAs: "launchable",
|
|
3568
|
-
toLaunchSpec: (modelId) =>
|
|
3702
|
+
toLaunchSpec: (modelId, ctx, opts) => {
|
|
3703
|
+
if (!ctx) return { args: ["--model", modelId] };
|
|
3704
|
+
const launchCtx = {
|
|
3705
|
+
...ctx,
|
|
3706
|
+
config: {
|
|
3707
|
+
...ctx.config,
|
|
3708
|
+
model: modelId
|
|
3709
|
+
}
|
|
3710
|
+
};
|
|
3711
|
+
const launch = buildKimiLaunchOptions(launchCtx, {
|
|
3712
|
+
home: opts?.home,
|
|
3713
|
+
writeGeneratedConfig: false
|
|
3714
|
+
});
|
|
3715
|
+
return {
|
|
3716
|
+
args: launch.args,
|
|
3717
|
+
env: launch.env,
|
|
3718
|
+
configFiles: launch.configFilePath ? [launch.configFilePath] : void 0
|
|
3719
|
+
};
|
|
3720
|
+
}
|
|
3569
3721
|
};
|
|
3570
3722
|
supportsStdinNotification = true;
|
|
3571
3723
|
mcpToolPrefix = "";
|
|
@@ -3619,6 +3771,7 @@ var KimiDriver = class {
|
|
|
3619
3771
|
}
|
|
3620
3772
|
}
|
|
3621
3773
|
}), "utf8");
|
|
3774
|
+
const launch = buildKimiLaunchOptions(ctx);
|
|
3622
3775
|
const args = [
|
|
3623
3776
|
"--wire",
|
|
3624
3777
|
"--yolo",
|
|
@@ -3627,14 +3780,15 @@ var KimiDriver = class {
|
|
|
3627
3780
|
"--mcp-config-file",
|
|
3628
3781
|
mcpConfigPath,
|
|
3629
3782
|
"--session",
|
|
3630
|
-
this.sessionId
|
|
3783
|
+
this.sessionId,
|
|
3784
|
+
...launch.args
|
|
3631
3785
|
];
|
|
3632
3786
|
if (ctx.config.model && ctx.config.model !== "default") {
|
|
3633
3787
|
args.push("--model", ctx.config.model);
|
|
3634
3788
|
}
|
|
3635
3789
|
const spawnEnv = prepareCliTransport(ctx, { NO_COLOR: "1" }).spawnEnv;
|
|
3636
|
-
const
|
|
3637
|
-
const proc = spawn6(
|
|
3790
|
+
const spawnTarget = resolveKimiSpawn(args);
|
|
3791
|
+
const proc = spawn6(spawnTarget.command, spawnTarget.args, {
|
|
3638
3792
|
cwd: ctx.workingDirectory,
|
|
3639
3793
|
stdio: ["pipe", "pipe", "pipe"],
|
|
3640
3794
|
env: spawnEnv,
|
|
@@ -3642,7 +3796,7 @@ var KimiDriver = class {
|
|
|
3642
3796
|
// and has an 8191-character command-line limit. Kimi's official
|
|
3643
3797
|
// installer/uv entrypoint is an executable, so launch it directly and
|
|
3644
3798
|
// keep prompts on stdin / files instead of routing through cmd.exe.
|
|
3645
|
-
shell:
|
|
3799
|
+
shell: spawnTarget.shell
|
|
3646
3800
|
});
|
|
3647
3801
|
proc.stdin?.write(JSON.stringify({
|
|
3648
3802
|
jsonrpc: "2.0",
|
|
@@ -3758,14 +3912,9 @@ var KimiDriver = class {
|
|
|
3758
3912
|
return detectKimiModels();
|
|
3759
3913
|
}
|
|
3760
3914
|
};
|
|
3761
|
-
function detectKimiModels(home = os4.homedir()) {
|
|
3762
|
-
const
|
|
3763
|
-
|
|
3764
|
-
try {
|
|
3765
|
-
raw = readFileSync3(configPath, "utf8");
|
|
3766
|
-
} catch {
|
|
3767
|
-
return null;
|
|
3768
|
-
}
|
|
3915
|
+
function detectKimiModels(home = os4.homedir(), opts = {}) {
|
|
3916
|
+
const raw = readKimiConfigSource(home, opts.env).raw;
|
|
3917
|
+
if (raw === null) return null;
|
|
3769
3918
|
const models = [];
|
|
3770
3919
|
const sectionRe = /^\s*\[models(?:\.([^\]]+)|"\.[^"]+"|\."[^"]+")\s*\]\s*$/gm;
|
|
3771
3920
|
const lineRe = /^\s*\[models\.(.+?)\s*\]\s*$/gm;
|
|
@@ -3786,7 +3935,7 @@ function detectKimiModels(home = os4.homedir()) {
|
|
|
3786
3935
|
|
|
3787
3936
|
// src/drivers/opencode.ts
|
|
3788
3937
|
import { spawn as spawn7, spawnSync as spawnSync2 } from "child_process";
|
|
3789
|
-
import { readFileSync as readFileSync4 } from "fs";
|
|
3938
|
+
import { existsSync as existsSync8, readFileSync as readFileSync4 } from "fs";
|
|
3790
3939
|
import os5 from "os";
|
|
3791
3940
|
import path10 from "path";
|
|
3792
3941
|
var CHAT_MCP_SERVER_NAME = "chat";
|
|
@@ -4028,7 +4177,7 @@ function runOpenCodeModelsCommand(home, deps = {}) {
|
|
|
4028
4177
|
const platform = deps.platform ?? process.platform;
|
|
4029
4178
|
const spawnSyncFn = deps.spawnSyncFn ?? spawnSync2;
|
|
4030
4179
|
const result = spawnSyncFn("opencode", ["models"], {
|
|
4031
|
-
env: { ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" },
|
|
4180
|
+
env: scrubDaemonChildEnv({ ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" }),
|
|
4032
4181
|
encoding: "utf8",
|
|
4033
4182
|
timeout: 5e3,
|
|
4034
4183
|
shell: platform === "win32"
|
|
@@ -4039,6 +4188,102 @@ function runOpenCodeModelsCommand(home, deps = {}) {
|
|
|
4039
4188
|
error: result.error
|
|
4040
4189
|
};
|
|
4041
4190
|
}
|
|
4191
|
+
function isWindowsCommandShim(commandPath) {
|
|
4192
|
+
const ext = path10.win32.extname(commandPath).toLowerCase();
|
|
4193
|
+
return ext === ".cmd" || ext === ".bat";
|
|
4194
|
+
}
|
|
4195
|
+
function opencodePackageEntryCandidates(packageRoot) {
|
|
4196
|
+
const winPath = path10.win32;
|
|
4197
|
+
return [
|
|
4198
|
+
winPath.join(packageRoot, "bin", "opencode.exe"),
|
|
4199
|
+
winPath.join(packageRoot, "bin", "opencode.js"),
|
|
4200
|
+
winPath.join(packageRoot, "bin", "opencode.mjs"),
|
|
4201
|
+
winPath.join(packageRoot, "dist", "index.js")
|
|
4202
|
+
];
|
|
4203
|
+
}
|
|
4204
|
+
function openCodeSpecForEntry(entry, commandArgs) {
|
|
4205
|
+
if (path10.win32.extname(entry).toLowerCase() === ".exe") {
|
|
4206
|
+
return { command: entry, args: commandArgs, shell: false };
|
|
4207
|
+
}
|
|
4208
|
+
return { command: process.execPath, args: [entry, ...commandArgs], shell: false };
|
|
4209
|
+
}
|
|
4210
|
+
function resolveWindowsOpenCodePackageEntry(commandPath, deps = {}) {
|
|
4211
|
+
const existsSyncFn = deps.existsSyncFn ?? existsSync8;
|
|
4212
|
+
const execFileSyncFn = deps.execFileSyncFn;
|
|
4213
|
+
const env = deps.env ?? process.env;
|
|
4214
|
+
const winPath = path10.win32;
|
|
4215
|
+
const candidates = [];
|
|
4216
|
+
if (execFileSyncFn) {
|
|
4217
|
+
try {
|
|
4218
|
+
const globalRoot = String(execFileSyncFn("npm", ["root", "-g"], {
|
|
4219
|
+
encoding: "utf8",
|
|
4220
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
4221
|
+
env
|
|
4222
|
+
})).trim();
|
|
4223
|
+
if (globalRoot) {
|
|
4224
|
+
candidates.push(...opencodePackageEntryCandidates(winPath.join(globalRoot, "opencode-ai")));
|
|
4225
|
+
}
|
|
4226
|
+
} catch {
|
|
4227
|
+
}
|
|
4228
|
+
}
|
|
4229
|
+
if (commandPath) {
|
|
4230
|
+
const commandDir = winPath.dirname(commandPath);
|
|
4231
|
+
candidates.push(...opencodePackageEntryCandidates(winPath.join(commandDir, "node_modules", "opencode-ai")));
|
|
4232
|
+
candidates.push(...extractWindowsShimTargets(commandPath, deps));
|
|
4233
|
+
}
|
|
4234
|
+
for (const candidate of candidates) {
|
|
4235
|
+
if (existsSyncFn(candidate)) return candidate;
|
|
4236
|
+
}
|
|
4237
|
+
return null;
|
|
4238
|
+
}
|
|
4239
|
+
function extractWindowsShimTargets(commandPath, deps = {}) {
|
|
4240
|
+
if (!isWindowsCommandShim(commandPath)) return [];
|
|
4241
|
+
const readFileSyncFn = deps.readFileSyncFn ?? readFileSync4;
|
|
4242
|
+
const commandDir = path10.win32.dirname(commandPath);
|
|
4243
|
+
let raw;
|
|
4244
|
+
try {
|
|
4245
|
+
raw = String(readFileSyncFn(commandPath, "utf8"));
|
|
4246
|
+
} catch {
|
|
4247
|
+
return [];
|
|
4248
|
+
}
|
|
4249
|
+
const candidates = [];
|
|
4250
|
+
const dp0Pattern = /%~dp0\\?([^"\r\n]*?opencode\.(?:exe|js|mjs|cjs))/gi;
|
|
4251
|
+
for (const match of raw.matchAll(dp0Pattern)) {
|
|
4252
|
+
const relative = match[1]?.replace(/^\\+/, "");
|
|
4253
|
+
if (relative) candidates.push(path10.win32.normalize(path10.win32.join(commandDir, relative)));
|
|
4254
|
+
}
|
|
4255
|
+
return candidates;
|
|
4256
|
+
}
|
|
4257
|
+
function resolveOpenCodeSpawn(commandArgs, deps = {}) {
|
|
4258
|
+
const platform = deps.platform ?? process.platform;
|
|
4259
|
+
if (platform !== "win32") {
|
|
4260
|
+
return {
|
|
4261
|
+
command: resolveCommandOnPath("opencode", deps) ?? "opencode",
|
|
4262
|
+
args: commandArgs,
|
|
4263
|
+
shell: false
|
|
4264
|
+
};
|
|
4265
|
+
}
|
|
4266
|
+
const command = resolveCommandOnPath("opencode", deps);
|
|
4267
|
+
if (command && path10.win32.extname(command).toLowerCase() === ".exe") {
|
|
4268
|
+
return { command, args: commandArgs, shell: false };
|
|
4269
|
+
}
|
|
4270
|
+
const packageEntry = resolveWindowsOpenCodePackageEntry(command, deps);
|
|
4271
|
+
if (packageEntry) return openCodeSpecForEntry(packageEntry, commandArgs);
|
|
4272
|
+
if (command && !isWindowsCommandShim(command)) {
|
|
4273
|
+
return { command, args: commandArgs, shell: false };
|
|
4274
|
+
}
|
|
4275
|
+
throw new Error(
|
|
4276
|
+
"Cannot resolve OpenCode CLI entry point on Windows without cmd.exe. Install the native OpenCode executable or install opencode-ai globally so Slock can launch node_modules/opencode-ai/bin/opencode.exe directly."
|
|
4277
|
+
);
|
|
4278
|
+
}
|
|
4279
|
+
function readOpenCodeVersion(deps = {}) {
|
|
4280
|
+
try {
|
|
4281
|
+
const launch = resolveOpenCodeSpawn([], deps);
|
|
4282
|
+
return readCommandVersion(launch.command, launch.args, deps);
|
|
4283
|
+
} catch {
|
|
4284
|
+
return null;
|
|
4285
|
+
}
|
|
4286
|
+
}
|
|
4042
4287
|
function isSystemFirstMessageTask(message) {
|
|
4043
4288
|
return message.sender_id === "system" && message.channel_type === "channel" && message.channel_name === "all" && message.content.trimStart().startsWith(FIRST_MESSAGE_TASK_PREFIX);
|
|
4044
4289
|
}
|
|
@@ -4081,7 +4326,7 @@ var OpenCodeDriver = class {
|
|
|
4081
4326
|
model: modelId
|
|
4082
4327
|
}
|
|
4083
4328
|
};
|
|
4084
|
-
const version =
|
|
4329
|
+
const version = readOpenCodeVersion();
|
|
4085
4330
|
const launch = buildOpenCodeLaunchOptions(launchCtx, opts?.home, version);
|
|
4086
4331
|
return {
|
|
4087
4332
|
args: launch.args,
|
|
@@ -4102,8 +4347,13 @@ var OpenCodeDriver = class {
|
|
|
4102
4347
|
sessionId = null;
|
|
4103
4348
|
sessionAnnounced = false;
|
|
4104
4349
|
probe() {
|
|
4105
|
-
|
|
4106
|
-
|
|
4350
|
+
let version;
|
|
4351
|
+
try {
|
|
4352
|
+
const launch = resolveOpenCodeSpawn([]);
|
|
4353
|
+
version = readCommandVersion(launch.command, launch.args);
|
|
4354
|
+
} catch {
|
|
4355
|
+
return { available: false };
|
|
4356
|
+
}
|
|
4107
4357
|
const unsupportedMessage = unsupportedOpenCodeVersionMessage(version);
|
|
4108
4358
|
if (unsupportedMessage) {
|
|
4109
4359
|
return {
|
|
@@ -4111,7 +4361,7 @@ var OpenCodeDriver = class {
|
|
|
4111
4361
|
version: `${version} (requires >= ${MIN_SUPPORTED_OPENCODE_VERSION})`
|
|
4112
4362
|
};
|
|
4113
4363
|
}
|
|
4114
|
-
return { available: true, version };
|
|
4364
|
+
return { available: true, version: version ?? void 0 };
|
|
4115
4365
|
}
|
|
4116
4366
|
async detectModels() {
|
|
4117
4367
|
return detectOpenCodeModels();
|
|
@@ -4119,17 +4369,18 @@ var OpenCodeDriver = class {
|
|
|
4119
4369
|
spawn(ctx) {
|
|
4120
4370
|
this.sessionId = ctx.config.sessionId || null;
|
|
4121
4371
|
this.sessionAnnounced = false;
|
|
4122
|
-
const version =
|
|
4372
|
+
const version = readOpenCodeVersion();
|
|
4123
4373
|
const unsupportedMessage = unsupportedOpenCodeVersionMessage(version);
|
|
4124
4374
|
if (unsupportedMessage) {
|
|
4125
4375
|
throw new Error(unsupportedMessage);
|
|
4126
4376
|
}
|
|
4127
4377
|
const launch = buildOpenCodeLaunchOptions(ctx, os5.homedir(), version);
|
|
4128
|
-
const
|
|
4378
|
+
const spawnSpec = resolveOpenCodeSpawn(launch.args);
|
|
4379
|
+
const proc = spawn7(spawnSpec.command, spawnSpec.args, {
|
|
4129
4380
|
cwd: ctx.workingDirectory,
|
|
4130
4381
|
stdio: ["pipe", "pipe", "pipe"],
|
|
4131
4382
|
env: launch.env,
|
|
4132
|
-
shell:
|
|
4383
|
+
shell: spawnSpec.shell
|
|
4133
4384
|
});
|
|
4134
4385
|
proc.stdin?.end();
|
|
4135
4386
|
return { process: proc };
|
|
@@ -4187,6 +4438,297 @@ var OpenCodeDriver = class {
|
|
|
4187
4438
|
}
|
|
4188
4439
|
};
|
|
4189
4440
|
|
|
4441
|
+
// src/drivers/pi.ts
|
|
4442
|
+
import { spawn as spawn8 } from "child_process";
|
|
4443
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync4, writeFileSync as writeFileSync7 } from "fs";
|
|
4444
|
+
import path11 from "path";
|
|
4445
|
+
import { fileURLToPath } from "url";
|
|
4446
|
+
import { getAgentDir, VERSION as PI_SDK_VERSION } from "@earendil-works/pi-coding-agent";
|
|
4447
|
+
var CHAT_MCP_TOOL_PREFIX2 = "chat_";
|
|
4448
|
+
var NO_MESSAGE_PROMPT2 = "No new messages are pending. Stop now.";
|
|
4449
|
+
var FIRST_MESSAGE_TASK_PREFIX2 = "First message task (system-triggered):";
|
|
4450
|
+
var MIN_SUPPORTED_PI_VERSION = "0.74.0";
|
|
4451
|
+
function parseSemver2(version) {
|
|
4452
|
+
const match = version.match(/(\d+)\.(\d+)\.(\d+)/);
|
|
4453
|
+
if (!match) return null;
|
|
4454
|
+
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
4455
|
+
}
|
|
4456
|
+
function isSupportedPiVersion(version) {
|
|
4457
|
+
if (!version) return true;
|
|
4458
|
+
const actual = parseSemver2(version);
|
|
4459
|
+
const minimum = parseSemver2(MIN_SUPPORTED_PI_VERSION);
|
|
4460
|
+
if (!actual || !minimum) return true;
|
|
4461
|
+
for (let i = 0; i < 3; i += 1) {
|
|
4462
|
+
if (actual[i] > minimum[i]) return true;
|
|
4463
|
+
if (actual[i] < minimum[i]) return false;
|
|
4464
|
+
}
|
|
4465
|
+
return true;
|
|
4466
|
+
}
|
|
4467
|
+
function unsupportedPiVersionMessage(version) {
|
|
4468
|
+
if (!version || isSupportedPiVersion(version)) return null;
|
|
4469
|
+
return `Pi SDK ${version} is unsupported; requires @earendil-works/pi-coding-agent >= ${MIN_SUPPORTED_PI_VERSION}. Upgrade the daemon Pi dependency before starting this runtime.`;
|
|
4470
|
+
}
|
|
4471
|
+
function probePi(version = PI_SDK_VERSION) {
|
|
4472
|
+
const unsupportedMessage = unsupportedPiVersionMessage(version);
|
|
4473
|
+
if (unsupportedMessage) {
|
|
4474
|
+
return {
|
|
4475
|
+
available: false,
|
|
4476
|
+
version: `${version} (requires @earendil-works/pi-coding-agent >= ${MIN_SUPPORTED_PI_VERSION})`
|
|
4477
|
+
};
|
|
4478
|
+
}
|
|
4479
|
+
return { available: true, version };
|
|
4480
|
+
}
|
|
4481
|
+
function resolvePiSdkRunnerPath(moduleUrl = import.meta.url) {
|
|
4482
|
+
const moduleDir = path11.dirname(fileURLToPath(moduleUrl));
|
|
4483
|
+
const sourceSibling = path11.join(moduleDir, "piSdkRunner.ts");
|
|
4484
|
+
if (existsSync9(sourceSibling)) return sourceSibling;
|
|
4485
|
+
const bundledEntry = path11.join(moduleDir, "drivers", "piSdkRunner.js");
|
|
4486
|
+
if (existsSync9(bundledEntry)) return bundledEntry;
|
|
4487
|
+
return path11.join(moduleDir, "piSdkRunner.js");
|
|
4488
|
+
}
|
|
4489
|
+
function buildPiSdkNodeArgs(runnerPath = resolvePiSdkRunnerPath()) {
|
|
4490
|
+
if (runnerPath.endsWith(".ts")) {
|
|
4491
|
+
return [...process.execArgv, runnerPath];
|
|
4492
|
+
}
|
|
4493
|
+
return [runnerPath];
|
|
4494
|
+
}
|
|
4495
|
+
function buildPiLaunchOptions(ctx, opts = {}) {
|
|
4496
|
+
const command = opts.command ?? process.execPath;
|
|
4497
|
+
const piDir = path11.join(ctx.workingDirectory, ".slock", "pi");
|
|
4498
|
+
const sessionDir = path11.join(piDir, "sessions");
|
|
4499
|
+
mkdirSync4(sessionDir, { recursive: true });
|
|
4500
|
+
const slock = prepareCliTransport(ctx, { NO_COLOR: "1" });
|
|
4501
|
+
const runnerPath = opts.runnerPath ?? resolvePiSdkRunnerPath();
|
|
4502
|
+
const agentDir = opts.agentDir ?? getAgentDir();
|
|
4503
|
+
const runnerConfigPath = path11.join(
|
|
4504
|
+
piDir,
|
|
4505
|
+
`sdk-run-${(ctx.launchId || "launch").replace(/[^a-zA-Z0-9_.-]/g, "_")}.json`
|
|
4506
|
+
);
|
|
4507
|
+
const turnPrompt = ctx.prompt === ctx.standingPrompt ? NO_MESSAGE_PROMPT2 : ctx.prompt;
|
|
4508
|
+
const runnerConfig = {
|
|
4509
|
+
cwd: ctx.workingDirectory,
|
|
4510
|
+
agentDir,
|
|
4511
|
+
sessionDir,
|
|
4512
|
+
sessionId: ctx.config.sessionId || null,
|
|
4513
|
+
standingPrompt: ctx.standingPrompt,
|
|
4514
|
+
prompt: turnPrompt,
|
|
4515
|
+
model: ctx.config.model && ctx.config.model !== "default" ? ctx.config.model : null
|
|
4516
|
+
};
|
|
4517
|
+
writeFileSync7(runnerConfigPath, `${JSON.stringify(runnerConfig)}
|
|
4518
|
+
`, { encoding: "utf8", mode: 384 });
|
|
4519
|
+
const args = [
|
|
4520
|
+
...buildPiSdkNodeArgs(runnerPath),
|
|
4521
|
+
"--config",
|
|
4522
|
+
runnerConfigPath
|
|
4523
|
+
];
|
|
4524
|
+
return {
|
|
4525
|
+
command,
|
|
4526
|
+
args,
|
|
4527
|
+
env: slock.spawnEnv,
|
|
4528
|
+
sessionDir,
|
|
4529
|
+
agentDir,
|
|
4530
|
+
runnerConfigPath,
|
|
4531
|
+
sdkVersion: PI_SDK_VERSION
|
|
4532
|
+
};
|
|
4533
|
+
}
|
|
4534
|
+
function isSystemFirstMessageTask2(message) {
|
|
4535
|
+
return message.sender_id === "system" && message.channel_type === "channel" && message.channel_name === "all" && message.content.trimStart().startsWith(FIRST_MESSAGE_TASK_PREFIX2);
|
|
4536
|
+
}
|
|
4537
|
+
function buildPiSystemPrompt(config) {
|
|
4538
|
+
return buildCliTransportSystemPrompt(config, {
|
|
4539
|
+
toolPrefix: CHAT_MCP_TOOL_PREFIX2,
|
|
4540
|
+
extraCriticalRules: [
|
|
4541
|
+
"- Runtime Profile migration controls are not available in the Pi runtime yet. If asked to acknowledge a runtime migration, explain the blocker instead of inventing a command."
|
|
4542
|
+
],
|
|
4543
|
+
postStartupNotes: [
|
|
4544
|
+
"**Pi runtime note:** Slock launches you as a per-turn process. Complete the current wake using `slock` CLI commands, then stop; the daemon will restart you when new messages arrive."
|
|
4545
|
+
],
|
|
4546
|
+
includeStdinNotificationSection: false,
|
|
4547
|
+
messageNotificationStyle: "poll"
|
|
4548
|
+
});
|
|
4549
|
+
}
|
|
4550
|
+
function contentText(content) {
|
|
4551
|
+
if (!content) return "";
|
|
4552
|
+
const chunks = [];
|
|
4553
|
+
for (const item of content) {
|
|
4554
|
+
if (item.type === "text" && typeof item.text === "string") {
|
|
4555
|
+
chunks.push(item.text);
|
|
4556
|
+
}
|
|
4557
|
+
}
|
|
4558
|
+
return chunks.join("");
|
|
4559
|
+
}
|
|
4560
|
+
function apiKeyErrorMessage(line) {
|
|
4561
|
+
const trimmed = line.trim();
|
|
4562
|
+
if (!trimmed) return null;
|
|
4563
|
+
if (/no api key found/i.test(trimmed)) return trimmed;
|
|
4564
|
+
if (/api key.+required/i.test(trimmed)) return trimmed;
|
|
4565
|
+
if (/no models available/i.test(trimmed)) return trimmed;
|
|
4566
|
+
return null;
|
|
4567
|
+
}
|
|
4568
|
+
var PiDriver = class {
|
|
4569
|
+
id = "pi";
|
|
4570
|
+
lifecycle = {
|
|
4571
|
+
kind: "per_turn",
|
|
4572
|
+
start: "defer_until_concrete_message",
|
|
4573
|
+
exit: "terminate_on_turn_end",
|
|
4574
|
+
inFlightWake: "coalesce_into_pending"
|
|
4575
|
+
};
|
|
4576
|
+
communication = {
|
|
4577
|
+
chat: "slock_cli",
|
|
4578
|
+
runtimeControl: "none"
|
|
4579
|
+
};
|
|
4580
|
+
session = {
|
|
4581
|
+
recovery: "resume_or_fresh"
|
|
4582
|
+
};
|
|
4583
|
+
model = {
|
|
4584
|
+
detectedModelsVerifiedAs: "launchable",
|
|
4585
|
+
toLaunchSpec: (modelId, ctx) => {
|
|
4586
|
+
if (!ctx) return modelId && modelId !== "default" ? { args: ["--model", modelId] } : { args: [] };
|
|
4587
|
+
const launchCtx = {
|
|
4588
|
+
...ctx,
|
|
4589
|
+
config: {
|
|
4590
|
+
...ctx.config,
|
|
4591
|
+
model: modelId
|
|
4592
|
+
}
|
|
4593
|
+
};
|
|
4594
|
+
const launch = buildPiLaunchOptions(launchCtx);
|
|
4595
|
+
return {
|
|
4596
|
+
args: launch.args,
|
|
4597
|
+
env: launch.env,
|
|
4598
|
+
configFiles: [launch.runnerConfigPath],
|
|
4599
|
+
params: {
|
|
4600
|
+
agentDir: launch.agentDir,
|
|
4601
|
+
sessionDir: launch.sessionDir,
|
|
4602
|
+
sdkVersion: launch.sdkVersion,
|
|
4603
|
+
resources: "extensions/skills/prompt-templates/themes/context-files disabled by Slock policy"
|
|
4604
|
+
}
|
|
4605
|
+
};
|
|
4606
|
+
}
|
|
4607
|
+
};
|
|
4608
|
+
supportsStdinNotification = false;
|
|
4609
|
+
mcpToolPrefix = CHAT_MCP_TOOL_PREFIX2;
|
|
4610
|
+
busyDeliveryMode = "none";
|
|
4611
|
+
terminateProcessOnTurnEnd = true;
|
|
4612
|
+
deferSpawnUntilMessage = true;
|
|
4613
|
+
usesSlockCliForCommunication = true;
|
|
4614
|
+
sessionId = null;
|
|
4615
|
+
sessionAnnounced = false;
|
|
4616
|
+
apiKeyErrorAnnounced = false;
|
|
4617
|
+
turnEnded = false;
|
|
4618
|
+
assistantTextByMessageId = /* @__PURE__ */ new Map();
|
|
4619
|
+
shouldDeferWakeMessage(message) {
|
|
4620
|
+
return isSystemFirstMessageTask2(message);
|
|
4621
|
+
}
|
|
4622
|
+
probe() {
|
|
4623
|
+
return probePi();
|
|
4624
|
+
}
|
|
4625
|
+
async detectModels() {
|
|
4626
|
+
return null;
|
|
4627
|
+
}
|
|
4628
|
+
spawn(ctx) {
|
|
4629
|
+
this.sessionId = ctx.config.sessionId || null;
|
|
4630
|
+
this.sessionAnnounced = false;
|
|
4631
|
+
this.apiKeyErrorAnnounced = false;
|
|
4632
|
+
this.turnEnded = false;
|
|
4633
|
+
this.assistantTextByMessageId.clear();
|
|
4634
|
+
const unsupportedMessage = unsupportedPiVersionMessage(PI_SDK_VERSION);
|
|
4635
|
+
if (unsupportedMessage) throw new Error(unsupportedMessage);
|
|
4636
|
+
const launch = buildPiLaunchOptions(ctx);
|
|
4637
|
+
const proc = spawn8(launch.command, launch.args, {
|
|
4638
|
+
cwd: ctx.workingDirectory,
|
|
4639
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
4640
|
+
env: launch.env,
|
|
4641
|
+
shell: false
|
|
4642
|
+
});
|
|
4643
|
+
proc.stdin?.end();
|
|
4644
|
+
return { process: proc };
|
|
4645
|
+
}
|
|
4646
|
+
parseLine(line) {
|
|
4647
|
+
let event;
|
|
4648
|
+
try {
|
|
4649
|
+
event = JSON.parse(line);
|
|
4650
|
+
} catch {
|
|
4651
|
+
if (this.apiKeyErrorAnnounced) return [];
|
|
4652
|
+
const message = apiKeyErrorMessage(line);
|
|
4653
|
+
if (!message) return [];
|
|
4654
|
+
this.apiKeyErrorAnnounced = true;
|
|
4655
|
+
this.turnEnded = true;
|
|
4656
|
+
return [
|
|
4657
|
+
{ kind: "error", message },
|
|
4658
|
+
{ kind: "turn_end", sessionId: this.sessionId || void 0 }
|
|
4659
|
+
];
|
|
4660
|
+
}
|
|
4661
|
+
const events = [];
|
|
4662
|
+
if (event.type === "session" && event.id) {
|
|
4663
|
+
this.sessionId = event.id;
|
|
4664
|
+
}
|
|
4665
|
+
if (!this.sessionAnnounced && this.sessionId) {
|
|
4666
|
+
events.push({ kind: "session_init", sessionId: this.sessionId });
|
|
4667
|
+
this.sessionAnnounced = true;
|
|
4668
|
+
}
|
|
4669
|
+
switch (event.type) {
|
|
4670
|
+
case "agent_start":
|
|
4671
|
+
case "turn_start":
|
|
4672
|
+
events.push({ kind: "thinking", text: "" });
|
|
4673
|
+
break;
|
|
4674
|
+
case "message_update":
|
|
4675
|
+
case "message_end":
|
|
4676
|
+
if (event.message?.role === "assistant") {
|
|
4677
|
+
const key = event.message.id || "current";
|
|
4678
|
+
const currentText = contentText(event.message.content);
|
|
4679
|
+
const previousText = this.assistantTextByMessageId.get(key) ?? "";
|
|
4680
|
+
if (currentText.length > previousText.length && currentText.startsWith(previousText)) {
|
|
4681
|
+
events.push({ kind: "text", text: currentText.slice(previousText.length) });
|
|
4682
|
+
} else if (currentText && currentText !== previousText) {
|
|
4683
|
+
events.push({ kind: "text", text: currentText });
|
|
4684
|
+
}
|
|
4685
|
+
this.assistantTextByMessageId.set(key, currentText);
|
|
4686
|
+
if (event.message.stopReason === "error" || event.message.stopReason === "aborted") {
|
|
4687
|
+
events.push({ kind: "error", message: event.message.errorMessage || `Request ${event.message.stopReason}` });
|
|
4688
|
+
}
|
|
4689
|
+
}
|
|
4690
|
+
break;
|
|
4691
|
+
case "tool_execution_start":
|
|
4692
|
+
events.push({
|
|
4693
|
+
kind: "tool_call",
|
|
4694
|
+
name: event.toolName || "unknown_tool",
|
|
4695
|
+
input: event.args
|
|
4696
|
+
});
|
|
4697
|
+
break;
|
|
4698
|
+
case "tool_execution_end":
|
|
4699
|
+
events.push({
|
|
4700
|
+
kind: "tool_output",
|
|
4701
|
+
name: event.toolName || "unknown_tool"
|
|
4702
|
+
});
|
|
4703
|
+
if (event.isError) {
|
|
4704
|
+
events.push({ kind: "error", message: `Pi tool ${event.toolName || "unknown_tool"} failed` });
|
|
4705
|
+
}
|
|
4706
|
+
break;
|
|
4707
|
+
case "compaction_start":
|
|
4708
|
+
events.push({ kind: "compaction_started" });
|
|
4709
|
+
break;
|
|
4710
|
+
case "compaction_end":
|
|
4711
|
+
events.push({ kind: "compaction_finished" });
|
|
4712
|
+
if (event.errorMessage) events.push({ kind: "error", message: event.errorMessage });
|
|
4713
|
+
break;
|
|
4714
|
+
case "turn_end":
|
|
4715
|
+
case "agent_end":
|
|
4716
|
+
if (!this.turnEnded) {
|
|
4717
|
+
events.push({ kind: "turn_end", sessionId: this.sessionId || void 0 });
|
|
4718
|
+
this.turnEnded = true;
|
|
4719
|
+
}
|
|
4720
|
+
break;
|
|
4721
|
+
}
|
|
4722
|
+
return events;
|
|
4723
|
+
}
|
|
4724
|
+
encodeStdinMessage(_text, _sessionId, _opts) {
|
|
4725
|
+
return null;
|
|
4726
|
+
}
|
|
4727
|
+
buildSystemPrompt(config, _agentId) {
|
|
4728
|
+
return buildPiSystemPrompt(config);
|
|
4729
|
+
}
|
|
4730
|
+
};
|
|
4731
|
+
|
|
4190
4732
|
// src/drivers/index.ts
|
|
4191
4733
|
var driverFactories = {
|
|
4192
4734
|
claude: () => new ClaudeDriver(),
|
|
@@ -4195,7 +4737,8 @@ var driverFactories = {
|
|
|
4195
4737
|
cursor: () => new CursorDriver(),
|
|
4196
4738
|
gemini: () => new GeminiDriver(),
|
|
4197
4739
|
kimi: () => new KimiDriver(),
|
|
4198
|
-
opencode: () => new OpenCodeDriver()
|
|
4740
|
+
opencode: () => new OpenCodeDriver(),
|
|
4741
|
+
pi: () => new PiDriver()
|
|
4199
4742
|
};
|
|
4200
4743
|
function getDriver(runtimeId) {
|
|
4201
4744
|
const createDriver = driverFactories[runtimeId];
|
|
@@ -4208,7 +4751,7 @@ function getDriver(runtimeId) {
|
|
|
4208
4751
|
|
|
4209
4752
|
// src/workspaces.ts
|
|
4210
4753
|
import { readdir, rm, stat } from "fs/promises";
|
|
4211
|
-
import
|
|
4754
|
+
import path12 from "path";
|
|
4212
4755
|
function isValidWorkspaceDirectoryName(directoryName) {
|
|
4213
4756
|
return !directoryName.includes("/") && !directoryName.includes("\\") && !directoryName.includes("..");
|
|
4214
4757
|
}
|
|
@@ -4216,7 +4759,7 @@ function resolveWorkspaceDirectoryPath(dataDir, directoryName) {
|
|
|
4216
4759
|
if (!isValidWorkspaceDirectoryName(directoryName)) {
|
|
4217
4760
|
return null;
|
|
4218
4761
|
}
|
|
4219
|
-
return
|
|
4762
|
+
return path12.join(dataDir, directoryName);
|
|
4220
4763
|
}
|
|
4221
4764
|
function emptyWorkspaceDirectorySummary(latestMtime = /* @__PURE__ */ new Date(0)) {
|
|
4222
4765
|
return {
|
|
@@ -4265,7 +4808,7 @@ async function summarizeWorkspaceDirectory(dirPath) {
|
|
|
4265
4808
|
return summary;
|
|
4266
4809
|
}
|
|
4267
4810
|
const childSummaries = await Promise.all(
|
|
4268
|
-
entries.map((entry) => summarizeWorkspaceEntry(
|
|
4811
|
+
entries.map((entry) => summarizeWorkspaceEntry(path12.join(dirPath, entry.name), entry))
|
|
4269
4812
|
);
|
|
4270
4813
|
for (const childSummary of childSummaries) {
|
|
4271
4814
|
summary = mergeWorkspaceDirectorySummaries(summary, childSummary);
|
|
@@ -4284,7 +4827,7 @@ async function scanWorkspaceDirectories(dataDir) {
|
|
|
4284
4827
|
if (!entry.isDirectory()) {
|
|
4285
4828
|
return null;
|
|
4286
4829
|
}
|
|
4287
|
-
const dirPath =
|
|
4830
|
+
const dirPath = path12.join(dataDir, entry.name);
|
|
4288
4831
|
try {
|
|
4289
4832
|
const summary = await summarizeWorkspaceDirectory(dirPath);
|
|
4290
4833
|
return {
|
|
@@ -4367,6 +4910,7 @@ function classifyRuntimeError(message, httpStatus) {
|
|
|
4367
4910
|
return "ProviderApiError";
|
|
4368
4911
|
}
|
|
4369
4912
|
if (/\btimeout|timed out\b/i.test(message)) return "TimeoutError";
|
|
4913
|
+
if (/stream closed before response\.completed|error decoding response body/i.test(message)) return "ProviderStreamError";
|
|
4370
4914
|
if (/\brate.?limit|too many requests\b/i.test(message)) return "RateLimitError";
|
|
4371
4915
|
if (/\bnot found\b/i.test(message)) return "NotFoundError";
|
|
4372
4916
|
return "RuntimeError";
|
|
@@ -4541,12 +5085,12 @@ function findSessionJsonl(root, predicate) {
|
|
|
4541
5085
|
for (const entry of entries) {
|
|
4542
5086
|
if (++visited > maxEntries) return null;
|
|
4543
5087
|
if (!entry.isFile() || !predicate(entry.name)) continue;
|
|
4544
|
-
return
|
|
5088
|
+
return path13.join(dir, entry.name);
|
|
4545
5089
|
}
|
|
4546
5090
|
for (const entry of entries) {
|
|
4547
5091
|
if (++visited > maxEntries) return null;
|
|
4548
5092
|
if (!entry.isDirectory()) continue;
|
|
4549
|
-
const found = visit(
|
|
5093
|
+
const found = visit(path13.join(dir, entry.name), depth - 1);
|
|
4550
5094
|
if (found) return found;
|
|
4551
5095
|
}
|
|
4552
5096
|
return null;
|
|
@@ -4559,10 +5103,10 @@ function safeSessionFilename(value) {
|
|
|
4559
5103
|
}
|
|
4560
5104
|
function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
|
|
4561
5105
|
try {
|
|
4562
|
-
const dir =
|
|
4563
|
-
|
|
4564
|
-
const filePath =
|
|
4565
|
-
|
|
5106
|
+
const dir = path13.join(fallbackDir, ".slock", "runtime-sessions");
|
|
5107
|
+
mkdirSync5(dir, { recursive: true });
|
|
5108
|
+
const filePath = path13.join(dir, `${runtime}-${safeSessionFilename(sessionId)}.jsonl`);
|
|
5109
|
+
writeFileSync8(filePath, JSON.stringify({
|
|
4566
5110
|
type: "runtime_session_handoff",
|
|
4567
5111
|
runtime,
|
|
4568
5112
|
sessionId,
|
|
@@ -4581,7 +5125,7 @@ function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
|
|
|
4581
5125
|
}
|
|
4582
5126
|
}
|
|
4583
5127
|
function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os6.homedir(), fallbackDir) {
|
|
4584
|
-
const directPath =
|
|
5128
|
+
const directPath = path13.isAbsolute(sessionId) ? sessionId : null;
|
|
4585
5129
|
if (directPath) {
|
|
4586
5130
|
try {
|
|
4587
5131
|
if (statSync2(directPath).isFile()) {
|
|
@@ -4590,7 +5134,7 @@ function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os6.homedir(), f
|
|
|
4590
5134
|
} catch {
|
|
4591
5135
|
}
|
|
4592
5136
|
}
|
|
4593
|
-
const resolvedPath = runtime === "claude" ? findSessionJsonl(
|
|
5137
|
+
const resolvedPath = runtime === "claude" ? findSessionJsonl(path13.join(homeDir, ".claude", "projects"), (filename) => filename === `${sessionId}.jsonl`) : runtime === "codex" ? findSessionJsonl(path13.join(homeDir, ".codex", "sessions"), (filename) => filename.endsWith(".jsonl") && filename.includes(sessionId)) : null;
|
|
4594
5138
|
if (!resolvedPath && fallbackDir) {
|
|
4595
5139
|
const fallback = writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir);
|
|
4596
5140
|
if (fallback) return fallback;
|
|
@@ -5241,12 +5785,21 @@ function classifyTerminalFailure(ap) {
|
|
|
5241
5785
|
].filter((value) => !!value);
|
|
5242
5786
|
for (const text of candidates) {
|
|
5243
5787
|
const lower = text.toLowerCase();
|
|
5244
|
-
if (lower.includes("usage limit") || lower.includes("quota exceeded") || lower.includes("quota limit") || lower.includes("budget limit exceeded") || lower.includes("usage not included in your plan") || lower.includes("modelnotfounderror") || lower.includes("requested entity was not found") || lower.includes("model deprecated") || lower.includes("model not found")) {
|
|
5788
|
+
if (lower.includes("usage limit") || lower.includes("quota exceeded") || lower.includes("quota limit") || lower.includes("budget limit exceeded") || lower.includes("usage not included in your plan") || lower.includes("modelnotfounderror") || lower.includes("requested entity was not found") || lower.includes("model deprecated") || lower.includes("model not found") || isProviderStreamFailureText(text)) {
|
|
5245
5789
|
return text;
|
|
5246
5790
|
}
|
|
5247
5791
|
}
|
|
5248
5792
|
return null;
|
|
5249
5793
|
}
|
|
5794
|
+
function isProviderStreamFailureText(text) {
|
|
5795
|
+
return /stream closed before response\.completed|error decoding response body/i.test(text);
|
|
5796
|
+
}
|
|
5797
|
+
function isCodexProviderReconnectLog(text) {
|
|
5798
|
+
return /Reconnecting\.\.\.\s*\d+\s*\/\s*\d+/i.test(text);
|
|
5799
|
+
}
|
|
5800
|
+
function isCodexBenignTransportLog(text) {
|
|
5801
|
+
return /Falling back from WebSockets/i.test(text);
|
|
5802
|
+
}
|
|
5250
5803
|
function hasDirectStdinRecoveryEvidence(ap) {
|
|
5251
5804
|
const candidates = [
|
|
5252
5805
|
ap.lastRuntimeError,
|
|
@@ -5890,26 +6443,26 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
5890
6443
|
this.recordDaemonTrace("daemon.agent.spawn.started", this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId));
|
|
5891
6444
|
try {
|
|
5892
6445
|
const driver = this.driverResolver(config.runtime || "claude");
|
|
5893
|
-
const agentDataDir =
|
|
6446
|
+
const agentDataDir = path13.join(this.dataDir, agentId);
|
|
5894
6447
|
await mkdir(agentDataDir, { recursive: true });
|
|
5895
6448
|
const runtimeConfig = withLocalRuntimeContext(config, agentId, agentDataDir);
|
|
5896
|
-
const memoryMdPath =
|
|
6449
|
+
const memoryMdPath = path13.join(agentDataDir, "MEMORY.md");
|
|
5897
6450
|
try {
|
|
5898
6451
|
await access(memoryMdPath);
|
|
5899
6452
|
} catch {
|
|
5900
6453
|
const initialMemoryMd = buildInitialMemoryMd(runtimeConfig);
|
|
5901
6454
|
await writeFile(memoryMdPath, initialMemoryMd);
|
|
5902
6455
|
}
|
|
5903
|
-
const notesDir =
|
|
6456
|
+
const notesDir = path13.join(agentDataDir, "notes");
|
|
5904
6457
|
await mkdir(notesDir, { recursive: true });
|
|
5905
6458
|
if (getOnboardingSeedMode(config) === FIRST_CINDY_SEED_MODE) {
|
|
5906
6459
|
const seedFiles = buildOnboardingSeedFiles();
|
|
5907
6460
|
for (const { relativePath, content } of seedFiles) {
|
|
5908
|
-
const fullPath =
|
|
6461
|
+
const fullPath = path13.join(agentDataDir, relativePath);
|
|
5909
6462
|
try {
|
|
5910
6463
|
await access(fullPath);
|
|
5911
6464
|
} catch {
|
|
5912
|
-
await mkdir(
|
|
6465
|
+
await mkdir(path13.dirname(fullPath), { recursive: true });
|
|
5913
6466
|
await writeFile(fullPath, content);
|
|
5914
6467
|
}
|
|
5915
6468
|
}
|
|
@@ -6098,8 +6651,24 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6098
6651
|
proc.stderr?.on("data", (chunk) => {
|
|
6099
6652
|
const text = chunk.toString().trim();
|
|
6100
6653
|
if (!text) return;
|
|
6101
|
-
if (/Reconnecting\.\.\.|Falling back from WebSockets/i.test(text)) return;
|
|
6102
6654
|
const current = this.agents.get(agentId);
|
|
6655
|
+
if (driver.id === "codex" && isCodexProviderReconnectLog(text)) {
|
|
6656
|
+
if (current) {
|
|
6657
|
+
current.recentStderr = pushRecentStderr(current.recentStderr, text);
|
|
6658
|
+
}
|
|
6659
|
+
this.recordDaemonTrace("daemon.agent.provider_reconnect", {
|
|
6660
|
+
agentId,
|
|
6661
|
+
launchId: current?.launchId || void 0,
|
|
6662
|
+
runtime: config.runtime,
|
|
6663
|
+
model: config.model
|
|
6664
|
+
});
|
|
6665
|
+
this.broadcastActivity(agentId, "working", "Codex reconnecting to provider\u2026", [
|
|
6666
|
+
{ kind: "text", text }
|
|
6667
|
+
]);
|
|
6668
|
+
logger.info(`[Agent ${agentId} stderr]: ${text}`);
|
|
6669
|
+
return;
|
|
6670
|
+
}
|
|
6671
|
+
if (driver.id === "codex" && isCodexBenignTransportLog(text)) return;
|
|
6103
6672
|
if (current) {
|
|
6104
6673
|
current.recentStderr = pushRecentStderr(current.recentStderr, text);
|
|
6105
6674
|
}
|
|
@@ -6243,10 +6812,20 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6243
6812
|
}
|
|
6244
6813
|
this.broadcastActivity(agentId, "online", "Process idle");
|
|
6245
6814
|
} else {
|
|
6246
|
-
this.idleAgentConfigs.delete(agentId);
|
|
6247
6815
|
const reason = formatCrashReason(finalCode, finalSignal, ap);
|
|
6248
|
-
|
|
6249
|
-
|
|
6816
|
+
if (terminalFailureDetail && isProviderStreamFailureText(terminalFailureDetail)) {
|
|
6817
|
+
this.idleAgentConfigs.set(agentId, {
|
|
6818
|
+
config: { ...ap.config, sessionId: ap.sessionId },
|
|
6819
|
+
sessionId: ap.sessionId,
|
|
6820
|
+
launchId: ap.launchId
|
|
6821
|
+
});
|
|
6822
|
+
logger.warn(`[Agent ${agentId}] Recoverable provider stream failure (${reason}) \u2014 keeping agent wakeable`);
|
|
6823
|
+
this.sendAgentStatus(agentId, "active", ap.launchId);
|
|
6824
|
+
} else {
|
|
6825
|
+
this.idleAgentConfigs.delete(agentId);
|
|
6826
|
+
logger.error(`[Agent ${agentId}] Process crashed (${reason}) \u2014 marking inactive`);
|
|
6827
|
+
this.sendAgentStatus(agentId, "inactive", ap.launchId);
|
|
6828
|
+
}
|
|
6250
6829
|
if (terminalFailureDetail) {
|
|
6251
6830
|
this.broadcastActivity(
|
|
6252
6831
|
agentId,
|
|
@@ -6760,7 +7339,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6760
7339
|
return true;
|
|
6761
7340
|
}
|
|
6762
7341
|
async resetWorkspace(agentId) {
|
|
6763
|
-
const agentDataDir =
|
|
7342
|
+
const agentDataDir = path13.join(this.dataDir, agentId);
|
|
6764
7343
|
try {
|
|
6765
7344
|
await rm2(agentDataDir, { recursive: true, force: true });
|
|
6766
7345
|
logger.info(`[Agent ${agentId}] Workspace reset complete (${agentDataDir})`);
|
|
@@ -6797,7 +7376,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
6797
7376
|
return result;
|
|
6798
7377
|
}
|
|
6799
7378
|
buildRuntimeProfileReport(agentId, config, sessionId, launchId) {
|
|
6800
|
-
const workspacePath =
|
|
7379
|
+
const workspacePath = path13.join(this.dataDir, agentId);
|
|
6801
7380
|
return {
|
|
6802
7381
|
agentId,
|
|
6803
7382
|
launchId,
|
|
@@ -7054,7 +7633,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7054
7633
|
}
|
|
7055
7634
|
// Workspace file browsing
|
|
7056
7635
|
async getFileTree(agentId, dirPath) {
|
|
7057
|
-
const agentDir =
|
|
7636
|
+
const agentDir = path13.join(this.dataDir, agentId);
|
|
7058
7637
|
try {
|
|
7059
7638
|
await stat2(agentDir);
|
|
7060
7639
|
} catch {
|
|
@@ -7062,8 +7641,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7062
7641
|
}
|
|
7063
7642
|
let targetDir = agentDir;
|
|
7064
7643
|
if (dirPath) {
|
|
7065
|
-
const resolved =
|
|
7066
|
-
if (!resolved.startsWith(agentDir +
|
|
7644
|
+
const resolved = path13.resolve(agentDir, dirPath);
|
|
7645
|
+
if (!resolved.startsWith(agentDir + path13.sep) && resolved !== agentDir) {
|
|
7067
7646
|
return [];
|
|
7068
7647
|
}
|
|
7069
7648
|
targetDir = resolved;
|
|
@@ -7071,14 +7650,14 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7071
7650
|
return this.listDirectoryChildren(targetDir, agentDir);
|
|
7072
7651
|
}
|
|
7073
7652
|
async readFile(agentId, filePath) {
|
|
7074
|
-
const agentDir =
|
|
7075
|
-
const resolved =
|
|
7076
|
-
if (!resolved.startsWith(agentDir +
|
|
7653
|
+
const agentDir = path13.join(this.dataDir, agentId);
|
|
7654
|
+
const resolved = path13.resolve(agentDir, filePath);
|
|
7655
|
+
if (!resolved.startsWith(agentDir + path13.sep) && resolved !== agentDir) {
|
|
7077
7656
|
throw new Error("Access denied");
|
|
7078
7657
|
}
|
|
7079
7658
|
const info = await stat2(resolved);
|
|
7080
7659
|
if (info.isDirectory()) throw new Error("Cannot read a directory");
|
|
7081
|
-
const ext =
|
|
7660
|
+
const ext = path13.extname(resolved).toLowerCase();
|
|
7082
7661
|
if (WORKSPACE_TEXT_EXTENSIONS.has(ext) || ext === "") {
|
|
7083
7662
|
if (info.size > WORKSPACE_TEXT_FILE_MAX_BYTES) throw new Error("File too large");
|
|
7084
7663
|
const content = await readFile(resolved, "utf-8");
|
|
@@ -7113,13 +7692,13 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7113
7692
|
const agent = this.agents.get(agentId);
|
|
7114
7693
|
const runtime = runtimeHint || agent?.config.runtime || "claude";
|
|
7115
7694
|
const home = os6.homedir();
|
|
7116
|
-
const workspaceDir =
|
|
7695
|
+
const workspaceDir = path13.join(this.dataDir, agentId);
|
|
7117
7696
|
const paths = _AgentProcessManager.SKILL_PATHS[runtime] || _AgentProcessManager.SKILL_PATHS.claude;
|
|
7118
7697
|
const globalResults = await Promise.all(
|
|
7119
|
-
paths.global.map((p) => this.scanSkillsDir(
|
|
7698
|
+
paths.global.map((p) => this.scanSkillsDir(path13.join(home, p)))
|
|
7120
7699
|
);
|
|
7121
7700
|
const workspaceResults = await Promise.all(
|
|
7122
|
-
paths.workspace.map((p) => this.scanSkillsDir(
|
|
7701
|
+
paths.workspace.map((p) => this.scanSkillsDir(path13.join(workspaceDir, p)))
|
|
7123
7702
|
);
|
|
7124
7703
|
const dedup = (skills) => {
|
|
7125
7704
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -7148,7 +7727,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7148
7727
|
const skills = [];
|
|
7149
7728
|
for (const entry of entries) {
|
|
7150
7729
|
if (entry.isDirectory() || entry.isSymbolicLink()) {
|
|
7151
|
-
const skillMd =
|
|
7730
|
+
const skillMd = path13.join(dir, entry.name, "SKILL.md");
|
|
7152
7731
|
try {
|
|
7153
7732
|
const content = await readFile(skillMd, "utf-8");
|
|
7154
7733
|
const skill = this.parseSkillMd(entry.name, content);
|
|
@@ -7159,7 +7738,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
7159
7738
|
} else if (entry.name.endsWith(".md")) {
|
|
7160
7739
|
const cmdName = entry.name.replace(/\.md$/, "");
|
|
7161
7740
|
try {
|
|
7162
|
-
const content = await readFile(
|
|
7741
|
+
const content = await readFile(path13.join(dir, entry.name), "utf-8");
|
|
7163
7742
|
const skill = this.parseSkillMd(cmdName, content);
|
|
7164
7743
|
skill.sourcePath = dir;
|
|
7165
7744
|
skills.push(skill);
|
|
@@ -8125,8 +8704,8 @@ ${RESPONSE_TARGET_HINT}`);
|
|
|
8125
8704
|
const nodes = [];
|
|
8126
8705
|
for (const entry of entries) {
|
|
8127
8706
|
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
8128
|
-
const fullPath =
|
|
8129
|
-
const relativePath =
|
|
8707
|
+
const fullPath = path13.join(dir, entry.name);
|
|
8708
|
+
const relativePath = path13.relative(rootDir, fullPath);
|
|
8130
8709
|
let info;
|
|
8131
8710
|
try {
|
|
8132
8711
|
info = await stat2(fullPath);
|
|
@@ -8431,9 +9010,9 @@ var ReminderCache = class {
|
|
|
8431
9010
|
|
|
8432
9011
|
// src/machineLock.ts
|
|
8433
9012
|
import { createHash as createHash3, randomUUID as randomUUID2 } from "crypto";
|
|
8434
|
-
import { mkdirSync as
|
|
9013
|
+
import { mkdirSync as mkdirSync6, readFileSync as readFileSync5, rmSync as rmSync3, statSync as statSync3, writeFileSync as writeFileSync9 } from "fs";
|
|
8435
9014
|
import os7 from "os";
|
|
8436
|
-
import
|
|
9015
|
+
import path14 from "path";
|
|
8437
9016
|
var INCOMPLETE_LOCK_STALE_MS = 3e4;
|
|
8438
9017
|
var DaemonMachineLockConflictError = class extends Error {
|
|
8439
9018
|
code = "DAEMON_MACHINE_LOCK_HELD";
|
|
@@ -8455,7 +9034,7 @@ function resolveDefaultMachineStateRoot() {
|
|
|
8455
9034
|
return resolveSlockHomePath("machines");
|
|
8456
9035
|
}
|
|
8457
9036
|
function ownerPath(lockDir) {
|
|
8458
|
-
return
|
|
9037
|
+
return path14.join(lockDir, "owner.json");
|
|
8459
9038
|
}
|
|
8460
9039
|
function readOwner(lockDir) {
|
|
8461
9040
|
try {
|
|
@@ -8485,13 +9064,13 @@ function acquireDaemonMachineLock(options) {
|
|
|
8485
9064
|
const rootDir = options.rootDir ?? resolveDefaultMachineStateRoot();
|
|
8486
9065
|
const fingerprint = apiKeyFingerprint(options.apiKey);
|
|
8487
9066
|
const lockId = getDaemonMachineLockId(options.apiKey);
|
|
8488
|
-
const machineDir =
|
|
8489
|
-
const lockDir =
|
|
9067
|
+
const machineDir = path14.join(rootDir, lockId);
|
|
9068
|
+
const lockDir = path14.join(machineDir, "daemon.lock");
|
|
8490
9069
|
const token = randomUUID2();
|
|
8491
|
-
|
|
9070
|
+
mkdirSync6(machineDir, { recursive: true });
|
|
8492
9071
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
8493
9072
|
try {
|
|
8494
|
-
|
|
9073
|
+
mkdirSync6(lockDir);
|
|
8495
9074
|
const owner = {
|
|
8496
9075
|
pid: process.pid,
|
|
8497
9076
|
token,
|
|
@@ -8501,7 +9080,7 @@ function acquireDaemonMachineLock(options) {
|
|
|
8501
9080
|
apiKeyFingerprint: fingerprint.slice(0, 16)
|
|
8502
9081
|
};
|
|
8503
9082
|
try {
|
|
8504
|
-
|
|
9083
|
+
writeFileSync9(ownerPath(lockDir), `${JSON.stringify(owner, null, 2)}
|
|
8505
9084
|
`, { mode: 384 });
|
|
8506
9085
|
} catch (err) {
|
|
8507
9086
|
rmSync3(lockDir, { recursive: true, force: true });
|
|
@@ -8538,8 +9117,8 @@ function acquireDaemonMachineLock(options) {
|
|
|
8538
9117
|
}
|
|
8539
9118
|
|
|
8540
9119
|
// src/localTraceSink.ts
|
|
8541
|
-
import { appendFileSync, mkdirSync as
|
|
8542
|
-
import
|
|
9120
|
+
import { appendFileSync, mkdirSync as mkdirSync7, readdirSync as readdirSync3, rmSync as rmSync4, statSync as statSync4, writeFileSync as writeFileSync10 } from "fs";
|
|
9121
|
+
import path15 from "path";
|
|
8543
9122
|
var DEFAULT_MAX_FILE_BYTES = 5 * 1024 * 1024;
|
|
8544
9123
|
var DEFAULT_MAX_FILE_AGE_MS = 5 * 60 * 1e3;
|
|
8545
9124
|
var DEFAULT_MAX_FILES = 8;
|
|
@@ -8575,7 +9154,7 @@ var LocalRotatingTraceSink = class {
|
|
|
8575
9154
|
currentSize = 0;
|
|
8576
9155
|
sequence = 0;
|
|
8577
9156
|
constructor(options) {
|
|
8578
|
-
this.traceDir =
|
|
9157
|
+
this.traceDir = path15.join(options.machineDir, "traces");
|
|
8579
9158
|
this.maxFileBytes = Math.max(1024, Math.floor(options.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES));
|
|
8580
9159
|
const baseAgeMs = Math.max(1e3, Math.floor(options.maxFileAgeMs ?? DEFAULT_MAX_FILE_AGE_MS));
|
|
8581
9160
|
const ageJitterMs = Math.max(0, Math.floor(options.maxFileAgeJitterMs ?? 0));
|
|
@@ -8601,15 +9180,15 @@ var LocalRotatingTraceSink = class {
|
|
|
8601
9180
|
return this.currentFile;
|
|
8602
9181
|
}
|
|
8603
9182
|
ensureFile(nextBytes) {
|
|
8604
|
-
|
|
9183
|
+
mkdirSync7(this.traceDir, { recursive: true, mode: 448 });
|
|
8605
9184
|
const nowMs = this.nowMsProvider();
|
|
8606
9185
|
const shouldRotateForAge = this.currentFileOpenedAtMs !== null && nowMs - this.currentFileOpenedAtMs >= this.maxFileAgeMs;
|
|
8607
9186
|
if (!this.currentFile || this.currentSize + nextBytes > this.maxFileBytes || shouldRotateForAge) {
|
|
8608
|
-
this.currentFile =
|
|
9187
|
+
this.currentFile = path15.join(
|
|
8609
9188
|
this.traceDir,
|
|
8610
9189
|
`daemon-trace-${safeTimestamp(nowMs)}-${process.pid}-${String(this.sequence++).padStart(4, "0")}.jsonl`
|
|
8611
9190
|
);
|
|
8612
|
-
|
|
9191
|
+
writeFileSync10(this.currentFile, "", { flag: "a", mode: 384 });
|
|
8613
9192
|
this.currentSize = statSync4(this.currentFile).size;
|
|
8614
9193
|
this.currentFileOpenedAtMs = nowMs;
|
|
8615
9194
|
this.pruneOldFiles();
|
|
@@ -8620,7 +9199,7 @@ var LocalRotatingTraceSink = class {
|
|
|
8620
9199
|
const excess = files.length - this.maxFiles;
|
|
8621
9200
|
if (excess <= 0) return;
|
|
8622
9201
|
for (const file of files.slice(0, excess)) {
|
|
8623
|
-
rmSync4(
|
|
9202
|
+
rmSync4(path15.join(this.traceDir, file), { force: true });
|
|
8624
9203
|
}
|
|
8625
9204
|
}
|
|
8626
9205
|
};
|
|
@@ -8707,11 +9286,11 @@ function isDiagnosticErrorAttr(key) {
|
|
|
8707
9286
|
import { createHash as createHash5, randomUUID as randomUUID3 } from "crypto";
|
|
8708
9287
|
import { gzipSync } from "zlib";
|
|
8709
9288
|
import { mkdir as mkdir2, readFile as readFile2, readdir as readdir3, stat as stat3, writeFile as writeFile2 } from "fs/promises";
|
|
8710
|
-
import
|
|
9289
|
+
import path16 from "path";
|
|
8711
9290
|
|
|
8712
9291
|
// src/directUploadCapability.ts
|
|
8713
|
-
function joinUrl(base,
|
|
8714
|
-
return `${base.replace(/\/+$/, "")}${
|
|
9292
|
+
function joinUrl(base, path18) {
|
|
9293
|
+
return `${base.replace(/\/+$/, "")}${path18}`;
|
|
8715
9294
|
}
|
|
8716
9295
|
function jsonHeaders(apiKey) {
|
|
8717
9296
|
return {
|
|
@@ -8930,7 +9509,7 @@ var DaemonTraceBundleUploader = class {
|
|
|
8930
9509
|
}, nextMs);
|
|
8931
9510
|
}
|
|
8932
9511
|
async findUploadCandidates() {
|
|
8933
|
-
const traceDir =
|
|
9512
|
+
const traceDir = path16.join(this.options.machineDir, "traces");
|
|
8934
9513
|
let names;
|
|
8935
9514
|
try {
|
|
8936
9515
|
names = await readdir3(traceDir);
|
|
@@ -8942,8 +9521,8 @@ var DaemonTraceBundleUploader = class {
|
|
|
8942
9521
|
const currentFile = this.options.currentFileProvider?.();
|
|
8943
9522
|
const candidates = [];
|
|
8944
9523
|
for (const name of names.filter((entry) => entry.startsWith("daemon-trace-") && entry.endsWith(".jsonl")).sort()) {
|
|
8945
|
-
const file =
|
|
8946
|
-
if (currentFile &&
|
|
9524
|
+
const file = path16.join(traceDir, name);
|
|
9525
|
+
if (currentFile && path16.resolve(file) === path16.resolve(currentFile)) continue;
|
|
8947
9526
|
if (await this.isUploaded(file)) continue;
|
|
8948
9527
|
try {
|
|
8949
9528
|
const info = await stat3(file);
|
|
@@ -9017,8 +9596,8 @@ var DaemonTraceBundleUploader = class {
|
|
|
9017
9596
|
}
|
|
9018
9597
|
}
|
|
9019
9598
|
uploadStatePath(file) {
|
|
9020
|
-
const stateDir =
|
|
9021
|
-
return
|
|
9599
|
+
const stateDir = path16.join(this.options.machineDir, "trace-uploads");
|
|
9600
|
+
return path16.join(stateDir, `${path16.basename(file)}.uploaded.json`);
|
|
9022
9601
|
}
|
|
9023
9602
|
async isUploaded(file) {
|
|
9024
9603
|
try {
|
|
@@ -9030,9 +9609,9 @@ var DaemonTraceBundleUploader = class {
|
|
|
9030
9609
|
}
|
|
9031
9610
|
async markUploaded(file, metadata) {
|
|
9032
9611
|
const stateFile = this.uploadStatePath(file);
|
|
9033
|
-
await mkdir2(
|
|
9612
|
+
await mkdir2(path16.dirname(stateFile), { recursive: true, mode: 448 });
|
|
9034
9613
|
await writeFile2(stateFile, `${JSON.stringify({
|
|
9035
|
-
file:
|
|
9614
|
+
file: path16.basename(file),
|
|
9036
9615
|
uploadedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9037
9616
|
...metadata
|
|
9038
9617
|
}, null, 2)}
|
|
@@ -9054,7 +9633,7 @@ var DEFAULT_TRACE_UPLOAD_URL = "https://slock-trace-upload.botiverse.dev";
|
|
|
9054
9633
|
var RUNNER_CREDENTIAL_SCOPES = ["send", "read", "mentions", "tasks", "reactions", "server", "channels"];
|
|
9055
9634
|
var RUNNER_CREDENTIAL_MINT_MAX_ATTEMPTS2 = 3;
|
|
9056
9635
|
var RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2 = 250;
|
|
9057
|
-
var DAEMON_CLI_USAGE =
|
|
9636
|
+
var DAEMON_CLI_USAGE = `Usage: slock-daemon --server-url <url> (--api-key <key> or ${DAEMON_API_KEY_ENV}=<key>)`;
|
|
9058
9637
|
var RunnerCredentialMintError2 = class extends Error {
|
|
9059
9638
|
code;
|
|
9060
9639
|
retryable;
|
|
@@ -9090,9 +9669,9 @@ function runnerCredentialErrorDetail2(error) {
|
|
|
9090
9669
|
async function waitForRunnerCredentialRetry2() {
|
|
9091
9670
|
await new Promise((resolve) => setTimeout(resolve, RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2));
|
|
9092
9671
|
}
|
|
9093
|
-
function parseDaemonCliArgs(args) {
|
|
9672
|
+
function parseDaemonCliArgs(args, env = {}) {
|
|
9094
9673
|
let serverUrl = "";
|
|
9095
|
-
let apiKey = "";
|
|
9674
|
+
let apiKey = env[DAEMON_API_KEY_ENV] ?? "";
|
|
9096
9675
|
for (let i = 0; i < args.length; i++) {
|
|
9097
9676
|
if (args[i] === "--server-url" && args[i + 1]) serverUrl = args[++i];
|
|
9098
9677
|
if (args[i] === "--api-key" && args[i + 1]) apiKey = args[++i];
|
|
@@ -9109,23 +9688,23 @@ function readDaemonVersion(moduleUrl = import.meta.url) {
|
|
|
9109
9688
|
}
|
|
9110
9689
|
}
|
|
9111
9690
|
function resolveChatBridgePath(moduleUrl = import.meta.url) {
|
|
9112
|
-
const dirname =
|
|
9113
|
-
const jsPath =
|
|
9691
|
+
const dirname = path17.dirname(fileURLToPath2(moduleUrl));
|
|
9692
|
+
const jsPath = path17.resolve(dirname, "chat-bridge.js");
|
|
9114
9693
|
try {
|
|
9115
9694
|
accessSync(jsPath);
|
|
9116
9695
|
return jsPath;
|
|
9117
9696
|
} catch {
|
|
9118
|
-
return
|
|
9697
|
+
return path17.resolve(dirname, "chat-bridge.ts");
|
|
9119
9698
|
}
|
|
9120
9699
|
}
|
|
9121
9700
|
function resolveSlockCliPath(moduleUrl = import.meta.url) {
|
|
9122
|
-
const thisDir =
|
|
9123
|
-
const bundledDistPath =
|
|
9701
|
+
const thisDir = path17.dirname(fileURLToPath2(moduleUrl));
|
|
9702
|
+
const bundledDistPath = path17.resolve(thisDir, "cli", "index.js");
|
|
9124
9703
|
try {
|
|
9125
9704
|
accessSync(bundledDistPath);
|
|
9126
9705
|
return bundledDistPath;
|
|
9127
9706
|
} catch {
|
|
9128
|
-
const workspaceDistPath =
|
|
9707
|
+
const workspaceDistPath = path17.resolve(thisDir, "..", "..", "cli", "dist", "index.js");
|
|
9129
9708
|
accessSync(workspaceDistPath);
|
|
9130
9709
|
return workspaceDistPath;
|
|
9131
9710
|
}
|
|
@@ -9304,7 +9883,7 @@ var DaemonCore = class {
|
|
|
9304
9883
|
}
|
|
9305
9884
|
resolveMachineStateRoot() {
|
|
9306
9885
|
if (this.options.machineStateDir) return this.options.machineStateDir;
|
|
9307
|
-
if (this.options.dataDir) return
|
|
9886
|
+
if (this.options.dataDir) return path17.join(path17.dirname(this.options.dataDir), "machines");
|
|
9308
9887
|
return resolveDefaultMachineStateRoot();
|
|
9309
9888
|
}
|
|
9310
9889
|
shouldEnableLocalTrace() {
|
|
@@ -9806,6 +10385,8 @@ var DaemonCore = class {
|
|
|
9806
10385
|
};
|
|
9807
10386
|
|
|
9808
10387
|
export {
|
|
10388
|
+
DAEMON_API_KEY_ENV,
|
|
10389
|
+
scrubDaemonAuthEnv,
|
|
9809
10390
|
resolveWorkspaceDirectoryPath,
|
|
9810
10391
|
scanWorkspaceDirectories,
|
|
9811
10392
|
deleteWorkspaceDirectory,
|