pi-forge 0.0.0 → 1.1.4
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/LICENSE +21 -0
- package/README.md +48 -4
- package/bin/pi-forge.mjs +37 -0
- package/dist/client/assets/CodeMirrorEditor-BqaaP1EE.js +34 -0
- package/dist/client/assets/CodeMirrorEditor-BqaaP1EE.js.map +1 -0
- package/dist/client/assets/index-B-529kgJ.css +32 -0
- package/dist/client/assets/index-BzKzxXFs.js +392 -0
- package/dist/client/assets/index-BzKzxXFs.js.map +1 -0
- package/dist/client/assets/workbox-window.prod.es5-BBnX5xw4.js +3 -0
- package/dist/client/assets/workbox-window.prod.es5-BBnX5xw4.js.map +1 -0
- package/dist/client/icons/icon-192.png +0 -0
- package/dist/client/icons/icon-512.png +0 -0
- package/dist/client/icons/icon-maskable-512.png +0 -0
- package/dist/client/icons/icon.svg +9 -0
- package/dist/client/index.html +24 -0
- package/dist/client/manifest.webmanifest +1 -0
- package/dist/client/offline.html +142 -0
- package/dist/client/sw.js +3 -0
- package/dist/client/sw.js.map +1 -0
- package/dist/client/workbox-6d7155ed.js +3 -0
- package/dist/client/workbox-6d7155ed.js.map +1 -0
- package/dist/server/agent-resource-loader.js +126 -0
- package/dist/server/agent-resource-loader.js.map +1 -0
- package/dist/server/attachment-converters.js +96 -0
- package/dist/server/attachment-converters.js.map +1 -0
- package/dist/server/auth.js +209 -0
- package/dist/server/auth.js.map +1 -0
- package/dist/server/compaction-history.js +106 -0
- package/dist/server/compaction-history.js.map +1 -0
- package/dist/server/concurrency.js +49 -0
- package/dist/server/concurrency.js.map +1 -0
- package/dist/server/config-export.js +220 -0
- package/dist/server/config-export.js.map +1 -0
- package/dist/server/config-manager.js +528 -0
- package/dist/server/config-manager.js.map +1 -0
- package/dist/server/config.js +326 -0
- package/dist/server/config.js.map +1 -0
- package/dist/server/conversion-worker.mjs +90 -0
- package/dist/server/diagnostics.js +137 -0
- package/dist/server/diagnostics.js.map +1 -0
- package/dist/server/extensions-discovery.js +147 -0
- package/dist/server/extensions-discovery.js.map +1 -0
- package/dist/server/file-manager.js +734 -0
- package/dist/server/file-manager.js.map +1 -0
- package/dist/server/file-references.js +215 -0
- package/dist/server/file-references.js.map +1 -0
- package/dist/server/file-searcher.js +385 -0
- package/dist/server/file-searcher.js.map +1 -0
- package/dist/server/git-runner.js +684 -0
- package/dist/server/git-runner.js.map +1 -0
- package/dist/server/index.js +468 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/mcp/config.js +133 -0
- package/dist/server/mcp/config.js.map +1 -0
- package/dist/server/mcp/manager.js +351 -0
- package/dist/server/mcp/manager.js.map +1 -0
- package/dist/server/mcp/tool-bridge.js +173 -0
- package/dist/server/mcp/tool-bridge.js.map +1 -0
- package/dist/server/project-manager.js +301 -0
- package/dist/server/project-manager.js.map +1 -0
- package/dist/server/pty-manager.js +354 -0
- package/dist/server/pty-manager.js.map +1 -0
- package/dist/server/routes/_schemas.js +73 -0
- package/dist/server/routes/_schemas.js.map +1 -0
- package/dist/server/routes/auth.js +164 -0
- package/dist/server/routes/auth.js.map +1 -0
- package/dist/server/routes/config.js +1163 -0
- package/dist/server/routes/config.js.map +1 -0
- package/dist/server/routes/control.js +464 -0
- package/dist/server/routes/control.js.map +1 -0
- package/dist/server/routes/exec.js +217 -0
- package/dist/server/routes/exec.js.map +1 -0
- package/dist/server/routes/files.js +847 -0
- package/dist/server/routes/files.js.map +1 -0
- package/dist/server/routes/git.js +837 -0
- package/dist/server/routes/git.js.map +1 -0
- package/dist/server/routes/health.js +97 -0
- package/dist/server/routes/health.js.map +1 -0
- package/dist/server/routes/mcp.js +300 -0
- package/dist/server/routes/mcp.js.map +1 -0
- package/dist/server/routes/projects.js +259 -0
- package/dist/server/routes/projects.js.map +1 -0
- package/dist/server/routes/prompt.js +496 -0
- package/dist/server/routes/prompt.js.map +1 -0
- package/dist/server/routes/sessions.js +783 -0
- package/dist/server/routes/sessions.js.map +1 -0
- package/dist/server/routes/stream.js +69 -0
- package/dist/server/routes/stream.js.map +1 -0
- package/dist/server/routes/terminal.js +335 -0
- package/dist/server/routes/terminal.js.map +1 -0
- package/dist/server/session-registry.js +1197 -0
- package/dist/server/session-registry.js.map +1 -0
- package/dist/server/skill-overrides.js +151 -0
- package/dist/server/skill-overrides.js.map +1 -0
- package/dist/server/skills-export.js +257 -0
- package/dist/server/skills-export.js.map +1 -0
- package/dist/server/sse-bridge.js +220 -0
- package/dist/server/sse-bridge.js.map +1 -0
- package/dist/server/tool-overrides.js +277 -0
- package/dist/server/tool-overrides.js.map +1 -0
- package/dist/server/turn-diff-builder.js +280 -0
- package/dist/server/turn-diff-builder.js.map +1 -0
- package/package.json +53 -12
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config export / import as a flat `.tar.gz`.
|
|
3
|
+
*
|
|
4
|
+
* What's included (and why):
|
|
5
|
+
* - `mcp.json` — pi-forge-owned MCP server registry
|
|
6
|
+
* - `settings.json` — pi-owned defaults (model, thinking level, skills patterns)
|
|
7
|
+
* - `models.json` — pi-owned custom providers
|
|
8
|
+
*
|
|
9
|
+
* What's deliberately EXCLUDED:
|
|
10
|
+
* - `auth.json` — provider API keys + OAuth tokens. OAuth tokens are
|
|
11
|
+
* installation-bound (a token issued for one pi-forge instance is
|
|
12
|
+
* not portable), and inline API keys are sensitive enough that
|
|
13
|
+
* bundling them into a download the user might forward by accident
|
|
14
|
+
* (Slack, Drive, ticket attachment) outweighs the convenience. The
|
|
15
|
+
* import flow tells the user to re-authenticate providers
|
|
16
|
+
* afterwards.
|
|
17
|
+
* - The auto-generated `jwt-secret` and `password-hash` files —
|
|
18
|
+
* installation-bound, intentionally not portable.
|
|
19
|
+
*
|
|
20
|
+
* Archive layout: a flat tar with the three files at the top level (no
|
|
21
|
+
* leading directory). Importing rejects any entry that isn't one of
|
|
22
|
+
* those three exact names — this is the only validation that matters
|
|
23
|
+
* for safety, since the names map deterministically to disk targets.
|
|
24
|
+
*
|
|
25
|
+
* Atomic writes: each imported file lands in `<dst>.import.tmp` first,
|
|
26
|
+
* then `rename`s into place — same shape config-manager / project-
|
|
27
|
+
* manager already use, so a crash mid-import never produces a half-
|
|
28
|
+
* written config file.
|
|
29
|
+
*/
|
|
30
|
+
import { mkdir, mkdtemp, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
31
|
+
import { tmpdir } from "node:os";
|
|
32
|
+
import { dirname, join } from "node:path";
|
|
33
|
+
import { Readable } from "node:stream";
|
|
34
|
+
import { create as tarCreate, extract as tarExtract } from "tar";
|
|
35
|
+
import { config } from "./config.js";
|
|
36
|
+
/**
|
|
37
|
+
* The exact set of filenames we accept on import and emit on export.
|
|
38
|
+
* Anything else in an uploaded tar is silently ignored (reported in
|
|
39
|
+
* `skipped`). Defined as a `Set` so the import filter is O(1) per
|
|
40
|
+
* entry and we can't accidentally accept a near-miss like
|
|
41
|
+
* `Settings.json` on a case-sensitive filesystem.
|
|
42
|
+
*/
|
|
43
|
+
const ALLOWED_FILES = ["mcp.json", "settings.json", "models.json"];
|
|
44
|
+
const ALLOWED_SET = new Set(ALLOWED_FILES);
|
|
45
|
+
/**
|
|
46
|
+
* Map each allowed name to its on-disk target. Functions (not
|
|
47
|
+
* constants) so changes to `config.piConfigDir` /
|
|
48
|
+
* `config.mcpConfigFile` at test time take effect.
|
|
49
|
+
*/
|
|
50
|
+
const TARGETS = {
|
|
51
|
+
"mcp.json": () => config.mcpConfigFile,
|
|
52
|
+
"settings.json": () => join(config.piConfigDir, "settings.json"),
|
|
53
|
+
"models.json": () => join(config.piConfigDir, "models.json"),
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Hard cap on uploaded tar size. Far above the realistic config size
|
|
57
|
+
* (the three files together are usually <50 KB) but low enough that a
|
|
58
|
+
* malicious or accidental large upload can't DoS the import path
|
|
59
|
+
* (which extracts to a tmp dir before validating). Also matches the
|
|
60
|
+
* route's multipart `fileSize` cap so the two layers agree.
|
|
61
|
+
*/
|
|
62
|
+
export const MAX_IMPORT_BYTES = 5 * 1024 * 1024;
|
|
63
|
+
/**
|
|
64
|
+
* Build the export tar. Stages each existing config file into a tmp
|
|
65
|
+
* directory and tars from there — emitting straight from
|
|
66
|
+
* `config.piConfigDir` would also pick up `auth.json` and any other
|
|
67
|
+
* pi-owned files that aren't part of our export contract.
|
|
68
|
+
*
|
|
69
|
+
* Returns the gzipped readable stream + a summary of what was
|
|
70
|
+
* included. Caller pipes the stream to the HTTP response and consumes
|
|
71
|
+
* `files` for the response header / log line.
|
|
72
|
+
*
|
|
73
|
+
* The temp staging dir is cleaned up on stream `end` / `error`. If
|
|
74
|
+
* the stream is abandoned (caller never reads it), the dir leaks —
|
|
75
|
+
* acceptable for an interactive download path that completes in ms.
|
|
76
|
+
*/
|
|
77
|
+
export async function buildExportTar() {
|
|
78
|
+
const stage = await mkdtemp(join(tmpdir(), "pi-config-export-"));
|
|
79
|
+
const files = [];
|
|
80
|
+
for (const name of ALLOWED_FILES) {
|
|
81
|
+
const src = TARGETS[name]();
|
|
82
|
+
try {
|
|
83
|
+
const data = await readFile(src);
|
|
84
|
+
await writeFile(join(stage, name), data);
|
|
85
|
+
files.push(name);
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
if (err.code !== "ENOENT") {
|
|
89
|
+
await rm(stage, { recursive: true, force: true }).catch(() => undefined);
|
|
90
|
+
throw err;
|
|
91
|
+
}
|
|
92
|
+
// Missing on disk — silently skip. An empty tar is valid.
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// tar's `Pack` extends `Minipass`, which is API-compatible with
|
|
96
|
+
// node:stream Readable but typed independently. Cast at the
|
|
97
|
+
// boundary so the public return type is the standard one.
|
|
98
|
+
const pack = tarCreate({ gzip: true, cwd: stage }, files);
|
|
99
|
+
const stream = pack;
|
|
100
|
+
const cleanup = () => {
|
|
101
|
+
void rm(stage, { recursive: true, force: true }).catch(() => undefined);
|
|
102
|
+
};
|
|
103
|
+
stream.once("end", cleanup);
|
|
104
|
+
stream.once("error", cleanup);
|
|
105
|
+
stream.once("close", cleanup);
|
|
106
|
+
return { files, stream };
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Extract a previously-exported tar from a Buffer and write any
|
|
110
|
+
* allowed files atomically into their on-disk targets.
|
|
111
|
+
*
|
|
112
|
+
* Two-phase, by design:
|
|
113
|
+
* 1. Extract entire archive to a private temp dir, refusing any
|
|
114
|
+
* entry name that isn't in `ALLOWED_FILES`.
|
|
115
|
+
* 2. Validate each accepted file (JSON.parse). Files that fail
|
|
116
|
+
* validation never make it to disk.
|
|
117
|
+
* 3. Atomic rename per file from temp into target.
|
|
118
|
+
*
|
|
119
|
+
* The "validate before any disk write" ordering matters: a partial
|
|
120
|
+
* import (e.g. `mcp.json` good, `settings.json` corrupt) would leave
|
|
121
|
+
* the pi-forge in worse shape than before the user clicked Import.
|
|
122
|
+
* Either everything valid lands, or nothing does — per file, ALL
|
|
123
|
+
* pass validation before ANY rename runs.
|
|
124
|
+
*
|
|
125
|
+
* Caller is responsible for the upload size cap on the route side;
|
|
126
|
+
* we double-check here against `MAX_IMPORT_BYTES` so a direct caller
|
|
127
|
+
* (test, future route) can't bypass the limit.
|
|
128
|
+
*/
|
|
129
|
+
export async function importConfigFromBuffer(buf) {
|
|
130
|
+
if (buf.byteLength > MAX_IMPORT_BYTES) {
|
|
131
|
+
throw new Error(`tar exceeds ${MAX_IMPORT_BYTES} bytes (got ${buf.byteLength}); refusing to import`);
|
|
132
|
+
}
|
|
133
|
+
const stage = await mkdtemp(join(tmpdir(), "pi-config-import-"));
|
|
134
|
+
try {
|
|
135
|
+
const accepted = [];
|
|
136
|
+
const skipped = [];
|
|
137
|
+
// tar.extract consumes a stream. Wrap the buffer as a Readable.
|
|
138
|
+
// The `filter` callback fires per-entry BEFORE bytes are written,
|
|
139
|
+
// so a rejected entry never touches disk.
|
|
140
|
+
await new Promise((resolve, reject) => {
|
|
141
|
+
const extractStream = tarExtract({
|
|
142
|
+
cwd: stage,
|
|
143
|
+
// Reject absolute paths and `..` segments at the tar layer;
|
|
144
|
+
// belt-and-suspenders since our filter also enforces it.
|
|
145
|
+
strict: true,
|
|
146
|
+
filter: (path, entry) => {
|
|
147
|
+
// path comes through as the entry name verbatim; reject
|
|
148
|
+
// anything that smells like a directory traversal or a
|
|
149
|
+
// non-file. Matching against the exact ALLOWED_SET is the
|
|
150
|
+
// primary safety boundary.
|
|
151
|
+
//
|
|
152
|
+
// `entry` is typed as `ReadEntry | Stats` because `tar` reuses
|
|
153
|
+
// this filter for both pack and unpack; on extract it's
|
|
154
|
+
// always a `ReadEntry` carrying `.type`. Narrow defensively.
|
|
155
|
+
const entryType = entry.type;
|
|
156
|
+
if (entryType !== undefined && entryType !== "File") {
|
|
157
|
+
skipped.push(path);
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
if (path.includes("/") ||
|
|
161
|
+
path.includes("\\") ||
|
|
162
|
+
path.includes("..") ||
|
|
163
|
+
path.startsWith(".")) {
|
|
164
|
+
skipped.push(path);
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
if (!ALLOWED_SET.has(path)) {
|
|
168
|
+
skipped.push(path);
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
accepted.push(path);
|
|
172
|
+
return true;
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
extractStream.on("error", reject);
|
|
176
|
+
extractStream.on("finish", () => resolve());
|
|
177
|
+
Readable.from(buf).pipe(extractStream);
|
|
178
|
+
});
|
|
179
|
+
// Validate every accepted file before ANY rename. JSON.parse is
|
|
180
|
+
// the contract — pi's loaders all assume parseable JSON, and an
|
|
181
|
+
// import that lands invalid JSON would brick the next agent
|
|
182
|
+
// session create.
|
|
183
|
+
const errors = [];
|
|
184
|
+
const valid = [];
|
|
185
|
+
for (const name of accepted) {
|
|
186
|
+
try {
|
|
187
|
+
const raw = await readFile(join(stage, name), "utf8");
|
|
188
|
+
JSON.parse(raw);
|
|
189
|
+
valid.push(name);
|
|
190
|
+
}
|
|
191
|
+
catch (err) {
|
|
192
|
+
errors.push({ file: name, reason: err instanceof Error ? err.message : String(err) });
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (errors.length > 0) {
|
|
196
|
+
// Fail the whole import on any per-file validation error so the
|
|
197
|
+
// user gets a single clear failure, not "imported half, broke
|
|
198
|
+
// half." The summary still surfaces every error so the user
|
|
199
|
+
// knows which file was bad.
|
|
200
|
+
return { imported: [], skipped, errors };
|
|
201
|
+
}
|
|
202
|
+
// Atomic move. mkdir parent dirs since pi config dir might not
|
|
203
|
+
// exist on a fresh deploy that's only setting these via import.
|
|
204
|
+
const imported = [];
|
|
205
|
+
for (const name of valid) {
|
|
206
|
+
const src = join(stage, name);
|
|
207
|
+
const dst = TARGETS[name]();
|
|
208
|
+
await mkdir(dirname(dst), { recursive: true });
|
|
209
|
+
const tmpDst = `${dst}.${Date.now()}.import.tmp`;
|
|
210
|
+
await rename(src, tmpDst);
|
|
211
|
+
await rename(tmpDst, dst);
|
|
212
|
+
imported.push(name);
|
|
213
|
+
}
|
|
214
|
+
return { imported, skipped, errors };
|
|
215
|
+
}
|
|
216
|
+
finally {
|
|
217
|
+
await rm(stage, { recursive: true, force: true }).catch(() => undefined);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=config-export.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-export.js","sourceRoot":"","sources":["../src/config-export.ts"],"names":[],"mappings":"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"}
|