@hyperdreamer/pi-webui 1.6.1
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 +23 -0
- package/README.md +148 -0
- package/dist/cli.js +975 -0
- package/dist/cli.js.map +1 -0
- package/dist/client/apple-touch-icon.png +0 -0
- package/dist/client/assets/CodeViewer-aB4ZVzEu.js +4 -0
- package/dist/client/assets/UnifiedDiffViewer-f8X7aTkU.js +39 -0
- package/dist/client/assets/index-Cffeusjy.js +6022 -0
- package/dist/client/assets/vendor-editor-core-vUi74DnD.js +12 -0
- package/dist/client/assets/vendor-editor-languages-DoaXUodB.js +46 -0
- package/dist/client/assets/vendor-editor-legacy-CYBnW6ZU.js +1 -0
- package/dist/client/assets/vendor-terminal-BrP-ENHg.css +1 -0
- package/dist/client/assets/vendor-terminal-D8k4UKM2.js +35 -0
- package/dist/client/favicon.svg +8 -0
- package/dist/client/index.html +65 -0
- package/dist/client/manifest.webmanifest +24 -0
- package/dist/client/pwa-icon-192.png +0 -0
- package/dist/client/pwa-icon-512.png +0 -0
- package/dist/config.js +417 -0
- package/dist/config.js.map +1 -0
- package/dist/docker/piWebUiDockerCommandPlan.js +211 -0
- package/dist/docker/piWebUiDockerCommandPlan.js.map +1 -0
- package/dist/nativeServices/serviceDoctor.js +480 -0
- package/dist/nativeServices/serviceDoctor.js.map +1 -0
- package/dist/nativeServices/serviceInstall.js +35 -0
- package/dist/nativeServices/serviceInstall.js.map +1 -0
- package/dist/nativeServices/servicePlan.js +358 -0
- package/dist/nativeServices/servicePlan.js.map +1 -0
- package/dist/nativeServices/serviceProbe.js +447 -0
- package/dist/nativeServices/serviceProbe.js.map +1 -0
- package/dist/nativeServices/serviceRendering.js +122 -0
- package/dist/nativeServices/serviceRendering.js.map +1 -0
- package/dist/pi-webui-plugins/info/package.json +9 -0
- package/dist/pi-webui-plugins/info/pi-webui-plugin.js +51 -0
- package/dist/pi-webui-plugins/updates/package.json +9 -0
- package/dist/pi-webui-plugins/updates/pi-webui-plugin.js +185 -0
- package/dist/pi-webui-plugins/updates/updatesLogic.js +97 -0
- package/dist/pi-webui-plugins/workspace-tasks/config.js +91 -0
- package/dist/pi-webui-plugins/workspace-tasks/package.json +9 -0
- package/dist/pi-webui-plugins/workspace-tasks/pi-webui-plugin.js +48 -0
- package/dist/pi-webui-plugins/workspace-tasks/taskRunner.js +12 -0
- package/dist/pi-webui-plugins/workspace-tasks/tasksPanelElement.js +288 -0
- package/dist/pi-webui-plugins/workspace-tasks/workspaceTasksClient.js +47 -0
- package/dist/piWebUiVersionReport.js +225 -0
- package/dist/piWebUiVersionReport.js.map +1 -0
- package/dist/plugin-api/unstable.d.ts +22 -0
- package/dist/plugin-api.d.ts +199 -0
- package/dist/server/activeAgentProfileProvider.js +25 -0
- package/dist/server/activeAgentProfileProvider.js.map +1 -0
- package/dist/server/activity/workspaceActivityRoutes.js +4 -0
- package/dist/server/activity/workspaceActivityRoutes.js.map +1 -0
- package/dist/server/activity/workspaceActivityService.js +98 -0
- package/dist/server/activity/workspaceActivityService.js.map +1 -0
- package/dist/server/app.js +211 -0
- package/dist/server/app.js.map +1 -0
- package/dist/server/browserMessageProjection.js +56 -0
- package/dist/server/browserMessageProjection.js.map +1 -0
- package/dist/server/configRoutes.js +309 -0
- package/dist/server/configRoutes.js.map +1 -0
- package/dist/server/diagnostics/nodePtyNativeModule.js +36 -0
- package/dist/server/diagnostics/nodePtyNativeModule.js.map +1 -0
- package/dist/server/diagnostics/nodePtySpawnHelper.js +135 -0
- package/dist/server/diagnostics/nodePtySpawnHelper.js.map +1 -0
- package/dist/server/git/gitEnv.js +15 -0
- package/dist/server/git/gitEnv.js.map +1 -0
- package/dist/server/git/gitService.js +123 -0
- package/dist/server/git/gitService.js.map +1 -0
- package/dist/server/gitRoutes.js +23 -0
- package/dist/server/gitRoutes.js.map +1 -0
- package/dist/server/index.js +7 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/machines/machineClient.js +172 -0
- package/dist/server/machines/machineClient.js.map +1 -0
- package/dist/server/machines/machinePluginProxyRoutes.js +190 -0
- package/dist/server/machines/machinePluginProxyRoutes.js.map +1 -0
- package/dist/server/machines/machineProxyRoutes.js +172 -0
- package/dist/server/machines/machineProxyRoutes.js.map +1 -0
- package/dist/server/machines/machineRoutes.js +56 -0
- package/dist/server/machines/machineRoutes.js.map +1 -0
- package/dist/server/machines/machineService.js +238 -0
- package/dist/server/machines/machineService.js.map +1 -0
- package/dist/server/machines/machineStore.js +128 -0
- package/dist/server/machines/machineStore.js.map +1 -0
- package/dist/server/models/modelsConfigRoutes.js +40 -0
- package/dist/server/models/modelsConfigRoutes.js.map +1 -0
- package/dist/server/models/modelsConfigService.js +339 -0
- package/dist/server/models/modelsConfigService.js.map +1 -0
- package/dist/server/piPackagePluginsConfigRoutes.js +86 -0
- package/dist/server/piPackagePluginsConfigRoutes.js.map +1 -0
- package/dist/server/piPackagePluginsConfigService.js +311 -0
- package/dist/server/piPackagePluginsConfigService.js.map +1 -0
- package/dist/server/piPackageRoutes.js +91 -0
- package/dist/server/piPackageRoutes.js.map +1 -0
- package/dist/server/piPackageService.js +101 -0
- package/dist/server/piPackageService.js.map +1 -0
- package/dist/server/piWebUiPluginService.js +326 -0
- package/dist/server/piWebUiPluginService.js.map +1 -0
- package/dist/server/piWebUiReleaseLookupCache.js +37 -0
- package/dist/server/piWebUiReleaseLookupCache.js.map +1 -0
- package/dist/server/piWebUiStatus.js +622 -0
- package/dist/server/piWebUiStatus.js.map +1 -0
- package/dist/server/piWebUiStatusCache.js +45 -0
- package/dist/server/piWebUiStatusCache.js.map +1 -0
- package/dist/server/projects/directorySuggestions.js +37 -0
- package/dist/server/projects/directorySuggestions.js.map +1 -0
- package/dist/server/projects/projectService.js +31 -0
- package/dist/server/projects/projectService.js.map +1 -0
- package/dist/server/realtime/sessionEventHub.js +70 -0
- package/dist/server/realtime/sessionEventHub.js.map +1 -0
- package/dist/server/sessiond/sessionDaemonStartup.js +49 -0
- package/dist/server/sessiond/sessionDaemonStartup.js.map +1 -0
- package/dist/server/sessiond/sessionProxyRoutes.js +87 -0
- package/dist/server/sessiond/sessionProxyRoutes.js.map +1 -0
- package/dist/server/sessiond/skillsGithubToken.js +7 -0
- package/dist/server/sessiond/skillsGithubToken.js.map +1 -0
- package/dist/server/sessiond.js +152 -0
- package/dist/server/sessiond.js.map +1 -0
- package/dist/server/sessions/attachmentService.js +119 -0
- package/dist/server/sessions/attachmentService.js.map +1 -0
- package/dist/server/sessions/authProviderOptions.js +50 -0
- package/dist/server/sessions/authProviderOptions.js.map +1 -0
- package/dist/server/sessions/authRoutes.js +69 -0
- package/dist/server/sessions/authRoutes.js.map +1 -0
- package/dist/server/sessions/authService.js +134 -0
- package/dist/server/sessions/authService.js.map +1 -0
- package/dist/server/sessions/builtinCommands.js +27 -0
- package/dist/server/sessions/builtinCommands.js.map +1 -0
- package/dist/server/sessions/editPreview.js +196 -0
- package/dist/server/sessions/editPreview.js.map +1 -0
- package/dist/server/sessions/messagePaging.js +43 -0
- package/dist/server/sessions/messagePaging.js.map +1 -0
- package/dist/server/sessions/oauthLoginFlowService.js +349 -0
- package/dist/server/sessions/oauthLoginFlowService.js.map +1 -0
- package/dist/server/sessions/piSessionManagerGateway.js +115 -0
- package/dist/server/sessions/piSessionManagerGateway.js.map +1 -0
- package/dist/server/sessions/piSessionService.js +3230 -0
- package/dist/server/sessions/piSessionService.js.map +1 -0
- package/dist/server/sessions/plainTextTheme.js +112 -0
- package/dist/server/sessions/plainTextTheme.js.map +1 -0
- package/dist/server/sessions/sessionArchiveMigration.js +590 -0
- package/dist/server/sessions/sessionArchiveMigration.js.map +1 -0
- package/dist/server/sessions/sessionArchiveStore.js +262 -0
- package/dist/server/sessions/sessionArchiveStore.js.map +1 -0
- package/dist/server/sessions/sessionArchiveTree.js +35 -0
- package/dist/server/sessions/sessionArchiveTree.js.map +1 -0
- package/dist/server/sessions/sessionCleanup.js +157 -0
- package/dist/server/sessions/sessionCleanup.js.map +1 -0
- package/dist/server/sessions/sessionCommandService.js +313 -0
- package/dist/server/sessions/sessionCommandService.js.map +1 -0
- package/dist/server/sessions/sessionDefaultsRoutes.js +65 -0
- package/dist/server/sessions/sessionDefaultsRoutes.js.map +1 -0
- package/dist/server/sessions/sessionDefaultsService.js +90 -0
- package/dist/server/sessions/sessionDefaultsService.js.map +1 -0
- package/dist/server/sessions/sessionHistoryExport.js +115 -0
- package/dist/server/sessions/sessionHistoryExport.js.map +1 -0
- package/dist/server/sessions/sessionMetadataStore.js +102 -0
- package/dist/server/sessions/sessionMetadataStore.js.map +1 -0
- package/dist/server/sessions/sessionNameGenerator.js +90 -0
- package/dist/server/sessions/sessionNameGenerator.js.map +1 -0
- package/dist/server/sessions/sessionNotificationStore.js +484 -0
- package/dist/server/sessions/sessionNotificationStore.js.map +1 -0
- package/dist/server/sessions/sessionRoutes.js +590 -0
- package/dist/server/sessions/sessionRoutes.js.map +1 -0
- package/dist/server/sessions/sessionRuntimeStore.js +2 -0
- package/dist/server/sessions/sessionRuntimeStore.js.map +1 -0
- package/dist/server/sessions/sessionService.js +2 -0
- package/dist/server/sessions/sessionService.js.map +1 -0
- package/dist/server/sessions/sessionTreeProjection.js +250 -0
- package/dist/server/sessions/sessionTreeProjection.js.map +1 -0
- package/dist/server/sessions/sessionUnreadStore.js +520 -0
- package/dist/server/sessions/sessionUnreadStore.js.map +1 -0
- package/dist/server/sessions/spawnSessionTool.js +41 -0
- package/dist/server/sessions/spawnSessionTool.js.map +1 -0
- package/dist/server/sessions/spawnSubsessionTool.js +214 -0
- package/dist/server/sessions/spawnSubsessionTool.js.map +1 -0
- package/dist/server/sessions/spawnTargetResolver.js +36 -0
- package/dist/server/sessions/spawnTargetResolver.js.map +1 -0
- package/dist/server/sessions/subsessionTranscript.js +235 -0
- package/dist/server/sessions/subsessionTranscript.js.map +1 -0
- package/dist/server/skills/runNpx.js +30 -0
- package/dist/server/skills/runNpx.js.map +1 -0
- package/dist/server/skills/skillLock.js +115 -0
- package/dist/server/skills/skillLock.js.map +1 -0
- package/dist/server/skills/skillUpdates.js +194 -0
- package/dist/server/skills/skillUpdates.js.map +1 -0
- package/dist/server/skills/skillsConfigRoutes.js +64 -0
- package/dist/server/skills/skillsConfigRoutes.js.map +1 -0
- package/dist/server/skills/skillsConfigService.js +391 -0
- package/dist/server/skills/skillsConfigService.js.map +1 -0
- package/dist/server/storage/projectStore.js +88 -0
- package/dist/server/storage/projectStore.js.map +1 -0
- package/dist/server/systemInfoRoutes.js +265 -0
- package/dist/server/systemInfoRoutes.js.map +1 -0
- package/dist/server/terminalProxyRoutes.js +140 -0
- package/dist/server/terminalProxyRoutes.js.map +1 -0
- package/dist/server/terminals/terminalRoutes.js +155 -0
- package/dist/server/terminals/terminalRoutes.js.map +1 -0
- package/dist/server/terminals/terminalService.js +305 -0
- package/dist/server/terminals/terminalService.js.map +1 -0
- package/dist/server/terminals/terminalSize.js +17 -0
- package/dist/server/terminals/terminalSize.js.map +1 -0
- package/dist/server/types.js +2 -0
- package/dist/server/types.js.map +1 -0
- package/dist/server/webSocketBridge.js +32 -0
- package/dist/server/webSocketBridge.js.map +1 -0
- package/dist/server/workingDirectory.js +44 -0
- package/dist/server/workingDirectory.js.map +1 -0
- package/dist/server/workspaceExplorerRoutes.js +109 -0
- package/dist/server/workspaceExplorerRoutes.js.map +1 -0
- package/dist/server/workspaces/effectivePathAccess.js +27 -0
- package/dist/server/workspaces/effectivePathAccess.js.map +1 -0
- package/dist/server/workspaces/fileContentService.js +181 -0
- package/dist/server/workspaces/fileContentService.js.map +1 -0
- package/dist/server/workspaces/fileSuggestions.js +520 -0
- package/dist/server/workspaces/fileSuggestions.js.map +1 -0
- package/dist/server/workspaces/fileTreeService.js +35 -0
- package/dist/server/workspaces/fileTreeService.js.map +1 -0
- package/dist/server/workspaces/gitWorktreeDiscovery.js +34 -0
- package/dist/server/workspaces/gitWorktreeDiscovery.js.map +1 -0
- package/dist/server/workspaces/imagePreviewService.js +40 -0
- package/dist/server/workspaces/imagePreviewService.js.map +1 -0
- package/dist/server/workspaces/pathAccessPolicy.js +90 -0
- package/dist/server/workspaces/pathAccessPolicy.js.map +1 -0
- package/dist/server/workspaces/pathSafety.js +45 -0
- package/dist/server/workspaces/pathSafety.js.map +1 -0
- package/dist/server/workspaces/projectPiWebUiConfig.js +61 -0
- package/dist/server/workspaces/projectPiWebUiConfig.js.map +1 -0
- package/dist/server/workspaces/workspaceContext.js +8 -0
- package/dist/server/workspaces/workspaceContext.js.map +1 -0
- package/dist/server/workspaces/workspaceDeletionRoutes.js +113 -0
- package/dist/server/workspaces/workspaceDeletionRoutes.js.map +1 -0
- package/dist/server/workspaces/workspaceService.js +39 -0
- package/dist/server/workspaces/workspaceService.js.map +1 -0
- package/dist/sessiond/activeAgentProfile.js +31 -0
- package/dist/sessiond/activeAgentProfile.js.map +1 -0
- package/dist/sessiond/config.js +9 -0
- package/dist/sessiond/config.js.map +1 -0
- package/dist/sessiond/sessionDaemonClient.js +103 -0
- package/dist/sessiond/sessionDaemonClient.js.map +1 -0
- package/dist/shared/activeAgentProfile.js +76 -0
- package/dist/shared/activeAgentProfile.js.map +1 -0
- package/dist/shared/activity.js +11 -0
- package/dist/shared/activity.js.map +1 -0
- package/dist/shared/apiTypes.d.ts +1212 -0
- package/dist/shared/apiTypes.js +25 -0
- package/dist/shared/apiTypes.js.map +1 -0
- package/dist/shared/capabilities.js +69 -0
- package/dist/shared/capabilities.js.map +1 -0
- package/dist/shared/federatedRoutes.js +109 -0
- package/dist/shared/federatedRoutes.js.map +1 -0
- package/dist/shared/machinePluginIds.js +41 -0
- package/dist/shared/machinePluginIds.js.map +1 -0
- package/dist/shared/piWebUiStatusParsing.js +108 -0
- package/dist/shared/piWebUiStatusParsing.js.map +1 -0
- package/dist/shared/pluginIds.js +5 -0
- package/dist/shared/pluginIds.js.map +1 -0
- package/dist/shared/promptAttachments.js +92 -0
- package/dist/shared/promptAttachments.js.map +1 -0
- package/dist/shared/thinkingLevels.d.ts +27 -0
- package/dist/shared/thinkingLevels.js +31 -0
- package/dist/shared/thinkingLevels.js.map +1 -0
- package/dist/shared/workspaceDeletion.js +12 -0
- package/dist/shared/workspaceDeletion.js.map +1 -0
- package/dist/shared/workspaceFiles.js +3 -0
- package/dist/shared/workspaceFiles.js.map +1 -0
- package/docs/assets/favicon.svg +8 -0
- package/docs/assets/pi-webui-banner.png +0 -0
- package/docs/assets/pi-webui-desktop.png +0 -0
- package/docs/assets/pi-webui-mobile.png +0 -0
- package/docs/assets/pi-webui-tablet.png +0 -0
- package/docs/config.md +265 -0
- package/docs/plugins.md +988 -0
- package/extensions/pi-webui.ts +133 -0
- package/package.json +143 -0
- package/plugin-api/unstable.d.ts +1 -0
- package/plugin-api.d.ts +1 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { mkdir, realpath, writeFile } from "node:fs/promises";
|
|
2
|
+
import { basename, extname, join } from "node:path";
|
|
3
|
+
import { formatDimensionNote, resizeImage } from "@earendil-works/pi-coding-agent";
|
|
4
|
+
import { extensionForImageMimeType } from "../../shared/promptAttachments.js";
|
|
5
|
+
import { ensureInside, isNodeErrorWithCode, resolveParentInsideWorkspace } from "../workspaces/pathSafety.js";
|
|
6
|
+
/**
|
|
7
|
+
* Default workspace-relative folder used when saving pasted/dropped
|
|
8
|
+
* attachments for the agent to read with its own tools.
|
|
9
|
+
*/
|
|
10
|
+
export const DEFAULT_ATTACHMENT_FOLDER = ".pi-webui/attachments";
|
|
11
|
+
/**
|
|
12
|
+
* Convert validated attachments into pi-compatible inline image content.
|
|
13
|
+
*
|
|
14
|
+
* Mirrors pi's own CLI/TUI behaviour: each image is run through pi's
|
|
15
|
+
* `resizeImage` so it fits within pi's max dimensions and inline byte budget
|
|
16
|
+
* (2000x2000, ~4.5MB base64). Images that cannot be resized below the limit
|
|
17
|
+
* are dropped, matching pi's `[Image omitted]` behaviour.
|
|
18
|
+
*/
|
|
19
|
+
export async function attachmentsToInlineImages(attachments) {
|
|
20
|
+
const results = [];
|
|
21
|
+
for (const attachment of attachments) {
|
|
22
|
+
const bytes = Buffer.from(attachment.data, "base64");
|
|
23
|
+
const resized = await resizeImage(bytes, attachment.mimeType);
|
|
24
|
+
if (resized === null)
|
|
25
|
+
continue;
|
|
26
|
+
const note = formatDimensionNote(resized);
|
|
27
|
+
results.push({
|
|
28
|
+
image: { type: "image", data: resized.data, mimeType: resized.mimeType },
|
|
29
|
+
...(note === undefined ? {} : { dimensionNote: note }),
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
return results;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Write attachments into a workspace folder and return their relative paths.
|
|
36
|
+
* Filenames are collision-safe and stay inside the workspace root.
|
|
37
|
+
*/
|
|
38
|
+
export async function saveAttachmentsToWorkspace(cwd, attachments, options = {}) {
|
|
39
|
+
const folder = options.folder ?? DEFAULT_ATTACHMENT_FOLDER;
|
|
40
|
+
const now = options.now ?? (() => new Date());
|
|
41
|
+
const { root, target: requestedFolderTarget, relativePath: normalizedFolder } = await resolveParentInsideWorkspace(cwd, folder);
|
|
42
|
+
await mkdir(requestedFolderTarget, { recursive: true });
|
|
43
|
+
const folderTarget = await realpath(requestedFolderTarget);
|
|
44
|
+
ensureInside(root, folderTarget);
|
|
45
|
+
const stamp = timestamp(now());
|
|
46
|
+
const saved = [];
|
|
47
|
+
for (const [index, attachment] of attachments.entries()) {
|
|
48
|
+
const bytes = Buffer.from(attachment.data, "base64");
|
|
49
|
+
const filename = await writeUniqueAttachmentFile(folderTarget, attachmentFilename(attachment, stamp, index), bytes);
|
|
50
|
+
const relativePath = normalizedFolder === "" ? filename : `${normalizedFolder}/${filename}`;
|
|
51
|
+
saved.push({ path: relativePath, mimeType: attachment.mimeType, size: bytes.byteLength });
|
|
52
|
+
}
|
|
53
|
+
return saved;
|
|
54
|
+
}
|
|
55
|
+
async function writeUniqueAttachmentFile(folderTarget, filename, bytes) {
|
|
56
|
+
for (let attempt = 0; attempt < 100; attempt += 1) {
|
|
57
|
+
const candidate = attempt === 0 ? filename : addCollisionSuffix(filename, attempt + 1);
|
|
58
|
+
try {
|
|
59
|
+
await writeFile(join(folderTarget, candidate), bytes, { flag: "wx" });
|
|
60
|
+
return candidate;
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
if (!isNodeErrorWithCode(error, "EEXIST"))
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
throw new Error("Unable to choose a unique attachment filename");
|
|
68
|
+
}
|
|
69
|
+
function addCollisionSuffix(filename, suffix) {
|
|
70
|
+
const extension = extname(filename);
|
|
71
|
+
const stem = filename.slice(0, filename.length - extension.length);
|
|
72
|
+
return `${stem}-${String(suffix)}${extension}`;
|
|
73
|
+
}
|
|
74
|
+
function attachmentFilename(attachment, stamp, index) {
|
|
75
|
+
const originalName = sanitizeOriginalFilename(attachment.name) ?? fallbackAttachmentFilename(attachment);
|
|
76
|
+
return `attachment-${stamp}-${String(index + 1)}-${originalName}`;
|
|
77
|
+
}
|
|
78
|
+
function fallbackAttachmentFilename(attachment) {
|
|
79
|
+
if (attachment.kind === "image")
|
|
80
|
+
return `image.${extensionForImageMimeType(attachment.mimeType)}`;
|
|
81
|
+
return "file.bin";
|
|
82
|
+
}
|
|
83
|
+
const MAX_ORIGINAL_FILENAME_LENGTH = 96;
|
|
84
|
+
function sanitizeOriginalFilename(name) {
|
|
85
|
+
const trimmed = name?.trim();
|
|
86
|
+
if (trimmed === undefined || trimmed === "")
|
|
87
|
+
return undefined;
|
|
88
|
+
const leaf = basename(trimmed.replace(/\\/g, "/"));
|
|
89
|
+
const sanitized = stripControlCharacters(leaf)
|
|
90
|
+
.normalize("NFKC")
|
|
91
|
+
.replace(/[^A-Za-z0-9._-]+/g, "-")
|
|
92
|
+
.replace(/-+/g, "-")
|
|
93
|
+
.replace(/-+\./g, ".")
|
|
94
|
+
.replace(/^\.+/, "")
|
|
95
|
+
.replace(/[.-]+$/, "");
|
|
96
|
+
if (sanitized === "")
|
|
97
|
+
return undefined;
|
|
98
|
+
return truncateFilename(sanitized, MAX_ORIGINAL_FILENAME_LENGTH);
|
|
99
|
+
}
|
|
100
|
+
function stripControlCharacters(value) {
|
|
101
|
+
return Array.from(value).filter((character) => {
|
|
102
|
+
const codePoint = character.codePointAt(0);
|
|
103
|
+
return codePoint !== undefined && codePoint > 0x1f && codePoint !== 0x7f;
|
|
104
|
+
}).join("");
|
|
105
|
+
}
|
|
106
|
+
function truncateFilename(filename, maxLength) {
|
|
107
|
+
if (filename.length <= maxLength)
|
|
108
|
+
return filename;
|
|
109
|
+
const extension = extname(filename);
|
|
110
|
+
if (extension.length >= maxLength)
|
|
111
|
+
return filename.slice(0, maxLength);
|
|
112
|
+
const stem = filename.slice(0, filename.length - extension.length);
|
|
113
|
+
return `${stem.slice(0, maxLength - extension.length)}${extension}`;
|
|
114
|
+
}
|
|
115
|
+
function timestamp(date) {
|
|
116
|
+
const pad = (value, length = 2) => String(value).padStart(length, "0");
|
|
117
|
+
return `${String(date.getFullYear())}${pad(date.getMonth() + 1)}${pad(date.getDate())}-${pad(date.getHours())}${pad(date.getMinutes())}${pad(date.getSeconds())}-${pad(date.getMilliseconds(), 3)}`;
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=attachmentService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attachmentService.js","sourceRoot":"","sources":["../../../src/server/sessions/attachmentService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export function getLoginProviderOptions(runtime, authType) {
|
|
2
|
+
const providers = runtime.getProviders();
|
|
3
|
+
const options = [];
|
|
4
|
+
for (const provider of providers) {
|
|
5
|
+
if (provider.auth.oauth === undefined)
|
|
6
|
+
continue;
|
|
7
|
+
options.push({
|
|
8
|
+
id: provider.id,
|
|
9
|
+
name: provider.name,
|
|
10
|
+
authType: "oauth",
|
|
11
|
+
status: truthfulProviderStatus(runtime, provider.id),
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
for (const provider of providers) {
|
|
15
|
+
if (provider.auth.apiKey?.login === undefined)
|
|
16
|
+
continue;
|
|
17
|
+
options.push({
|
|
18
|
+
id: provider.id,
|
|
19
|
+
name: provider.name,
|
|
20
|
+
authType: "api_key",
|
|
21
|
+
status: truthfulProviderStatus(runtime, provider.id),
|
|
22
|
+
loginFlow: "interactive",
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
return filterAndSort(options, authType);
|
|
26
|
+
}
|
|
27
|
+
export async function getLogoutProviderOptions(runtime) {
|
|
28
|
+
const providerNames = new Map(runtime.getProviders().map((provider) => [provider.id, provider.name]));
|
|
29
|
+
const options = [];
|
|
30
|
+
for (const credential of await runtime.listCredentials()) {
|
|
31
|
+
options.push({
|
|
32
|
+
id: credential.providerId,
|
|
33
|
+
name: providerNames.get(credential.providerId) ?? credential.providerId,
|
|
34
|
+
authType: credential.type,
|
|
35
|
+
status: truthfulProviderStatus(runtime, credential.providerId),
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
return filterAndSort(options);
|
|
39
|
+
}
|
|
40
|
+
function truthfulProviderStatus(runtime, providerId) {
|
|
41
|
+
const reported = runtime.getProviderAuthStatus(providerId);
|
|
42
|
+
// ModelRuntime reports any stored entry as configured before checking whether
|
|
43
|
+
// the provider can resolve all required credential and ambient fields.
|
|
44
|
+
return reported.configured && !runtime.hasConfiguredAuth(providerId) ? { configured: false } : reported;
|
|
45
|
+
}
|
|
46
|
+
function filterAndSort(options, authType) {
|
|
47
|
+
const filtered = authType === undefined ? options : options.filter((option) => option.authType === authType);
|
|
48
|
+
return filtered.sort((a, b) => a.name.localeCompare(b.name) || a.authType.localeCompare(b.authType) || a.id.localeCompare(b.id));
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=authProviderOptions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authProviderOptions.js","sourceRoot":"","sources":["../../../src/server/sessions/authProviderOptions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
export function registerAuthRoutes(app, auth, prefix = "") {
|
|
2
|
+
app.get(`${prefix}/auth/providers`, async (request, reply) => {
|
|
3
|
+
try {
|
|
4
|
+
return await auth.authProviders(request.query.mode ?? "login", request.query.authType);
|
|
5
|
+
}
|
|
6
|
+
catch (error) {
|
|
7
|
+
return reply.code(404).send({ error: error instanceof Error ? error.message : String(error) });
|
|
8
|
+
}
|
|
9
|
+
});
|
|
10
|
+
app.post(`${prefix}/auth/api-key`, async (request, reply) => {
|
|
11
|
+
try {
|
|
12
|
+
return await auth.saveApiKey(request.body.providerId, request.body.key);
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
return reply.code(400).send({ error: error instanceof Error ? error.message : String(error) });
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
// Additive endpoint for newer browsers; the one-secret route remains for
|
|
19
|
+
// rolling compatibility with older browser bundles.
|
|
20
|
+
app.post(`${prefix}/auth/api-key/interactive`, async (request, reply) => {
|
|
21
|
+
try {
|
|
22
|
+
return await auth.startApiKeyLogin(request.body.providerId);
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
return reply.code(400).send({ error: error instanceof Error ? error.message : String(error) });
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
app.post(`${prefix}/auth/logout`, async (request, reply) => {
|
|
29
|
+
try {
|
|
30
|
+
return await auth.logoutProvider(request.body.providerId);
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
return reply.code(400).send({ error: error instanceof Error ? error.message : String(error) });
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
app.post(`${prefix}/auth/oauth`, async (request, reply) => {
|
|
37
|
+
try {
|
|
38
|
+
return await auth.startOAuthLogin(request.body.providerId);
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
return reply.code(400).send({ error: error instanceof Error ? error.message : String(error) });
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
app.get(`${prefix}/auth/oauth/:flowId`, async (request, reply) => {
|
|
45
|
+
try {
|
|
46
|
+
return auth.oauthFlow(request.params.flowId);
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
return reply.code(404).send({ error: error instanceof Error ? error.message : String(error) });
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
app.post(`${prefix}/auth/oauth/:flowId/respond`, async (request, reply) => {
|
|
53
|
+
try {
|
|
54
|
+
return auth.respondToOAuthFlow(request.params.flowId, request.body.requestId, request.body.value);
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
return reply.code(400).send({ error: error instanceof Error ? error.message : String(error) });
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
app.post(`${prefix}/auth/oauth/:flowId/cancel`, async (request, reply) => {
|
|
61
|
+
try {
|
|
62
|
+
return auth.cancelOAuthFlow(request.params.flowId);
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
return reply.code(400).send({ error: error instanceof Error ? error.message : String(error) });
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=authRoutes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authRoutes.js","sourceRoot":"","sources":["../../../src/server/sessions/authRoutes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { ModelRuntime } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
import { getLoginProviderOptions, getLogoutProviderOptions } from "./authProviderOptions.js";
|
|
4
|
+
import { OAuthLoginFlowService } from "./oauthLoginFlowService.js";
|
|
5
|
+
const noopLogger = { error() { } };
|
|
6
|
+
export function createModelRuntimeForAgentDir(agentDir, allowModelNetwork) {
|
|
7
|
+
return ModelRuntime.create({
|
|
8
|
+
authPath: join(agentDir, "auth.json"),
|
|
9
|
+
modelsPath: join(agentDir, "models.json"),
|
|
10
|
+
...(allowModelNetwork === undefined ? {} : { allowModelNetwork }),
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
export class AuthService {
|
|
14
|
+
constructor(runtime, authFlows, logger) {
|
|
15
|
+
this.listeners = new Set();
|
|
16
|
+
this.runtime = runtime;
|
|
17
|
+
this.authFlows = authFlows;
|
|
18
|
+
this.logger = logger;
|
|
19
|
+
}
|
|
20
|
+
static async create(deps = {}) {
|
|
21
|
+
const runtime = deps.runtime ?? (deps.agentDir === undefined ? await ModelRuntime.create({}) : await createModelRuntimeForAgentDir(deps.agentDir));
|
|
22
|
+
const logger = deps.logger ?? noopLogger;
|
|
23
|
+
const authFlows = deps.authFlows ?? new OAuthLoginFlowService({ logger });
|
|
24
|
+
return new AuthService(runtime, authFlows, logger);
|
|
25
|
+
}
|
|
26
|
+
subscribe(listener) {
|
|
27
|
+
this.listeners.add(listener);
|
|
28
|
+
return () => {
|
|
29
|
+
this.listeners.delete(listener);
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
dispose() {
|
|
33
|
+
this.authFlows.dispose();
|
|
34
|
+
this.listeners.clear();
|
|
35
|
+
}
|
|
36
|
+
async authProviders(mode, authType) {
|
|
37
|
+
await this.runtime.reloadConfig();
|
|
38
|
+
const providers = mode === "logout" ? await getLogoutProviderOptions(this.runtime) : getLoginProviderOptions(this.runtime, authType);
|
|
39
|
+
return { providers };
|
|
40
|
+
}
|
|
41
|
+
async saveApiKey(providerId, key) {
|
|
42
|
+
if (key.trim() === "")
|
|
43
|
+
throw new Error("API key is required");
|
|
44
|
+
const provider = await this.requireApiKeyLoginProvider(providerId);
|
|
45
|
+
let promptAttempted = false;
|
|
46
|
+
const interaction = {
|
|
47
|
+
prompt: (prompt) => {
|
|
48
|
+
if (promptAttempted) {
|
|
49
|
+
throw new Error(`${provider.name} requires interactive setup; use Pi's generic /login flow`);
|
|
50
|
+
}
|
|
51
|
+
promptAttempted = true;
|
|
52
|
+
if (prompt.signal?.aborted === true)
|
|
53
|
+
throw new Error("Login cancelled");
|
|
54
|
+
if (prompt.type !== "secret") {
|
|
55
|
+
throw new Error(`${provider.name} requires interactive setup; use Pi's generic /login flow`);
|
|
56
|
+
}
|
|
57
|
+
return Promise.resolve(key);
|
|
58
|
+
},
|
|
59
|
+
notify: () => undefined,
|
|
60
|
+
};
|
|
61
|
+
await this.runtime.login(providerId, "api_key", interaction);
|
|
62
|
+
await this.emit({}, { operation: "login", providerId, authType: "api_key" });
|
|
63
|
+
return { accepted: true };
|
|
64
|
+
}
|
|
65
|
+
async logoutProvider(providerId) {
|
|
66
|
+
await this.runtime.logout(providerId);
|
|
67
|
+
await this.emit({ removedProviderId: providerId }, { operation: "logout", providerId });
|
|
68
|
+
return { accepted: true };
|
|
69
|
+
}
|
|
70
|
+
async startApiKeyLogin(providerId) {
|
|
71
|
+
const provider = await this.requireApiKeyLoginProvider(providerId);
|
|
72
|
+
return this.authFlows.start({
|
|
73
|
+
providerId,
|
|
74
|
+
providerName: provider.name,
|
|
75
|
+
runtime: this.runtime,
|
|
76
|
+
authType: "api_key",
|
|
77
|
+
onComplete: () => this.emit({}, { operation: "login", providerId, authType: "api_key" }),
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
async startOAuthLogin(providerId) {
|
|
81
|
+
const provider = await this.requireOAuthLoginProvider(providerId);
|
|
82
|
+
return this.authFlows.start({
|
|
83
|
+
providerId,
|
|
84
|
+
providerName: provider.name,
|
|
85
|
+
runtime: this.runtime,
|
|
86
|
+
authType: "oauth",
|
|
87
|
+
onComplete: () => this.emit({}, { operation: "login", providerId, authType: "oauth" }),
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
oauthFlow(flowId) {
|
|
91
|
+
return this.authFlows.get(flowId);
|
|
92
|
+
}
|
|
93
|
+
respondToOAuthFlow(flowId, requestId, value) {
|
|
94
|
+
return this.authFlows.respond(flowId, requestId, value);
|
|
95
|
+
}
|
|
96
|
+
cancelOAuthFlow(flowId) {
|
|
97
|
+
return this.authFlows.cancel(flowId);
|
|
98
|
+
}
|
|
99
|
+
async emit(change, context) {
|
|
100
|
+
const results = await Promise.allSettled([...this.listeners].map(async (listener) => listener(change)));
|
|
101
|
+
for (const result of results) {
|
|
102
|
+
if (result.status === "rejected") {
|
|
103
|
+
this.logErrorNoThrow({ err: result.reason, ...context }, "auth-change listener failed");
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
logErrorNoThrow(details, message) {
|
|
108
|
+
try {
|
|
109
|
+
this.logger.error(details, message);
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
// A diagnostic failure cannot turn an already-committed auth mutation into an API failure.
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async requireApiKeyLoginProvider(providerId) {
|
|
116
|
+
await this.runtime.reloadConfig();
|
|
117
|
+
const provider = getLoginProviderOptions(this.runtime, "api_key").find((option) => option.id === providerId);
|
|
118
|
+
if (provider !== undefined)
|
|
119
|
+
return provider;
|
|
120
|
+
const knownProvider = this.runtime.getProviders().find((option) => option.id === providerId);
|
|
121
|
+
if (knownProvider !== undefined) {
|
|
122
|
+
throw new Error(`${knownProvider.name} does not support interactive API-key setup`);
|
|
123
|
+
}
|
|
124
|
+
throw new Error(`API key provider not found: ${providerId}`);
|
|
125
|
+
}
|
|
126
|
+
async requireOAuthLoginProvider(providerId) {
|
|
127
|
+
await this.runtime.reloadConfig();
|
|
128
|
+
const provider = getLoginProviderOptions(this.runtime, "oauth").find((option) => option.id === providerId);
|
|
129
|
+
if (provider === undefined)
|
|
130
|
+
throw new Error(`OAuth provider not found: ${providerId}`);
|
|
131
|
+
return provider;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=authService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authService.js","sourceRoot":"","sources":["../../../src/server/sessions/authService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export const BUILTIN_COMMANDS = [
|
|
2
|
+
{ name: "settings", description: "Open settings menu", source: "builtin" },
|
|
3
|
+
{ name: "model", description: "Select model", source: "builtin" },
|
|
4
|
+
{ name: "scoped-models", description: "Enable/disable models for cycling", source: "builtin" },
|
|
5
|
+
{ name: "export", description: "Export session", source: "builtin" },
|
|
6
|
+
{ name: "import", description: "Import and resume a session from JSONL", source: "builtin" },
|
|
7
|
+
{ name: "share", description: "Share session as a secret GitHub gist", source: "builtin" },
|
|
8
|
+
{ name: "copy", description: "Copy last agent message", source: "builtin" },
|
|
9
|
+
{ name: "name", description: "Set session display name", source: "builtin" },
|
|
10
|
+
{ name: "session", description: "Show session info and stats", source: "builtin" },
|
|
11
|
+
{ name: "changelog", description: "Show changelog entries", source: "builtin" },
|
|
12
|
+
{ name: "hotkeys", description: "Show keyboard shortcuts", source: "builtin" },
|
|
13
|
+
{ name: "fork", description: "Create a new fork from a previous user message", source: "builtin" },
|
|
14
|
+
{ name: "clone", description: "Duplicate current session at current position", source: "builtin" },
|
|
15
|
+
{ name: "tree", description: "Navigate session tree", source: "builtin" },
|
|
16
|
+
{ name: "login", description: "Configure provider authentication", source: "builtin" },
|
|
17
|
+
{ name: "logout", description: "Remove provider authentication", source: "builtin" },
|
|
18
|
+
{ name: "new", description: "Start a new session", source: "builtin" },
|
|
19
|
+
{ name: "compact", description: "Manually compact session context", source: "builtin" },
|
|
20
|
+
{ name: "resume", description: "Resume a different session", source: "builtin" },
|
|
21
|
+
{ name: "reload", description: "Reload Pi runtime resources for this session", source: "builtin" },
|
|
22
|
+
{ name: "quit", description: "Quit pi", source: "builtin" },
|
|
23
|
+
];
|
|
24
|
+
export function isBuiltinCommand(name) {
|
|
25
|
+
return BUILTIN_COMMANDS.some((command) => command.name === name);
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=builtinCommands.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builtinCommands.js","sourceRoot":"","sources":["../../../src/server/sessions/builtinCommands.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,gBAAgB,GAAoB;IAC/C,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,EAAE,SAAS,EAAE;IAC1E,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,EAAE,SAAS,EAAE;IACjE,EAAE,IAAI,EAAE,eAAe,EAAE,WAAW,EAAE,mCAAmC,EAAE,MAAM,EAAE,SAAS,EAAE;IAC9F,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,EAAE,SAAS,EAAE;IACpE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,wCAAwC,EAAE,MAAM,EAAE,SAAS,EAAE;IAC5F,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,uCAAuC,EAAE,MAAM,EAAE,SAAS,EAAE;IAC1F,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,yBAAyB,EAAE,MAAM,EAAE,SAAS,EAAE;IAC3E,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,0BAA0B,EAAE,MAAM,EAAE,SAAS,EAAE;IAC5E,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,6BAA6B,EAAE,MAAM,EAAE,SAAS,EAAE;IAClF,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,wBAAwB,EAAE,MAAM,EAAE,SAAS,EAAE;IAC/E,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,yBAAyB,EAAE,MAAM,EAAE,SAAS,EAAE;IAC9E,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,gDAAgD,EAAE,MAAM,EAAE,SAAS,EAAE;IAClG,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,+CAA+C,EAAE,MAAM,EAAE,SAAS,EAAE;IAClG,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,uBAAuB,EAAE,MAAM,EAAE,SAAS,EAAE;IACzE,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,mCAAmC,EAAE,MAAM,EAAE,SAAS,EAAE;IACtF,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,gCAAgC,EAAE,MAAM,EAAE,SAAS,EAAE;IACpF,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,EAAE,SAAS,EAAE;IACtE,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,kCAAkC,EAAE,MAAM,EAAE,SAAS,EAAE;IACvF,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,4BAA4B,EAAE,MAAM,EAAE,SAAS,EAAE;IAChF,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,8CAA8C,EAAE,MAAM,EAAE,SAAS,EAAE;IAClG,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE;CAC5D,CAAC;AAEF,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,OAAO,gBAAgB,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;AACnE,CAAC"}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { access, readFile } from "node:fs/promises";
|
|
2
|
+
import { constants } from "node:fs";
|
|
3
|
+
import { isAbsolute, resolve } from "node:path";
|
|
4
|
+
import { diffLines } from "diff";
|
|
5
|
+
export async function computeEditPreview(path, edits, cwd) {
|
|
6
|
+
const absolutePath = isAbsolute(path) ? path : resolve(cwd, path);
|
|
7
|
+
try {
|
|
8
|
+
try {
|
|
9
|
+
await access(absolutePath, constants.R_OK);
|
|
10
|
+
}
|
|
11
|
+
catch (error) {
|
|
12
|
+
const message = error instanceof Error && "code" in error ? `Error code: ${String(error.code)}` : String(error);
|
|
13
|
+
return { error: `Could not edit file: ${path}. ${message}.` };
|
|
14
|
+
}
|
|
15
|
+
const rawContent = await readFile(absolutePath, "utf8");
|
|
16
|
+
const { text: content } = stripBom(rawContent);
|
|
17
|
+
const normalizedContent = normalizeToLF(content);
|
|
18
|
+
const { baseContent, newContent } = applyEditsToNormalizedContent(normalizedContent, edits, path);
|
|
19
|
+
return generateDiffString(baseContent, newContent);
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
return { error: error instanceof Error ? error.message : String(error) };
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function normalizeToLF(text) {
|
|
26
|
+
return text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
27
|
+
}
|
|
28
|
+
function stripBom(content) {
|
|
29
|
+
return content.startsWith("\uFEFF") ? { bom: "\uFEFF", text: content.slice(1) } : { bom: "", text: content };
|
|
30
|
+
}
|
|
31
|
+
function normalizeForFuzzyMatch(text) {
|
|
32
|
+
return text
|
|
33
|
+
.normalize("NFKC")
|
|
34
|
+
.split("\n")
|
|
35
|
+
.map((line) => line.trimEnd())
|
|
36
|
+
.join("\n")
|
|
37
|
+
.replace(/[\u2018\u2019\u201A\u201B]/g, "'")
|
|
38
|
+
.replace(/[\u201C\u201D\u201E\u201F]/g, '"')
|
|
39
|
+
.replace(/[\u2010\u2011\u2012\u2013\u2014\u2015\u2212]/g, "-")
|
|
40
|
+
.replace(/[\u00A0\u2002-\u200A\u202F\u205F\u3000]/g, " ");
|
|
41
|
+
}
|
|
42
|
+
function fuzzyFindText(content, oldText) {
|
|
43
|
+
const exactIndex = content.indexOf(oldText);
|
|
44
|
+
if (exactIndex !== -1)
|
|
45
|
+
return { found: true, index: exactIndex, matchLength: oldText.length, usedFuzzyMatch: false };
|
|
46
|
+
const fuzzyContent = normalizeForFuzzyMatch(content);
|
|
47
|
+
const fuzzyOldText = normalizeForFuzzyMatch(oldText);
|
|
48
|
+
const fuzzyIndex = fuzzyContent.indexOf(fuzzyOldText);
|
|
49
|
+
if (fuzzyIndex === -1)
|
|
50
|
+
return { found: false, index: -1, matchLength: 0, usedFuzzyMatch: false };
|
|
51
|
+
return { found: true, index: fuzzyIndex, matchLength: fuzzyOldText.length, usedFuzzyMatch: true };
|
|
52
|
+
}
|
|
53
|
+
function countOccurrences(content, oldText) {
|
|
54
|
+
const fuzzyContent = normalizeForFuzzyMatch(content);
|
|
55
|
+
const fuzzyOldText = normalizeForFuzzyMatch(oldText);
|
|
56
|
+
if (fuzzyOldText === "")
|
|
57
|
+
return 0;
|
|
58
|
+
return fuzzyContent.split(fuzzyOldText).length - 1;
|
|
59
|
+
}
|
|
60
|
+
function applyEditsToNormalizedContent(normalizedContent, edits, path) {
|
|
61
|
+
const normalizedEdits = edits.map((edit) => ({ oldText: normalizeToLF(edit.oldText), newText: normalizeToLF(edit.newText) }));
|
|
62
|
+
for (let index = 0; index < normalizedEdits.length; index++) {
|
|
63
|
+
if ((normalizedEdits[index]?.oldText ?? "") === "")
|
|
64
|
+
throw editError("empty", path, index, normalizedEdits.length);
|
|
65
|
+
}
|
|
66
|
+
const initialMatches = normalizedEdits.map((edit) => fuzzyFindText(normalizedContent, edit.oldText));
|
|
67
|
+
const baseContent = initialMatches.some((match) => match.usedFuzzyMatch) ? normalizeForFuzzyMatch(normalizedContent) : normalizedContent;
|
|
68
|
+
const matchedEdits = [];
|
|
69
|
+
for (let index = 0; index < normalizedEdits.length; index++) {
|
|
70
|
+
const edit = normalizedEdits[index];
|
|
71
|
+
if (edit === undefined)
|
|
72
|
+
continue;
|
|
73
|
+
const match = fuzzyFindText(baseContent, edit.oldText);
|
|
74
|
+
if (!match.found)
|
|
75
|
+
throw editError("missing", path, index, normalizedEdits.length);
|
|
76
|
+
const occurrences = countOccurrences(baseContent, edit.oldText);
|
|
77
|
+
if (occurrences > 1)
|
|
78
|
+
throw editError("duplicate", path, index, normalizedEdits.length, occurrences);
|
|
79
|
+
matchedEdits.push({ editIndex: index, matchIndex: match.index, matchLength: match.matchLength, newText: edit.newText });
|
|
80
|
+
}
|
|
81
|
+
matchedEdits.sort((a, b) => a.matchIndex - b.matchIndex);
|
|
82
|
+
for (let index = 1; index < matchedEdits.length; index++) {
|
|
83
|
+
const previous = matchedEdits[index - 1];
|
|
84
|
+
const current = matchedEdits[index];
|
|
85
|
+
if (previous !== undefined && current !== undefined && previous.matchIndex + previous.matchLength > current.matchIndex) {
|
|
86
|
+
throw new Error(`edits[${String(previous.editIndex)}] and edits[${String(current.editIndex)}] overlap in ${path}. Merge them into one edit or target disjoint regions.`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
let newContent = baseContent;
|
|
90
|
+
for (let index = matchedEdits.length - 1; index >= 0; index--) {
|
|
91
|
+
const edit = matchedEdits[index];
|
|
92
|
+
if (edit === undefined)
|
|
93
|
+
continue;
|
|
94
|
+
newContent = `${newContent.slice(0, edit.matchIndex)}${edit.newText}${newContent.slice(edit.matchIndex + edit.matchLength)}`;
|
|
95
|
+
}
|
|
96
|
+
if (baseContent === newContent)
|
|
97
|
+
throw editError("nochange", path, 0, normalizedEdits.length);
|
|
98
|
+
return { baseContent, newContent };
|
|
99
|
+
}
|
|
100
|
+
function editError(kind, path, editIndex, totalEdits, occurrences) {
|
|
101
|
+
const prefix = totalEdits === 1 ? "" : `edits[${String(editIndex)}].`;
|
|
102
|
+
if (kind === "empty")
|
|
103
|
+
return new Error(totalEdits === 1 ? `oldText must not be empty in ${path}.` : `${prefix}oldText must not be empty in ${path}.`);
|
|
104
|
+
if (kind === "missing")
|
|
105
|
+
return new Error(totalEdits === 1 ? `Could not find the exact text in ${path}. The old text must match exactly including all whitespace and newlines.` : `Could not find edits[${String(editIndex)}] in ${path}. The oldText must match exactly including all whitespace and newlines.`);
|
|
106
|
+
if (kind === "duplicate")
|
|
107
|
+
return new Error(totalEdits === 1 ? `Found ${String(occurrences)} occurrences of the text in ${path}. The text must be unique. Please provide more context to make it unique.` : `Found ${String(occurrences)} occurrences of edits[${String(editIndex)}] in ${path}. Each oldText must be unique. Please provide more context to make it unique.`);
|
|
108
|
+
return new Error(totalEdits === 1 ? `No changes made to ${path}. The replacement produced identical content.` : `No changes made to ${path}. The replacements produced identical content.`);
|
|
109
|
+
}
|
|
110
|
+
function generateDiffString(oldContent, newContent, contextLines = 4) {
|
|
111
|
+
const parts = diffLines(oldContent, newContent);
|
|
112
|
+
const output = [];
|
|
113
|
+
const oldLines = oldContent.split("\n");
|
|
114
|
+
const newLines = newContent.split("\n");
|
|
115
|
+
const maxLineNum = Math.max(oldLines.length, newLines.length);
|
|
116
|
+
const lineNumWidth = String(maxLineNum).length;
|
|
117
|
+
let oldLineNum = 1;
|
|
118
|
+
let newLineNum = 1;
|
|
119
|
+
let lastWasChange = false;
|
|
120
|
+
let firstChangedLine;
|
|
121
|
+
for (let index = 0; index < parts.length; index++) {
|
|
122
|
+
const part = parts[index];
|
|
123
|
+
if (part === undefined)
|
|
124
|
+
continue;
|
|
125
|
+
const raw = part.value.split("\n");
|
|
126
|
+
if (raw.at(-1) === "")
|
|
127
|
+
raw.pop();
|
|
128
|
+
if (part.added || part.removed) {
|
|
129
|
+
firstChangedLine ??= newLineNum;
|
|
130
|
+
for (const line of raw) {
|
|
131
|
+
if (part.added) {
|
|
132
|
+
output.push(`+${String(newLineNum).padStart(lineNumWidth, " ")} ${line}`);
|
|
133
|
+
newLineNum++;
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
output.push(`-${String(oldLineNum).padStart(lineNumWidth, " ")} ${line}`);
|
|
137
|
+
oldLineNum++;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
lastWasChange = true;
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
const nextPart = parts[index + 1];
|
|
144
|
+
const nextPartIsChange = (nextPart?.added ?? false) || (nextPart?.removed ?? false);
|
|
145
|
+
const hasLeadingChange = lastWasChange;
|
|
146
|
+
const hasTrailingChange = nextPartIsChange;
|
|
147
|
+
if (hasLeadingChange && hasTrailingChange) {
|
|
148
|
+
if (raw.length <= contextLines * 2) {
|
|
149
|
+
appendContextLines(output, raw, lineNumWidth, () => oldLineNum++, () => newLineNum++);
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
const leadingLines = raw.slice(0, contextLines);
|
|
153
|
+
const trailingLines = raw.slice(raw.length - contextLines);
|
|
154
|
+
const skippedLines = raw.length - leadingLines.length - trailingLines.length;
|
|
155
|
+
appendContextLines(output, leadingLines, lineNumWidth, () => oldLineNum++, () => newLineNum++);
|
|
156
|
+
output.push(` ${"".padStart(lineNumWidth, " ")} ...`);
|
|
157
|
+
oldLineNum += skippedLines;
|
|
158
|
+
newLineNum += skippedLines;
|
|
159
|
+
appendContextLines(output, trailingLines, lineNumWidth, () => oldLineNum++, () => newLineNum++);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
else if (hasLeadingChange) {
|
|
163
|
+
const shownLines = raw.slice(0, contextLines);
|
|
164
|
+
const skippedLines = raw.length - shownLines.length;
|
|
165
|
+
appendContextLines(output, shownLines, lineNumWidth, () => oldLineNum++, () => newLineNum++);
|
|
166
|
+
if (skippedLines > 0) {
|
|
167
|
+
output.push(` ${"".padStart(lineNumWidth, " ")} ...`);
|
|
168
|
+
oldLineNum += skippedLines;
|
|
169
|
+
newLineNum += skippedLines;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
else if (hasTrailingChange) {
|
|
173
|
+
const skippedLines = Math.max(0, raw.length - contextLines);
|
|
174
|
+
if (skippedLines > 0) {
|
|
175
|
+
output.push(` ${"".padStart(lineNumWidth, " ")} ...`);
|
|
176
|
+
oldLineNum += skippedLines;
|
|
177
|
+
newLineNum += skippedLines;
|
|
178
|
+
}
|
|
179
|
+
appendContextLines(output, raw.slice(skippedLines), lineNumWidth, () => oldLineNum++, () => newLineNum++);
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
oldLineNum += raw.length;
|
|
183
|
+
newLineNum += raw.length;
|
|
184
|
+
}
|
|
185
|
+
lastWasChange = false;
|
|
186
|
+
}
|
|
187
|
+
return { diff: output.join("\n"), ...(firstChangedLine === undefined ? {} : { firstChangedLine }) };
|
|
188
|
+
}
|
|
189
|
+
function appendContextLines(output, lines, lineNumWidth, nextOldLine, nextNewLine) {
|
|
190
|
+
for (const line of lines) {
|
|
191
|
+
const oldLine = nextOldLine();
|
|
192
|
+
nextNewLine();
|
|
193
|
+
output.push(` ${String(oldLine).padStart(lineNumWidth, " ")} ${line}`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=editPreview.js.map
|