@xpufx/paseo-top 0.4.3 → 0.4.5
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/OVERVIEW.md +17 -0
- package/client/host-ui.tsx +184 -0
- package/client/multi-host-view.tsx +57 -43
- package/client/multi-host.ts +36 -13
- package/client/pill-labels.ts +124 -7
- package/client/pill.tsx +724 -669
- package/client/resources-query.ts +1 -1
- package/client/settings-events.ts +33 -0
- package/client/settings-ui.tsx +86 -9
- package/client/surface.tsx +310 -274
- package/client/telemetry-copy.ts +8 -2
- package/client/telemetry.tsx +372 -321
- package/client/turn-panel.tsx +126 -104
- package/client/vendor/paseo-plugin-helper/{host.ts → core/host.ts} +21 -61
- package/client/vendor/paseo-plugin-helper/core/index.ts +76 -0
- package/client/vendor/paseo-plugin-helper/core/plugin-load-count.ts +89 -0
- package/client/vendor/paseo-plugin-helper/{query-refresh.ts → core/query-refresh.ts} +2 -2
- package/client/vendor/paseo-plugin-helper/{query.ts → core/query.ts} +2 -2
- package/client/vendor/paseo-plugin-helper/{settings.ts → core/settings.ts} +1 -1
- package/client/vendor/paseo-plugin-helper/{shared-settings.ts → core/shared-settings.ts} +2 -2
- package/client/vendor/paseo-plugin-helper/{utils → lifecycle}/clipboard.ts +2 -2
- package/client/vendor/paseo-plugin-helper/lifecycle/flair.ts +26 -0
- package/client/vendor/paseo-plugin-helper/{theme/color-utils.ts → lifecycle/host-color.ts} +95 -38
- package/client/vendor/paseo-plugin-helper/lifecycle/host-theme.tsx +109 -0
- package/client/vendor/paseo-plugin-helper/lifecycle/index.ts +23 -0
- package/client/vendor/paseo-plugin-helper/lifecycle/multi-host.ts +66 -0
- package/client/vendor/paseo-plugin-helper/{panel.tsx → lifecycle/panel.tsx} +7 -7
- package/client/vendor/paseo-plugin-helper/{pill.tsx → lifecycle/pill.tsx} +118 -338
- package/client/vendor/paseo-plugin-helper/lifecycle/providers.tsx +46 -0
- package/client/vendor/paseo-plugin-helper/lifecycle/scroll-owner.ts +26 -0
- package/client/vendor/paseo-plugin-helper/lifecycle/surface.tsx +140 -0
- package/client/vendor/paseo-plugin-helper/ui/color.ts +1 -0
- package/client/vendor/paseo-plugin-helper/ui/content.tsx +1326 -0
- package/client/vendor/paseo-plugin-helper/ui/controls.tsx +1018 -0
- package/client/vendor/paseo-plugin-helper/{components/DataTable.tsx → ui/data.tsx} +55 -47
- package/client/vendor/paseo-plugin-helper/ui/index.ts +44 -0
- package/client/vendor/paseo-plugin-helper/ui/layout.tsx +263 -0
- package/client/vendor/paseo-plugin-helper/ui/modal.tsx +111 -0
- package/client/vendor/paseo-plugin-helper/ui/plugin-reload-banner.tsx +119 -0
- package/client/vendor/paseo-plugin-helper/{settings-screen.tsx → ui/settings-screen.tsx} +3 -3
- package/client/vendor/paseo-plugin-helper/ui/settings.ts +36 -0
- package/client/vendor/paseo-plugin-helper/ui/text.tsx +821 -0
- package/client/vendor/paseo-plugin-helper/ui/theme.tsx +1 -0
- package/client/vendor/permission-audit/client.tsx +706 -0
- package/client/vendor/permission-audit/index.tsx +1 -0
- package/index.client.tsx +58 -37
- package/index.server.ts +256 -227
- package/package.json +14 -2
- package/paseo-plugin.json +2 -0
- package/screenshots/1-paseo-top-modal-system.jpg +0 -0
- package/screenshots/2-paseo-top-modal-workspace.jpg +0 -0
- package/screenshots/3-paseo-top-modal-settings.jpg +0 -0
- package/screenshots/4-paseo-top-modal-about.jpg +0 -0
- package/screenshots/paseo-top-dashboard.jpg +0 -0
- package/screenshots/paseo-top-general-view.jpg +0 -0
- package/server/resources.ts +98 -0
- package/server/vendor/paseo-plugin-helper/custom-pills.ts +70 -0
- package/server/vendor/paseo-plugin-helper/index.ts +4 -0
- package/server/vendor/paseo-plugin-helper/logger.ts +5 -16
- package/server/vendor/paseo-plugin-helper/mcp-injection.ts +20 -2
- package/server/vendor/paseo-plugin-helper/plugins.ts +184 -19
- package/server/vendor/paseo-plugin-helper/rpc-guard.ts +2 -2
- package/server/vendor/paseo-plugin-helper/storage-guard.ts +294 -0
- package/server/vendor/paseo-plugin-helper/storage.ts +36 -2
- package/server/vendor/paseo-plugin-helper/telemetry.ts +486 -0
- package/server/vendor/paseo-plugin-helper/tickets.ts +1232 -0
- package/server/vendor/permission-audit/index.ts +1 -0
- package/server/vendor/permission-audit/server.ts +839 -0
- package/shared/helper-version.ts +20 -0
- package/shared/multi-host.ts +156 -155
- package/shared/resources.ts +146 -1
- package/shared/vendor/paseo-plugin-helper/async.ts +14 -7
- package/shared/vendor/paseo-plugin-helper/features.ts +244 -0
- package/shared/vendor/paseo-plugin-helper/forge.ts +27 -0
- package/shared/vendor/paseo-plugin-helper/index.ts +2 -0
- package/shared/vendor/paseo-plugin-helper/tickets.ts +1104 -0
- package/shared/vendor/permission-audit/filter.ts +61 -0
- package/shared/vendor/permission-audit/index.ts +2 -0
- package/shared/vendor/permission-audit/shared.ts +119 -0
- package/shared/version.ts +1 -1
- package/client/vendor/paseo-plugin-helper/components/AboutSection.tsx +0 -493
- package/client/vendor/paseo-plugin-helper/components/AttentionBeacon.tsx +0 -250
- package/client/vendor/paseo-plugin-helper/components/Badge.tsx +0 -156
- package/client/vendor/paseo-plugin-helper/components/Button.tsx +0 -184
- package/client/vendor/paseo-plugin-helper/components/Card.tsx +0 -225
- package/client/vendor/paseo-plugin-helper/components/CodeBlock.tsx +0 -196
- package/client/vendor/paseo-plugin-helper/components/Collapsible.tsx +0 -277
- package/client/vendor/paseo-plugin-helper/components/CommandBox.tsx +0 -172
- package/client/vendor/paseo-plugin-helper/components/CopyButton.tsx +0 -180
- package/client/vendor/paseo-plugin-helper/components/EmptyState.tsx +0 -97
- package/client/vendor/paseo-plugin-helper/components/HighlightedText.tsx +0 -70
- package/client/vendor/paseo-plugin-helper/components/InlineButton.tsx +0 -75
- package/client/vendor/paseo-plugin-helper/components/KeyValue.tsx +0 -446
- package/client/vendor/paseo-plugin-helper/components/MetricGauge.tsx +0 -247
- package/client/vendor/paseo-plugin-helper/components/ProgressBar.tsx +0 -117
- package/client/vendor/paseo-plugin-helper/components/Responsive.tsx +0 -53
- package/client/vendor/paseo-plugin-helper/components/SearchInput.tsx +0 -118
- package/client/vendor/paseo-plugin-helper/components/SectionHeader.tsx +0 -80
- package/client/vendor/paseo-plugin-helper/components/Select.tsx +0 -215
- package/client/vendor/paseo-plugin-helper/components/StatusDot.tsx +0 -80
- package/client/vendor/paseo-plugin-helper/components/Tabs.tsx +0 -319
- package/client/vendor/paseo-plugin-helper/components/TextInput.tsx +0 -150
- package/client/vendor/paseo-plugin-helper/components/Toggle.tsx +0 -163
- package/client/vendor/paseo-plugin-helper/components/TruncatedText.tsx +0 -157
- package/client/vendor/paseo-plugin-helper/components/index.ts +0 -25
- package/client/vendor/paseo-plugin-helper/custom-pills.tsx +0 -224
- package/client/vendor/paseo-plugin-helper/forge-icon.tsx +0 -79
- package/client/vendor/paseo-plugin-helper/icon.tsx +0 -39
- package/client/vendor/paseo-plugin-helper/index.ts +0 -29
- package/client/vendor/paseo-plugin-helper/layout/ActionBar.tsx +0 -49
- package/client/vendor/paseo-plugin-helper/layout/FormRow.tsx +0 -103
- package/client/vendor/paseo-plugin-helper/layout/Grid.tsx +0 -65
- package/client/vendor/paseo-plugin-helper/layout/ModalBody.tsx +0 -378
- package/client/vendor/paseo-plugin-helper/layout/ModalContent.tsx +0 -51
- package/client/vendor/paseo-plugin-helper/layout/Row.tsx +0 -39
- package/client/vendor/paseo-plugin-helper/layout/Stack.tsx +0 -39
- package/client/vendor/paseo-plugin-helper/layout/index.ts +0 -7
- package/client/vendor/paseo-plugin-helper/styles/index.ts +0 -1
- package/client/vendor/paseo-plugin-helper/styles/recipes.ts +0 -550
- package/client/vendor/paseo-plugin-helper/surface.tsx +0 -80
- package/client/vendor/paseo-plugin-helper/theme/flair.ts +0 -76
- package/client/vendor/paseo-plugin-helper/theme/host-variables.ts +0 -121
- package/client/vendor/paseo-plugin-helper/theme/index.ts +0 -7
- package/client/vendor/paseo-plugin-helper/theme/provider.tsx +0 -178
- package/client/vendor/paseo-plugin-helper/theme/responsive.ts +0 -213
- package/client/vendor/paseo-plugin-helper/theme/tokens.ts +0 -200
- package/client/vendor/paseo-plugin-helper/theme/useResponsive.ts +0 -57
- /package/client/vendor/paseo-plugin-helper/{command-center.ts → core/command-center.ts} +0 -0
- /package/client/vendor/paseo-plugin-helper/{snapshot.ts → core/snapshot.ts} +0 -0
- /package/client/vendor/paseo-plugin-helper/{utils → lifecycle}/haptics.ts +0 -0
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import type { PathLike } from "node:fs";
|
|
3
|
+
import fsp from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import os from "node:os";
|
|
6
|
+
import { resolvePaseoHome } from "./storage";
|
|
7
|
+
|
|
8
|
+
export class ScopedStorageViolationError extends Error {
|
|
9
|
+
readonly code = "ERR_SCOPED_STORAGE_VIOLATION";
|
|
10
|
+
readonly attemptedPath: string;
|
|
11
|
+
readonly allowedRoots: string[];
|
|
12
|
+
|
|
13
|
+
constructor(message: string, attemptedPath: string, allowedRoots: string[]) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.name = "ScopedStorageViolationError";
|
|
16
|
+
this.attemptedPath = attemptedPath;
|
|
17
|
+
this.allowedRoots = allowedRoots;
|
|
18
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface InstallStorageGuardOptions {
|
|
23
|
+
pluginId: string;
|
|
24
|
+
namespace?: string; // defaults to "plugin-data/xpufx"
|
|
25
|
+
baseDir?: string;
|
|
26
|
+
allowTmp?: boolean; // defaults to true
|
|
27
|
+
mode?: "enforce" | "warn"; // defaults to "enforce"
|
|
28
|
+
onViolation?: (violation: { path: string; operation: string; allowedRoots: string[] }) => void;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function resolvePathString(target: PathLike | number): string | null {
|
|
32
|
+
if (typeof target === "string") {
|
|
33
|
+
return path.resolve(target);
|
|
34
|
+
}
|
|
35
|
+
if (Buffer.isBuffer(target)) {
|
|
36
|
+
return path.resolve(target.toString("utf8"));
|
|
37
|
+
}
|
|
38
|
+
if (target instanceof URL) {
|
|
39
|
+
if (target.protocol === "file:") {
|
|
40
|
+
const pathname = decodeURIComponent(target.pathname);
|
|
41
|
+
return path.resolve(pathname);
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function isPathWithinRoot(targetPath: string, rootDir: string): boolean {
|
|
49
|
+
const resolvedTarget = path.resolve(targetPath);
|
|
50
|
+
const resolvedRoot = path.resolve(rootDir);
|
|
51
|
+
|
|
52
|
+
if (resolvedTarget === resolvedRoot) {
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const relative = path.relative(resolvedRoot, resolvedTarget);
|
|
57
|
+
return !relative.startsWith("..") && !path.isAbsolute(relative);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function installStorageGuard(options: InstallStorageGuardOptions): () => void {
|
|
61
|
+
const pluginId = options.pluginId;
|
|
62
|
+
const namespace = options.namespace ?? "plugin-data/xpufx";
|
|
63
|
+
const allowTmp = options.allowTmp ?? true;
|
|
64
|
+
const mode = options.mode ?? "enforce";
|
|
65
|
+
|
|
66
|
+
const pluginStorageDir = options.baseDir
|
|
67
|
+
? path.resolve(options.baseDir, pluginId)
|
|
68
|
+
: path.resolve(resolvePaseoHome(), namespace, pluginId);
|
|
69
|
+
const allowedRoots: string[] = [pluginStorageDir];
|
|
70
|
+
|
|
71
|
+
if (allowTmp) {
|
|
72
|
+
allowedRoots.push(path.resolve(os.tmpdir()));
|
|
73
|
+
allowedRoots.push(path.resolve(process.cwd(), ".tmp"));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function validateTarget(target: PathLike | number, operation: string): void {
|
|
77
|
+
const resolved = resolvePathString(target);
|
|
78
|
+
if (!resolved) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const isAllowed = allowedRoots.some((root) => isPathWithinRoot(resolved, root));
|
|
83
|
+
if (!isAllowed) {
|
|
84
|
+
options.onViolation?.({
|
|
85
|
+
path: resolved,
|
|
86
|
+
operation,
|
|
87
|
+
allowedRoots: [...allowedRoots],
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const message = `Scoped storage violation: write operation '${operation}' on '${resolved}' is outside permitted scopes: [${allowedRoots.join(", ")}]`;
|
|
91
|
+
|
|
92
|
+
if (mode === "enforce") {
|
|
93
|
+
throw new ScopedStorageViolationError(message, resolved, [...allowedRoots]);
|
|
94
|
+
} else {
|
|
95
|
+
console.warn(`[WARN] ${message}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Backup originals
|
|
101
|
+
const origWriteFile = fs.writeFile;
|
|
102
|
+
const origWriteFileSync = fs.writeFileSync;
|
|
103
|
+
const origAppendFile = fs.appendFile;
|
|
104
|
+
const origAppendFileSync = fs.appendFileSync;
|
|
105
|
+
const origMkdir = fs.mkdir;
|
|
106
|
+
const origMkdirSync = fs.mkdirSync;
|
|
107
|
+
const origRmdir = fs.rmdir;
|
|
108
|
+
const origRmdirSync = fs.rmdirSync;
|
|
109
|
+
const origUnlink = fs.unlink;
|
|
110
|
+
const origUnlinkSync = fs.unlinkSync;
|
|
111
|
+
const origRm = fs.rm;
|
|
112
|
+
const origRmSync = fs.rmSync;
|
|
113
|
+
const origRename = fs.rename;
|
|
114
|
+
const origRenameSync = fs.renameSync;
|
|
115
|
+
const origCopyFile = fs.copyFile;
|
|
116
|
+
const origCopyFileSync = fs.copyFileSync;
|
|
117
|
+
const origCreateWriteStream = fs.createWriteStream;
|
|
118
|
+
|
|
119
|
+
const origPromisesWriteFile = fsp.writeFile;
|
|
120
|
+
const origPromisesAppendFile = fsp.appendFile;
|
|
121
|
+
const origPromisesMkdir = fsp.mkdir;
|
|
122
|
+
const origPromisesRmdir = fsp.rmdir;
|
|
123
|
+
const origPromisesUnlink = fsp.unlink;
|
|
124
|
+
const origPromisesRm = fsp.rm;
|
|
125
|
+
const origPromisesRename = fsp.rename;
|
|
126
|
+
const origPromisesCopyFile = fsp.copyFile;
|
|
127
|
+
|
|
128
|
+
// Intercept node:fs sync & async
|
|
129
|
+
const anyFs = fs as any;
|
|
130
|
+
|
|
131
|
+
anyFs.writeFile = function (file: PathLike | number, ...args: unknown[]) {
|
|
132
|
+
validateTarget(file, "writeFile");
|
|
133
|
+
return Reflect.apply(origWriteFile, fs, [file, ...args]);
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
anyFs.writeFileSync = function (file: PathLike | number, ...args: unknown[]) {
|
|
137
|
+
validateTarget(file, "writeFileSync");
|
|
138
|
+
return Reflect.apply(origWriteFileSync, fs, [file, ...args]);
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
anyFs.appendFile = function (file: PathLike | number, ...args: unknown[]) {
|
|
142
|
+
validateTarget(file, "appendFile");
|
|
143
|
+
return Reflect.apply(origAppendFile, fs, [file, ...args]);
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
anyFs.appendFileSync = function (file: PathLike | number, ...args: unknown[]) {
|
|
147
|
+
validateTarget(file, "appendFileSync");
|
|
148
|
+
return Reflect.apply(origAppendFileSync, fs, [file, ...args]);
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
anyFs.mkdir = function (pathArg: PathLike, ...args: unknown[]) {
|
|
152
|
+
validateTarget(pathArg, "mkdir");
|
|
153
|
+
return Reflect.apply(origMkdir, fs, [pathArg, ...args]);
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
anyFs.mkdirSync = function (pathArg: PathLike, ...args: unknown[]) {
|
|
157
|
+
validateTarget(pathArg, "mkdirSync");
|
|
158
|
+
return Reflect.apply(origMkdirSync, fs, [pathArg, ...args]);
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
anyFs.rmdir = function (pathArg: PathLike, ...args: unknown[]) {
|
|
162
|
+
validateTarget(pathArg, "rmdir");
|
|
163
|
+
return Reflect.apply(origRmdir, fs, [pathArg, ...args]);
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
anyFs.rmdirSync = function (pathArg: PathLike, ...args: unknown[]) {
|
|
167
|
+
validateTarget(pathArg, "rmdirSync");
|
|
168
|
+
return Reflect.apply(origRmdirSync, fs, [pathArg, ...args]);
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
anyFs.unlink = function (pathArg: PathLike, ...args: unknown[]) {
|
|
172
|
+
validateTarget(pathArg, "unlink");
|
|
173
|
+
return Reflect.apply(origUnlink, fs, [pathArg, ...args]);
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
anyFs.unlinkSync = function (pathArg: PathLike, ...args: unknown[]) {
|
|
177
|
+
validateTarget(pathArg, "unlinkSync");
|
|
178
|
+
return Reflect.apply(origUnlinkSync, fs, [pathArg, ...args]);
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
anyFs.rm = function (pathArg: PathLike, ...args: unknown[]) {
|
|
182
|
+
validateTarget(pathArg, "rm");
|
|
183
|
+
return Reflect.apply(origRm, fs, [pathArg, ...args]);
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
anyFs.rmSync = function (pathArg: PathLike, ...args: unknown[]) {
|
|
187
|
+
validateTarget(pathArg, "rmSync");
|
|
188
|
+
return Reflect.apply(origRmSync, fs, [pathArg, ...args]);
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
anyFs.rename = function (oldPath: PathLike, newPath: PathLike, ...args: unknown[]) {
|
|
192
|
+
validateTarget(oldPath, "rename:source");
|
|
193
|
+
validateTarget(newPath, "rename:destination");
|
|
194
|
+
return Reflect.apply(origRename, fs, [oldPath, newPath, ...args]);
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
anyFs.renameSync = function (oldPath: PathLike, newPath: PathLike, ...args: unknown[]) {
|
|
198
|
+
validateTarget(oldPath, "renameSync:source");
|
|
199
|
+
validateTarget(newPath, "renameSync:destination");
|
|
200
|
+
return Reflect.apply(origRenameSync, fs, [oldPath, newPath, ...args]);
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
anyFs.copyFile = function (src: PathLike, dest: PathLike, ...args: unknown[]) {
|
|
204
|
+
validateTarget(dest, "copyFile:destination");
|
|
205
|
+
return Reflect.apply(origCopyFile, fs, [src, dest, ...args]);
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
anyFs.copyFileSync = function (src: PathLike, dest: PathLike, ...args: unknown[]) {
|
|
209
|
+
validateTarget(dest, "copyFileSync:destination");
|
|
210
|
+
return Reflect.apply(origCopyFileSync, fs, [src, dest, ...args]);
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
anyFs.createWriteStream = function (pathArg: PathLike, ...args: unknown[]) {
|
|
214
|
+
validateTarget(pathArg, "createWriteStream");
|
|
215
|
+
return Reflect.apply(origCreateWriteStream, fs, [pathArg, ...args]);
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
// Intercept node:fs/promises
|
|
219
|
+
const anyFsp = fsp as any;
|
|
220
|
+
|
|
221
|
+
anyFsp.writeFile = async function (file: PathLike | number | fsp.FileHandle, ...args: unknown[]) {
|
|
222
|
+
if (typeof file === "string" || Buffer.isBuffer(file) || file instanceof URL) {
|
|
223
|
+
validateTarget(file as unknown as PathLike, "fsp.writeFile");
|
|
224
|
+
}
|
|
225
|
+
return Reflect.apply(origPromisesWriteFile, fsp, [file, ...args]);
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
anyFsp.appendFile = async function (file: PathLike | number | fsp.FileHandle, ...args: unknown[]) {
|
|
229
|
+
if (typeof file === "string" || Buffer.isBuffer(file) || file instanceof URL) {
|
|
230
|
+
validateTarget(file as unknown as PathLike, "fsp.appendFile");
|
|
231
|
+
}
|
|
232
|
+
return Reflect.apply(origPromisesAppendFile, fsp, [file, ...args]);
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
anyFsp.mkdir = async function (pathArg: PathLike, ...args: unknown[]) {
|
|
236
|
+
validateTarget(pathArg, "fsp.mkdir");
|
|
237
|
+
return Reflect.apply(origPromisesMkdir, fsp, [pathArg, ...args]);
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
anyFsp.rmdir = async function (pathArg: PathLike, ...args: unknown[]) {
|
|
241
|
+
validateTarget(pathArg, "fsp.rmdir");
|
|
242
|
+
return Reflect.apply(origPromisesRmdir, fsp, [pathArg, ...args]);
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
anyFsp.unlink = async function (pathArg: PathLike, ...args: unknown[]) {
|
|
246
|
+
validateTarget(pathArg, "fsp.unlink");
|
|
247
|
+
return Reflect.apply(origPromisesUnlink, fsp, [pathArg, ...args]);
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
anyFsp.rm = async function (pathArg: PathLike, ...args: unknown[]) {
|
|
251
|
+
validateTarget(pathArg, "fsp.rm");
|
|
252
|
+
return Reflect.apply(origPromisesRm, fsp, [pathArg, ...args]);
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
anyFsp.rename = async function (oldPath: PathLike, newPath: PathLike) {
|
|
256
|
+
validateTarget(oldPath, "fsp.rename:source");
|
|
257
|
+
validateTarget(newPath, "fsp.rename:destination");
|
|
258
|
+
return Reflect.apply(origPromisesRename, fsp, [oldPath, newPath]);
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
anyFsp.copyFile = async function (src: PathLike, dest: PathLike, ...args: unknown[]) {
|
|
262
|
+
validateTarget(dest, "fsp.copyFile:destination");
|
|
263
|
+
return Reflect.apply(origPromisesCopyFile, fsp, [src, dest, ...args]);
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
return function uninstallStorageGuard(): void {
|
|
267
|
+
fs.writeFile = origWriteFile;
|
|
268
|
+
fs.writeFileSync = origWriteFileSync;
|
|
269
|
+
fs.appendFile = origAppendFile;
|
|
270
|
+
fs.appendFileSync = origAppendFileSync;
|
|
271
|
+
fs.mkdir = origMkdir;
|
|
272
|
+
fs.mkdirSync = origMkdirSync;
|
|
273
|
+
fs.rmdir = origRmdir;
|
|
274
|
+
fs.rmdirSync = origRmdirSync;
|
|
275
|
+
fs.unlink = origUnlink;
|
|
276
|
+
fs.unlinkSync = origUnlinkSync;
|
|
277
|
+
fs.rm = origRm;
|
|
278
|
+
fs.rmSync = origRmSync;
|
|
279
|
+
fs.rename = origRename;
|
|
280
|
+
fs.renameSync = origRenameSync;
|
|
281
|
+
fs.copyFile = origCopyFile;
|
|
282
|
+
fs.copyFileSync = origCopyFileSync;
|
|
283
|
+
fs.createWriteStream = origCreateWriteStream;
|
|
284
|
+
|
|
285
|
+
fsp.writeFile = origPromisesWriteFile;
|
|
286
|
+
fsp.appendFile = origPromisesAppendFile;
|
|
287
|
+
fsp.mkdir = origPromisesMkdir;
|
|
288
|
+
fsp.rmdir = origPromisesRmdir;
|
|
289
|
+
fsp.unlink = origPromisesUnlink;
|
|
290
|
+
fsp.rm = origPromisesRm;
|
|
291
|
+
fsp.rename = origPromisesRename;
|
|
292
|
+
fsp.copyFile = origPromisesCopyFile;
|
|
293
|
+
};
|
|
294
|
+
}
|
|
@@ -7,7 +7,7 @@ export interface PluginStorageOptions<T> {
|
|
|
7
7
|
defaultData?: T;
|
|
8
8
|
/**
|
|
9
9
|
* Base directory override. When provided, storage is located at path.join(baseDir, pluginId).
|
|
10
|
-
* When omitted, defaults to path.join(
|
|
10
|
+
* When omitted, defaults to path.join(resolvePaseoHome(), namespace ?? "plugin-data/xpufx", pluginId).
|
|
11
11
|
*/
|
|
12
12
|
baseDir?: string;
|
|
13
13
|
/**
|
|
@@ -27,6 +27,40 @@ export interface StorageStats {
|
|
|
27
27
|
lastModified: Date | null;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
const AGENT_MUX_PROFILE_HOME_RE = /(?:^|[\\/])\.agent-mux[\\/]profiles[\\/]/;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Resolve the real user home even when `HOME` points into an agent-mux profile.
|
|
34
|
+
* A profile session has `HOME=~/.agent-mux/profiles/<provider>/<profile>` while
|
|
35
|
+
* the live Paseo state stays on the host home. agent-mux exports `REAL_HOME` for
|
|
36
|
+
* every profile session, so prefer it; otherwise strip the profile prefix so
|
|
37
|
+
* paths under `~/.paseo` still resolve to the host (#1158, #973).
|
|
38
|
+
*/
|
|
39
|
+
export function resolveHostHome(env: NodeJS.ProcessEnv = process.env): string {
|
|
40
|
+
const home = (env.HOME || os.homedir() || "").trim();
|
|
41
|
+
if (!home || !AGENT_MUX_PROFILE_HOME_RE.test(home)) {
|
|
42
|
+
return home;
|
|
43
|
+
}
|
|
44
|
+
const realHome = (env.REAL_HOME || "").trim();
|
|
45
|
+
if (realHome) return realHome;
|
|
46
|
+
const marker = home.match(AGENT_MUX_PROFILE_HOME_RE);
|
|
47
|
+
if (marker && typeof marker.index === "number" && marker.index > 0) {
|
|
48
|
+
return home.slice(0, marker.index);
|
|
49
|
+
}
|
|
50
|
+
return home;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Resolve the canonical `~/.paseo` directory. `PASEO_HOME` is the daemon's own
|
|
55
|
+
* declaration of that root, so prefer it; otherwise anchor to the host home
|
|
56
|
+
* rather than an agent-mux profile (#1158).
|
|
57
|
+
*/
|
|
58
|
+
export function resolvePaseoHome(env: NodeJS.ProcessEnv = process.env): string {
|
|
59
|
+
const paseoHome = (env.PASEO_HOME || "").trim();
|
|
60
|
+
if (paseoHome) return paseoHome;
|
|
61
|
+
return path.join(resolveHostHome(env), ".paseo");
|
|
62
|
+
}
|
|
63
|
+
|
|
30
64
|
export const DEFAULT_NAMESPACE_README = `# Paseo Plugins Storage (xpufx)
|
|
31
65
|
|
|
32
66
|
This directory is managed by \`paseo-plugin-helper\` to store persistent settings, cached metrics, and state for plugins.
|
|
@@ -60,7 +94,7 @@ export class PluginStorage<T extends Record<string, any>> {
|
|
|
60
94
|
this.namespaceDir = options.baseDir;
|
|
61
95
|
this.pluginDir = path.join(options.baseDir, pluginId);
|
|
62
96
|
} else {
|
|
63
|
-
this.namespaceDir = path.join(
|
|
97
|
+
this.namespaceDir = path.join(resolvePaseoHome(), namespace);
|
|
64
98
|
this.pluginDir = path.join(this.namespaceDir, pluginId);
|
|
65
99
|
}
|
|
66
100
|
|