@4pm/cli 1.19.9 → 1.20.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 +318 -109
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -29519,8 +29519,8 @@ async function runLink(serverUrl, explicitProfile, token = null) {
|
|
|
29519
29519
|
}
|
|
29520
29520
|
|
|
29521
29521
|
// src/commands/start.ts
|
|
29522
|
-
import { spawn as
|
|
29523
|
-
import { join as
|
|
29522
|
+
import { spawn as spawn11 } from "child_process";
|
|
29523
|
+
import { join as join37 } from "path";
|
|
29524
29524
|
|
|
29525
29525
|
// src/core/update.ts
|
|
29526
29526
|
import { execSync as execSync2 } from "child_process";
|
|
@@ -32732,9 +32732,9 @@ async function checkAndUpdate(serverUrl, autoUpdate) {
|
|
|
32732
32732
|
|
|
32733
32733
|
// src/core/ws-client.ts
|
|
32734
32734
|
init_wrapper();
|
|
32735
|
-
import { randomUUID as
|
|
32736
|
-
import { basename as basename7, join as
|
|
32737
|
-
import { existsSync as
|
|
32735
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
32736
|
+
import { basename as basename7, join as join32 } from "path";
|
|
32737
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync8 } from "fs";
|
|
32738
32738
|
|
|
32739
32739
|
// vendor/@4pm/utils/helpers.ts
|
|
32740
32740
|
function backoffJitterMs(attempt, baseMs = 500, maxMs = 1e4) {
|
|
@@ -38389,6 +38389,12 @@ var WsChannels = {
|
|
|
38389
38389
|
// AI knowledge distill (ADR-0190): server → project cli request–reply. The worker runs the AI CLI
|
|
38390
38390
|
// in the project's working dir with a distillation prompt and replies with the composed markdown.
|
|
38391
38391
|
KNOWLEDGE_COMPOSE: "knowledge.compose",
|
|
38392
|
+
// Ticket → FAQ synthesis (ADR-0333): server (platform-pool dispatcher) → platform-pool cli
|
|
38393
|
+
// request–reply. The dispatcher sends the selected support tickets + the 4pm-faq repo with a
|
|
38394
|
+
// short-lived WRITE token; the worker runs an agent-write that distils the tickets into FAQ
|
|
38395
|
+
// entries, commits, pushes a branch, opens a PR, then wipes the token/clone, and replies with the
|
|
38396
|
+
// PR URL. The write token never persists on the worker (leak-safety on release/reassign).
|
|
38397
|
+
FAQ_COMPOSE: "faq.compose",
|
|
38392
38398
|
// Shared AI memory (ADR-0245): cli → server one-way write-back of the compacted rolling memory for
|
|
38393
38399
|
// the project it serves (persisted per project × machine-user link). The read direction rides
|
|
38394
38400
|
// `ws_token` (seeds the cli cache on connect); this channel only carries updates.
|
|
@@ -42799,6 +42805,187 @@ async function runKnowledgeCompose(req, ai2, physicRoot) {
|
|
|
42799
42805
|
return { bodyMarkdown: "", error: lastReason };
|
|
42800
42806
|
}
|
|
42801
42807
|
|
|
42808
|
+
// src/core/faq-compose.ts
|
|
42809
|
+
import { spawn as spawn9, execFile as execFile10 } from "child_process";
|
|
42810
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync7, rmSync as rmSync7 } from "fs";
|
|
42811
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
42812
|
+
import { join as join24 } from "path";
|
|
42813
|
+
import { tmpdir as tmpdir3 } from "os";
|
|
42814
|
+
import { promisify as promisify10 } from "util";
|
|
42815
|
+
var execFileAsync2 = promisify10(execFile10);
|
|
42816
|
+
var NO_USAGE2 = { tokens: 0, input: 0, output: 0, cacheRead: 0, cacheCreation: 0 };
|
|
42817
|
+
var COMPOSE_TIMEOUT_MS2 = 10 * 60 * 1e3;
|
|
42818
|
+
function authedUrl2(url, token) {
|
|
42819
|
+
if (url.startsWith("https://")) return url.replace("https://", `https://x-access-token:${token}@`);
|
|
42820
|
+
return url;
|
|
42821
|
+
}
|
|
42822
|
+
function buildPrompt4(tickets, customPrompt) {
|
|
42823
|
+
const transcript = tickets.map((t2) => {
|
|
42824
|
+
const thread = t2.messages.map((m2) => ` [${m2.author}] ${m2.body}`).join("\n");
|
|
42825
|
+
return `### Ticket ${t2.id} \u2014 ${t2.subject} (${t2.category})
|
|
42826
|
+
${thread}`;
|
|
42827
|
+
}).join("\n\n");
|
|
42828
|
+
const ids = tickets.map((t2) => t2.id);
|
|
42829
|
+
return [
|
|
42830
|
+
"You are maintaining the 4PM product FAQ, stored as markdown in THIS git repository (your CWD).",
|
|
42831
|
+
"From the resolved support tickets below, distil reusable, product-general FAQ entries and add or",
|
|
42832
|
+
"update the appropriate markdown file(s) in this repo (e.g. an FAQ.md, or the closest existing",
|
|
42833
|
+
"topic file \u2014 inspect the repo first and match its structure/style).",
|
|
42834
|
+
"",
|
|
42835
|
+
"Rules:",
|
|
42836
|
+
"- Write generalized Q&A, NOT ticket-specific replies; never include customer names, emails, ids,",
|
|
42837
|
+
" org names or any personal data from the tickets.",
|
|
42838
|
+
"- Merge with existing entries instead of duplicating; keep the existing formatting and headings.",
|
|
42839
|
+
"- If a ticket yields no reusable FAQ value, skip it. If nothing is worth adding, make NO changes.",
|
|
42840
|
+
"- Only edit markdown documentation files; do not touch code, CI, or unrelated files.",
|
|
42841
|
+
"- Do NOT run git commit/push or open a PR yourself \u2014 just leave the edited files in the working",
|
|
42842
|
+
" tree; the surrounding tooling handles commit + PR.",
|
|
42843
|
+
...customPrompt?.trim() ? ["", "Additional instruction from the operator (follow it too):", customPrompt.trim()] : [],
|
|
42844
|
+
"",
|
|
42845
|
+
"===== RESOLVED TICKETS =====",
|
|
42846
|
+
transcript,
|
|
42847
|
+
"",
|
|
42848
|
+
"===== REQUIRED FINAL OUTPUT =====",
|
|
42849
|
+
"After finishing all file edits, output \u2014 as the LAST thing, on its own \u2014 a single fenced JSON",
|
|
42850
|
+
"block summarizing what you did PER TICKET (created/updated which FAQ entry, or skipped + why):",
|
|
42851
|
+
"```json",
|
|
42852
|
+
`{"perTicket":[${ids.map((id) => `{"ticketId":"${id}","summary":"..."}`).join(",")}]}`,
|
|
42853
|
+
"```",
|
|
42854
|
+
"Include exactly one entry per ticket id above; keep each summary to one or two sentences."
|
|
42855
|
+
].join("\n");
|
|
42856
|
+
}
|
|
42857
|
+
function parsePerTicket(text) {
|
|
42858
|
+
const idx = text.lastIndexOf('"perTicket"');
|
|
42859
|
+
if (idx === -1) return [];
|
|
42860
|
+
const open = text.lastIndexOf("{", idx);
|
|
42861
|
+
if (open === -1) return [];
|
|
42862
|
+
let depth = 0;
|
|
42863
|
+
for (let i = open; i < text.length; i++) {
|
|
42864
|
+
if (text[i] === "{") depth++;
|
|
42865
|
+
else if (text[i] === "}") {
|
|
42866
|
+
depth--;
|
|
42867
|
+
if (depth === 0) {
|
|
42868
|
+
try {
|
|
42869
|
+
const obj = JSON.parse(text.slice(open, i + 1));
|
|
42870
|
+
if (Array.isArray(obj.perTicket)) {
|
|
42871
|
+
return obj.perTicket.filter((e) => !!e && typeof e.ticketId === "string").map((e) => ({ ticketId: e.ticketId, summary: String(e.summary ?? "") }));
|
|
42872
|
+
}
|
|
42873
|
+
} catch {
|
|
42874
|
+
return [];
|
|
42875
|
+
}
|
|
42876
|
+
return [];
|
|
42877
|
+
}
|
|
42878
|
+
}
|
|
42879
|
+
}
|
|
42880
|
+
return [];
|
|
42881
|
+
}
|
|
42882
|
+
function runClaudeOnce2(cmd, profile, prompt, cwd2, extraEnv) {
|
|
42883
|
+
return new Promise((resolve10, reject) => {
|
|
42884
|
+
const isClaude = cmd.includes("claude");
|
|
42885
|
+
const args = [
|
|
42886
|
+
"-p",
|
|
42887
|
+
...profile?.model ? ["--model", profile.model] : [],
|
|
42888
|
+
...isClaude ? ["--output-format", "stream-json", "--verbose"] : [],
|
|
42889
|
+
// Write-capable headless agent (ADR-0271): auto-approve tools so it never stalls on a prompt.
|
|
42890
|
+
...isClaude ? ["--permission-mode", "bypassPermissions"] : [],
|
|
42891
|
+
...cmd.includes("codex") ? ["--dangerously-bypass-approvals-and-sandbox"] : []
|
|
42892
|
+
];
|
|
42893
|
+
const env3 = {
|
|
42894
|
+
...process.env,
|
|
42895
|
+
...extraEnv,
|
|
42896
|
+
...profile ? { CLAUDE_CONFIG_DIR: profile.dir } : {}
|
|
42897
|
+
};
|
|
42898
|
+
const child = spawn9(cmd, args, { stdio: ["pipe", "pipe", "pipe"], cwd: cwd2, env: env3 });
|
|
42899
|
+
const parser = createAiStreamParser(cmd);
|
|
42900
|
+
let out = "";
|
|
42901
|
+
let err = "";
|
|
42902
|
+
const timer = setTimeout(() => {
|
|
42903
|
+
child.kill("SIGKILL");
|
|
42904
|
+
reject(new Error("faq-compose agent timed out"));
|
|
42905
|
+
}, COMPOSE_TIMEOUT_MS2);
|
|
42906
|
+
timer.unref();
|
|
42907
|
+
child.stdout.on("data", (d) => out += parser.push(d.toString()));
|
|
42908
|
+
child.stderr.on("data", (d) => err += d.toString());
|
|
42909
|
+
child.on("error", (e) => {
|
|
42910
|
+
clearTimeout(timer);
|
|
42911
|
+
reject(e);
|
|
42912
|
+
});
|
|
42913
|
+
child.on("close", (code) => {
|
|
42914
|
+
clearTimeout(timer);
|
|
42915
|
+
out += parser.flush();
|
|
42916
|
+
resolve10({ code: code ?? -1, out, err, usage: parser.usage() });
|
|
42917
|
+
});
|
|
42918
|
+
child.stdin.write(prompt);
|
|
42919
|
+
child.stdin.end();
|
|
42920
|
+
});
|
|
42921
|
+
}
|
|
42922
|
+
async function runClaudeWithFailover2(prompt, cwd2, ai2) {
|
|
42923
|
+
const attempts = ai2.profiles.length > 0 ? ai2.profiles : [null];
|
|
42924
|
+
let lastReason = "no attempt";
|
|
42925
|
+
for (let i = 0; i < attempts.length; i++) {
|
|
42926
|
+
const { code, out, err, usage } = await runClaudeOnce2(ai2.cmd, attempts[i], prompt, cwd2, ai2.env);
|
|
42927
|
+
if (code === 0) return { usage, text: out.trim() };
|
|
42928
|
+
lastReason = `exited ${code}: ${(err || out).slice(0, 500)}`;
|
|
42929
|
+
if (i === attempts.length - 1) break;
|
|
42930
|
+
}
|
|
42931
|
+
throw new Error(`claude ${lastReason}`);
|
|
42932
|
+
}
|
|
42933
|
+
var MAX_OUTPUT_CHARS = 8e3;
|
|
42934
|
+
async function runFaqCompose(req, ai2) {
|
|
42935
|
+
if (!req.repo.token) return { prUrl: "", error: "no write token" };
|
|
42936
|
+
const workDir = join24(tmpdir3(), "4pm-faq-sync", randomUUID3());
|
|
42937
|
+
const gitEnv = { ...process.env, GIT_TERMINAL_PROMPT: "0" };
|
|
42938
|
+
const authed = authedUrl2(req.repo.url, req.repo.token);
|
|
42939
|
+
try {
|
|
42940
|
+
mkdirSync7(workDir, { recursive: true });
|
|
42941
|
+
await execFileAsync2("git", ["clone", "--depth", "1", "--branch", req.repo.branch, authed, workDir], { env: gitEnv });
|
|
42942
|
+
await execFileAsync2("git", ["-C", workDir, "config", "user.name", "4PM FAQ Bot"], { env: gitEnv });
|
|
42943
|
+
await execFileAsync2("git", ["-C", workDir, "config", "user.email", "faq-bot@4pm.app"], { env: gitEnv });
|
|
42944
|
+
await execFileAsync2("git", ["-C", workDir, "checkout", "-b", req.headBranch], { env: gitEnv });
|
|
42945
|
+
const { usage, text } = await runClaudeWithFailover2(buildPrompt4(req.tickets, req.customPrompt), workDir, ai2);
|
|
42946
|
+
const output = text.slice(0, MAX_OUTPUT_CHARS);
|
|
42947
|
+
const perTicket = parsePerTicket(text);
|
|
42948
|
+
await execFileAsync2("git", ["-C", workDir, "add", "-A"], { env: gitEnv });
|
|
42949
|
+
const staged = await execFileAsync2("git", ["-C", workDir, "diff", "--cached", "--name-only"], { env: gitEnv });
|
|
42950
|
+
if (!staged.stdout.trim()) {
|
|
42951
|
+
return { prUrl: "", output, perTicket, tokens: usage.tokens || estimateTokens2(text), finishedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
42952
|
+
}
|
|
42953
|
+
await execFileAsync2("git", ["-C", workDir, "commit", "-m", "docs(faq): synthesize from support tickets (4PM)"], { env: gitEnv });
|
|
42954
|
+
await execFileAsync2("git", ["-C", workDir, "push", "-u", "origin", req.headBranch], { env: gitEnv });
|
|
42955
|
+
let prUrl = "";
|
|
42956
|
+
try {
|
|
42957
|
+
const pr2 = await execFileAsync2(
|
|
42958
|
+
"gh",
|
|
42959
|
+
["pr", "create", "--fill", "--head", req.headBranch, "--base", req.repo.branch],
|
|
42960
|
+
{ cwd: workDir, env: { ...gitEnv, GH_TOKEN: req.repo.token } }
|
|
42961
|
+
);
|
|
42962
|
+
prUrl = pr2.stdout.trim().split("\n").pop() ?? "";
|
|
42963
|
+
} catch (err) {
|
|
42964
|
+
logger.warn("faq.compose.pr.failed", { error: String(err) });
|
|
42965
|
+
}
|
|
42966
|
+
const u2 = usage.tokens > 0 ? usage : { ...NO_USAGE2 };
|
|
42967
|
+
return {
|
|
42968
|
+
prUrl,
|
|
42969
|
+
branch: req.headBranch,
|
|
42970
|
+
output,
|
|
42971
|
+
perTicket,
|
|
42972
|
+
tokens: u2.tokens,
|
|
42973
|
+
...usage.tokens > 0 ? { tokensBreakdown: { input: usage.input, output: usage.output, cacheRead: usage.cacheRead, cacheCreation: usage.cacheCreation } } : {},
|
|
42974
|
+
finishedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
42975
|
+
};
|
|
42976
|
+
} catch (err) {
|
|
42977
|
+
logger.warn("faq.compose.failed", { error: String(err) });
|
|
42978
|
+
return { prUrl: "", error: String(err) };
|
|
42979
|
+
} finally {
|
|
42980
|
+
if (existsSync14(workDir)) {
|
|
42981
|
+
try {
|
|
42982
|
+
rmSync7(workDir, { recursive: true, force: true });
|
|
42983
|
+
} catch {
|
|
42984
|
+
}
|
|
42985
|
+
}
|
|
42986
|
+
}
|
|
42987
|
+
}
|
|
42988
|
+
|
|
42802
42989
|
// src/core/ws-client/handlers/support.ts
|
|
42803
42990
|
function handleSupportChannels(ctx, message, payload) {
|
|
42804
42991
|
switch (message.channel) {
|
|
@@ -42855,6 +43042,28 @@ function handleSupportChannels(ctx, message, payload) {
|
|
|
42855
43042
|
}).finally(() => ctx.bus.endBusy("knowledge-compose"));
|
|
42856
43043
|
return true;
|
|
42857
43044
|
}
|
|
43045
|
+
case WsChannels.FAQ_COMPOSE: {
|
|
43046
|
+
const freq = payload;
|
|
43047
|
+
const fcfg = readProfileConfig(ctx.profileDir);
|
|
43048
|
+
const fcmd = fcfg.aiCli || "claude";
|
|
43049
|
+
const fai = {
|
|
43050
|
+
cmd: fcmd,
|
|
43051
|
+
profiles: resolveClaudeProfiles(fcfg, getWorkingProfile(ctx.profileDir, fcmd)),
|
|
43052
|
+
env: fcfg.aiEnv
|
|
43053
|
+
};
|
|
43054
|
+
ctx.bus.push({ source: "server", kind: "aireq", text: `${fcmd} \u2039 synthesize FAQ (${freq.tickets.length} tickets)` });
|
|
43055
|
+
ctx.bus.startBusy("faq-compose");
|
|
43056
|
+
void runFaqCompose(freq, fai).then((reply) => {
|
|
43057
|
+
if (reply.error) {
|
|
43058
|
+
ctx.bus.push({ source: "server", kind: "log", text: `faq-compose failed: ${reply.error}`, level: "warn" });
|
|
43059
|
+
} else {
|
|
43060
|
+
ctx.bus.push({ source: "server", kind: "aires", text: `${fcmd} \u203A ${reply.prUrl || "no changes"}` });
|
|
43061
|
+
}
|
|
43062
|
+
reportToolResult(fcmd, !reply.error, reply.error);
|
|
43063
|
+
ctx.send(WsChannels.FAQ_COMPOSE, reply, message.id);
|
|
43064
|
+
}).finally(() => ctx.bus.endBusy("faq-compose"));
|
|
43065
|
+
return true;
|
|
43066
|
+
}
|
|
42858
43067
|
default:
|
|
42859
43068
|
return false;
|
|
42860
43069
|
}
|
|
@@ -42953,7 +43162,7 @@ function handleToolsChannels(ctx, message, payload) {
|
|
|
42953
43162
|
|
|
42954
43163
|
// src/core/agents.ts
|
|
42955
43164
|
import { readFile as readFile10, writeFile as writeFile8, mkdir as mkdir8, readdir as readdir4, rm as rm5 } from "fs/promises";
|
|
42956
|
-
import { join as
|
|
43165
|
+
import { join as join25 } from "path";
|
|
42957
43166
|
var AGENTS_REL = ".claude/agents";
|
|
42958
43167
|
var SKILLS_REL = ".claude/skills";
|
|
42959
43168
|
var MEMORY_DIRS = /* @__PURE__ */ new Set(["short-memory", "long-memory"]);
|
|
@@ -42977,17 +43186,17 @@ async function listAgents(root) {
|
|
|
42977
43186
|
const subagents = [];
|
|
42978
43187
|
const skills = [];
|
|
42979
43188
|
try {
|
|
42980
|
-
for (const e of await readdir4(
|
|
43189
|
+
for (const e of await readdir4(join25(root, AGENTS_REL), { withFileTypes: true })) {
|
|
42981
43190
|
if (!e.isFile() || !e.name.endsWith(".md") || MEMORY_DIRS.has(e.name)) continue;
|
|
42982
|
-
const fm = frontmatter(await readText2(
|
|
43191
|
+
const fm = frontmatter(await readText2(join25(root, AGENTS_REL, e.name)));
|
|
42983
43192
|
subagents.push({ name: e.name.replace(/\.md$/, ""), description: fm.description });
|
|
42984
43193
|
}
|
|
42985
43194
|
} catch {
|
|
42986
43195
|
}
|
|
42987
43196
|
try {
|
|
42988
|
-
for (const e of await readdir4(
|
|
43197
|
+
for (const e of await readdir4(join25(root, SKILLS_REL), { withFileTypes: true })) {
|
|
42989
43198
|
if (!e.isDirectory()) continue;
|
|
42990
|
-
const fm = frontmatter(await readText2(
|
|
43199
|
+
const fm = frontmatter(await readText2(join25(root, SKILLS_REL, e.name, "SKILL.md")));
|
|
42991
43200
|
skills.push({ name: e.name, description: fm.description });
|
|
42992
43201
|
}
|
|
42993
43202
|
} catch {
|
|
@@ -42998,15 +43207,15 @@ async function listAgents(root) {
|
|
|
42998
43207
|
}
|
|
42999
43208
|
function itemPath(root, kind, name) {
|
|
43000
43209
|
const n = safeName(name);
|
|
43001
|
-
return kind === "subagent" ?
|
|
43210
|
+
return kind === "subagent" ? join25(root, AGENTS_REL, `${n}.md`) : join25(root, SKILLS_REL, n, "SKILL.md");
|
|
43002
43211
|
}
|
|
43003
43212
|
async function readAgent(root, kind, name) {
|
|
43004
43213
|
const content = await readText2(itemPath(root, kind, name));
|
|
43005
43214
|
if (kind !== "subagent") return { content };
|
|
43006
43215
|
const n = safeName(name);
|
|
43007
43216
|
const [short, long] = await Promise.all([
|
|
43008
|
-
readText2(
|
|
43009
|
-
readText2(
|
|
43217
|
+
readText2(join25(root, AGENTS_REL, "short-memory", `${n}.md`)),
|
|
43218
|
+
readText2(join25(root, AGENTS_REL, "long-memory", `${n}.md`))
|
|
43010
43219
|
]);
|
|
43011
43220
|
return { content, memory: { short, long } };
|
|
43012
43221
|
}
|
|
@@ -43016,12 +43225,12 @@ async function writeAgent(root, req) {
|
|
|
43016
43225
|
try {
|
|
43017
43226
|
if (req.action === "delete") {
|
|
43018
43227
|
if (req.kind === "subagent") await rm5(itemPath(root, "subagent", n), { force: true });
|
|
43019
|
-
else await rm5(
|
|
43228
|
+
else await rm5(join25(root, SKILLS_REL, n), { recursive: true, force: true });
|
|
43020
43229
|
return { ok: true };
|
|
43021
43230
|
}
|
|
43022
43231
|
if (req.content === void 0) return { ok: false, error: "content is required" };
|
|
43023
43232
|
const path = itemPath(root, req.kind, n);
|
|
43024
|
-
await mkdir8(
|
|
43233
|
+
await mkdir8(join25(path, ".."), { recursive: true });
|
|
43025
43234
|
await writeFile8(path, req.content, "utf8");
|
|
43026
43235
|
return { ok: true };
|
|
43027
43236
|
} catch (err) {
|
|
@@ -43031,7 +43240,7 @@ async function writeAgent(root, req) {
|
|
|
43031
43240
|
|
|
43032
43241
|
// src/core/secrets.ts
|
|
43033
43242
|
import { readFile as readFile11, writeFile as writeFile9 } from "fs/promises";
|
|
43034
|
-
import { join as
|
|
43243
|
+
import { join as join26 } from "path";
|
|
43035
43244
|
var SECURITY_REL = "AI_SECURITY.md";
|
|
43036
43245
|
var PLACEHOLDER_REL = "AI_PLACEHOLDER.md";
|
|
43037
43246
|
var SECRETS_REL = "project.secrets.json";
|
|
@@ -43044,7 +43253,7 @@ async function readText3(path, fallback = "") {
|
|
|
43044
43253
|
}
|
|
43045
43254
|
async function readSecretsObject(root) {
|
|
43046
43255
|
try {
|
|
43047
|
-
const v2 = JSON.parse(await readFile11(
|
|
43256
|
+
const v2 = JSON.parse(await readFile11(join26(root, SECRETS_REL), "utf8"));
|
|
43048
43257
|
return v2 && typeof v2 === "object" ? v2 : {};
|
|
43049
43258
|
} catch {
|
|
43050
43259
|
return {};
|
|
@@ -43063,8 +43272,8 @@ function placeholderKeys(text) {
|
|
|
43063
43272
|
}
|
|
43064
43273
|
async function readSecrets(root) {
|
|
43065
43274
|
const [security, placeholder, secrets] = await Promise.all([
|
|
43066
|
-
readText3(
|
|
43067
|
-
readText3(
|
|
43275
|
+
readText3(join26(root, SECURITY_REL)),
|
|
43276
|
+
readText3(join26(root, PLACEHOLDER_REL)),
|
|
43068
43277
|
readSecretsObject(root)
|
|
43069
43278
|
]);
|
|
43070
43279
|
const names = /* @__PURE__ */ new Set([...placeholderKeys(placeholder), ...Object.keys(secrets)]);
|
|
@@ -43077,14 +43286,14 @@ async function readSecrets(root) {
|
|
|
43077
43286
|
async function writeSecrets(root, req) {
|
|
43078
43287
|
try {
|
|
43079
43288
|
if (req.kind === "security") {
|
|
43080
|
-
await writeFile9(
|
|
43289
|
+
await writeFile9(join26(root, SECURITY_REL), req.content, "utf8");
|
|
43081
43290
|
} else if (req.kind === "placeholder") {
|
|
43082
|
-
await writeFile9(
|
|
43291
|
+
await writeFile9(join26(root, PLACEHOLDER_REL), req.content, "utf8");
|
|
43083
43292
|
} else {
|
|
43084
43293
|
const secrets = await readSecretsObject(root);
|
|
43085
43294
|
if (req.kind === "secret") secrets[req.key] = req.value;
|
|
43086
43295
|
else delete secrets[req.key];
|
|
43087
|
-
await writeFile9(
|
|
43296
|
+
await writeFile9(join26(root, SECRETS_REL), JSON.stringify(secrets, null, 2) + "\n", "utf8");
|
|
43088
43297
|
}
|
|
43089
43298
|
return { ok: true };
|
|
43090
43299
|
} catch (err) {
|
|
@@ -43094,12 +43303,12 @@ async function writeSecrets(root, req) {
|
|
|
43094
43303
|
|
|
43095
43304
|
// src/core/agent-tools.ts
|
|
43096
43305
|
import { readFile as readFile12, writeFile as writeFile10, mkdir as mkdir9 } from "fs/promises";
|
|
43097
|
-
import { dirname as dirname8, join as
|
|
43306
|
+
import { dirname as dirname8, join as join27 } from "path";
|
|
43098
43307
|
var SHARED_REL = ".claude/settings.json";
|
|
43099
43308
|
var LOCAL_REL = ".claude/settings.local.json";
|
|
43100
43309
|
var SECRETS_DENY = "Read(./project.secrets.json)";
|
|
43101
43310
|
function fileFor(root, scope) {
|
|
43102
|
-
return
|
|
43311
|
+
return join27(root, scope === "shared" ? SHARED_REL : LOCAL_REL);
|
|
43103
43312
|
}
|
|
43104
43313
|
async function readSettingsObject(path) {
|
|
43105
43314
|
try {
|
|
@@ -43136,7 +43345,7 @@ async function writeAgentTools(root, scope, permissions) {
|
|
|
43136
43345
|
// src/core/packages.ts
|
|
43137
43346
|
import { createHash as createHash4 } from "crypto";
|
|
43138
43347
|
import { readFile as readFile13, writeFile as writeFile11, mkdir as mkdir10, readdir as readdir5, rm as rm6, stat as stat4 } from "fs/promises";
|
|
43139
|
-
import { dirname as dirname9, join as
|
|
43348
|
+
import { dirname as dirname9, join as join28, relative } from "path";
|
|
43140
43349
|
var AGENTS_REL2 = ".claude/agents";
|
|
43141
43350
|
var SKILLS_REL2 = ".claude/skills";
|
|
43142
43351
|
var MANIFEST_REL = ".claude/.4pm-packages.json";
|
|
@@ -43160,7 +43369,7 @@ async function listFilesRel(dir2) {
|
|
|
43160
43369
|
return;
|
|
43161
43370
|
}
|
|
43162
43371
|
for (const e of entries2) {
|
|
43163
|
-
const full =
|
|
43372
|
+
const full = join28(d, e.name);
|
|
43164
43373
|
if (e.isDirectory()) await walk2(full);
|
|
43165
43374
|
else if (e.isFile()) out.push(relative(dir2, full));
|
|
43166
43375
|
}
|
|
@@ -43171,16 +43380,16 @@ async function listFilesRel(dir2) {
|
|
|
43171
43380
|
async function readArtifactFiles(root, kind, slug) {
|
|
43172
43381
|
const files = [];
|
|
43173
43382
|
if (kind === "subagent") {
|
|
43174
|
-
const agent = await readMaybe(
|
|
43383
|
+
const agent = await readMaybe(join28(root, AGENTS_REL2, `${slug}.md`));
|
|
43175
43384
|
if (agent) files.push({ path: "agent.md", contentBase64: agent.toString("base64") });
|
|
43176
|
-
const short = await readMaybe(
|
|
43385
|
+
const short = await readMaybe(join28(root, AGENTS_REL2, "short-memory", `${slug}.md`));
|
|
43177
43386
|
if (short) files.push({ path: "short-memory.md", contentBase64: short.toString("base64") });
|
|
43178
|
-
const long = await readMaybe(
|
|
43387
|
+
const long = await readMaybe(join28(root, AGENTS_REL2, "long-memory", `${slug}.md`));
|
|
43179
43388
|
if (long) files.push({ path: "long-memory.md", contentBase64: long.toString("base64") });
|
|
43180
43389
|
} else {
|
|
43181
|
-
const base =
|
|
43390
|
+
const base = join28(root, SKILLS_REL2, slug);
|
|
43182
43391
|
for (const rel of await listFilesRel(base)) {
|
|
43183
|
-
const buf = await readMaybe(
|
|
43392
|
+
const buf = await readMaybe(join28(base, rel));
|
|
43184
43393
|
if (buf) files.push({ path: rel.split("\\").join("/"), contentBase64: buf.toString("base64") });
|
|
43185
43394
|
}
|
|
43186
43395
|
}
|
|
@@ -43198,7 +43407,7 @@ async function artifactSha256(root, kind, slug) {
|
|
|
43198
43407
|
return h.digest("hex");
|
|
43199
43408
|
}
|
|
43200
43409
|
async function artifactExists(root, kind, slug) {
|
|
43201
|
-
const target = kind === "subagent" ?
|
|
43410
|
+
const target = kind === "subagent" ? join28(root, AGENTS_REL2, `${slug}.md`) : join28(root, SKILLS_REL2, slug);
|
|
43202
43411
|
try {
|
|
43203
43412
|
await stat4(target);
|
|
43204
43413
|
return true;
|
|
@@ -43207,7 +43416,7 @@ async function artifactExists(root, kind, slug) {
|
|
|
43207
43416
|
}
|
|
43208
43417
|
}
|
|
43209
43418
|
async function readManifest(root) {
|
|
43210
|
-
const buf = await readMaybe(
|
|
43419
|
+
const buf = await readMaybe(join28(root, MANIFEST_REL));
|
|
43211
43420
|
if (!buf) return {};
|
|
43212
43421
|
try {
|
|
43213
43422
|
return JSON.parse(buf.toString("utf8"));
|
|
@@ -43216,7 +43425,7 @@ async function readManifest(root) {
|
|
|
43216
43425
|
}
|
|
43217
43426
|
}
|
|
43218
43427
|
async function writeManifest(root, manifest) {
|
|
43219
|
-
const path =
|
|
43428
|
+
const path = join28(root, MANIFEST_REL);
|
|
43220
43429
|
await mkdir10(dirname9(path), { recursive: true });
|
|
43221
43430
|
await writeFile11(path, JSON.stringify(manifest, null, 2), "utf8");
|
|
43222
43431
|
}
|
|
@@ -43225,24 +43434,24 @@ async function writeArtifactFiles(root, kind, slug, files) {
|
|
|
43225
43434
|
if (kind === "subagent") {
|
|
43226
43435
|
const agent = byPath.get("agent.md");
|
|
43227
43436
|
if (agent) {
|
|
43228
|
-
await mkdir10(
|
|
43229
|
-
await writeFile11(
|
|
43437
|
+
await mkdir10(join28(root, AGENTS_REL2), { recursive: true });
|
|
43438
|
+
await writeFile11(join28(root, AGENTS_REL2, `${slug}.md`), agent);
|
|
43230
43439
|
}
|
|
43231
43440
|
const short = byPath.get("short-memory.md");
|
|
43232
43441
|
if (short) {
|
|
43233
|
-
await mkdir10(
|
|
43234
|
-
await writeFile11(
|
|
43442
|
+
await mkdir10(join28(root, AGENTS_REL2, "short-memory"), { recursive: true });
|
|
43443
|
+
await writeFile11(join28(root, AGENTS_REL2, "short-memory", `${slug}.md`), short);
|
|
43235
43444
|
}
|
|
43236
43445
|
const long = byPath.get("long-memory.md");
|
|
43237
43446
|
if (long) {
|
|
43238
|
-
await mkdir10(
|
|
43239
|
-
await writeFile11(
|
|
43447
|
+
await mkdir10(join28(root, AGENTS_REL2, "long-memory"), { recursive: true });
|
|
43448
|
+
await writeFile11(join28(root, AGENTS_REL2, "long-memory", `${slug}.md`), long);
|
|
43240
43449
|
}
|
|
43241
43450
|
} else {
|
|
43242
|
-
const base =
|
|
43451
|
+
const base = join28(root, SKILLS_REL2, slug);
|
|
43243
43452
|
for (const [rel, buf] of byPath) {
|
|
43244
43453
|
if (rel.includes("..") || rel.startsWith("/")) continue;
|
|
43245
|
-
const dest =
|
|
43454
|
+
const dest = join28(base, rel);
|
|
43246
43455
|
await mkdir10(dirname9(dest), { recursive: true });
|
|
43247
43456
|
await writeFile11(dest, buf);
|
|
43248
43457
|
}
|
|
@@ -43308,11 +43517,11 @@ async function removePackage(root, slug) {
|
|
|
43308
43517
|
const entry = manifest[n];
|
|
43309
43518
|
if (!entry) return { ok: false, error: "not installed" };
|
|
43310
43519
|
if (entry.kind === "subagent") {
|
|
43311
|
-
await rm6(
|
|
43312
|
-
await rm6(
|
|
43313
|
-
await rm6(
|
|
43520
|
+
await rm6(join28(root, AGENTS_REL2, `${n}.md`), { force: true });
|
|
43521
|
+
await rm6(join28(root, AGENTS_REL2, "short-memory", `${n}.md`), { force: true });
|
|
43522
|
+
await rm6(join28(root, AGENTS_REL2, "long-memory", `${n}.md`), { force: true });
|
|
43314
43523
|
} else {
|
|
43315
|
-
await rm6(
|
|
43524
|
+
await rm6(join28(root, SKILLS_REL2, n), { recursive: true, force: true });
|
|
43316
43525
|
}
|
|
43317
43526
|
delete manifest[n];
|
|
43318
43527
|
await writeManifest(root, manifest);
|
|
@@ -43324,7 +43533,7 @@ async function removePackage(root, slug) {
|
|
|
43324
43533
|
|
|
43325
43534
|
// src/core/graph.ts
|
|
43326
43535
|
import { readFile as readFile14, readdir as readdir6 } from "fs/promises";
|
|
43327
|
-
import { join as
|
|
43536
|
+
import { join as join29, relative as relative2, dirname as dirname10, resolve as resolve9, extname as extname2, basename as basename6 } from "path";
|
|
43328
43537
|
var MAX_FILES = 2e3;
|
|
43329
43538
|
var MAX_NODES = 3e3;
|
|
43330
43539
|
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", "out", "coverage", ".turbo"]);
|
|
@@ -43344,7 +43553,7 @@ async function walk(root, accept) {
|
|
|
43344
43553
|
if (e.name.startsWith(".") && e.name !== ".claude") {
|
|
43345
43554
|
if (e.isDirectory()) continue;
|
|
43346
43555
|
}
|
|
43347
|
-
const full =
|
|
43556
|
+
const full = join29(dir2, e.name);
|
|
43348
43557
|
if (e.isDirectory()) {
|
|
43349
43558
|
if (!SKIP_DIRS.has(e.name)) stack.push(full);
|
|
43350
43559
|
} else if (accept(full)) {
|
|
@@ -43464,12 +43673,12 @@ async function buildGraph(root, mode) {
|
|
|
43464
43673
|
|
|
43465
43674
|
// src/core/rag.ts
|
|
43466
43675
|
import { readFile as readFile15, writeFile as writeFile12, statfs as statfs2, mkdir as mkdir11 } from "fs/promises";
|
|
43467
|
-
import { existsSync as
|
|
43468
|
-
import { execFile as
|
|
43676
|
+
import { existsSync as existsSync15 } from "fs";
|
|
43677
|
+
import { execFile as execFile11, spawn as spawn10 } from "child_process";
|
|
43469
43678
|
import { totalmem as totalmem2, cpus as cpus2 } from "os";
|
|
43470
|
-
import { join as
|
|
43471
|
-
import { promisify as
|
|
43472
|
-
var run10 =
|
|
43679
|
+
import { join as join30 } from "path";
|
|
43680
|
+
import { promisify as promisify11 } from "util";
|
|
43681
|
+
var run10 = promisify11(execFile11);
|
|
43473
43682
|
var RAG_REL = ".claude/rag";
|
|
43474
43683
|
async function tryRun(cmd, args) {
|
|
43475
43684
|
try {
|
|
@@ -43488,7 +43697,7 @@ async function detectPython() {
|
|
|
43488
43697
|
}
|
|
43489
43698
|
async function installLog(root) {
|
|
43490
43699
|
try {
|
|
43491
|
-
const text = await readFile15(
|
|
43700
|
+
const text = await readFile15(join30(root, RAG_REL, "install.log"), "utf8");
|
|
43492
43701
|
return text.split("\n").filter((l) => l.length > 0).slice(-200);
|
|
43493
43702
|
} catch {
|
|
43494
43703
|
return [];
|
|
@@ -43550,14 +43759,14 @@ async function ragStatus(root) {
|
|
|
43550
43759
|
};
|
|
43551
43760
|
let modelName = "";
|
|
43552
43761
|
try {
|
|
43553
|
-
modelName = (await readFile15(
|
|
43762
|
+
modelName = (await readFile15(join30(root, RAG_REL, ".installed"), "utf8")).trim();
|
|
43554
43763
|
} catch {
|
|
43555
43764
|
modelName = "";
|
|
43556
43765
|
}
|
|
43557
43766
|
const modelPresent = modelName.length > 0;
|
|
43558
43767
|
return {
|
|
43559
43768
|
installed: fastembed && sqliteVec && modelPresent,
|
|
43560
|
-
installing:
|
|
43769
|
+
installing: existsSync15(join30(root, RAG_REL, ".installing")),
|
|
43561
43770
|
python: { found: py.found, version: py.version },
|
|
43562
43771
|
deps: { pip, fastembed, sqliteVec },
|
|
43563
43772
|
model: { present: modelPresent, name: modelName },
|
|
@@ -43568,9 +43777,9 @@ async function ragStatus(root) {
|
|
|
43568
43777
|
};
|
|
43569
43778
|
}
|
|
43570
43779
|
async function readIndexMeta(root) {
|
|
43571
|
-
const indexing =
|
|
43780
|
+
const indexing = existsSync15(join30(root, RAG_REL, ".indexing"));
|
|
43572
43781
|
try {
|
|
43573
|
-
const m2 = JSON.parse(await readFile15(
|
|
43782
|
+
const m2 = JSON.parse(await readFile15(join30(root, RAG_REL, ".index.json"), "utf8"));
|
|
43574
43783
|
return {
|
|
43575
43784
|
present: true,
|
|
43576
43785
|
indexing,
|
|
@@ -43637,14 +43846,14 @@ if __name__ == "__main__":
|
|
|
43637
43846
|
elif cmd == "query": query(sys.argv[2], int(sys.argv[3]) if len(sys.argv) > 3 else 8)
|
|
43638
43847
|
`;
|
|
43639
43848
|
async function ensureEngine(root) {
|
|
43640
|
-
await mkdir11(
|
|
43641
|
-
await writeFile12(
|
|
43849
|
+
await mkdir11(join30(root, RAG_REL), { recursive: true }).catch(() => void 0);
|
|
43850
|
+
await writeFile12(join30(root, RAG_REL, "rag_engine.py"), RAG_ENGINE_PY, "utf8");
|
|
43642
43851
|
}
|
|
43643
43852
|
async function ragReindex(root) {
|
|
43644
43853
|
const py = await detectPython();
|
|
43645
43854
|
if (!py.found) return { ok: false, started: false, error: "python3 not found" };
|
|
43646
|
-
if (!
|
|
43647
|
-
if (
|
|
43855
|
+
if (!existsSync15(join30(root, RAG_REL, ".installed"))) return { ok: false, started: false, error: "RAG is not installed" };
|
|
43856
|
+
if (existsSync15(join30(root, RAG_REL, ".indexing"))) return { ok: true, started: true };
|
|
43648
43857
|
await ensureEngine(root);
|
|
43649
43858
|
const script = `
|
|
43650
43859
|
set +e
|
|
@@ -43652,7 +43861,7 @@ touch "${RAG_REL}/.indexing"
|
|
|
43652
43861
|
{ echo "[reindex] $(date)"; "$RAG_PY" "${RAG_REL}/rag_engine.py" reindex 2>&1; } >> "${RAG_REL}/index.log" 2>&1
|
|
43653
43862
|
rm -f "${RAG_REL}/.indexing"
|
|
43654
43863
|
`;
|
|
43655
|
-
const child =
|
|
43864
|
+
const child = spawn10("bash", ["-c", script], {
|
|
43656
43865
|
cwd: root,
|
|
43657
43866
|
env: { ...process.env, RAG_PY: py.bin },
|
|
43658
43867
|
detached: true,
|
|
@@ -43664,12 +43873,12 @@ rm -f "${RAG_REL}/.indexing"
|
|
|
43664
43873
|
async function ragQuery(root, queryText, k2 = 8) {
|
|
43665
43874
|
const py = await detectPython();
|
|
43666
43875
|
if (!py.found) return { results: [], error: "python3 not found" };
|
|
43667
|
-
if (!
|
|
43876
|
+
if (!existsSync15(join30(root, RAG_REL, "index.db"))) return { results: [], error: "no index \u2014 reindex first" };
|
|
43668
43877
|
await ensureEngine(root);
|
|
43669
43878
|
try {
|
|
43670
43879
|
const { stdout } = await run10(
|
|
43671
43880
|
py.bin,
|
|
43672
|
-
[
|
|
43881
|
+
[join30(RAG_REL, "rag_engine.py"), "query", queryText, String(Math.min(Math.max(k2, 1), 20))],
|
|
43673
43882
|
{ cwd: root, timeout: 3e4, maxBuffer: 4 * 1024 * 1024 }
|
|
43674
43883
|
);
|
|
43675
43884
|
const parsed = JSON.parse(stdout.trim() || '{"results":[]}');
|
|
@@ -43684,9 +43893,9 @@ async function ragInstall(root, model) {
|
|
|
43684
43893
|
}
|
|
43685
43894
|
const py = await detectPython();
|
|
43686
43895
|
if (!py.found) return { ok: false, started: false, error: "python3 not found on the worker" };
|
|
43687
|
-
if (
|
|
43896
|
+
if (existsSync15(join30(root, RAG_REL, ".installing"))) return { ok: true, started: true };
|
|
43688
43897
|
try {
|
|
43689
|
-
await mkdir11(
|
|
43898
|
+
await mkdir11(join30(root, RAG_REL), { recursive: true });
|
|
43690
43899
|
} catch {
|
|
43691
43900
|
}
|
|
43692
43901
|
const script = `
|
|
@@ -43702,7 +43911,7 @@ rm -f "${RAG_REL}/.installed"
|
|
|
43702
43911
|
} >> "${RAG_REL}/install.log" 2>&1
|
|
43703
43912
|
rm -f "${RAG_REL}/.installing"
|
|
43704
43913
|
`;
|
|
43705
|
-
const child =
|
|
43914
|
+
const child = spawn10("bash", ["-c", script], {
|
|
43706
43915
|
cwd: root,
|
|
43707
43916
|
env: { ...process.env, RAG_PY: py.bin, RAG_MODEL: model },
|
|
43708
43917
|
detached: true,
|
|
@@ -43713,8 +43922,8 @@ rm -f "${RAG_REL}/.installing"
|
|
|
43713
43922
|
}
|
|
43714
43923
|
|
|
43715
43924
|
// src/core/config-sync.ts
|
|
43716
|
-
import { existsSync as
|
|
43717
|
-
import { join as
|
|
43925
|
+
import { existsSync as existsSync16, readFileSync as readFileSync16 } from "fs";
|
|
43926
|
+
import { join as join31 } from "path";
|
|
43718
43927
|
var SERVER_MANAGED_KEYS = [
|
|
43719
43928
|
"physicPath",
|
|
43720
43929
|
"sessionSwitchPct",
|
|
@@ -43731,8 +43940,8 @@ var SERVER_MANAGED_KEYS = [
|
|
|
43731
43940
|
"projectAiMemoryBudgetChars"
|
|
43732
43941
|
];
|
|
43733
43942
|
function readConfigText(profileDir2) {
|
|
43734
|
-
const path =
|
|
43735
|
-
if (
|
|
43943
|
+
const path = join31(profileDir2, "config.json");
|
|
43944
|
+
if (existsSync16(path)) {
|
|
43736
43945
|
try {
|
|
43737
43946
|
return readFileSync16(path, "utf8");
|
|
43738
43947
|
} catch {
|
|
@@ -44521,7 +44730,7 @@ var WsClient = class {
|
|
|
44521
44730
|
}
|
|
44522
44731
|
const { payload, nonce } = encryptPayload(this.sessionKey, data);
|
|
44523
44732
|
const envelope = {
|
|
44524
|
-
id:
|
|
44733
|
+
id: randomUUID4(),
|
|
44525
44734
|
replyTo,
|
|
44526
44735
|
channel,
|
|
44527
44736
|
wsToken: this.wsToken,
|
|
@@ -44542,7 +44751,7 @@ var WsClient = class {
|
|
|
44542
44751
|
}
|
|
44543
44752
|
const { payload, nonce } = encryptPayload(this.sessionKey, data);
|
|
44544
44753
|
const envelope = {
|
|
44545
|
-
id:
|
|
44754
|
+
id: randomUUID4(),
|
|
44546
44755
|
replyTo: null,
|
|
44547
44756
|
channel,
|
|
44548
44757
|
wsToken: this.wsToken,
|
|
@@ -44609,8 +44818,8 @@ var WsClient = class {
|
|
|
44609
44818
|
/** Create the physic folder at an absolute path if missing (best-effort). */
|
|
44610
44819
|
ensurePhysicFolderPath(folder) {
|
|
44611
44820
|
try {
|
|
44612
|
-
if (!
|
|
44613
|
-
|
|
44821
|
+
if (!existsSync17(folder)) {
|
|
44822
|
+
mkdirSync8(folder, { recursive: true });
|
|
44614
44823
|
this.bus.log(t("project.createdFolder", { folder }));
|
|
44615
44824
|
}
|
|
44616
44825
|
} catch (err) {
|
|
@@ -44624,7 +44833,7 @@ var WsClient = class {
|
|
|
44624
44833
|
*/
|
|
44625
44834
|
physicFolderPath(projectName) {
|
|
44626
44835
|
const safe = projectName.replace(/[/\\]/g, "_").replace(/\.\./g, "_").trim();
|
|
44627
|
-
return safe ?
|
|
44836
|
+
return safe ? join32(this.context.profileDir, safe) : null;
|
|
44628
44837
|
}
|
|
44629
44838
|
/**
|
|
44630
44839
|
* Report machine.status: version + the state of the physic project being served
|
|
@@ -44639,7 +44848,7 @@ var WsClient = class {
|
|
|
44639
44848
|
projects: [],
|
|
44640
44849
|
version: CLI_VERSION,
|
|
44641
44850
|
physicPath,
|
|
44642
|
-
physicPathExists: physicPath ?
|
|
44851
|
+
physicPathExists: physicPath ? existsSync17(physicPath) : void 0,
|
|
44643
44852
|
// Real coarse state (ADR-0152): !paused and a recent tick (was hard-coded false).
|
|
44644
44853
|
autonomousRunning: physicPath ? isAutonomousRunning(physicPath, this.context.profileDir) : false,
|
|
44645
44854
|
// The machine-user AI-run limit (0 = unlimited) + profile count so a dispatch can resolve the
|
|
@@ -44733,7 +44942,7 @@ var WsClient = class {
|
|
|
44733
44942
|
*/
|
|
44734
44943
|
uploadLog() {
|
|
44735
44944
|
try {
|
|
44736
|
-
const upload = readLogUpload(
|
|
44945
|
+
const upload = readLogUpload(join32(this.context.profileDir, "logs"));
|
|
44737
44946
|
if (!upload) return;
|
|
44738
44947
|
const sig = `${upload.fileName}:${upload.totalBytes}`;
|
|
44739
44948
|
if (sig === this.lastLogSig) return;
|
|
@@ -44895,10 +45104,10 @@ var WsClient = class {
|
|
|
44895
45104
|
};
|
|
44896
45105
|
|
|
44897
45106
|
// src/core/instance-lock.ts
|
|
44898
|
-
import { readFileSync as readFileSync17, rmSync as
|
|
44899
|
-
import { join as
|
|
45107
|
+
import { readFileSync as readFileSync17, rmSync as rmSync8, writeFileSync as writeFileSync9 } from "fs";
|
|
45108
|
+
import { join as join33 } from "path";
|
|
44900
45109
|
function lockPath(profileDir2) {
|
|
44901
|
-
return
|
|
45110
|
+
return join33(profileDir2, "start.lock");
|
|
44902
45111
|
}
|
|
44903
45112
|
function isAlive(pid) {
|
|
44904
45113
|
try {
|
|
@@ -44924,14 +45133,14 @@ function acquireInstanceLock(profileDir2) {
|
|
|
44924
45133
|
function releaseInstanceLock(profileDir2) {
|
|
44925
45134
|
try {
|
|
44926
45135
|
const pid = Number.parseInt(readFileSync17(lockPath(profileDir2), "utf8").trim(), 10);
|
|
44927
|
-
if (pid === process.pid)
|
|
45136
|
+
if (pid === process.pid) rmSync8(lockPath(profileDir2), { force: true });
|
|
44928
45137
|
} catch {
|
|
44929
45138
|
}
|
|
44930
45139
|
}
|
|
44931
45140
|
|
|
44932
45141
|
// src/core/session-bus.ts
|
|
44933
45142
|
import { EventEmitter } from "events";
|
|
44934
|
-
import { randomUUID as
|
|
45143
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
44935
45144
|
var REPLAY_CAP = 500;
|
|
44936
45145
|
var SessionBus = class {
|
|
44937
45146
|
emitter = new EventEmitter();
|
|
@@ -44962,7 +45171,7 @@ var SessionBus = class {
|
|
|
44962
45171
|
/** Producer → consumers: push one transcript entry. Returns its id (for `updateEntry`). */
|
|
44963
45172
|
push(input) {
|
|
44964
45173
|
const entry = {
|
|
44965
|
-
id:
|
|
45174
|
+
id: randomUUID5(),
|
|
44966
45175
|
level: "info",
|
|
44967
45176
|
...input,
|
|
44968
45177
|
ts: Date.now()
|
|
@@ -45219,8 +45428,8 @@ var SessionBus = class {
|
|
|
45219
45428
|
|
|
45220
45429
|
// src/core/control-server.ts
|
|
45221
45430
|
import { createServer } from "net";
|
|
45222
|
-
import { chmodSync as chmodSync4, rmSync as
|
|
45223
|
-
import { join as
|
|
45431
|
+
import { chmodSync as chmodSync4, rmSync as rmSync9 } from "fs";
|
|
45432
|
+
import { join as join35 } from "path";
|
|
45224
45433
|
|
|
45225
45434
|
// src/core/control-protocol.ts
|
|
45226
45435
|
var CONTROL_SOCKET_FILE = "control.sock";
|
|
@@ -45251,9 +45460,9 @@ function createFrameParser() {
|
|
|
45251
45460
|
// src/core/control-token.ts
|
|
45252
45461
|
import { chmodSync as chmodSync3, readFileSync as readFileSync18, writeFileSync as writeFileSync10 } from "fs";
|
|
45253
45462
|
import { randomBytes as randomBytes3, timingSafeEqual } from "crypto";
|
|
45254
|
-
import { join as
|
|
45463
|
+
import { join as join34 } from "path";
|
|
45255
45464
|
function controlTokenPath(profileDir2) {
|
|
45256
|
-
return
|
|
45465
|
+
return join34(profileDir2, CONTROL_TOKEN_FILE);
|
|
45257
45466
|
}
|
|
45258
45467
|
function writeControlToken(profileDir2) {
|
|
45259
45468
|
const token = randomBytes3(32).toString("hex");
|
|
@@ -45282,9 +45491,9 @@ function tokenMatches(expected, given) {
|
|
|
45282
45491
|
|
|
45283
45492
|
// src/core/control-server.ts
|
|
45284
45493
|
function startControlServer(bus, profileDir2, info) {
|
|
45285
|
-
const socketPath =
|
|
45494
|
+
const socketPath = join35(profileDir2, CONTROL_SOCKET_FILE);
|
|
45286
45495
|
try {
|
|
45287
|
-
|
|
45496
|
+
rmSync9(socketPath, { force: true });
|
|
45288
45497
|
} catch {
|
|
45289
45498
|
}
|
|
45290
45499
|
const clients = /* @__PURE__ */ new Set();
|
|
@@ -45370,8 +45579,8 @@ function startControlServer(bus, profileDir2, info) {
|
|
|
45370
45579
|
clients.clear();
|
|
45371
45580
|
server.close();
|
|
45372
45581
|
try {
|
|
45373
|
-
|
|
45374
|
-
|
|
45582
|
+
rmSync9(socketPath, { force: true });
|
|
45583
|
+
rmSync9(controlTokenPath(profileDir2), { force: true });
|
|
45375
45584
|
} catch {
|
|
45376
45585
|
}
|
|
45377
45586
|
};
|
|
@@ -51034,15 +51243,15 @@ var import_react21 = __toESM(require_react(), 1);
|
|
|
51034
51243
|
var import_react26 = __toESM(require_react(), 1);
|
|
51035
51244
|
|
|
51036
51245
|
// src/core/input-history.ts
|
|
51037
|
-
import { existsSync as
|
|
51038
|
-
import { join as
|
|
51246
|
+
import { existsSync as existsSync19, readFileSync as readFileSync20, writeFileSync as writeFileSync11 } from "fs";
|
|
51247
|
+
import { join as join36 } from "path";
|
|
51039
51248
|
var MAX_HISTORY = 200;
|
|
51040
51249
|
function historyPath(profileDir2) {
|
|
51041
|
-
return
|
|
51250
|
+
return join36(profileDir2, "input-history.json");
|
|
51042
51251
|
}
|
|
51043
51252
|
function loadInputHistory(profileDir2) {
|
|
51044
51253
|
const path = historyPath(profileDir2);
|
|
51045
|
-
if (!
|
|
51254
|
+
if (!existsSync19(path)) return [];
|
|
51046
51255
|
try {
|
|
51047
51256
|
const data = JSON.parse(readFileSync20(path, "utf8"));
|
|
51048
51257
|
return Array.isArray(data) ? data.filter((x) => typeof x === "string") : [];
|
|
@@ -51652,7 +51861,7 @@ async function runStart(profileDir2, profileName) {
|
|
|
51652
51861
|
return;
|
|
51653
51862
|
}
|
|
51654
51863
|
logger.configure({
|
|
51655
|
-
dir:
|
|
51864
|
+
dir: join37(profileDir2, "logs"),
|
|
51656
51865
|
level: resolveLogLevel(),
|
|
51657
51866
|
base: { version: CLI_VERSION, profile: profileName, scope: credential.scope }
|
|
51658
51867
|
});
|
|
@@ -51672,7 +51881,7 @@ async function runStart(profileDir2, profileName) {
|
|
|
51672
51881
|
if (result.action === "updated") {
|
|
51673
51882
|
logger.info("update.updated", { version: result.version });
|
|
51674
51883
|
console.log(t("start.restarting"));
|
|
51675
|
-
const child =
|
|
51884
|
+
const child = spawn11(process.execPath, process.argv.slice(1), {
|
|
51676
51885
|
stdio: "inherit",
|
|
51677
51886
|
env: { ...process.env, FOURPM_NO_UPDATE: "1" }
|
|
51678
51887
|
});
|
|
@@ -51761,7 +51970,7 @@ async function runStart(profileDir2, profileName) {
|
|
|
51761
51970
|
}
|
|
51762
51971
|
|
|
51763
51972
|
// src/commands/attach.ts
|
|
51764
|
-
import { join as
|
|
51973
|
+
import { join as join38 } from "path";
|
|
51765
51974
|
|
|
51766
51975
|
// src/core/control-client.ts
|
|
51767
51976
|
import { connect as connect2 } from "net";
|
|
@@ -51870,7 +52079,7 @@ async function runAttach(profileDir2, profileName) {
|
|
|
51870
52079
|
process.exitCode = 1;
|
|
51871
52080
|
return;
|
|
51872
52081
|
}
|
|
51873
|
-
const socketPath =
|
|
52082
|
+
const socketPath = join38(profileDir2, CONTROL_SOCKET_FILE);
|
|
51874
52083
|
const bus = new SessionBus();
|
|
51875
52084
|
let conn;
|
|
51876
52085
|
try {
|
|
@@ -51901,11 +52110,11 @@ async function runAttach(profileDir2, profileName) {
|
|
|
51901
52110
|
|
|
51902
52111
|
// src/commands/auto-run.ts
|
|
51903
52112
|
import { connect as connect3 } from "net";
|
|
51904
|
-
import { join as
|
|
52113
|
+
import { join as join39 } from "path";
|
|
51905
52114
|
var AUTO_RUN_TIMEOUT_MS = 3 * 60 * 60 * 1e3;
|
|
51906
52115
|
async function runAutoRun(profileDir2, profileName) {
|
|
51907
52116
|
initI18n(readProfileConfig(profileDir2).locale);
|
|
51908
|
-
const socketPath =
|
|
52117
|
+
const socketPath = join39(profileDir2, CONTROL_SOCKET_FILE);
|
|
51909
52118
|
const token = readControlToken(profileDir2);
|
|
51910
52119
|
const code = await new Promise((resolve10) => {
|
|
51911
52120
|
let settled = false;
|
|
@@ -51961,12 +52170,12 @@ async function runAutoRun(profileDir2, profileName) {
|
|
|
51961
52170
|
}
|
|
51962
52171
|
|
|
51963
52172
|
// src/commands/ai-login.ts
|
|
51964
|
-
import { spawn as
|
|
51965
|
-
import { mkdirSync as
|
|
52173
|
+
import { spawn as spawn12 } from "child_process";
|
|
52174
|
+
import { mkdirSync as mkdirSync9 } from "fs";
|
|
51966
52175
|
import { basename as basename8 } from "path";
|
|
51967
52176
|
function runProviderLogin(cmd, envVar, dir2) {
|
|
51968
52177
|
return new Promise((resolve10) => {
|
|
51969
|
-
const child =
|
|
52178
|
+
const child = spawn12(cmd, [], {
|
|
51970
52179
|
stdio: "inherit",
|
|
51971
52180
|
env: { ...process.env, [envVar]: dir2 }
|
|
51972
52181
|
});
|
|
@@ -51999,7 +52208,7 @@ async function runAiLogin(profileDir2, profileName, aiFilter) {
|
|
|
51999
52208
|
continue;
|
|
52000
52209
|
}
|
|
52001
52210
|
const dir2 = resolveHomePath(cred.profile);
|
|
52002
|
-
|
|
52211
|
+
mkdirSync9(dir2, { recursive: true });
|
|
52003
52212
|
console.log(t("aiLogin.loggingIn", { provider: cred.provider, profile: cred.profile, envVar, dir: dir2 }));
|
|
52004
52213
|
console.log(t("aiLogin.completeSignIn"));
|
|
52005
52214
|
const code = await runProviderLogin(cred.provider, envVar, dir2);
|
|
@@ -52011,7 +52220,7 @@ async function runAiLogin(profileDir2, profileName, aiFilter) {
|
|
|
52011
52220
|
}
|
|
52012
52221
|
|
|
52013
52222
|
// src/commands/unlink.ts
|
|
52014
|
-
import { rmSync as
|
|
52223
|
+
import { rmSync as rmSync10 } from "fs";
|
|
52015
52224
|
async function runUnlink(profileDir2, profileName) {
|
|
52016
52225
|
initI18n(readProfileConfig(profileDir2).locale);
|
|
52017
52226
|
const cred = readCredential(profileDir2, profileName);
|
|
@@ -52025,7 +52234,7 @@ async function runUnlink(profileDir2, profileName) {
|
|
|
52025
52234
|
} catch (err) {
|
|
52026
52235
|
console.log(t("unlink.revokeFailed", { message: err.message }));
|
|
52027
52236
|
}
|
|
52028
|
-
|
|
52237
|
+
rmSync10(profileDir2, { recursive: true, force: true });
|
|
52029
52238
|
clearDefaultProfileIf(profileName);
|
|
52030
52239
|
console.log(t("unlink.deleted"));
|
|
52031
52240
|
}
|