@4pm/cli 1.31.0 → 1.32.0-b
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +524 -131
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -28708,6 +28708,16 @@ var ErrorCode = {
|
|
|
28708
28708
|
WORKER_POOL_NAME_TAKEN: "WORKER_POOL_NAME_TAKEN",
|
|
28709
28709
|
WORKER_POOL_ATTACHED: "WORKER_POOL_ATTACHED",
|
|
28710
28710
|
WORKER_POOL_ALREADY_ATTACHED: "WORKER_POOL_ALREADY_ATTACHED",
|
|
28711
|
+
// A pool can't be both a project pool and the org AI pool (ADR-0376 mutual exclusivity).
|
|
28712
|
+
AI_POOL_CONFLICT: "AI_POOL_CONFLICT",
|
|
28713
|
+
// No org AI pool configured (Settings) for a project-less org AI task (ADR-0376).
|
|
28714
|
+
AI_POOL_NOT_CONFIGURED: "AI_POOL_NOT_CONFIGURED",
|
|
28715
|
+
// Change-request (ADR-0379) not found / already resolved.
|
|
28716
|
+
CHANGE_REQUEST_NOT_FOUND: "CHANGE_REQUEST_NOT_FOUND",
|
|
28717
|
+
CHANGE_REQUEST_ALREADY_RESOLVED: "CHANGE_REQUEST_ALREADY_RESOLVED",
|
|
28718
|
+
// org AI Research (ADR-0380): question refused by the content policy / research-guard; row not found.
|
|
28719
|
+
RESEARCH_REFUSED: "RESEARCH_REFUSED",
|
|
28720
|
+
RESEARCH_NOT_FOUND: "RESEARCH_NOT_FOUND",
|
|
28711
28721
|
MACHINE_ALREADY_IN_POOL: "MACHINE_ALREADY_IN_POOL",
|
|
28712
28722
|
NOT_MACHINE_USER: "NOT_MACHINE_USER",
|
|
28713
28723
|
// worker · physic project
|
|
@@ -29925,8 +29935,8 @@ async function runLink(serverUrl, explicitProfile, token = null) {
|
|
|
29925
29935
|
}
|
|
29926
29936
|
|
|
29927
29937
|
// src/commands/start.ts
|
|
29928
|
-
import { spawn as
|
|
29929
|
-
import { join as
|
|
29938
|
+
import { spawn as spawn12 } from "child_process";
|
|
29939
|
+
import { join as join43 } from "path";
|
|
29930
29940
|
|
|
29931
29941
|
// src/core/update.ts
|
|
29932
29942
|
import { execSync as execSync2 } from "child_process";
|
|
@@ -33145,7 +33155,7 @@ async function checkAndUpdate(serverUrl, autoUpdate) {
|
|
|
33145
33155
|
// src/core/ws-client.ts
|
|
33146
33156
|
init_wrapper();
|
|
33147
33157
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
33148
|
-
import { basename as basename7, join as
|
|
33158
|
+
import { basename as basename7, join as join38 } from "path";
|
|
33149
33159
|
import { existsSync as existsSync19, mkdirSync as mkdirSync9 } from "fs";
|
|
33150
33160
|
|
|
33151
33161
|
// vendor/@4pm/utils/helpers.ts
|
|
@@ -39010,6 +39020,20 @@ var WsChannels = {
|
|
|
39010
39020
|
// entries, commits, pushes a branch, opens a PR, then wipes the token/clone, and replies with the
|
|
39011
39021
|
// PR URL. The write token never persists on the worker (leak-safety on release/reassign).
|
|
39012
39022
|
FAQ_COMPOSE: "faq.compose",
|
|
39023
|
+
// Org-level "AI author checklist items" (ADR-0376): server (picks an idle cli of the org AI pool) →
|
|
39024
|
+
// cli request–reply. The server forwards a ready-built prompt; the worker runs claude one-shot (no
|
|
39025
|
+
// tools, empty dir — like support-answer) and replies with the raw output for the web to parse. No
|
|
39026
|
+
// project is involved (checklists are org-scoped).
|
|
39027
|
+
CHECKLIST_AUTHOR: "checklist.author",
|
|
39028
|
+
// Org-level "AI Research" (ADR-0380): server (picks an idle cli of the org AI pool) → cli
|
|
39029
|
+
// request–reply. The server forwards the user's question; the worker runs a one-shot research agent
|
|
39030
|
+
// (read-only web/search tools) under a content policy + a research-guard post-check, and replies with
|
|
39031
|
+
// the markdown answer (or a structured refusal) plus the run's token usage so the server meters it to
|
|
39032
|
+
// the org (projectId=null). No project is involved.
|
|
39033
|
+
RESEARCH_ASK: "research.ask",
|
|
39034
|
+
// Org AI Research live progress (ADR-0380): cli → server one-way stream of answer chunks / status for
|
|
39035
|
+
// a running research question, keyed by the query id, relayed to the web over SSE.
|
|
39036
|
+
RESEARCH_PROGRESS: "research.progress",
|
|
39013
39037
|
// Shared AI memory (ADR-0245): cli → server one-way write-back of the compacted rolling memory for
|
|
39014
39038
|
// the project it serves (persisted per project × machine-user link). The read direction rides
|
|
39015
39039
|
// `ws_token` (seeds the cli cache on connect); this channel only carries updates.
|
|
@@ -39961,7 +39985,8 @@ function planUnifiedRun(prompt, config, workingCredential, resume, oneShot, over
|
|
|
39961
39985
|
key,
|
|
39962
39986
|
args: buildRunArgs(cred, cmd, resume.get(key), oneShot, override, readOnly, bypass, [...allAiProfileDirs(config), dir2]),
|
|
39963
39987
|
stdin: prompt,
|
|
39964
|
-
env: envVar ? { ...baseEnv, [envVar]: dir2, ...overrideEnv(cmd, override) } : { ...baseEnv, ...overrideEnv(cmd, override) }
|
|
39988
|
+
env: envVar ? { ...baseEnv, [envVar]: dir2, ...overrideEnv(cmd, override) } : { ...baseEnv, ...overrideEnv(cmd, override) },
|
|
39989
|
+
...cred.apiKey ? { apiKey: cred.apiKey } : {}
|
|
39965
39990
|
}));
|
|
39966
39991
|
return { cmd: attempts[0]?.cmd ?? DEFAULT_AI_CLI, attempts };
|
|
39967
39992
|
}
|
|
@@ -39991,7 +40016,8 @@ function planLegacyRun(prompt, config, workingDir, resume, oneShot, override, re
|
|
|
39991
40016
|
key,
|
|
39992
40017
|
args: buildRunArgs(profile, cmd, resume.get(key), oneShot, override, readOnly, bypass, [...allAiProfileDirs(config), dir2]),
|
|
39993
40018
|
stdin: prompt,
|
|
39994
|
-
env: { ...baseEnv, [envVar]: dir2, ...overrideEnv(cmd, override) }
|
|
40019
|
+
env: { ...baseEnv, [envVar]: dir2, ...overrideEnv(cmd, override) },
|
|
40020
|
+
...profile.apiKey ? { apiKey: profile.apiKey } : {}
|
|
39995
40021
|
};
|
|
39996
40022
|
});
|
|
39997
40023
|
return { cmd, attempts };
|
|
@@ -40807,6 +40833,16 @@ async function runClaudeWithFailover(prompt, ai2, imageDir, timeoutMs = ANSWER_T
|
|
|
40807
40833
|
}
|
|
40808
40834
|
throw new Error(`claude ${lastReason}`);
|
|
40809
40835
|
}
|
|
40836
|
+
async function runChecklistAuthor(req, ai2) {
|
|
40837
|
+
try {
|
|
40838
|
+
const { text, usage } = await runClaudeWithFailover(req.prompt, ai2);
|
|
40839
|
+
if (!text) return { reply: { error: "empty output" }, usage };
|
|
40840
|
+
return { reply: { output: text }, usage };
|
|
40841
|
+
} catch (err) {
|
|
40842
|
+
logger.warn("checklist.author.failed", { error: String(err) });
|
|
40843
|
+
return { reply: { error: String(err) }, usage: { ...NO_USAGE } };
|
|
40844
|
+
}
|
|
40845
|
+
}
|
|
40810
40846
|
async function runSupportAnswer(req, ai2, profileDir2) {
|
|
40811
40847
|
let imageDir;
|
|
40812
40848
|
let question = req.question;
|
|
@@ -42211,6 +42247,58 @@ async function runCommand(dispatch, emitRaw, opts) {
|
|
|
42211
42247
|
});
|
|
42212
42248
|
}
|
|
42213
42249
|
|
|
42250
|
+
// src/core/resolve-api-key.ts
|
|
42251
|
+
var cache = /* @__PURE__ */ new Map();
|
|
42252
|
+
async function loadAws(pkg) {
|
|
42253
|
+
const spec = pkg;
|
|
42254
|
+
return await import(spec);
|
|
42255
|
+
}
|
|
42256
|
+
async function fetchSsm(d) {
|
|
42257
|
+
const aws = await loadAws("@aws-sdk/client-ssm");
|
|
42258
|
+
const SSMClient = aws.SSMClient;
|
|
42259
|
+
const GetParameterCommand = aws.GetParameterCommand;
|
|
42260
|
+
const client = new SSMClient(d.region ? { region: d.region } : {});
|
|
42261
|
+
const res = await client.send(new GetParameterCommand({ Name: d.name, WithDecryption: true }));
|
|
42262
|
+
return res.Parameter?.Value?.trim() || null;
|
|
42263
|
+
}
|
|
42264
|
+
async function fetchSecretsManager(d) {
|
|
42265
|
+
const aws = await loadAws("@aws-sdk/client-secrets-manager");
|
|
42266
|
+
const SecretsManagerClient = aws.SecretsManagerClient;
|
|
42267
|
+
const GetSecretValueCommand = aws.GetSecretValueCommand;
|
|
42268
|
+
const client = new SecretsManagerClient(d.region ? { region: d.region } : {});
|
|
42269
|
+
const res = await client.send(new GetSecretValueCommand({ SecretId: d.secretId }));
|
|
42270
|
+
const raw = (res.SecretString ?? "").trim();
|
|
42271
|
+
if (!raw) return null;
|
|
42272
|
+
if (d.jsonKey) {
|
|
42273
|
+
try {
|
|
42274
|
+
const obj = JSON.parse(raw);
|
|
42275
|
+
const v2 = obj[d.jsonKey];
|
|
42276
|
+
return typeof v2 === "string" && v2.trim() ? v2.trim() : null;
|
|
42277
|
+
} catch {
|
|
42278
|
+
return null;
|
|
42279
|
+
}
|
|
42280
|
+
}
|
|
42281
|
+
return raw;
|
|
42282
|
+
}
|
|
42283
|
+
async function resolveApiKey(desc) {
|
|
42284
|
+
if (!desc || !desc.source) return null;
|
|
42285
|
+
if (desc.source === "inline") return desc.value?.trim() || null;
|
|
42286
|
+
const cacheKey = JSON.stringify(desc);
|
|
42287
|
+
const hit = cache.get(cacheKey);
|
|
42288
|
+
if (hit) return hit;
|
|
42289
|
+
try {
|
|
42290
|
+
const val = desc.source === "ssm" ? await fetchSsm(desc) : desc.source === "secretsManager" ? await fetchSecretsManager(desc) : null;
|
|
42291
|
+
if (val) {
|
|
42292
|
+
cache.set(cacheKey, val);
|
|
42293
|
+
return val;
|
|
42294
|
+
}
|
|
42295
|
+
return null;
|
|
42296
|
+
} catch (err) {
|
|
42297
|
+
logger.warn("ai.apiKey.resolve.failed", { source: desc.source, error: String(err) });
|
|
42298
|
+
return null;
|
|
42299
|
+
}
|
|
42300
|
+
}
|
|
42301
|
+
|
|
42214
42302
|
// src/core/ai-runner.ts
|
|
42215
42303
|
var TIMEOUT_EXIT = 124;
|
|
42216
42304
|
function isAuthFailure(text) {
|
|
@@ -42259,7 +42347,9 @@ async function runAiFailover(plan, commandId, cwd2, handlers, timeoutMs = 0, sig
|
|
|
42259
42347
|
handlers.onChunk(text);
|
|
42260
42348
|
}
|
|
42261
42349
|
};
|
|
42262
|
-
|
|
42350
|
+
const resolvedKey = attempt.apiKey ? await resolveApiKey(attempt.apiKey) : null;
|
|
42351
|
+
const attemptEnv = resolvedKey ? { ...attempt.env, ANTHROPIC_API_KEY: resolvedKey } : attempt.env;
|
|
42352
|
+
finalExit = await runAttempt(commandId, attempt.cmd, attempt.args, attempt.stdin, cwd2, attemptEnv, emit, timeoutMs, signal);
|
|
42263
42353
|
const tail = parser.flush();
|
|
42264
42354
|
if (tail) {
|
|
42265
42355
|
captured += tail;
|
|
@@ -42941,10 +43031,10 @@ function activeAiRunCount() {
|
|
|
42941
43031
|
return activeRuns.size;
|
|
42942
43032
|
}
|
|
42943
43033
|
function cancelActiveRun(commandId) {
|
|
42944
|
-
const
|
|
42945
|
-
if (!
|
|
42946
|
-
|
|
42947
|
-
return { ok: true, state:
|
|
43034
|
+
const run16 = activeRuns.get(commandId);
|
|
43035
|
+
if (!run16) return { ok: false, state: "unknown" };
|
|
43036
|
+
run16.controller.abort();
|
|
43037
|
+
return { ok: true, state: run16.state };
|
|
42948
43038
|
}
|
|
42949
43039
|
function handleCommandChannels(ctx, message, payload) {
|
|
42950
43040
|
if (message.channel === WsChannels.COMMAND_CANCEL) {
|
|
@@ -43042,16 +43132,16 @@ async function runLocalCommand(ctx, input) {
|
|
|
43042
43132
|
}
|
|
43043
43133
|
var REFUSED = { exitCode: 1, cancelled: false, exhausted: false, neverStarted: true, output: "" };
|
|
43044
43134
|
async function runAiPrompt(ctx, prompt, commandId, origin, oneShot = false, images, aiConfig, readOnly = false, bypass = false) {
|
|
43045
|
-
const
|
|
43046
|
-
activeRuns.set(commandId,
|
|
43135
|
+
const run16 = { controller: new AbortController(), state: "queued" };
|
|
43136
|
+
activeRuns.set(commandId, run16);
|
|
43047
43137
|
try {
|
|
43048
|
-
return await runAiPromptInner(ctx, prompt, commandId, origin,
|
|
43138
|
+
return await runAiPromptInner(ctx, prompt, commandId, origin, run16, oneShot, images, aiConfig, readOnly, bypass);
|
|
43049
43139
|
} finally {
|
|
43050
43140
|
activeRuns.delete(commandId);
|
|
43051
43141
|
if (!readOnly) requestGitSnapshot("ai-run");
|
|
43052
43142
|
}
|
|
43053
43143
|
}
|
|
43054
|
-
async function runAiPromptInner(ctx, prompt, commandId, origin,
|
|
43144
|
+
async function runAiPromptInner(ctx, prompt, commandId, origin, run16, oneShot = false, images, aiConfig, readOnly = false, bypass = false) {
|
|
43055
43145
|
const config = readProfileConfig(ctx.profileDir);
|
|
43056
43146
|
const projectTimeoutSec = config.projectAiRunTimeoutSec ?? 0;
|
|
43057
43147
|
const machineTimeoutSec = config.aiRunTimeoutSec ?? 0;
|
|
@@ -43234,7 +43324,7 @@ ${guardedPrompt}`;
|
|
|
43234
43324
|
queue: { position, limit, running: running2 }
|
|
43235
43325
|
});
|
|
43236
43326
|
},
|
|
43237
|
-
|
|
43327
|
+
run16.controller.signal
|
|
43238
43328
|
);
|
|
43239
43329
|
if (slot.kind === "timeout") {
|
|
43240
43330
|
neverStarted = true;
|
|
@@ -43255,9 +43345,9 @@ ${guardedPrompt}`;
|
|
|
43255
43345
|
result = { exitCode: COMMAND_CANCELLED_EXIT_CODE, workedDir: null, workedKey: null, workedCmd: null, usage: { tokens: 0, input: 0, output: 0, cacheRead: 0, cacheCreation: 0 }, sessionId: "", cancelled: true };
|
|
43256
43346
|
} else {
|
|
43257
43347
|
if (slot.queued) ctx.send(WsChannels.COMMAND_OUTPUT, { commandId, seq: seq++, chunk: "", log: true, queue: null });
|
|
43258
|
-
|
|
43348
|
+
run16.state = "running";
|
|
43259
43349
|
try {
|
|
43260
|
-
result = await runAiFailover(plan, commandId, cwd2, handlers, aiRunTimeoutMs,
|
|
43350
|
+
result = await runAiFailover(plan, commandId, cwd2, handlers, aiRunTimeoutMs, run16.controller.signal);
|
|
43261
43351
|
} finally {
|
|
43262
43352
|
slot.handle.release();
|
|
43263
43353
|
}
|
|
@@ -45509,8 +45599,258 @@ function applyGitAuth(ctx, method, host) {
|
|
|
45509
45599
|
);
|
|
45510
45600
|
}
|
|
45511
45601
|
|
|
45512
|
-
// src/core/
|
|
45602
|
+
// src/core/research-run.ts
|
|
45513
45603
|
import { spawn as spawn8 } from "child_process";
|
|
45604
|
+
import { mkdtempSync as mkdtempSync3, rmSync as rmSync8 } from "fs";
|
|
45605
|
+
import { tmpdir as tmpdir5 } from "os";
|
|
45606
|
+
import { join as join29 } from "path";
|
|
45607
|
+
var RESEARCH_TIMEOUT_MS = 9 * 60 * 1e3;
|
|
45608
|
+
var RESEARCH_MAX_TURNS = 16;
|
|
45609
|
+
var NO_USAGE2 = { tokens: 0, input: 0, output: 0, cacheRead: 0, cacheCreation: 0 };
|
|
45610
|
+
var RESEARCH_ALLOWED_TOOLS = "WebSearch,WebFetch";
|
|
45611
|
+
var RESEARCH_DENIED_TOOLS = [
|
|
45612
|
+
"Bash",
|
|
45613
|
+
"Edit",
|
|
45614
|
+
"Write",
|
|
45615
|
+
"Read",
|
|
45616
|
+
"Glob",
|
|
45617
|
+
"Grep",
|
|
45618
|
+
"NotebookEdit",
|
|
45619
|
+
"Task",
|
|
45620
|
+
"SlashCommand",
|
|
45621
|
+
"Skill",
|
|
45622
|
+
"ToolSearch",
|
|
45623
|
+
"TaskCreate",
|
|
45624
|
+
"TaskGet",
|
|
45625
|
+
"TaskList",
|
|
45626
|
+
"TaskOutput",
|
|
45627
|
+
"TaskStop",
|
|
45628
|
+
"TaskUpdate",
|
|
45629
|
+
"Monitor",
|
|
45630
|
+
"DesignSync",
|
|
45631
|
+
"CronCreate",
|
|
45632
|
+
"CronDelete",
|
|
45633
|
+
"CronList",
|
|
45634
|
+
"EnterWorktree",
|
|
45635
|
+
"ExitWorktree",
|
|
45636
|
+
"RemoteTrigger",
|
|
45637
|
+
"ScheduleWakeup",
|
|
45638
|
+
"SendMessage",
|
|
45639
|
+
"PushNotification"
|
|
45640
|
+
].join(",");
|
|
45641
|
+
function buildResearchPrompt(question) {
|
|
45642
|
+
return [
|
|
45643
|
+
"You are a research assistant for the 4PM platform. Answer the user's question thoroughly and",
|
|
45644
|
+
"accurately, in Markdown. Use the read-only web tools (WebSearch / WebFetch) to find current, factual",
|
|
45645
|
+
"information when helpful, and cite sources inline where relevant.",
|
|
45646
|
+
"",
|
|
45647
|
+
"STRICT CONTENT POLICY \u2014 you MUST refuse, and must not answer, if the question (or a faithful answer",
|
|
45648
|
+
"to it) would involve any of:",
|
|
45649
|
+
"- sexual, abusive, hateful, or illegal content;",
|
|
45650
|
+
"- political persuasion, campaigning, or partisan advocacy;",
|
|
45651
|
+
"- discrimination or demeaning content targeting a race, ethnicity, religion, gender, nationality, or",
|
|
45652
|
+
" other protected group;",
|
|
45653
|
+
"- probing, scanning, exploiting, or attacking the security of ANY system, network, account, device,",
|
|
45654
|
+
" or person you do not own (no vulnerability hunting, no exploit/malware creation, no credential or",
|
|
45655
|
+
" PII harvesting, no evasion of security controls). General defensive-security education is allowed.",
|
|
45656
|
+
"- any other seriously harmful activity.",
|
|
45657
|
+
"",
|
|
45658
|
+
'When you must refuse, reply with EXACTLY one line starting with "REFUSED:" followed by a short,',
|
|
45659
|
+
"neutral reason, and output nothing else.",
|
|
45660
|
+
"",
|
|
45661
|
+
"You have READ-ONLY web tools only. Never attempt to read local files, run shell commands, or interact",
|
|
45662
|
+
"with other systems.",
|
|
45663
|
+
"",
|
|
45664
|
+
"---",
|
|
45665
|
+
"",
|
|
45666
|
+
"Question:",
|
|
45667
|
+
question
|
|
45668
|
+
].join("\n");
|
|
45669
|
+
}
|
|
45670
|
+
function runClaudeOnce2(cmd, profile, prompt, extraEnv, onChunk, denyDirs) {
|
|
45671
|
+
return new Promise((resolve13, reject) => {
|
|
45672
|
+
const isClaude = cmd.includes("claude");
|
|
45673
|
+
const args = [
|
|
45674
|
+
"-p",
|
|
45675
|
+
...profile?.model ? ["--model", profile.model] : [],
|
|
45676
|
+
...isClaude ? ["--output-format", "stream-json", "--verbose"] : [],
|
|
45677
|
+
// Secret-path deny rules (ADR-0347) — reads are not confined to the working dir.
|
|
45678
|
+
...denySettingsArgs(cmd, [...denyDirs, profile?.dir]),
|
|
45679
|
+
// Web-only agent: allow WebSearch/WebFetch (auto-approved), deny everything else, bound the loop.
|
|
45680
|
+
// `--permission-mode default` is the non-variadic terminator (so the variadic tool flags consume
|
|
45681
|
+
// only their own comma-token — same trick as the one-shot/read-only runners).
|
|
45682
|
+
...isClaude ? [
|
|
45683
|
+
`--max-turns=${RESEARCH_MAX_TURNS}`,
|
|
45684
|
+
"--allowedTools",
|
|
45685
|
+
RESEARCH_ALLOWED_TOOLS,
|
|
45686
|
+
"--disallowedTools",
|
|
45687
|
+
RESEARCH_DENIED_TOOLS,
|
|
45688
|
+
"--permission-mode",
|
|
45689
|
+
"default"
|
|
45690
|
+
] : []
|
|
45691
|
+
];
|
|
45692
|
+
const env3 = agentEnv(extraEnv, profile ? { CLAUDE_CONFIG_DIR: profile.dir } : void 0);
|
|
45693
|
+
const cwd2 = mkdtempSync3(join29(tmpdir5(), "4pm-research-"));
|
|
45694
|
+
const cleanup = () => {
|
|
45695
|
+
try {
|
|
45696
|
+
rmSync8(cwd2, { recursive: true, force: true });
|
|
45697
|
+
} catch {
|
|
45698
|
+
}
|
|
45699
|
+
};
|
|
45700
|
+
const child = spawn8(cmd, args, { stdio: ["pipe", "pipe", "pipe"], env: env3, cwd: cwd2 });
|
|
45701
|
+
const parser = createAiStreamParser(cmd);
|
|
45702
|
+
let out = "";
|
|
45703
|
+
let err = "";
|
|
45704
|
+
const timer = setTimeout(() => {
|
|
45705
|
+
child.kill("SIGKILL");
|
|
45706
|
+
reject(new Error("research run timed out"));
|
|
45707
|
+
}, RESEARCH_TIMEOUT_MS);
|
|
45708
|
+
timer.unref();
|
|
45709
|
+
child.stdout.on("data", (d) => {
|
|
45710
|
+
const delta = parser.push(d.toString());
|
|
45711
|
+
if (delta) {
|
|
45712
|
+
out += delta;
|
|
45713
|
+
onChunk(delta);
|
|
45714
|
+
}
|
|
45715
|
+
});
|
|
45716
|
+
child.stderr.on("data", (d) => err += d.toString());
|
|
45717
|
+
child.on("error", (e) => {
|
|
45718
|
+
clearTimeout(timer);
|
|
45719
|
+
cleanup();
|
|
45720
|
+
reject(e);
|
|
45721
|
+
});
|
|
45722
|
+
child.on("close", (code) => {
|
|
45723
|
+
clearTimeout(timer);
|
|
45724
|
+
cleanup();
|
|
45725
|
+
const tail = parser.flush();
|
|
45726
|
+
if (tail) {
|
|
45727
|
+
out += tail;
|
|
45728
|
+
onChunk(tail);
|
|
45729
|
+
}
|
|
45730
|
+
resolve13({ code: code ?? -1, out, err, usage: parser.usage() });
|
|
45731
|
+
});
|
|
45732
|
+
child.stdin.write(prompt);
|
|
45733
|
+
child.stdin.end();
|
|
45734
|
+
});
|
|
45735
|
+
}
|
|
45736
|
+
async function runResearch(question, ai2, onChunk) {
|
|
45737
|
+
const prompt = buildResearchPrompt(question);
|
|
45738
|
+
const denyDirs = ai2.profiles.map((p2) => p2.dir);
|
|
45739
|
+
const attempts = ai2.profiles.length > 0 ? ai2.profiles : [null];
|
|
45740
|
+
let lastReason = "no attempt";
|
|
45741
|
+
for (let i = 0; i < attempts.length; i++) {
|
|
45742
|
+
try {
|
|
45743
|
+
const { code, out, err, usage } = await runClaudeOnce2(ai2.cmd, attempts[i], prompt, ai2.env, onChunk, denyDirs);
|
|
45744
|
+
if (code === 0 && out.trim()) {
|
|
45745
|
+
const u2 = usage.tokens > 0 ? usage : { ...NO_USAGE2, tokens: estimateTokens(out) };
|
|
45746
|
+
return { text: out.trim(), usage: u2 };
|
|
45747
|
+
}
|
|
45748
|
+
lastReason = `exited ${code}: ${(err || out).slice(0, 300)}`;
|
|
45749
|
+
} catch (e) {
|
|
45750
|
+
lastReason = e instanceof Error ? e.message : String(e);
|
|
45751
|
+
}
|
|
45752
|
+
}
|
|
45753
|
+
return { text: "", usage: { ...NO_USAGE2 }, error: lastReason };
|
|
45754
|
+
}
|
|
45755
|
+
|
|
45756
|
+
// src/core/research-guard.ts
|
|
45757
|
+
var REFUSAL_PREFIX = "REFUSED:";
|
|
45758
|
+
var ATTACK_INTENT = [
|
|
45759
|
+
/\b(hack|break|breare|breach|compromise|exploit|gain (unauthorized )?access to|take over|take control of|ddos|dos attack)\b[^.?!]{0,60}\b(into|someone|their|his|her|another|the victim'?s|a target|account|server|network|website|wifi|camera|phone|system|database)\b/i,
|
|
45760
|
+
/\b(write|create|build|generate|give me)\b[^.?!]{0,40}\b(malware|ransomware|a virus|a keylogger|a trojan|spyware|a rootkit|a worm|a botnet|an exploit|a phishing (kit|page|email|site))\b/i,
|
|
45761
|
+
/\bbypass\b[^.?!]{0,40}\b(authentication|login|the firewall|a paywall|security|2fa|mfa|licen[sc]e|drm)\b/i,
|
|
45762
|
+
/\b(steal|harvest|exfiltrate|dump|crack)\b[^.?!]{0,40}\b(credentials|passwords|password hashes|credit cards?|personal data|pii|session tokens)\b/i,
|
|
45763
|
+
/\bbrute[- ]?force\b[^.?!]{0,40}\b(login|password|account|credential)/i
|
|
45764
|
+
];
|
|
45765
|
+
function isModelRefusal(text) {
|
|
45766
|
+
return text.trimStart().toUpperCase().startsWith(REFUSAL_PREFIX);
|
|
45767
|
+
}
|
|
45768
|
+
function refusalReasonOf(text) {
|
|
45769
|
+
const after = text.trim().slice(REFUSAL_PREFIX.length).trim();
|
|
45770
|
+
return (after || "This question is outside what Research can answer.").slice(0, 300);
|
|
45771
|
+
}
|
|
45772
|
+
function screenQuestion(question) {
|
|
45773
|
+
if (ATTACK_INTENT.some((re2) => re2.test(question))) {
|
|
45774
|
+
return { blocked: true, reason: "The question asks to attack or compromise a system, which Research does not do." };
|
|
45775
|
+
}
|
|
45776
|
+
return { blocked: false };
|
|
45777
|
+
}
|
|
45778
|
+
function screenAnswer(answer) {
|
|
45779
|
+
if (isModelRefusal(answer)) return { blocked: true, reason: refusalReasonOf(answer) };
|
|
45780
|
+
if (ATTACK_INTENT.some((re2) => re2.test(answer))) {
|
|
45781
|
+
return { blocked: true, reason: "The answer was withheld by the content policy." };
|
|
45782
|
+
}
|
|
45783
|
+
return { blocked: false };
|
|
45784
|
+
}
|
|
45785
|
+
|
|
45786
|
+
// src/core/ws-client/handlers/research.ts
|
|
45787
|
+
function handleResearchChannels(ctx, message, payload) {
|
|
45788
|
+
if (message.channel !== WsChannels.RESEARCH_ASK) return false;
|
|
45789
|
+
const req = payload;
|
|
45790
|
+
const config = readProfileConfig(ctx.profileDir);
|
|
45791
|
+
const cmd = config.aiCli || "claude";
|
|
45792
|
+
ctx.bus.push({ source: "server", kind: "aireq", text: `${cmd} \u2039 research` });
|
|
45793
|
+
ctx.bus.startBusy("research");
|
|
45794
|
+
void run14(ctx, message, req, config, cmd).finally(() => ctx.bus.endBusy("research"));
|
|
45795
|
+
return true;
|
|
45796
|
+
}
|
|
45797
|
+
async function run14(ctx, message, req, config, cmd) {
|
|
45798
|
+
const emit = (p2) => ctx.send(WsChannels.RESEARCH_PROGRESS, { queryId: req.queryId, ...p2 });
|
|
45799
|
+
const reply = (r) => ctx.send(WsChannels.RESEARCH_ASK, r, message.id);
|
|
45800
|
+
const pre = screenQuestion(req.question);
|
|
45801
|
+
if (pre.blocked) {
|
|
45802
|
+
reply({ refused: true, refusalReason: pre.reason });
|
|
45803
|
+
return;
|
|
45804
|
+
}
|
|
45805
|
+
const slot = await acquireRunSlot(ctx, (info) => emit({ message: `queued #${info.position}` }));
|
|
45806
|
+
if (slot.kind !== "granted") {
|
|
45807
|
+
reply({ error: slot.kind === "denied" ? "storage full" : "no idle run slot" });
|
|
45808
|
+
return;
|
|
45809
|
+
}
|
|
45810
|
+
try {
|
|
45811
|
+
const ai2 = {
|
|
45812
|
+
cmd,
|
|
45813
|
+
profiles: resolveClaudeProfiles(config, getWorkingProfile(ctx.profileDir, cmd)),
|
|
45814
|
+
env: config.aiEnv
|
|
45815
|
+
};
|
|
45816
|
+
const result = await runResearch(req.question, ai2, (delta) => emit({ chunk: delta }));
|
|
45817
|
+
const occurredAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
45818
|
+
const events = [{ metric: UsageMetric.COMMANDS, amount: 1, occurredAt }];
|
|
45819
|
+
if (result.usage.tokens > 0) {
|
|
45820
|
+
events.push({
|
|
45821
|
+
metric: UsageMetric.AI_TOKENS,
|
|
45822
|
+
amount: result.usage.tokens,
|
|
45823
|
+
occurredAt,
|
|
45824
|
+
inputTokens: result.usage.input,
|
|
45825
|
+
outputTokens: result.usage.output,
|
|
45826
|
+
cacheReadTokens: result.usage.cacheRead,
|
|
45827
|
+
cacheCreationTokens: result.usage.cacheCreation,
|
|
45828
|
+
provider: aiProviderOf(cmd),
|
|
45829
|
+
authMode: resolveClaudeAuthMode(config)
|
|
45830
|
+
});
|
|
45831
|
+
}
|
|
45832
|
+
ctx.reportUsage(events, null);
|
|
45833
|
+
if (result.error || !result.text) {
|
|
45834
|
+
ctx.bus.push({ source: "server", kind: "log", text: `research failed: ${result.error ?? "empty"}`, level: "warn" });
|
|
45835
|
+
reply({ error: result.error || "empty answer", tokens: result.usage.tokens });
|
|
45836
|
+
return;
|
|
45837
|
+
}
|
|
45838
|
+
const post2 = screenAnswer(result.text);
|
|
45839
|
+
if (post2.blocked) {
|
|
45840
|
+
reply({ refused: true, refusalReason: post2.reason, tokens: result.usage.tokens });
|
|
45841
|
+
return;
|
|
45842
|
+
}
|
|
45843
|
+
ctx.bus.push({ source: "server", kind: "aires", text: `${cmd} \u203A` });
|
|
45844
|
+
reply({ answer: result.text, tokens: result.usage.tokens });
|
|
45845
|
+
} catch (err) {
|
|
45846
|
+
reply({ error: err instanceof Error ? err.message : String(err) });
|
|
45847
|
+
} finally {
|
|
45848
|
+
slot.handle.release();
|
|
45849
|
+
}
|
|
45850
|
+
}
|
|
45851
|
+
|
|
45852
|
+
// src/core/knowledge-compose.ts
|
|
45853
|
+
import { spawn as spawn9 } from "child_process";
|
|
45514
45854
|
var COMPOSE_TIMEOUT_MS = 18e4;
|
|
45515
45855
|
function buildPrompt3(req) {
|
|
45516
45856
|
const focus = req.prompt ? `
|
|
@@ -45536,7 +45876,7 @@ function runOnce2(cmd, profile, prompt, cwd2, extraEnv, denyDirs = []) {
|
|
|
45536
45876
|
...extraEnv,
|
|
45537
45877
|
...profile ? { CLAUDE_CONFIG_DIR: profile.dir } : {}
|
|
45538
45878
|
};
|
|
45539
|
-
const child =
|
|
45879
|
+
const child = spawn9(cmd, args, { stdio: ["pipe", "pipe", "pipe"], cwd: cwd2, env: env3 });
|
|
45540
45880
|
let out = "";
|
|
45541
45881
|
let err = "";
|
|
45542
45882
|
const timer = setTimeout(() => {
|
|
@@ -45582,14 +45922,14 @@ async function runKnowledgeCompose(req, ai2, physicRoot) {
|
|
|
45582
45922
|
}
|
|
45583
45923
|
|
|
45584
45924
|
// src/core/faq-compose.ts
|
|
45585
|
-
import { spawn as
|
|
45586
|
-
import { existsSync as existsSync16, mkdirSync as mkdirSync8, rmSync as
|
|
45925
|
+
import { spawn as spawn10, execFile as execFile14 } from "child_process";
|
|
45926
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync8, rmSync as rmSync9 } from "fs";
|
|
45587
45927
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
45588
|
-
import { join as
|
|
45589
|
-
import { tmpdir as
|
|
45928
|
+
import { join as join30 } from "path";
|
|
45929
|
+
import { tmpdir as tmpdir6 } from "os";
|
|
45590
45930
|
import { promisify as promisify14 } from "util";
|
|
45591
45931
|
var execFileAsync2 = promisify14(execFile14);
|
|
45592
|
-
var
|
|
45932
|
+
var NO_USAGE3 = { tokens: 0, input: 0, output: 0, cacheRead: 0, cacheCreation: 0 };
|
|
45593
45933
|
var COMPOSE_TIMEOUT_MS2 = 10 * 60 * 1e3;
|
|
45594
45934
|
var AGENT_DENIED_TOOLS = ["Bash", "WebFetch", "WebSearch", "NotebookEdit", "Task"];
|
|
45595
45935
|
function buildPrompt4(tickets, customPrompt) {
|
|
@@ -45652,7 +45992,7 @@ function parsePerTicket(text) {
|
|
|
45652
45992
|
}
|
|
45653
45993
|
return [];
|
|
45654
45994
|
}
|
|
45655
|
-
function
|
|
45995
|
+
function runClaudeOnce3(cmd, profile, prompt, cwd2, extraEnv, denyDirs = []) {
|
|
45656
45996
|
return new Promise((resolve13, reject) => {
|
|
45657
45997
|
const isClaude = cmd.includes("claude");
|
|
45658
45998
|
const args = [
|
|
@@ -45668,7 +46008,7 @@ function runClaudeOnce2(cmd, profile, prompt, cwd2, extraEnv, denyDirs = []) {
|
|
|
45668
46008
|
...cmd.includes("codex") ? ["--sandbox", "workspace-write"] : []
|
|
45669
46009
|
];
|
|
45670
46010
|
const env3 = agentEnv(extraEnv, profile ? { CLAUDE_CONFIG_DIR: profile.dir } : void 0);
|
|
45671
|
-
const child =
|
|
46011
|
+
const child = spawn10(cmd, args, { stdio: ["pipe", "pipe", "pipe"], cwd: cwd2, env: env3 });
|
|
45672
46012
|
const parser = createAiStreamParser(cmd);
|
|
45673
46013
|
let out = "";
|
|
45674
46014
|
let err = "";
|
|
@@ -45696,7 +46036,7 @@ async function runClaudeWithFailover2(prompt, cwd2, ai2) {
|
|
|
45696
46036
|
const attempts = ai2.profiles.length > 0 ? ai2.profiles : [null];
|
|
45697
46037
|
let lastReason = "no attempt";
|
|
45698
46038
|
for (let i = 0; i < attempts.length; i++) {
|
|
45699
|
-
const { code, out, err, usage } = await
|
|
46039
|
+
const { code, out, err, usage } = await runClaudeOnce3(ai2.cmd, attempts[i], prompt, cwd2, ai2.env, ai2.profiles.map((p2) => p2.dir));
|
|
45700
46040
|
if (code === 0) return { usage, text: out.trim() };
|
|
45701
46041
|
lastReason = `exited ${code}: ${(err || out).slice(0, 500)}`;
|
|
45702
46042
|
if (i === attempts.length - 1) break;
|
|
@@ -45706,7 +46046,7 @@ async function runClaudeWithFailover2(prompt, cwd2, ai2) {
|
|
|
45706
46046
|
var MAX_OUTPUT_CHARS = 8e3;
|
|
45707
46047
|
async function runFaqCompose(req, ai2) {
|
|
45708
46048
|
if (!req.repo.token) return { prUrl: "", error: "no write token" };
|
|
45709
|
-
const workDir =
|
|
46049
|
+
const workDir = join30(tmpdir6(), "4pm-faq-sync", randomUUID3());
|
|
45710
46050
|
const gitEnv = { ...process.env, GIT_TERMINAL_PROMPT: "0" };
|
|
45711
46051
|
const auth = gitAuthArgs("x-access-token", req.repo.token);
|
|
45712
46052
|
try {
|
|
@@ -45736,7 +46076,7 @@ async function runFaqCompose(req, ai2) {
|
|
|
45736
46076
|
} catch (err) {
|
|
45737
46077
|
logger.warn("faq.compose.pr.failed", { error: String(err) });
|
|
45738
46078
|
}
|
|
45739
|
-
const u2 = usage.tokens > 0 ? usage : { ...
|
|
46079
|
+
const u2 = usage.tokens > 0 ? usage : { ...NO_USAGE3 };
|
|
45740
46080
|
return {
|
|
45741
46081
|
prUrl,
|
|
45742
46082
|
branch: req.headBranch,
|
|
@@ -45752,7 +46092,7 @@ async function runFaqCompose(req, ai2) {
|
|
|
45752
46092
|
} finally {
|
|
45753
46093
|
if (existsSync16(workDir)) {
|
|
45754
46094
|
try {
|
|
45755
|
-
|
|
46095
|
+
rmSync9(workDir, { recursive: true, force: true });
|
|
45756
46096
|
} catch {
|
|
45757
46097
|
}
|
|
45758
46098
|
}
|
|
@@ -45837,6 +46177,58 @@ function handleSupportChannels(ctx, message, payload) {
|
|
|
45837
46177
|
}).finally(() => ctx.bus.endBusy("faq-compose"));
|
|
45838
46178
|
return true;
|
|
45839
46179
|
}
|
|
46180
|
+
case WsChannels.CHECKLIST_AUTHOR: {
|
|
46181
|
+
const creq = payload;
|
|
46182
|
+
const ccfg = readProfileConfig(ctx.profileDir);
|
|
46183
|
+
const ccmd = ccfg.aiCli || "claude";
|
|
46184
|
+
const cai = {
|
|
46185
|
+
cmd: ccmd,
|
|
46186
|
+
profiles: resolveClaudeProfiles(ccfg, getWorkingProfile(ctx.profileDir, ccmd)),
|
|
46187
|
+
env: ccfg.aiEnv
|
|
46188
|
+
};
|
|
46189
|
+
ctx.bus.push({ source: "server", kind: "aireq", text: `${ccmd} \u2039 author checklist items` });
|
|
46190
|
+
ctx.bus.startBusy("checklist-author");
|
|
46191
|
+
void (async () => {
|
|
46192
|
+
const slot = await acquireRunSlot(ctx, () => void 0);
|
|
46193
|
+
if (slot.kind !== "granted") {
|
|
46194
|
+
ctx.send(
|
|
46195
|
+
WsChannels.CHECKLIST_AUTHOR,
|
|
46196
|
+
{ error: slot.kind === "denied" ? "storage full" : "no idle run slot" },
|
|
46197
|
+
message.id
|
|
46198
|
+
);
|
|
46199
|
+
return;
|
|
46200
|
+
}
|
|
46201
|
+
try {
|
|
46202
|
+
const { reply, usage } = await runChecklistAuthor(creq, cai);
|
|
46203
|
+
const occurredAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
46204
|
+
const events = [{ metric: UsageMetric.COMMANDS, amount: 1, occurredAt }];
|
|
46205
|
+
if (usage.tokens > 0) {
|
|
46206
|
+
events.push({
|
|
46207
|
+
metric: UsageMetric.AI_TOKENS,
|
|
46208
|
+
amount: usage.tokens,
|
|
46209
|
+
occurredAt,
|
|
46210
|
+
inputTokens: usage.input,
|
|
46211
|
+
outputTokens: usage.output,
|
|
46212
|
+
cacheReadTokens: usage.cacheRead,
|
|
46213
|
+
cacheCreationTokens: usage.cacheCreation,
|
|
46214
|
+
provider: aiProviderOf(ccmd),
|
|
46215
|
+
authMode: resolveClaudeAuthMode(ccfg)
|
|
46216
|
+
});
|
|
46217
|
+
}
|
|
46218
|
+
ctx.reportUsage(events, null);
|
|
46219
|
+
if (reply.error) {
|
|
46220
|
+
ctx.bus.push({ source: "server", kind: "log", text: `checklist-author failed: ${reply.error}`, level: "warn" });
|
|
46221
|
+
} else {
|
|
46222
|
+
ctx.bus.push({ source: "server", kind: "aires", text: `${ccmd} \u203A` });
|
|
46223
|
+
}
|
|
46224
|
+
reportToolResult(ccmd, !reply.error, reply.error);
|
|
46225
|
+
ctx.send(WsChannels.CHECKLIST_AUTHOR, reply, message.id);
|
|
46226
|
+
} finally {
|
|
46227
|
+
slot.handle.release();
|
|
46228
|
+
}
|
|
46229
|
+
})().finally(() => ctx.bus.endBusy("checklist-author"));
|
|
46230
|
+
return true;
|
|
46231
|
+
}
|
|
45840
46232
|
default:
|
|
45841
46233
|
return false;
|
|
45842
46234
|
}
|
|
@@ -45935,7 +46327,7 @@ function handleToolsChannels(ctx, message, payload) {
|
|
|
45935
46327
|
|
|
45936
46328
|
// src/core/agents.ts
|
|
45937
46329
|
import { readFile as readFile14, writeFile as writeFile10, mkdir as mkdir8, readdir as readdir6, rm as rm7 } from "fs/promises";
|
|
45938
|
-
import { join as
|
|
46330
|
+
import { join as join31 } from "path";
|
|
45939
46331
|
var AGENTS_REL = ".claude/agents";
|
|
45940
46332
|
var SKILLS_REL = ".claude/skills";
|
|
45941
46333
|
var MEMORY_DIRS = /* @__PURE__ */ new Set(["short-memory", "long-memory"]);
|
|
@@ -45959,17 +46351,17 @@ async function listAgents(root) {
|
|
|
45959
46351
|
const subagents = [];
|
|
45960
46352
|
const skills = [];
|
|
45961
46353
|
try {
|
|
45962
|
-
for (const e of await readdir6(
|
|
46354
|
+
for (const e of await readdir6(join31(root, AGENTS_REL), { withFileTypes: true })) {
|
|
45963
46355
|
if (!e.isFile() || !e.name.endsWith(".md") || MEMORY_DIRS.has(e.name)) continue;
|
|
45964
|
-
const fm = frontmatter(await readText2(
|
|
46356
|
+
const fm = frontmatter(await readText2(join31(root, AGENTS_REL, e.name)));
|
|
45965
46357
|
subagents.push({ name: e.name.replace(/\.md$/, ""), description: fm.description });
|
|
45966
46358
|
}
|
|
45967
46359
|
} catch {
|
|
45968
46360
|
}
|
|
45969
46361
|
try {
|
|
45970
|
-
for (const e of await readdir6(
|
|
46362
|
+
for (const e of await readdir6(join31(root, SKILLS_REL), { withFileTypes: true })) {
|
|
45971
46363
|
if (!e.isDirectory()) continue;
|
|
45972
|
-
const fm = frontmatter(await readText2(
|
|
46364
|
+
const fm = frontmatter(await readText2(join31(root, SKILLS_REL, e.name, "SKILL.md")));
|
|
45973
46365
|
skills.push({ name: e.name, description: fm.description });
|
|
45974
46366
|
}
|
|
45975
46367
|
} catch {
|
|
@@ -45980,15 +46372,15 @@ async function listAgents(root) {
|
|
|
45980
46372
|
}
|
|
45981
46373
|
function itemPath(root, kind, name) {
|
|
45982
46374
|
const n = safeName(name);
|
|
45983
|
-
return kind === "subagent" ?
|
|
46375
|
+
return kind === "subagent" ? join31(root, AGENTS_REL, `${n}.md`) : join31(root, SKILLS_REL, n, "SKILL.md");
|
|
45984
46376
|
}
|
|
45985
46377
|
async function readAgent(root, kind, name) {
|
|
45986
46378
|
const content = await readText2(itemPath(root, kind, name));
|
|
45987
46379
|
if (kind !== "subagent") return { content };
|
|
45988
46380
|
const n = safeName(name);
|
|
45989
46381
|
const [short, long] = await Promise.all([
|
|
45990
|
-
readText2(
|
|
45991
|
-
readText2(
|
|
46382
|
+
readText2(join31(root, AGENTS_REL, "short-memory", `${n}.md`)),
|
|
46383
|
+
readText2(join31(root, AGENTS_REL, "long-memory", `${n}.md`))
|
|
45992
46384
|
]);
|
|
45993
46385
|
return { content, memory: { short, long } };
|
|
45994
46386
|
}
|
|
@@ -45998,12 +46390,12 @@ async function writeAgent(root, req) {
|
|
|
45998
46390
|
try {
|
|
45999
46391
|
if (req.action === "delete") {
|
|
46000
46392
|
if (req.kind === "subagent") await rm7(itemPath(root, "subagent", n), { force: true });
|
|
46001
|
-
else await rm7(
|
|
46393
|
+
else await rm7(join31(root, SKILLS_REL, n), { recursive: true, force: true });
|
|
46002
46394
|
return { ok: true };
|
|
46003
46395
|
}
|
|
46004
46396
|
if (req.content === void 0) return { ok: false, error: "content is required" };
|
|
46005
46397
|
const path = itemPath(root, req.kind, n);
|
|
46006
|
-
await mkdir8(
|
|
46398
|
+
await mkdir8(join31(path, ".."), { recursive: true });
|
|
46007
46399
|
await writeFile10(path, req.content, "utf8");
|
|
46008
46400
|
return { ok: true };
|
|
46009
46401
|
} catch (err) {
|
|
@@ -46013,7 +46405,7 @@ async function writeAgent(root, req) {
|
|
|
46013
46405
|
|
|
46014
46406
|
// src/core/secrets.ts
|
|
46015
46407
|
import { readFile as readFile15, writeFile as writeFile11 } from "fs/promises";
|
|
46016
|
-
import { join as
|
|
46408
|
+
import { join as join32 } from "path";
|
|
46017
46409
|
var SECURITY_REL = "AI_SECURITY.md";
|
|
46018
46410
|
var PLACEHOLDER_REL = "AI_PLACEHOLDER.md";
|
|
46019
46411
|
var SECRETS_REL = "project.secrets.json";
|
|
@@ -46026,7 +46418,7 @@ async function readText3(path, fallback = "") {
|
|
|
46026
46418
|
}
|
|
46027
46419
|
async function readSecretsObject(root) {
|
|
46028
46420
|
try {
|
|
46029
|
-
const v2 = JSON.parse(await readFile15(
|
|
46421
|
+
const v2 = JSON.parse(await readFile15(join32(root, SECRETS_REL), "utf8"));
|
|
46030
46422
|
return v2 && typeof v2 === "object" ? v2 : {};
|
|
46031
46423
|
} catch {
|
|
46032
46424
|
return {};
|
|
@@ -46045,8 +46437,8 @@ function placeholderKeys(text) {
|
|
|
46045
46437
|
}
|
|
46046
46438
|
async function readSecrets(root) {
|
|
46047
46439
|
const [security, placeholder, secrets] = await Promise.all([
|
|
46048
|
-
readText3(
|
|
46049
|
-
readText3(
|
|
46440
|
+
readText3(join32(root, SECURITY_REL)),
|
|
46441
|
+
readText3(join32(root, PLACEHOLDER_REL)),
|
|
46050
46442
|
readSecretsObject(root)
|
|
46051
46443
|
]);
|
|
46052
46444
|
const names = /* @__PURE__ */ new Set([...placeholderKeys(placeholder), ...Object.keys(secrets)]);
|
|
@@ -46059,14 +46451,14 @@ async function readSecrets(root) {
|
|
|
46059
46451
|
async function writeSecrets(root, req) {
|
|
46060
46452
|
try {
|
|
46061
46453
|
if (req.kind === "security") {
|
|
46062
|
-
await writeFile11(
|
|
46454
|
+
await writeFile11(join32(root, SECURITY_REL), req.content, "utf8");
|
|
46063
46455
|
} else if (req.kind === "placeholder") {
|
|
46064
|
-
await writeFile11(
|
|
46456
|
+
await writeFile11(join32(root, PLACEHOLDER_REL), req.content, "utf8");
|
|
46065
46457
|
} else {
|
|
46066
46458
|
const secrets = await readSecretsObject(root);
|
|
46067
46459
|
if (req.kind === "secret") secrets[req.key] = req.value;
|
|
46068
46460
|
else delete secrets[req.key];
|
|
46069
|
-
await writeFile11(
|
|
46461
|
+
await writeFile11(join32(root, SECRETS_REL), JSON.stringify(secrets, null, 2) + "\n", "utf8");
|
|
46070
46462
|
}
|
|
46071
46463
|
return { ok: true };
|
|
46072
46464
|
} catch (err) {
|
|
@@ -46076,12 +46468,12 @@ async function writeSecrets(root, req) {
|
|
|
46076
46468
|
|
|
46077
46469
|
// src/core/agent-tools.ts
|
|
46078
46470
|
import { readFile as readFile16, writeFile as writeFile12, mkdir as mkdir9 } from "fs/promises";
|
|
46079
|
-
import { dirname as dirname9, join as
|
|
46471
|
+
import { dirname as dirname9, join as join33 } from "path";
|
|
46080
46472
|
var SHARED_REL = ".claude/settings.json";
|
|
46081
46473
|
var LOCAL_REL = ".claude/settings.local.json";
|
|
46082
46474
|
var SECRETS_DENY = "Read(./project.secrets.json)";
|
|
46083
46475
|
function fileFor(root, scope) {
|
|
46084
|
-
return
|
|
46476
|
+
return join33(root, scope === "shared" ? SHARED_REL : LOCAL_REL);
|
|
46085
46477
|
}
|
|
46086
46478
|
async function readSettingsObject(path) {
|
|
46087
46479
|
try {
|
|
@@ -46118,7 +46510,7 @@ async function writeAgentTools(root, scope, permissions) {
|
|
|
46118
46510
|
// src/core/packages.ts
|
|
46119
46511
|
import { createHash as createHash4 } from "crypto";
|
|
46120
46512
|
import { readFile as readFile17, writeFile as writeFile13, mkdir as mkdir10, readdir as readdir7, rm as rm8, stat as stat5 } from "fs/promises";
|
|
46121
|
-
import { dirname as dirname10, join as
|
|
46513
|
+
import { dirname as dirname10, join as join34, relative } from "path";
|
|
46122
46514
|
var AGENTS_REL2 = ".claude/agents";
|
|
46123
46515
|
var SKILLS_REL2 = ".claude/skills";
|
|
46124
46516
|
var MANIFEST_REL = ".claude/.4pm-packages.json";
|
|
@@ -46142,7 +46534,7 @@ async function listFilesRel(dir2) {
|
|
|
46142
46534
|
return;
|
|
46143
46535
|
}
|
|
46144
46536
|
for (const e of entries2) {
|
|
46145
|
-
const full =
|
|
46537
|
+
const full = join34(d, e.name);
|
|
46146
46538
|
if (e.isDirectory()) await walk2(full);
|
|
46147
46539
|
else if (e.isFile()) out.push(relative(dir2, full));
|
|
46148
46540
|
}
|
|
@@ -46153,16 +46545,16 @@ async function listFilesRel(dir2) {
|
|
|
46153
46545
|
async function readArtifactFiles(root, kind, slug) {
|
|
46154
46546
|
const files = [];
|
|
46155
46547
|
if (kind === "subagent") {
|
|
46156
|
-
const agent = await readMaybe(
|
|
46548
|
+
const agent = await readMaybe(join34(root, AGENTS_REL2, `${slug}.md`));
|
|
46157
46549
|
if (agent) files.push({ path: "agent.md", contentBase64: agent.toString("base64") });
|
|
46158
|
-
const short = await readMaybe(
|
|
46550
|
+
const short = await readMaybe(join34(root, AGENTS_REL2, "short-memory", `${slug}.md`));
|
|
46159
46551
|
if (short) files.push({ path: "short-memory.md", contentBase64: short.toString("base64") });
|
|
46160
|
-
const long = await readMaybe(
|
|
46552
|
+
const long = await readMaybe(join34(root, AGENTS_REL2, "long-memory", `${slug}.md`));
|
|
46161
46553
|
if (long) files.push({ path: "long-memory.md", contentBase64: long.toString("base64") });
|
|
46162
46554
|
} else {
|
|
46163
|
-
const base =
|
|
46555
|
+
const base = join34(root, SKILLS_REL2, slug);
|
|
46164
46556
|
for (const rel of await listFilesRel(base)) {
|
|
46165
|
-
const buf = await readMaybe(
|
|
46557
|
+
const buf = await readMaybe(join34(base, rel));
|
|
46166
46558
|
if (buf) files.push({ path: rel.split("\\").join("/"), contentBase64: buf.toString("base64") });
|
|
46167
46559
|
}
|
|
46168
46560
|
}
|
|
@@ -46180,7 +46572,7 @@ async function artifactSha256(root, kind, slug) {
|
|
|
46180
46572
|
return h.digest("hex");
|
|
46181
46573
|
}
|
|
46182
46574
|
async function artifactExists(root, kind, slug) {
|
|
46183
|
-
const target = kind === "subagent" ?
|
|
46575
|
+
const target = kind === "subagent" ? join34(root, AGENTS_REL2, `${slug}.md`) : join34(root, SKILLS_REL2, slug);
|
|
46184
46576
|
try {
|
|
46185
46577
|
await stat5(target);
|
|
46186
46578
|
return true;
|
|
@@ -46189,7 +46581,7 @@ async function artifactExists(root, kind, slug) {
|
|
|
46189
46581
|
}
|
|
46190
46582
|
}
|
|
46191
46583
|
async function readManifest(root) {
|
|
46192
|
-
const buf = await readMaybe(
|
|
46584
|
+
const buf = await readMaybe(join34(root, MANIFEST_REL));
|
|
46193
46585
|
if (!buf) return {};
|
|
46194
46586
|
try {
|
|
46195
46587
|
return JSON.parse(buf.toString("utf8"));
|
|
@@ -46198,7 +46590,7 @@ async function readManifest(root) {
|
|
|
46198
46590
|
}
|
|
46199
46591
|
}
|
|
46200
46592
|
async function writeManifest(root, manifest) {
|
|
46201
|
-
const path =
|
|
46593
|
+
const path = join34(root, MANIFEST_REL);
|
|
46202
46594
|
await mkdir10(dirname10(path), { recursive: true });
|
|
46203
46595
|
await writeFile13(path, JSON.stringify(manifest, null, 2), "utf8");
|
|
46204
46596
|
}
|
|
@@ -46207,24 +46599,24 @@ async function writeArtifactFiles(root, kind, slug, files) {
|
|
|
46207
46599
|
if (kind === "subagent") {
|
|
46208
46600
|
const agent = byPath.get("agent.md");
|
|
46209
46601
|
if (agent) {
|
|
46210
|
-
await mkdir10(
|
|
46211
|
-
await writeFile13(
|
|
46602
|
+
await mkdir10(join34(root, AGENTS_REL2), { recursive: true });
|
|
46603
|
+
await writeFile13(join34(root, AGENTS_REL2, `${slug}.md`), agent);
|
|
46212
46604
|
}
|
|
46213
46605
|
const short = byPath.get("short-memory.md");
|
|
46214
46606
|
if (short) {
|
|
46215
|
-
await mkdir10(
|
|
46216
|
-
await writeFile13(
|
|
46607
|
+
await mkdir10(join34(root, AGENTS_REL2, "short-memory"), { recursive: true });
|
|
46608
|
+
await writeFile13(join34(root, AGENTS_REL2, "short-memory", `${slug}.md`), short);
|
|
46217
46609
|
}
|
|
46218
46610
|
const long = byPath.get("long-memory.md");
|
|
46219
46611
|
if (long) {
|
|
46220
|
-
await mkdir10(
|
|
46221
|
-
await writeFile13(
|
|
46612
|
+
await mkdir10(join34(root, AGENTS_REL2, "long-memory"), { recursive: true });
|
|
46613
|
+
await writeFile13(join34(root, AGENTS_REL2, "long-memory", `${slug}.md`), long);
|
|
46222
46614
|
}
|
|
46223
46615
|
} else {
|
|
46224
|
-
const base =
|
|
46616
|
+
const base = join34(root, SKILLS_REL2, slug);
|
|
46225
46617
|
for (const [rel, buf] of byPath) {
|
|
46226
46618
|
if (rel.includes("..") || rel.startsWith("/")) continue;
|
|
46227
|
-
const dest =
|
|
46619
|
+
const dest = join34(base, rel);
|
|
46228
46620
|
await mkdir10(dirname10(dest), { recursive: true });
|
|
46229
46621
|
await writeFile13(dest, buf);
|
|
46230
46622
|
}
|
|
@@ -46290,11 +46682,11 @@ async function removePackage(root, slug) {
|
|
|
46290
46682
|
const entry = manifest[n];
|
|
46291
46683
|
if (!entry) return { ok: false, error: "not installed" };
|
|
46292
46684
|
if (entry.kind === "subagent") {
|
|
46293
|
-
await rm8(
|
|
46294
|
-
await rm8(
|
|
46295
|
-
await rm8(
|
|
46685
|
+
await rm8(join34(root, AGENTS_REL2, `${n}.md`), { force: true });
|
|
46686
|
+
await rm8(join34(root, AGENTS_REL2, "short-memory", `${n}.md`), { force: true });
|
|
46687
|
+
await rm8(join34(root, AGENTS_REL2, "long-memory", `${n}.md`), { force: true });
|
|
46296
46688
|
} else {
|
|
46297
|
-
await rm8(
|
|
46689
|
+
await rm8(join34(root, SKILLS_REL2, n), { recursive: true, force: true });
|
|
46298
46690
|
}
|
|
46299
46691
|
delete manifest[n];
|
|
46300
46692
|
await writeManifest(root, manifest);
|
|
@@ -46306,7 +46698,7 @@ async function removePackage(root, slug) {
|
|
|
46306
46698
|
|
|
46307
46699
|
// src/core/graph.ts
|
|
46308
46700
|
import { readFile as readFile18, readdir as readdir8 } from "fs/promises";
|
|
46309
|
-
import { join as
|
|
46701
|
+
import { join as join35, relative as relative2, dirname as dirname11, resolve as resolve12, extname as extname2, basename as basename6 } from "path";
|
|
46310
46702
|
var MAX_FILES = 2e3;
|
|
46311
46703
|
var MAX_NODES = 3e3;
|
|
46312
46704
|
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", "out", "coverage", ".turbo"]);
|
|
@@ -46326,7 +46718,7 @@ async function walk(root, accept) {
|
|
|
46326
46718
|
if (e.name.startsWith(".") && e.name !== ".claude") {
|
|
46327
46719
|
if (e.isDirectory()) continue;
|
|
46328
46720
|
}
|
|
46329
|
-
const full =
|
|
46721
|
+
const full = join35(dir2, e.name);
|
|
46330
46722
|
if (e.isDirectory()) {
|
|
46331
46723
|
if (!SKIP_DIRS.has(e.name)) stack.push(full);
|
|
46332
46724
|
} else if (accept(full)) {
|
|
@@ -46447,15 +46839,15 @@ async function buildGraph(root, mode) {
|
|
|
46447
46839
|
// src/core/rag.ts
|
|
46448
46840
|
import { readFile as readFile19, writeFile as writeFile14, statfs as statfs2, mkdir as mkdir11 } from "fs/promises";
|
|
46449
46841
|
import { existsSync as existsSync17 } from "fs";
|
|
46450
|
-
import { execFile as execFile15, spawn as
|
|
46842
|
+
import { execFile as execFile15, spawn as spawn11 } from "child_process";
|
|
46451
46843
|
import { totalmem as totalmem2, cpus as cpus2 } from "os";
|
|
46452
|
-
import { join as
|
|
46844
|
+
import { join as join36 } from "path";
|
|
46453
46845
|
import { promisify as promisify15 } from "util";
|
|
46454
|
-
var
|
|
46846
|
+
var run15 = promisify15(execFile15);
|
|
46455
46847
|
var RAG_REL = ".claude/rag";
|
|
46456
46848
|
async function tryRun(cmd, args) {
|
|
46457
46849
|
try {
|
|
46458
|
-
const { stdout } = await
|
|
46850
|
+
const { stdout } = await run15(cmd, args, { timeout: 8e3 });
|
|
46459
46851
|
return { ok: true, out: stdout.trim() };
|
|
46460
46852
|
} catch {
|
|
46461
46853
|
return { ok: false, out: "" };
|
|
@@ -46470,7 +46862,7 @@ async function detectPython() {
|
|
|
46470
46862
|
}
|
|
46471
46863
|
async function installLog(root) {
|
|
46472
46864
|
try {
|
|
46473
|
-
const text = await readFile19(
|
|
46865
|
+
const text = await readFile19(join36(root, RAG_REL, "install.log"), "utf8");
|
|
46474
46866
|
return text.split("\n").filter((l) => l.length > 0).slice(-200);
|
|
46475
46867
|
} catch {
|
|
46476
46868
|
return [];
|
|
@@ -46532,14 +46924,14 @@ async function ragStatus(root) {
|
|
|
46532
46924
|
};
|
|
46533
46925
|
let modelName = "";
|
|
46534
46926
|
try {
|
|
46535
|
-
modelName = (await readFile19(
|
|
46927
|
+
modelName = (await readFile19(join36(root, RAG_REL, ".installed"), "utf8")).trim();
|
|
46536
46928
|
} catch {
|
|
46537
46929
|
modelName = "";
|
|
46538
46930
|
}
|
|
46539
46931
|
const modelPresent = modelName.length > 0;
|
|
46540
46932
|
return {
|
|
46541
46933
|
installed: fastembed && sqliteVec && modelPresent,
|
|
46542
|
-
installing: existsSync17(
|
|
46934
|
+
installing: existsSync17(join36(root, RAG_REL, ".installing")),
|
|
46543
46935
|
python: { found: py.found, version: py.version },
|
|
46544
46936
|
deps: { pip, fastembed, sqliteVec },
|
|
46545
46937
|
model: { present: modelPresent, name: modelName },
|
|
@@ -46550,9 +46942,9 @@ async function ragStatus(root) {
|
|
|
46550
46942
|
};
|
|
46551
46943
|
}
|
|
46552
46944
|
async function readIndexMeta(root) {
|
|
46553
|
-
const indexing = existsSync17(
|
|
46945
|
+
const indexing = existsSync17(join36(root, RAG_REL, ".indexing"));
|
|
46554
46946
|
try {
|
|
46555
|
-
const m2 = JSON.parse(await readFile19(
|
|
46947
|
+
const m2 = JSON.parse(await readFile19(join36(root, RAG_REL, ".index.json"), "utf8"));
|
|
46556
46948
|
return {
|
|
46557
46949
|
present: true,
|
|
46558
46950
|
indexing,
|
|
@@ -46619,14 +47011,14 @@ if __name__ == "__main__":
|
|
|
46619
47011
|
elif cmd == "query": query(sys.argv[2], int(sys.argv[3]) if len(sys.argv) > 3 else 8)
|
|
46620
47012
|
`;
|
|
46621
47013
|
async function ensureEngine(root) {
|
|
46622
|
-
await mkdir11(
|
|
46623
|
-
await writeFile14(
|
|
47014
|
+
await mkdir11(join36(root, RAG_REL), { recursive: true }).catch(() => void 0);
|
|
47015
|
+
await writeFile14(join36(root, RAG_REL, "rag_engine.py"), RAG_ENGINE_PY, "utf8");
|
|
46624
47016
|
}
|
|
46625
47017
|
async function ragReindex(root) {
|
|
46626
47018
|
const py = await detectPython();
|
|
46627
47019
|
if (!py.found) return { ok: false, started: false, error: "python3 not found" };
|
|
46628
|
-
if (!existsSync17(
|
|
46629
|
-
if (existsSync17(
|
|
47020
|
+
if (!existsSync17(join36(root, RAG_REL, ".installed"))) return { ok: false, started: false, error: "RAG is not installed" };
|
|
47021
|
+
if (existsSync17(join36(root, RAG_REL, ".indexing"))) return { ok: true, started: true };
|
|
46630
47022
|
await ensureEngine(root);
|
|
46631
47023
|
const script = `
|
|
46632
47024
|
set +e
|
|
@@ -46634,7 +47026,7 @@ touch "${RAG_REL}/.indexing"
|
|
|
46634
47026
|
{ echo "[reindex] $(date)"; "$RAG_PY" "${RAG_REL}/rag_engine.py" reindex 2>&1; } >> "${RAG_REL}/index.log" 2>&1
|
|
46635
47027
|
rm -f "${RAG_REL}/.indexing"
|
|
46636
47028
|
`;
|
|
46637
|
-
const child =
|
|
47029
|
+
const child = spawn11("bash", ["-c", script], {
|
|
46638
47030
|
cwd: root,
|
|
46639
47031
|
env: { ...process.env, RAG_PY: py.bin },
|
|
46640
47032
|
detached: true,
|
|
@@ -46646,12 +47038,12 @@ rm -f "${RAG_REL}/.indexing"
|
|
|
46646
47038
|
async function ragQuery(root, queryText, k2 = 8) {
|
|
46647
47039
|
const py = await detectPython();
|
|
46648
47040
|
if (!py.found) return { results: [], error: "python3 not found" };
|
|
46649
|
-
if (!existsSync17(
|
|
47041
|
+
if (!existsSync17(join36(root, RAG_REL, "index.db"))) return { results: [], error: "no index \u2014 reindex first" };
|
|
46650
47042
|
await ensureEngine(root);
|
|
46651
47043
|
try {
|
|
46652
|
-
const { stdout } = await
|
|
47044
|
+
const { stdout } = await run15(
|
|
46653
47045
|
py.bin,
|
|
46654
|
-
[
|
|
47046
|
+
[join36(RAG_REL, "rag_engine.py"), "query", queryText, String(Math.min(Math.max(k2, 1), 20))],
|
|
46655
47047
|
{ cwd: root, timeout: 3e4, maxBuffer: 4 * 1024 * 1024 }
|
|
46656
47048
|
);
|
|
46657
47049
|
const parsed = JSON.parse(stdout.trim() || '{"results":[]}');
|
|
@@ -46666,9 +47058,9 @@ async function ragInstall(root, model) {
|
|
|
46666
47058
|
}
|
|
46667
47059
|
const py = await detectPython();
|
|
46668
47060
|
if (!py.found) return { ok: false, started: false, error: "python3 not found on the worker" };
|
|
46669
|
-
if (existsSync17(
|
|
47061
|
+
if (existsSync17(join36(root, RAG_REL, ".installing"))) return { ok: true, started: true };
|
|
46670
47062
|
try {
|
|
46671
|
-
await mkdir11(
|
|
47063
|
+
await mkdir11(join36(root, RAG_REL), { recursive: true });
|
|
46672
47064
|
} catch {
|
|
46673
47065
|
}
|
|
46674
47066
|
const script = `
|
|
@@ -46684,7 +47076,7 @@ rm -f "${RAG_REL}/.installed"
|
|
|
46684
47076
|
} >> "${RAG_REL}/install.log" 2>&1
|
|
46685
47077
|
rm -f "${RAG_REL}/.installing"
|
|
46686
47078
|
`;
|
|
46687
|
-
const child =
|
|
47079
|
+
const child = spawn11("bash", ["-c", script], {
|
|
46688
47080
|
cwd: root,
|
|
46689
47081
|
env: { ...process.env, RAG_PY: py.bin, RAG_MODEL: model },
|
|
46690
47082
|
detached: true,
|
|
@@ -46696,7 +47088,7 @@ rm -f "${RAG_REL}/.installing"
|
|
|
46696
47088
|
|
|
46697
47089
|
// src/core/config-sync.ts
|
|
46698
47090
|
import { existsSync as existsSync18, readFileSync as readFileSync16 } from "fs";
|
|
46699
|
-
import { join as
|
|
47091
|
+
import { join as join37 } from "path";
|
|
46700
47092
|
var SERVER_MANAGED_KEYS = [
|
|
46701
47093
|
"physicPath",
|
|
46702
47094
|
"sessionSwitchPct",
|
|
@@ -46713,7 +47105,7 @@ var SERVER_MANAGED_KEYS = [
|
|
|
46713
47105
|
"projectAiMemoryBudgetChars"
|
|
46714
47106
|
];
|
|
46715
47107
|
function readConfigText(profileDir2) {
|
|
46716
|
-
const path =
|
|
47108
|
+
const path = join37(profileDir2, "config.json");
|
|
46717
47109
|
if (existsSync18(path)) {
|
|
46718
47110
|
try {
|
|
46719
47111
|
return readFileSync16(path, "utf8");
|
|
@@ -47504,6 +47896,7 @@ var WsClient = class {
|
|
|
47504
47896
|
if (handleToolsChannels(this.hctx, message, payload)) return;
|
|
47505
47897
|
if (handleGitChannels(this.hctx, message, payload)) return;
|
|
47506
47898
|
if (handleSupportChannels(this.hctx, message, payload)) return;
|
|
47899
|
+
if (handleResearchChannels(this.hctx, message, payload)) return;
|
|
47507
47900
|
if (handleMiscChannels(this.hctx, message, payload)) return;
|
|
47508
47901
|
this.bus.log(t("error.unsupportedChannel", { channel: message.channel }), "warn");
|
|
47509
47902
|
}
|
|
@@ -47622,7 +48015,7 @@ var WsClient = class {
|
|
|
47622
48015
|
*/
|
|
47623
48016
|
physicFolderPath(projectName) {
|
|
47624
48017
|
const safe = projectName.replace(/[/\\]/g, "_").replace(/\.\./g, "_").trim();
|
|
47625
|
-
return safe ?
|
|
48018
|
+
return safe ? join38(this.context.profileDir, safe) : null;
|
|
47626
48019
|
}
|
|
47627
48020
|
/**
|
|
47628
48021
|
* Report machine.status: version + the state of the physic project being served
|
|
@@ -47734,7 +48127,7 @@ var WsClient = class {
|
|
|
47734
48127
|
*/
|
|
47735
48128
|
uploadLog() {
|
|
47736
48129
|
try {
|
|
47737
|
-
const upload = readLogUpload(
|
|
48130
|
+
const upload = readLogUpload(join38(this.context.profileDir, "logs"));
|
|
47738
48131
|
if (!upload) return;
|
|
47739
48132
|
const sig = `${upload.fileName}:${upload.totalBytes}`;
|
|
47740
48133
|
if (sig === this.lastLogSig) return;
|
|
@@ -47896,10 +48289,10 @@ var WsClient = class {
|
|
|
47896
48289
|
};
|
|
47897
48290
|
|
|
47898
48291
|
// src/core/instance-lock.ts
|
|
47899
|
-
import { readFileSync as readFileSync17, rmSync as
|
|
47900
|
-
import { join as
|
|
48292
|
+
import { readFileSync as readFileSync17, rmSync as rmSync10, writeFileSync as writeFileSync9 } from "fs";
|
|
48293
|
+
import { join as join39 } from "path";
|
|
47901
48294
|
function lockPath(profileDir2) {
|
|
47902
|
-
return
|
|
48295
|
+
return join39(profileDir2, "start.lock");
|
|
47903
48296
|
}
|
|
47904
48297
|
function isAlive(pid) {
|
|
47905
48298
|
try {
|
|
@@ -47925,7 +48318,7 @@ function acquireInstanceLock(profileDir2) {
|
|
|
47925
48318
|
function releaseInstanceLock(profileDir2) {
|
|
47926
48319
|
try {
|
|
47927
48320
|
const pid = Number.parseInt(readFileSync17(lockPath(profileDir2), "utf8").trim(), 10);
|
|
47928
|
-
if (pid === process.pid)
|
|
48321
|
+
if (pid === process.pid) rmSync10(lockPath(profileDir2), { force: true });
|
|
47929
48322
|
} catch {
|
|
47930
48323
|
}
|
|
47931
48324
|
}
|
|
@@ -48220,8 +48613,8 @@ var SessionBus = class {
|
|
|
48220
48613
|
|
|
48221
48614
|
// src/core/control-server.ts
|
|
48222
48615
|
import { createServer } from "net";
|
|
48223
|
-
import { chmodSync as chmodSync5, rmSync as
|
|
48224
|
-
import { join as
|
|
48616
|
+
import { chmodSync as chmodSync5, rmSync as rmSync11 } from "fs";
|
|
48617
|
+
import { join as join41 } from "path";
|
|
48225
48618
|
|
|
48226
48619
|
// src/core/control-protocol.ts
|
|
48227
48620
|
var CONTROL_SOCKET_FILE = "control.sock";
|
|
@@ -48252,9 +48645,9 @@ function createFrameParser() {
|
|
|
48252
48645
|
// src/core/control-token.ts
|
|
48253
48646
|
import { chmodSync as chmodSync4, readFileSync as readFileSync18, writeFileSync as writeFileSync10 } from "fs";
|
|
48254
48647
|
import { randomBytes as randomBytes3, timingSafeEqual } from "crypto";
|
|
48255
|
-
import { join as
|
|
48648
|
+
import { join as join40 } from "path";
|
|
48256
48649
|
function controlTokenPath(profileDir2) {
|
|
48257
|
-
return
|
|
48650
|
+
return join40(profileDir2, CONTROL_TOKEN_FILE);
|
|
48258
48651
|
}
|
|
48259
48652
|
function writeControlToken(profileDir2) {
|
|
48260
48653
|
const token = randomBytes3(32).toString("hex");
|
|
@@ -48283,9 +48676,9 @@ function tokenMatches(expected, given) {
|
|
|
48283
48676
|
|
|
48284
48677
|
// src/core/control-server.ts
|
|
48285
48678
|
function startControlServer(bus, profileDir2, info) {
|
|
48286
|
-
const socketPath =
|
|
48679
|
+
const socketPath = join41(profileDir2, CONTROL_SOCKET_FILE);
|
|
48287
48680
|
try {
|
|
48288
|
-
|
|
48681
|
+
rmSync11(socketPath, { force: true });
|
|
48289
48682
|
} catch {
|
|
48290
48683
|
}
|
|
48291
48684
|
const clients = /* @__PURE__ */ new Set();
|
|
@@ -48376,8 +48769,8 @@ function startControlServer(bus, profileDir2, info) {
|
|
|
48376
48769
|
clients.clear();
|
|
48377
48770
|
server.close();
|
|
48378
48771
|
try {
|
|
48379
|
-
|
|
48380
|
-
|
|
48772
|
+
rmSync11(socketPath, { force: true });
|
|
48773
|
+
rmSync11(controlTokenPath(profileDir2), { force: true });
|
|
48381
48774
|
} catch {
|
|
48382
48775
|
}
|
|
48383
48776
|
};
|
|
@@ -50356,7 +50749,7 @@ var src_default = Yoga;
|
|
|
50356
50749
|
|
|
50357
50750
|
// ../../node_modules/.pnpm/ink@5.2.1_@types+react@18.3.31_react@18.3.1/node_modules/ink/build/reconciler.js
|
|
50358
50751
|
var import_react_reconciler = __toESM(require_react_reconciler(), 1);
|
|
50359
|
-
var
|
|
50752
|
+
var import_constants14 = __toESM(require_constants2(), 1);
|
|
50360
50753
|
import process3 from "process";
|
|
50361
50754
|
|
|
50362
50755
|
// ../../node_modules/.pnpm/ansi-regex@6.2.2/node_modules/ansi-regex/index.js
|
|
@@ -50527,7 +50920,7 @@ function widestLine(string) {
|
|
|
50527
50920
|
}
|
|
50528
50921
|
|
|
50529
50922
|
// ../../node_modules/.pnpm/ink@5.2.1_@types+react@18.3.31_react@18.3.1/node_modules/ink/build/measure-text.js
|
|
50530
|
-
var
|
|
50923
|
+
var cache2 = {};
|
|
50531
50924
|
var measureText = (text) => {
|
|
50532
50925
|
if (text.length === 0) {
|
|
50533
50926
|
return {
|
|
@@ -50535,13 +50928,13 @@ var measureText = (text) => {
|
|
|
50535
50928
|
height: 0
|
|
50536
50929
|
};
|
|
50537
50930
|
}
|
|
50538
|
-
const cachedDimensions =
|
|
50931
|
+
const cachedDimensions = cache2[text];
|
|
50539
50932
|
if (cachedDimensions) {
|
|
50540
50933
|
return cachedDimensions;
|
|
50541
50934
|
}
|
|
50542
50935
|
const width = widestLine(text);
|
|
50543
50936
|
const height = text.split("\n").length;
|
|
50544
|
-
|
|
50937
|
+
cache2[text] = { width, height };
|
|
50545
50938
|
return { width, height };
|
|
50546
50939
|
};
|
|
50547
50940
|
var measure_text_default = measureText;
|
|
@@ -51066,10 +51459,10 @@ function cliTruncate(text, columns, options = {}) {
|
|
|
51066
51459
|
}
|
|
51067
51460
|
|
|
51068
51461
|
// ../../node_modules/.pnpm/ink@5.2.1_@types+react@18.3.31_react@18.3.1/node_modules/ink/build/wrap-text.js
|
|
51069
|
-
var
|
|
51462
|
+
var cache3 = {};
|
|
51070
51463
|
var wrapText = (text, maxWidth, wrapType) => {
|
|
51071
51464
|
const cacheKey = text + String(maxWidth) + String(wrapType);
|
|
51072
|
-
const cachedText =
|
|
51465
|
+
const cachedText = cache3[cacheKey];
|
|
51073
51466
|
if (cachedText) {
|
|
51074
51467
|
return cachedText;
|
|
51075
51468
|
}
|
|
@@ -51090,7 +51483,7 @@ var wrapText = (text, maxWidth, wrapType) => {
|
|
|
51090
51483
|
}
|
|
51091
51484
|
wrappedText = cliTruncate(text, maxWidth, { position });
|
|
51092
51485
|
}
|
|
51093
|
-
|
|
51486
|
+
cache3[cacheKey] = wrappedText;
|
|
51094
51487
|
return wrappedText;
|
|
51095
51488
|
};
|
|
51096
51489
|
var wrap_text_default = wrapText;
|
|
@@ -51598,7 +51991,7 @@ var reconciler_default = (0, import_react_reconciler.default)({
|
|
|
51598
51991
|
scheduleTimeout: setTimeout,
|
|
51599
51992
|
cancelTimeout: clearTimeout,
|
|
51600
51993
|
noTimeout: -1,
|
|
51601
|
-
getCurrentEventPriority: () =>
|
|
51994
|
+
getCurrentEventPriority: () => import_constants14.DefaultEventPriority,
|
|
51602
51995
|
beforeActiveInstanceBlur() {
|
|
51603
51996
|
},
|
|
51604
51997
|
afterActiveInstanceBlur() {
|
|
@@ -54041,10 +54434,10 @@ var import_react26 = __toESM(require_react(), 1);
|
|
|
54041
54434
|
|
|
54042
54435
|
// src/core/input-history.ts
|
|
54043
54436
|
import { existsSync as existsSync21, readFileSync as readFileSync20, writeFileSync as writeFileSync11 } from "fs";
|
|
54044
|
-
import { join as
|
|
54437
|
+
import { join as join42 } from "path";
|
|
54045
54438
|
var MAX_HISTORY = 200;
|
|
54046
54439
|
function historyPath(profileDir2) {
|
|
54047
|
-
return
|
|
54440
|
+
return join42(profileDir2, "input-history.json");
|
|
54048
54441
|
}
|
|
54049
54442
|
function loadInputHistory(profileDir2) {
|
|
54050
54443
|
const path = historyPath(profileDir2);
|
|
@@ -54658,7 +55051,7 @@ async function runStart(profileDir2, profileName) {
|
|
|
54658
55051
|
return;
|
|
54659
55052
|
}
|
|
54660
55053
|
logger.configure({
|
|
54661
|
-
dir:
|
|
55054
|
+
dir: join43(profileDir2, "logs"),
|
|
54662
55055
|
level: resolveLogLevel(),
|
|
54663
55056
|
base: { version: CLI_VERSION, profile: profileName, scope: credential.scope }
|
|
54664
55057
|
});
|
|
@@ -54678,7 +55071,7 @@ async function runStart(profileDir2, profileName) {
|
|
|
54678
55071
|
if (result.action === "updated") {
|
|
54679
55072
|
logger.info("update.updated", { version: result.version });
|
|
54680
55073
|
console.log(t("start.restarting"));
|
|
54681
|
-
const child =
|
|
55074
|
+
const child = spawn12(process.execPath, process.argv.slice(1), {
|
|
54682
55075
|
stdio: "inherit",
|
|
54683
55076
|
env: { ...process.env, FOURPM_NO_UPDATE: "1" }
|
|
54684
55077
|
});
|
|
@@ -54768,7 +55161,7 @@ async function runStart(profileDir2, profileName) {
|
|
|
54768
55161
|
}
|
|
54769
55162
|
|
|
54770
55163
|
// src/commands/attach.ts
|
|
54771
|
-
import { join as
|
|
55164
|
+
import { join as join44 } from "path";
|
|
54772
55165
|
|
|
54773
55166
|
// src/core/control-client.ts
|
|
54774
55167
|
import { connect as connect2 } from "net";
|
|
@@ -54877,7 +55270,7 @@ async function runAttach(profileDir2, profileName) {
|
|
|
54877
55270
|
process.exitCode = 1;
|
|
54878
55271
|
return;
|
|
54879
55272
|
}
|
|
54880
|
-
const socketPath =
|
|
55273
|
+
const socketPath = join44(profileDir2, CONTROL_SOCKET_FILE);
|
|
54881
55274
|
const bus = new SessionBus();
|
|
54882
55275
|
let conn;
|
|
54883
55276
|
try {
|
|
@@ -54908,11 +55301,11 @@ async function runAttach(profileDir2, profileName) {
|
|
|
54908
55301
|
|
|
54909
55302
|
// src/commands/auto-run.ts
|
|
54910
55303
|
import { connect as connect3 } from "net";
|
|
54911
|
-
import { join as
|
|
55304
|
+
import { join as join45 } from "path";
|
|
54912
55305
|
var AUTO_RUN_TIMEOUT_MS = 3 * 60 * 60 * 1e3;
|
|
54913
55306
|
async function runAutoRun(profileDir2, profileName) {
|
|
54914
55307
|
initI18n(readProfileConfig(profileDir2).locale);
|
|
54915
|
-
const socketPath =
|
|
55308
|
+
const socketPath = join45(profileDir2, CONTROL_SOCKET_FILE);
|
|
54916
55309
|
const token = readControlToken(profileDir2);
|
|
54917
55310
|
const code = await new Promise((resolve13) => {
|
|
54918
55311
|
let settled = false;
|
|
@@ -54969,7 +55362,7 @@ async function runAutoRun(profileDir2, profileName) {
|
|
|
54969
55362
|
|
|
54970
55363
|
// src/commands/git-credential.ts
|
|
54971
55364
|
import { connect as connect4 } from "net";
|
|
54972
|
-
import { join as
|
|
55365
|
+
import { join as join46 } from "path";
|
|
54973
55366
|
var REQUEST_TIMEOUT_MS = 2e4;
|
|
54974
55367
|
function requestToken(host, path) {
|
|
54975
55368
|
const profileDir2 = process.env[PROFILE_DIR_ENV];
|
|
@@ -54984,7 +55377,7 @@ function requestToken(host, path) {
|
|
|
54984
55377
|
socket.destroy();
|
|
54985
55378
|
resolve13(value);
|
|
54986
55379
|
};
|
|
54987
|
-
const socket = connect4(
|
|
55380
|
+
const socket = connect4(join46(profileDir2, CONTROL_SOCKET_FILE));
|
|
54988
55381
|
socket.setEncoding("utf8");
|
|
54989
55382
|
const timer = setTimeout(() => finish(null), REQUEST_TIMEOUT_MS);
|
|
54990
55383
|
socket.on("connect", () => {
|
|
@@ -55038,12 +55431,12 @@ async function runGitToken() {
|
|
|
55038
55431
|
}
|
|
55039
55432
|
|
|
55040
55433
|
// src/commands/ai-login.ts
|
|
55041
|
-
import { spawn as
|
|
55434
|
+
import { spawn as spawn13 } from "child_process";
|
|
55042
55435
|
import { mkdirSync as mkdirSync10 } from "fs";
|
|
55043
55436
|
import { basename as basename8 } from "path";
|
|
55044
55437
|
function runProviderLogin(cmd, envVar, dir2) {
|
|
55045
55438
|
return new Promise((resolve13) => {
|
|
55046
|
-
const child =
|
|
55439
|
+
const child = spawn13(cmd, [], {
|
|
55047
55440
|
stdio: "inherit",
|
|
55048
55441
|
env: { ...process.env, [envVar]: dir2 }
|
|
55049
55442
|
});
|
|
@@ -55088,7 +55481,7 @@ async function runAiLogin(profileDir2, profileName, aiFilter) {
|
|
|
55088
55481
|
}
|
|
55089
55482
|
|
|
55090
55483
|
// src/commands/unlink.ts
|
|
55091
|
-
import { rmSync as
|
|
55484
|
+
import { rmSync as rmSync12 } from "fs";
|
|
55092
55485
|
async function runUnlink(profileDir2, profileName) {
|
|
55093
55486
|
initI18n(readProfileConfig(profileDir2).locale);
|
|
55094
55487
|
const cred = readCredential(profileDir2, profileName);
|
|
@@ -55102,7 +55495,7 @@ async function runUnlink(profileDir2, profileName) {
|
|
|
55102
55495
|
} catch (err) {
|
|
55103
55496
|
console.log(t("unlink.revokeFailed", { message: err.message }));
|
|
55104
55497
|
}
|
|
55105
|
-
|
|
55498
|
+
rmSync12(profileDir2, { recursive: true, force: true });
|
|
55106
55499
|
clearDefaultProfileIf(profileName);
|
|
55107
55500
|
console.log(t("unlink.deleted"));
|
|
55108
55501
|
}
|