pi-fabric 0.94.0 → 0.96.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/chunks/{chunk-PVSADHF4.js → chunk-4BW7JF2W.js} +1 -1
- package/dist/chunks/{chunk-AAHHYDTO.js → chunk-4NF3S3NM.js} +1 -1
- package/dist/chunks/{chunk-AAHHYDTO.js.map → chunk-4NF3S3NM.js.map} +2 -2
- package/dist/chunks/{chunk-7VCPGNDY.js → chunk-B2IVWASP.js} +2 -2
- package/dist/chunks/{chunk-H6RSDEWW.js → chunk-F72MAIQY.js} +31 -367
- package/dist/chunks/chunk-F72MAIQY.js.map +7 -0
- package/dist/chunks/{chunk-W3FRE4VI.js → chunk-FDIEQEXS.js} +2 -2
- package/dist/chunks/{chunk-RTXZHKMS.js → chunk-QNMQJNXE.js} +7 -3
- package/dist/chunks/chunk-QNMQJNXE.js.map +7 -0
- package/dist/chunks/{chunk-FLKGJPCN.js → chunk-ULQFHZJE.js} +4 -1
- package/dist/chunks/chunk-ULQFHZJE.js.map +7 -0
- package/dist/chunks/{chunk-7VAZ4N43.js → chunk-V3PIO7QL.js} +2 -2
- package/dist/chunks/{chunk-HCBV735P.js → chunk-YC3QQDZ3.js} +2 -2
- package/dist/chunks/{chunk-DBLT6J5B.js → chunk-ZRI433JP.js} +6 -6
- package/dist/chunks/{chunk-36B4AIRT.js → chunk-ZTXZ6JDO.js} +2 -2
- package/dist/commands/fabric.d.ts.map +1 -1
- package/dist/components/configuration.js +2 -3
- package/dist/core/shell-inbox.d.ts +13 -0
- package/dist/core/shell-inbox.d.ts.map +1 -0
- package/dist/core/shell-jobs.d.ts +42 -7
- package/dist/core/shell-jobs.d.ts.map +1 -1
- package/dist/core/shell-limits.d.ts +3 -0
- package/dist/core/shell-limits.d.ts.map +1 -0
- package/dist/core/shell-monitor.d.ts +51 -0
- package/dist/core/shell-monitor.d.ts.map +1 -0
- package/dist/fabric-runtime-state.d.ts +1 -1
- package/dist/fabric-runtime-state.d.ts.map +1 -1
- package/dist/fabric-runtime-state.js +940 -179
- package/dist/fabric-runtime-state.js.map +4 -4
- package/dist/index.js +131 -20
- package/dist/index.js.map +3 -3
- package/dist/jev.js +1 -1
- package/dist/providers/jev-provider.js +1 -1
- package/dist/providers/pi-tools-provider.d.ts.map +1 -1
- package/dist/providers/tasks-provider.d.ts +12 -0
- package/dist/providers/tasks-provider.d.ts.map +1 -0
- package/dist/residency/host.js +4 -4
- package/dist/residency/pi-entry.js +4 -4
- package/dist/runtime/core-override-guest-types.js +2 -2
- package/dist/runtime/guest-types.d.ts +1 -1
- package/dist/runtime/guest-types.d.ts.map +1 -1
- package/dist/runtime/guest-types.js +1 -1
- package/dist/runtime/monty-runtime.js +1 -2
- package/dist/runtime/monty-runtime.js.map +1 -1
- package/dist/runtime/typescript-kernel.js +2 -2
- package/dist/runtime-state-builtins.d.ts +1 -1
- package/dist/runtime-state-builtins.d.ts.map +1 -1
- package/dist/ui/controller.d.ts +1 -0
- package/dist/ui/controller.d.ts.map +1 -1
- package/dist/ui/conversation-targets.js +2 -2
- package/dist/ui/conversation.js +7 -7
- package/dist/ui/dashboard.js +6 -6
- package/dist/ui/highlight.d.ts.map +1 -1
- package/dist/ui/languages/bend.d.ts +4 -0
- package/dist/ui/languages/bend.d.ts.map +1 -0
- package/dist/ui/languages/bend.js +73 -0
- package/dist/ui/languages/bend.js.map +7 -0
- package/dist/ui/settings.js +1 -2
- package/dist/ui/settings.js.map +1 -1
- package/dist/ui/shell-hang-keys.d.ts.map +1 -1
- package/dist/ui/shell-tasks.d.ts +22 -0
- package/dist/ui/shell-tasks.d.ts.map +1 -0
- package/dist/ui/shell-tasks.js +161 -0
- package/dist/ui/shell-tasks.js.map +7 -0
- package/dist/ui/shiki-catalog.d.ts +2 -1
- package/dist/ui/shiki-catalog.d.ts.map +1 -1
- package/dist/ui/snapshot.d.ts.map +1 -1
- package/dist/ui/types.d.ts +1 -0
- package/dist/ui/types.d.ts.map +1 -1
- package/dist/ui/widget.d.ts.map +1 -1
- package/docs/background-tasks.md +122 -0
- package/docs/interface.md +7 -0
- package/docs/shell-middleware.md +2 -2
- package/package.json +1 -1
- package/skillsets/typescript/fabric-exec/SKILL.md +4 -2
- package/dist/chunks/chunk-FLKGJPCN.js.map +0 -7
- package/dist/chunks/chunk-H6RSDEWW.js.map +0 -7
- package/dist/chunks/chunk-RTXZHKMS.js.map +0 -7
- /package/dist/chunks/{chunk-PVSADHF4.js.map → chunk-4BW7JF2W.js.map} +0 -0
- /package/dist/chunks/{chunk-7VCPGNDY.js.map → chunk-B2IVWASP.js.map} +0 -0
- /package/dist/chunks/{chunk-W3FRE4VI.js.map → chunk-FDIEQEXS.js.map} +0 -0
- /package/dist/chunks/{chunk-7VAZ4N43.js.map → chunk-V3PIO7QL.js.map} +0 -0
- /package/dist/chunks/{chunk-HCBV735P.js.map → chunk-YC3QQDZ3.js.map} +0 -0
- /package/dist/chunks/{chunk-DBLT6J5B.js.map → chunk-ZRI433JP.js.map} +0 -0
- /package/dist/chunks/{chunk-36B4AIRT.js.map → chunk-ZTXZ6JDO.js.map} +0 -0
package/README.md
CHANGED
|
@@ -133,6 +133,7 @@ Fabric includes a live activity surface in Pi:
|
|
|
133
133
|
- A compact widget above the chat (like `pi-supervisor`) whose header follows the current phase while its rows show active/completed agents, active actors, and their recent nested tool or code-change activity.
|
|
134
134
|
- `/fabric` (or `/fabric dashboard`): opens the **Activity** and **Topology** views. The user-facing Pi session appears as **Main**. You can queue or steer participants and inspect the project topology.
|
|
135
135
|
- `/fabric settings`: mirrors Pi's `/settings` and writes changes to `fabric.json`. TUI hosts get the searchable settings component; RPC hosts get the same nested sections, value/input/model pickers, list editors, and project/global save scopes through native dialog primitives.
|
|
136
|
+
- Code previews automatically highlight `.bend` files using Bend 2 syntax, including laws and proof terms. The grammar loads only when needed, with no Bend installation required.
|
|
136
137
|
- `Tool display` (`compact` by default, or `full`) is configured under `/fabric settings` → **UI**; compact elevates the declared display intent, hides the outer program, and applies to the current transcript immediately. Pi's tool-expand keybinding (`ctrl+o` by default) expands a compact card to the full transcript.
|
|
137
138
|
|
|
138
139
|
See the [interface & commands reference](docs/interface.md) for every view, keybinding, and slash command.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/ui/types.ts"],
|
|
4
|
-
"sourcesContent": ["import type { FabricActorInfo, FabricActorMessage, GlobalActorDefinition } from \"../actors/types.js\";\nimport type { FabricActivityRun } from \"../activity/types.js\";\nimport type { FabricComponentGraph } from \"../components/types.js\";\nimport type { MeshEvent } from \"../mesh/store.js\";\nimport type { FabricMainAgentInfo } from \"../main-agent.js\";\nimport type {\n FabricParticipantCapability,\n FabricParticipantInfo,\n FabricParticipantKind,\n FabricParticipantResidency,\n FabricPeerInfo,\n} from \"../topology/types.js\";\nimport type { AgentUsage } from \"../agents/types.js\";\n\nexport type FabricUiMain = FabricMainAgentInfo;\nexport type FabricUiPeer = FabricPeerInfo;\n\nexport interface FabricUiAgent {\n id: string;\n name: string;\n status: string;\n runner?: \"pi\" | \"claude\" | \"veda\";\n residency?: FabricParticipantResidency;\n transport: string;\n cwd: string;\n task?: string;\n model?: string;\n thinking?: string;\n currentTool?: string;\n startedAt?: number;\n updatedAt?: number;\n finishedAt?: number;\n turns?: number;\n toolCalls?: number;\n usage?: AgentUsage;\n text?: string;\n value?: unknown;\n error?: string;\n logFile?: string;\n attachCommand?: string;\n branch?: string;\n worktree?: string;\n actorId?: string;\n actorName?: string;\n runId?: string;\n phaseId?: string;\n parentId?: string;\n nestingDepth?: number;\n rootId?: string;\n ownerHostId?: string;\n local?: boolean;\n stale?: boolean;\n participantKind?: FabricParticipantKind;\n capabilities?: FabricParticipantCapability[];\n}\n\nexport interface FabricUiActor extends FabricActorInfo {\n /** The actor's default instruction (persona text); shown and edited in the dashboard. */\n instructions: string;\n recentMessages: FabricActorMessage[];\n ownerHostId?: string;\n local?: boolean;\n worker?: FabricUiAgent;\n}\n\nexport interface FabricUiStateEntry {\n key: string;\n label: string;\n status: string;\n owner?: string;\n detail?: string;\n value: unknown;\n version: number;\n updatedAt: number;\n}\n\nexport interface FabricDashboardSnapshot {\n now: number;\n widgetDismissedAt?: number;\n runs: FabricActivityRun[];\n main: FabricUiMain;\n peers: FabricUiPeer[];\n participants?: FabricParticipantInfo[];\n agents: FabricUiAgent[];\n actors: FabricUiActor[];\n componentGraph: FabricComponentGraph;\n /** Project-independent actor templates from the global registry. */\n globalActors: GlobalActorDefinition[];\n state: FabricUiStateEntry[];\n events: MeshEvent[];\n}\n\nexport const activeStatuses = new Set([\n \"queued\",\n \"pending\",\n \"ready\",\n \"claimed\",\n \"running\",\n \"in_progress\",\n \"blocked\",\n \"loading\",\n \"active\",\n \"unloading\",\n]);\n\nexport const isActiveStatus = (status: string): boolean => activeStatuses.has(status);\n\nexport const orderAgentsByCreation = (agents: FabricUiAgent[]): FabricUiAgent[] =>\n agents\n .map((agent, index) => ({ agent, index }))\n .sort(\n (left, right) =>\n (left.agent.startedAt ?? Number.MAX_SAFE_INTEGER) -\n (right.agent.startedAt ?? Number.MAX_SAFE_INTEGER) ||\n left.index - right.index,\n )\n .map(({ agent }) => agent);\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["import type { FabricActorInfo, FabricActorMessage, GlobalActorDefinition } from \"../actors/types.js\";\nimport type { FabricActivityRun } from \"../activity/types.js\";\nimport type { FabricComponentGraph } from \"../components/types.js\";\nimport type { MeshEvent } from \"../mesh/store.js\";\nimport type { FabricMainAgentInfo } from \"../main-agent.js\";\nimport type {\n FabricParticipantCapability,\n FabricParticipantInfo,\n FabricParticipantKind,\n FabricParticipantResidency,\n FabricPeerInfo,\n} from \"../topology/types.js\";\nimport type { AgentUsage } from \"../agents/types.js\";\n\nexport type FabricUiMain = FabricMainAgentInfo;\nexport type FabricUiPeer = FabricPeerInfo;\n\nexport interface FabricUiAgent {\n id: string;\n name: string;\n status: string;\n runner?: \"pi\" | \"claude\" | \"veda\";\n residency?: FabricParticipantResidency;\n transport: string;\n cwd: string;\n task?: string;\n model?: string;\n thinking?: string;\n currentTool?: string;\n startedAt?: number;\n updatedAt?: number;\n finishedAt?: number;\n turns?: number;\n toolCalls?: number;\n usage?: AgentUsage;\n text?: string;\n value?: unknown;\n error?: string;\n logFile?: string;\n attachCommand?: string;\n branch?: string;\n worktree?: string;\n actorId?: string;\n actorName?: string;\n runId?: string;\n phaseId?: string;\n parentId?: string;\n nestingDepth?: number;\n rootId?: string;\n ownerHostId?: string;\n local?: boolean;\n stale?: boolean;\n participantKind?: FabricParticipantKind;\n capabilities?: FabricParticipantCapability[];\n}\n\nexport interface FabricUiActor extends FabricActorInfo {\n /** The actor's default instruction (persona text); shown and edited in the dashboard. */\n instructions: string;\n recentMessages: FabricActorMessage[];\n ownerHostId?: string;\n local?: boolean;\n worker?: FabricUiAgent;\n}\n\nexport interface FabricUiStateEntry {\n key: string;\n label: string;\n status: string;\n owner?: string;\n detail?: string;\n value: unknown;\n version: number;\n updatedAt: number;\n}\n\nexport interface FabricDashboardSnapshot {\n shells?: import(\"../core/shell-jobs.js\").FabricShellJobInfo[];\n now: number;\n widgetDismissedAt?: number;\n runs: FabricActivityRun[];\n main: FabricUiMain;\n peers: FabricUiPeer[];\n participants?: FabricParticipantInfo[];\n agents: FabricUiAgent[];\n actors: FabricUiActor[];\n componentGraph: FabricComponentGraph;\n /** Project-independent actor templates from the global registry. */\n globalActors: GlobalActorDefinition[];\n state: FabricUiStateEntry[];\n events: MeshEvent[];\n}\n\nexport const activeStatuses = new Set([\n \"queued\",\n \"pending\",\n \"ready\",\n \"claimed\",\n \"running\",\n \"in_progress\",\n \"blocked\",\n \"loading\",\n \"active\",\n \"unloading\",\n]);\n\nexport const isActiveStatus = (status: string): boolean => activeStatuses.has(status);\n\nexport const orderAgentsByCreation = (agents: FabricUiAgent[]): FabricUiAgent[] =>\n agents\n .map((agent, index) => ({ agent, index }))\n .sort(\n (left, right) =>\n (left.agent.startedAt ?? Number.MAX_SAFE_INTEGER) -\n (right.agent.startedAt ?? Number.MAX_SAFE_INTEGER) ||\n left.index - right.index,\n )\n .map(({ agent }) => agent);\n"],
|
|
5
|
+
"mappings": ";AA6FO,IAAM,iBAAiB,oBAAI,IAAI;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAEM,IAAM,iBAAiB,CAAC,WAA4B,eAAe,IAAI,MAAM;AAE7E,IAAM,wBAAwB,CAAC,WACpC,OACG,IAAI,CAAC,OAAO,WAAW,EAAE,OAAO,MAAM,EAAE,EACxC;AAAA,EACC,CAAC,MAAM,WACJ,KAAK,MAAM,aAAa,OAAO,qBAC7B,MAAM,MAAM,aAAa,OAAO,qBACnC,KAAK,QAAQ,MAAM;AACvB,EACC,IAAI,CAAC,EAAE,MAAM,MAAM,KAAK;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
agentParticipantRecords,
|
|
10
10
|
loadModelUsage,
|
|
11
11
|
useBudgetLedger
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-ZRI433JP.js";
|
|
13
13
|
import {
|
|
14
14
|
RESIDENT_HOST_FORMAT,
|
|
15
15
|
residentDeliveryPrefix,
|
|
@@ -691,4 +691,4 @@ if (isMain) {
|
|
|
691
691
|
export {
|
|
692
692
|
runResidentHostFromConfigPath
|
|
693
693
|
};
|
|
694
|
-
//# sourceMappingURL=chunk-
|
|
694
|
+
//# sourceMappingURL=chunk-B2IVWASP.js.map
|
|
@@ -2,10 +2,6 @@ import {
|
|
|
2
2
|
defaultCodePreviewSettings,
|
|
3
3
|
normalizeCodePreviewSettings
|
|
4
4
|
} from "./chunk-OKOHRXGF.js";
|
|
5
|
-
import {
|
|
6
|
-
closeScratch,
|
|
7
|
-
createScratch
|
|
8
|
-
} from "./chunk-RAO6TQFP.js";
|
|
9
5
|
import {
|
|
10
6
|
renameAtomic
|
|
11
7
|
} from "./chunk-KAD7UWT2.js";
|
|
@@ -27,352 +23,25 @@ import {
|
|
|
27
23
|
PI_CORE_TOOL_NAME_SET
|
|
28
24
|
} from "./chunk-YU62DDKK.js";
|
|
29
25
|
|
|
30
|
-
// src/core/shell-
|
|
31
|
-
import fs from "node:fs";
|
|
32
|
-
import { readFile, unlink } from "node:fs/promises";
|
|
33
|
-
import { tmpdir } from "node:os";
|
|
34
|
-
import path from "node:path";
|
|
35
|
-
import { randomUUID } from "node:crypto";
|
|
26
|
+
// src/core/shell-limits.ts
|
|
36
27
|
var DEFAULT_SHELL_HANG_MS = 12e4;
|
|
37
28
|
var SHELL_HANG_MAX_MS = 6e5;
|
|
38
|
-
var SHELL_HANG_SNAPSHOT_BYTES = 8e3;
|
|
39
|
-
var SHELL_TAIL_BYTES = 1024 * 1024;
|
|
40
|
-
var SHELL_LOG_BYTES = 8 * 1024 * 1024;
|
|
41
|
-
var SHELL_COMPLETED_HANDLES = 256;
|
|
42
|
-
var SHELL_COMPLETED_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
43
|
-
var LOG_HEADER = "[Bounded shell log: starts with retained pre-spill tail; 8 MiB total cap, then further output is omitted. Not a full-output archive.]\n";
|
|
44
|
-
var LOG_TRUNCATED = "\n[Shell log truncated: disk limit reached; subsequent output omitted.]\n";
|
|
45
|
-
var posixQuote = (value) => "'" + value.replaceAll("'", "'\\''") + "'";
|
|
46
|
-
var powershellQuote = (value) => "'" + value.replaceAll("'", "''") + "'";
|
|
47
|
-
var wrapShellCommandForPid = (command, pidPath, tool) => tool === "powershell" ? `Set-Content -LiteralPath ${powershellQuote(pidPath)} -Value $PID
|
|
48
|
-
${command}` : `printf '%s\\n' "$(cat /proc/$$/winpid 2>/dev/null || printf '%s' "$$")" > ${posixQuote(pidPath)}
|
|
49
|
-
${command}`;
|
|
50
|
-
var formatShellHangNotice = (input) => {
|
|
51
|
-
const seconds = Math.max(1, Math.round(input.elapsedMs / 1e3));
|
|
52
|
-
const pid = input.pid !== void 0 ? ` (pid ${input.pid})` : "";
|
|
53
|
-
return `[Still running after ${seconds}s${pid}. Bounded live output (may be truncated): ${input.logPath}]`;
|
|
54
|
-
};
|
|
55
|
-
var appendShellHangNotice = (output, notice) => output ? `${output}
|
|
56
|
-
|
|
57
|
-
${notice}` : notice;
|
|
58
|
-
var parseShellPid = (text) => {
|
|
59
|
-
const pid = Number(text.trim());
|
|
60
|
-
return Number.isSafeInteger(pid) && pid > 1 ? pid : void 0;
|
|
61
|
-
};
|
|
62
|
-
var FabricShellJob = class {
|
|
63
|
-
constructor(tool, command, onFinish, tempRoot) {
|
|
64
|
-
this.onFinish = onFinish;
|
|
65
|
-
this.id = randomUUID();
|
|
66
|
-
this.tool = tool;
|
|
67
|
-
this.command = command;
|
|
68
|
-
this.#directory = createScratch("shell", tempRoot);
|
|
69
|
-
this.pidPath = path.join(this.#directory, "child.pid");
|
|
70
|
-
}
|
|
71
|
-
id;
|
|
72
|
-
tool;
|
|
73
|
-
command;
|
|
74
|
-
abort = new AbortController();
|
|
75
|
-
startedAt = Date.now();
|
|
76
|
-
pidPath;
|
|
77
|
-
pid;
|
|
78
|
-
logPath;
|
|
79
|
-
spilled = false;
|
|
80
|
-
finished = false;
|
|
81
|
-
spilledAt;
|
|
82
|
-
finishedAt;
|
|
83
|
-
exitCode;
|
|
84
|
-
status = "running";
|
|
85
|
-
#tail = Buffer.alloc(0);
|
|
86
|
-
#omitted = false;
|
|
87
|
-
#directory;
|
|
88
|
-
#descriptor;
|
|
89
|
-
#logBytes = 0;
|
|
90
|
-
#logTruncated = false;
|
|
91
|
-
#spill = new AbortController();
|
|
92
|
-
#pidRead;
|
|
93
|
-
append(data) {
|
|
94
|
-
if (this.finished) return;
|
|
95
|
-
const keep = Math.max(0, SHELL_TAIL_BYTES - data.length);
|
|
96
|
-
if (this.#tail.length + data.length > SHELL_TAIL_BYTES) this.#omitted = true;
|
|
97
|
-
this.#tail = Buffer.concat([
|
|
98
|
-
this.#tail.subarray(Math.max(0, this.#tail.length - keep)),
|
|
99
|
-
data.subarray(Math.max(0, data.length - SHELL_TAIL_BYTES))
|
|
100
|
-
]);
|
|
101
|
-
this.#writeLog(data);
|
|
102
|
-
}
|
|
103
|
-
#writeLog(data) {
|
|
104
|
-
if (this.#descriptor === void 0 || this.#logTruncated) return;
|
|
105
|
-
const available = Math.max(0, SHELL_LOG_BYTES - Buffer.byteLength(LOG_TRUNCATED) - this.#logBytes);
|
|
106
|
-
try {
|
|
107
|
-
const chunk = data.subarray(0, available);
|
|
108
|
-
let offset = 0;
|
|
109
|
-
while (offset < chunk.length) {
|
|
110
|
-
const written = fs.writeSync(this.#descriptor, chunk, offset);
|
|
111
|
-
if (written <= 0) throw new Error("Shell log write made no progress");
|
|
112
|
-
offset += written;
|
|
113
|
-
}
|
|
114
|
-
this.#logBytes += chunk.length;
|
|
115
|
-
if (chunk.length < data.length) {
|
|
116
|
-
fs.writeSync(this.#descriptor, LOG_TRUNCATED);
|
|
117
|
-
this.#logTruncated = true;
|
|
118
|
-
}
|
|
119
|
-
} catch {
|
|
120
|
-
try {
|
|
121
|
-
fs.closeSync(this.#descriptor);
|
|
122
|
-
} catch {
|
|
123
|
-
}
|
|
124
|
-
this.#descriptor = void 0;
|
|
125
|
-
this.#logTruncated = true;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
snapshotText(maxBytes = SHELL_HANG_SNAPSHOT_BYTES) {
|
|
129
|
-
const limit = Number.isFinite(maxBytes) ? Math.max(0, Math.floor(maxBytes)) : SHELL_TAIL_BYTES;
|
|
130
|
-
const slice = this.#tail.subarray(Math.max(0, this.#tail.length - limit));
|
|
131
|
-
const truncated = this.#omitted || slice.length < this.#tail.length;
|
|
132
|
-
return `${truncated ? "[Output truncated; retained tail follows]\n" : ""}${slice.toString("utf8")}`;
|
|
133
|
-
}
|
|
134
|
-
async persistLog() {
|
|
135
|
-
if (this.logPath) return this.logPath;
|
|
136
|
-
if (this.finished) throw new Error("Shell job finished before a log was requested");
|
|
137
|
-
const logPath = path.join(this.#directory, "output.log");
|
|
138
|
-
try {
|
|
139
|
-
this.#descriptor = fs.openSync(logPath, "wx", 384);
|
|
140
|
-
fs.writeSync(this.#descriptor, LOG_HEADER);
|
|
141
|
-
this.#logBytes = Buffer.byteLength(LOG_HEADER);
|
|
142
|
-
if (this.#omitted) this.#writeLog(Buffer.from("[Pre-spill output truncated: only the last 1 MiB was retained.]\n"));
|
|
143
|
-
this.#writeLog(this.#tail);
|
|
144
|
-
this.logPath = logPath;
|
|
145
|
-
return logPath;
|
|
146
|
-
} catch (error) {
|
|
147
|
-
if (this.#descriptor !== void 0) {
|
|
148
|
-
try {
|
|
149
|
-
fs.closeSync(this.#descriptor);
|
|
150
|
-
} catch {
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
this.#descriptor = void 0;
|
|
154
|
-
try {
|
|
155
|
-
fs.unlinkSync(logPath);
|
|
156
|
-
} catch {
|
|
157
|
-
}
|
|
158
|
-
throw error;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
async readPid() {
|
|
162
|
-
if (this.pid !== void 0) return this.pid;
|
|
163
|
-
this.#pidRead ??= (async () => {
|
|
164
|
-
for (let attempt = 0; attempt < 25; attempt += 1) {
|
|
165
|
-
try {
|
|
166
|
-
const pid = parseShellPid(await readFile(this.pidPath, "utf8"));
|
|
167
|
-
if (pid !== void 0) {
|
|
168
|
-
this.pid = pid;
|
|
169
|
-
return pid;
|
|
170
|
-
}
|
|
171
|
-
} catch {
|
|
172
|
-
}
|
|
173
|
-
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
174
|
-
}
|
|
175
|
-
return void 0;
|
|
176
|
-
})();
|
|
177
|
-
return this.#pidRead;
|
|
178
|
-
}
|
|
179
|
-
spill() {
|
|
180
|
-
if (this.spilled || this.finished) return;
|
|
181
|
-
this.spilled = true;
|
|
182
|
-
this.spilledAt = Date.now();
|
|
183
|
-
this.status = "spilled";
|
|
184
|
-
if (!this.#spill.signal.aborted) this.#spill.abort();
|
|
185
|
-
}
|
|
186
|
-
whenSpill() {
|
|
187
|
-
if (this.spilled || this.#spill.signal.aborted) return Promise.resolve();
|
|
188
|
-
return new Promise((resolve) => {
|
|
189
|
-
this.#spill.signal.addEventListener("abort", () => resolve(), { once: true });
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
async finish(exitCode, footer) {
|
|
193
|
-
if (this.finished) return;
|
|
194
|
-
const persistence = this.spilled && !this.logPath ? this.persistLog() : void 0;
|
|
195
|
-
this.finished = true;
|
|
196
|
-
await persistence?.catch(() => void 0);
|
|
197
|
-
this.finishedAt = Date.now();
|
|
198
|
-
if (exitCode !== void 0) this.exitCode = exitCode;
|
|
199
|
-
if (this.status === "running") {
|
|
200
|
-
this.status = this.abort.signal.aborted ? "killed" : "exited";
|
|
201
|
-
} else if (this.abort.signal.aborted && this.status === "spilled") {
|
|
202
|
-
this.status = "killed";
|
|
203
|
-
}
|
|
204
|
-
if (footer) this.#writeLog(Buffer.from(footer.endsWith("\n") ? footer : `${footer}
|
|
205
|
-
`));
|
|
206
|
-
if (this.#descriptor !== void 0) {
|
|
207
|
-
try {
|
|
208
|
-
fs.closeSync(this.#descriptor);
|
|
209
|
-
} catch {
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
this.#descriptor = void 0;
|
|
213
|
-
this.#tail = Buffer.alloc(0);
|
|
214
|
-
this.#omitted = false;
|
|
215
|
-
if (!this.#spill.signal.aborted) this.#spill.abort();
|
|
216
|
-
await unlink(this.pidPath).catch(() => void 0);
|
|
217
|
-
if (this.logPath) closeScratch(this.#directory);
|
|
218
|
-
else {
|
|
219
|
-
try {
|
|
220
|
-
fs.rmSync(this.#directory, { recursive: true, force: true });
|
|
221
|
-
} catch {
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
this.onFinish();
|
|
225
|
-
}
|
|
226
|
-
info() {
|
|
227
|
-
return {
|
|
228
|
-
id: this.id,
|
|
229
|
-
tool: this.tool,
|
|
230
|
-
command: this.command,
|
|
231
|
-
...this.pid !== void 0 ? { pid: this.pid } : {},
|
|
232
|
-
...this.logPath ? { logPath: this.logPath } : {},
|
|
233
|
-
startedAt: this.startedAt,
|
|
234
|
-
...this.spilledAt !== void 0 ? { spilledAt: this.spilledAt } : {},
|
|
235
|
-
...this.finishedAt !== void 0 ? { finishedAt: this.finishedAt } : {},
|
|
236
|
-
status: this.status,
|
|
237
|
-
...this.exitCode !== void 0 ? { exitCode: this.exitCode } : {}
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
|
-
};
|
|
241
|
-
var trackShellOperations = (inner, job, tool) => ({
|
|
242
|
-
exec: (command, cwd, options) => inner.exec(wrapShellCommandForPid(command, job.pidPath, tool), cwd, {
|
|
243
|
-
...options,
|
|
244
|
-
onData: (data) => {
|
|
245
|
-
job.append(data);
|
|
246
|
-
options.onData(data);
|
|
247
|
-
}
|
|
248
|
-
})
|
|
249
|
-
});
|
|
250
|
-
var FabricShellJobStore = class {
|
|
251
|
-
constructor(tempRoot = tmpdir()) {
|
|
252
|
-
this.tempRoot = tempRoot;
|
|
253
|
-
}
|
|
254
|
-
#jobs = /* @__PURE__ */ new Map();
|
|
255
|
-
#prune() {
|
|
256
|
-
const completed = [...this.#jobs.values()].filter((job) => job.finished);
|
|
257
|
-
completed.sort((a, b) => (a.finishedAt ?? 0) - (b.finishedAt ?? 0));
|
|
258
|
-
for (const [index, job] of completed.entries()) {
|
|
259
|
-
if (index < completed.length - SHELL_COMPLETED_HANDLES || Date.now() - (job.finishedAt ?? 0) >= SHELL_COMPLETED_MAX_AGE_MS) this.#jobs.delete(job.id);
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
begin(tool, command) {
|
|
263
|
-
this.#prune();
|
|
264
|
-
const job = new FabricShellJob(tool, command, () => this.#prune(), this.tempRoot);
|
|
265
|
-
this.#jobs.set(job.id, job);
|
|
266
|
-
return job;
|
|
267
|
-
}
|
|
268
|
-
get(id) {
|
|
269
|
-
this.#prune();
|
|
270
|
-
return this.#jobs.get(id);
|
|
271
|
-
}
|
|
272
|
-
list() {
|
|
273
|
-
this.#prune();
|
|
274
|
-
return [...this.#jobs.values()].map((job) => job.info());
|
|
275
|
-
}
|
|
276
|
-
waiting() {
|
|
277
|
-
return [...this.#jobs.values()].filter((job) => !job.spilled && !job.finished);
|
|
278
|
-
}
|
|
279
|
-
live() {
|
|
280
|
-
return [...this.#jobs.values()].filter((job) => !job.finished);
|
|
281
|
-
}
|
|
282
|
-
spillWaiting() {
|
|
283
|
-
const jobs = this.waiting();
|
|
284
|
-
for (const job of jobs) job.spill();
|
|
285
|
-
return jobs.length;
|
|
286
|
-
}
|
|
287
|
-
killWaiting() {
|
|
288
|
-
const jobs = this.waiting();
|
|
289
|
-
for (const job of jobs) {
|
|
290
|
-
if (!job.abort.signal.aborted) job.abort.abort(new Error("Command aborted"));
|
|
291
|
-
}
|
|
292
|
-
return jobs.length;
|
|
293
|
-
}
|
|
294
|
-
async close() {
|
|
295
|
-
const live = this.live();
|
|
296
|
-
for (const job of live) {
|
|
297
|
-
if (!job.abort.signal.aborted) job.abort.abort(new Error("Fabric session ended"));
|
|
298
|
-
}
|
|
299
|
-
await Promise.allSettled(live.map((job) => job.finish(null, "\n\n[Process ended: session closed]\n")));
|
|
300
|
-
this.#jobs.clear();
|
|
301
|
-
}
|
|
302
|
-
};
|
|
303
|
-
var raceShellHang = async (options) => {
|
|
304
|
-
const { job, parentSignal, hangMs } = options;
|
|
305
|
-
const onParentAbort = () => {
|
|
306
|
-
if (job.spilled || job.finished || job.abort.signal.aborted) return;
|
|
307
|
-
job.abort.abort(parentSignal?.reason ?? new Error("Command aborted"));
|
|
308
|
-
};
|
|
309
|
-
if (parentSignal) {
|
|
310
|
-
if (parentSignal.aborted) onParentAbort();
|
|
311
|
-
else parentSignal.addEventListener("abort", onParentAbort, { once: true });
|
|
312
|
-
}
|
|
313
|
-
const detachParent = () => {
|
|
314
|
-
parentSignal?.removeEventListener("abort", onParentAbort);
|
|
315
|
-
};
|
|
316
|
-
let hangTimer;
|
|
317
|
-
const hang = new Promise((resolve) => {
|
|
318
|
-
const finish = () => resolve("spill");
|
|
319
|
-
if (hangMs > 0) {
|
|
320
|
-
hangTimer = setTimeout(() => {
|
|
321
|
-
job.spill();
|
|
322
|
-
finish();
|
|
323
|
-
}, hangMs);
|
|
324
|
-
hangTimer.unref?.();
|
|
325
|
-
}
|
|
326
|
-
if (options.immediate) {
|
|
327
|
-
void job.readPid().then(() => {
|
|
328
|
-
if (!job.finished) job.spill();
|
|
329
|
-
});
|
|
330
|
-
}
|
|
331
|
-
void job.whenSpill().then(finish);
|
|
332
|
-
});
|
|
333
|
-
const execute = options.execute(job.abort.signal).then(
|
|
334
|
-
(value) => ({ status: "done", value }),
|
|
335
|
-
(error) => ({ status: "error", error })
|
|
336
|
-
);
|
|
337
|
-
try {
|
|
338
|
-
const first = await Promise.race([execute, hang]);
|
|
339
|
-
if (first === "spill") {
|
|
340
|
-
if (job.finished) return execute;
|
|
341
|
-
job.spill();
|
|
342
|
-
detachParent();
|
|
343
|
-
void execute.then(async (result) => {
|
|
344
|
-
if (job.finished) return;
|
|
345
|
-
if (result.status === "done") {
|
|
346
|
-
await job.finish(0, "\n\n[Process exited with code 0]\n");
|
|
347
|
-
return;
|
|
348
|
-
}
|
|
349
|
-
const message = result.error instanceof Error ? result.error.message : String(result.error);
|
|
350
|
-
await job.finish(null, "\n\n[" + message + "]\n");
|
|
351
|
-
});
|
|
352
|
-
return { status: "spilled" };
|
|
353
|
-
}
|
|
354
|
-
detachParent();
|
|
355
|
-
return first;
|
|
356
|
-
} finally {
|
|
357
|
-
if (hangTimer) clearTimeout(hangTimer);
|
|
358
|
-
}
|
|
359
|
-
};
|
|
360
29
|
|
|
361
30
|
// src/config.ts
|
|
362
|
-
import
|
|
31
|
+
import fs2 from "node:fs";
|
|
363
32
|
import os from "node:os";
|
|
364
|
-
import
|
|
33
|
+
import path2 from "node:path";
|
|
365
34
|
|
|
366
35
|
// src/core/damaged-file.ts
|
|
367
|
-
import
|
|
368
|
-
import
|
|
36
|
+
import fs from "node:fs";
|
|
37
|
+
import path from "node:path";
|
|
369
38
|
var quarantineDamagedFile = (filePath) => {
|
|
370
|
-
const destination =
|
|
371
|
-
|
|
372
|
-
`${
|
|
39
|
+
const destination = path.join(
|
|
40
|
+
path.dirname(filePath),
|
|
41
|
+
`${path.basename(filePath)}.damaged.${process.pid}.${Date.now()}`
|
|
373
42
|
);
|
|
374
43
|
try {
|
|
375
|
-
|
|
44
|
+
fs.renameSync(filePath, destination);
|
|
376
45
|
return destination;
|
|
377
46
|
} catch {
|
|
378
47
|
return void 0;
|
|
@@ -696,7 +365,7 @@ var DEFAULT_FABRIC_CONFIG = {
|
|
|
696
365
|
var readJsonObjectFile = (filePath) => {
|
|
697
366
|
let source;
|
|
698
367
|
try {
|
|
699
|
-
source =
|
|
368
|
+
source = fs2.readFileSync(filePath, "utf8");
|
|
700
369
|
} catch (error) {
|
|
701
370
|
if (error instanceof Error && "code" in error && error.code === "ENOENT") return void 0;
|
|
702
371
|
return void 0;
|
|
@@ -1360,30 +1029,30 @@ var planConfigFile = (filePath) => {
|
|
|
1360
1029
|
};
|
|
1361
1030
|
};
|
|
1362
1031
|
var writeJsonAtomic = (filePath, document, expectedSource) => {
|
|
1363
|
-
const resolvedPath =
|
|
1364
|
-
const directory =
|
|
1365
|
-
if (!
|
|
1366
|
-
const mode =
|
|
1367
|
-
const temporaryPath =
|
|
1032
|
+
const resolvedPath = fs2.existsSync(filePath) ? fs2.realpathSync(filePath) : filePath;
|
|
1033
|
+
const directory = path2.dirname(resolvedPath);
|
|
1034
|
+
if (!fs2.existsSync(directory)) fs2.mkdirSync(directory, { recursive: true });
|
|
1035
|
+
const mode = fs2.existsSync(resolvedPath) ? fs2.statSync(resolvedPath).mode & 511 : 384;
|
|
1036
|
+
const temporaryPath = path2.join(
|
|
1368
1037
|
directory,
|
|
1369
|
-
`.${
|
|
1038
|
+
`.${path2.basename(resolvedPath)}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`
|
|
1370
1039
|
);
|
|
1371
1040
|
let descriptor;
|
|
1372
1041
|
try {
|
|
1373
|
-
descriptor =
|
|
1374
|
-
|
|
1042
|
+
descriptor = fs2.openSync(temporaryPath, "wx", mode);
|
|
1043
|
+
fs2.writeFileSync(descriptor, `${JSON.stringify(document, null, 2)}
|
|
1375
1044
|
`, "utf8");
|
|
1376
|
-
|
|
1377
|
-
|
|
1045
|
+
fs2.fsyncSync(descriptor);
|
|
1046
|
+
fs2.closeSync(descriptor);
|
|
1378
1047
|
descriptor = void 0;
|
|
1379
1048
|
if (expectedSource === null) {
|
|
1380
|
-
if (
|
|
1049
|
+
if (fs2.existsSync(resolvedPath)) {
|
|
1381
1050
|
throw new Error(`Fabric configuration changed while updating ${filePath}`);
|
|
1382
1051
|
}
|
|
1383
1052
|
} else {
|
|
1384
1053
|
let currentSource;
|
|
1385
1054
|
try {
|
|
1386
|
-
currentSource =
|
|
1055
|
+
currentSource = fs2.readFileSync(resolvedPath, "utf8");
|
|
1387
1056
|
} catch (error) {
|
|
1388
1057
|
throw new Error(`Fabric configuration changed while updating ${filePath}`, { cause: error });
|
|
1389
1058
|
}
|
|
@@ -1393,27 +1062,27 @@ var writeJsonAtomic = (filePath, document, expectedSource) => {
|
|
|
1393
1062
|
}
|
|
1394
1063
|
renameAtomic(temporaryPath, resolvedPath);
|
|
1395
1064
|
try {
|
|
1396
|
-
const directoryDescriptor =
|
|
1065
|
+
const directoryDescriptor = fs2.openSync(directory, "r");
|
|
1397
1066
|
try {
|
|
1398
|
-
|
|
1067
|
+
fs2.fsyncSync(directoryDescriptor);
|
|
1399
1068
|
} finally {
|
|
1400
|
-
|
|
1069
|
+
fs2.closeSync(directoryDescriptor);
|
|
1401
1070
|
}
|
|
1402
1071
|
} catch (error) {
|
|
1403
1072
|
const code = error instanceof Error && "code" in error ? error.code : void 0;
|
|
1404
1073
|
if (code !== "EINVAL" && code !== "ENOTSUP" && code !== "EISDIR" && code !== "EPERM") throw error;
|
|
1405
1074
|
}
|
|
1406
1075
|
} catch (error) {
|
|
1407
|
-
if (descriptor !== void 0)
|
|
1408
|
-
|
|
1076
|
+
if (descriptor !== void 0) fs2.closeSync(descriptor);
|
|
1077
|
+
fs2.rmSync(temporaryPath, { force: true });
|
|
1409
1078
|
throw error;
|
|
1410
1079
|
}
|
|
1411
1080
|
};
|
|
1412
1081
|
var resolveFabricConfig = (options, includeProject, applyEnvironmentOverrides) => {
|
|
1413
1082
|
let merged = structuredClone(DEFAULT_FABRIC_CONFIG);
|
|
1414
1083
|
const plans = [
|
|
1415
|
-
planConfigFile(
|
|
1416
|
-
...includeProject ? [planConfigFile(
|
|
1084
|
+
planConfigFile(path2.join(options.agentDir, "fabric.json")),
|
|
1085
|
+
...includeProject ? [planConfigFile(path2.join(options.cwd, ".pi", "fabric.json"))] : []
|
|
1417
1086
|
].filter((plan) => plan !== void 0);
|
|
1418
1087
|
for (const plan of plans) {
|
|
1419
1088
|
if (plan.changed) writeJsonAtomic(plan.path, plan.document, plan.source);
|
|
@@ -1459,7 +1128,7 @@ var saveFabricConfig = (options, partial) => {
|
|
|
1459
1128
|
if (scope === "project" && !options.projectTrusted) {
|
|
1460
1129
|
throw new Error("Cannot save project Fabric configuration for an untrusted project");
|
|
1461
1130
|
}
|
|
1462
|
-
const targetPath = scope === "project" ?
|
|
1131
|
+
const targetPath = scope === "project" ? path2.join(options.cwd, ".pi", "fabric.json") : path2.join(options.agentDir, "fabric.json");
|
|
1463
1132
|
if (Object.hasOwn(partial, "configVersion") || Object.hasOwn(partial, "subagents")) {
|
|
1464
1133
|
throw new Error("Fabric configuration updates must use the current schema");
|
|
1465
1134
|
}
|
|
@@ -1476,11 +1145,6 @@ var saveFabricConfig = (options, partial) => {
|
|
|
1476
1145
|
|
|
1477
1146
|
export {
|
|
1478
1147
|
DEFAULT_SHELL_HANG_MS,
|
|
1479
|
-
formatShellHangNotice,
|
|
1480
|
-
appendShellHangNotice,
|
|
1481
|
-
trackShellOperations,
|
|
1482
|
-
FabricShellJobStore,
|
|
1483
|
-
raceShellHang,
|
|
1484
1148
|
MIN_COMPACTION_TOKEN_THRESHOLD,
|
|
1485
1149
|
MAX_COMPACTION_TOKEN_THRESHOLD,
|
|
1486
1150
|
MIN_COMPACTION_RATIO_THRESHOLD,
|
|
@@ -1499,4 +1163,4 @@ export {
|
|
|
1499
1163
|
loadFabricConfig,
|
|
1500
1164
|
saveFabricConfig
|
|
1501
1165
|
};
|
|
1502
|
-
//# sourceMappingURL=chunk-
|
|
1166
|
+
//# sourceMappingURL=chunk-F72MAIQY.js.map
|