pim-agent 0.0.1 → 0.9.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/LICENSE +21 -0
- package/README.md +293 -7
- package/bin/pim.ts +151 -0
- package/package.json +82 -4
- package/packages/core/src/attachments/AttachmentStore.ts +99 -0
- package/packages/core/src/attachments/Attachments.ts +69 -0
- package/packages/core/src/extensions/CoreExtensions.ts +51 -0
- package/packages/core/src/extensions/apply-patch/coordinator.ts +53 -0
- package/packages/core/src/extensions/apply-patch/executor.ts +550 -0
- package/packages/core/src/extensions/apply-patch/index.ts +69 -0
- package/packages/core/src/extensions/apply-patch/matcher.ts +47 -0
- package/packages/core/src/extensions/apply-patch/model.ts +39 -0
- package/packages/core/src/extensions/apply-patch/parser.ts +379 -0
- package/packages/core/src/extensions/apply-patch/render.ts +216 -0
- package/packages/core/src/extensions/apply-patch/schema.ts +37 -0
- package/packages/core/src/extensions/apply-patch/types.ts +30 -0
- package/packages/core/src/extensions/bash/capture.ts +86 -0
- package/packages/core/src/extensions/bash/format.ts +110 -0
- package/packages/core/src/extensions/bash/image.ts +31 -0
- package/packages/core/src/extensions/bash/index.ts +88 -0
- package/packages/core/src/extensions/bash/render.ts +31 -0
- package/packages/core/src/extensions/bash/run.ts +221 -0
- package/packages/core/src/extensions/bash/schema.ts +64 -0
- package/packages/core/src/extensions/edit/edit.ts +366 -0
- package/packages/core/src/extensions/edit/index.ts +36 -0
- package/packages/core/src/extensions/edit/render.ts +16 -0
- package/packages/core/src/extensions/edit/schema.ts +37 -0
- package/packages/core/src/extensions/glob/glob.ts +34 -0
- package/packages/core/src/extensions/glob/index.ts +83 -0
- package/packages/core/src/extensions/glob/render.ts +72 -0
- package/packages/core/src/extensions/glob/schema.ts +66 -0
- package/packages/core/src/extensions/grep/grep.ts +256 -0
- package/packages/core/src/extensions/grep/index.ts +114 -0
- package/packages/core/src/extensions/grep/render.ts +271 -0
- package/packages/core/src/extensions/grep/schema.ts +92 -0
- package/packages/core/src/extensions/read/ImageMemory.ts +42 -0
- package/packages/core/src/extensions/read/image.ts +57 -0
- package/packages/core/src/extensions/read/index.ts +88 -0
- package/packages/core/src/extensions/read/read.ts +191 -0
- package/packages/core/src/extensions/read/render.ts +58 -0
- package/packages/core/src/extensions/read/schema.ts +49 -0
- package/packages/core/src/extensions/subagent/index.ts +33 -0
- package/packages/core/src/extensions/subagent/render.ts +127 -0
- package/packages/core/src/extensions/subagent/schema.ts +9 -0
- package/packages/core/src/extensions/subagent/subagent.ts +419 -0
- package/packages/core/src/extensions/system-prompt/index.ts +28 -0
- package/packages/core/src/extensions/system-prompt/prompt.ts +228 -0
- package/packages/core/src/extensions/todo/index.ts +123 -0
- package/packages/core/src/extensions/todo/render.ts +156 -0
- package/packages/core/src/extensions/todo/schema.ts +24 -0
- package/packages/core/src/extensions/todo/todo.ts +185 -0
- package/packages/core/src/extensions/web-fetch/JinaReaderClient.ts +187 -0
- package/packages/core/src/extensions/web-fetch/WebViewFetchClient.ts +183 -0
- package/packages/core/src/extensions/web-fetch/WebViewMarkdownSnapshot.ts +512 -0
- package/packages/core/src/extensions/web-fetch/fetch.ts +265 -0
- package/packages/core/src/extensions/web-fetch/image.ts +165 -0
- package/packages/core/src/extensions/web-fetch/index.ts +83 -0
- package/packages/core/src/extensions/web-fetch/render.ts +100 -0
- package/packages/core/src/extensions/web-fetch/schema.ts +46 -0
- package/packages/core/src/extensions/web-search/ExaMcpClient.ts +251 -0
- package/packages/core/src/extensions/web-search/SearchBreaker.ts +147 -0
- package/packages/core/src/extensions/web-search/SearchChain.ts +77 -0
- package/packages/core/src/extensions/web-search/index.ts +90 -0
- package/packages/core/src/extensions/web-search/providers/DuckDuckGoProvider.ts +195 -0
- package/packages/core/src/extensions/web-search/providers/ExaProvider.ts +54 -0
- package/packages/core/src/extensions/web-search/providers/FirecrawlProvider.ts +144 -0
- package/packages/core/src/extensions/web-search/providers/SearchProvider.ts +118 -0
- package/packages/core/src/extensions/web-search/render.ts +49 -0
- package/packages/core/src/extensions/web-search/schema.ts +29 -0
- package/packages/core/src/extensions/web-search/search.ts +23 -0
- package/packages/core/src/extensions/write/index.ts +58 -0
- package/packages/core/src/extensions/write/render.ts +16 -0
- package/packages/core/src/extensions/write/schema.ts +14 -0
- package/packages/core/src/extensions/write/write.ts +87 -0
- package/packages/core/src/picker/FilePickerSuggestionEngine.ts +14 -0
- package/packages/core/src/picker/InProcessFilePickerSuggestionEngine.ts +52 -0
- package/packages/core/src/picker/PickerItem.ts +6 -0
- package/packages/core/src/picker/PickerService.ts +110 -0
- package/packages/core/src/picker/RemoteFilePickerSuggestionEngine.ts +56 -0
- package/packages/core/src/picker/WorkerFilePickerSuggestionEngine.ts +268 -0
- package/packages/core/src/picker/catalog.ts +220 -0
- package/packages/core/src/picker/commandRanker.ts +17 -0
- package/packages/core/src/picker/filePickerWorker.ts +66 -0
- package/packages/core/src/picker/filePickerWorkerMessages.ts +39 -0
- package/packages/core/src/picker/ranker.ts +245 -0
- package/packages/core/src/picker/token.ts +70 -0
- package/packages/core/src/session/AgentRuntime.ts +85 -0
- package/packages/core/src/session/EventLog.ts +213 -0
- package/packages/core/src/session/MessageText.ts +21 -0
- package/packages/core/src/session/ReadCursors.ts +94 -0
- package/packages/core/src/session/SessionCache.ts +68 -0
- package/packages/core/src/session/SessionHost.ts +876 -0
- package/packages/core/src/session/SessionLease.ts +279 -0
- package/packages/core/src/session/SessionRegistry.ts +169 -0
- package/packages/core/src/session/WriteMark.ts +37 -0
- package/packages/core/src/shared/Cli.ts +25 -0
- package/packages/core/src/shared/DaemonUnit.ts +13 -0
- package/packages/core/src/shared/DiffLines.ts +304 -0
- package/packages/core/src/shared/DiffRenderer.ts +283 -0
- package/packages/core/src/shared/DiffView.ts +29 -0
- package/packages/core/src/shared/Directories.ts +61 -0
- package/packages/core/src/shared/EditMatcher.ts +767 -0
- package/packages/core/src/shared/Errors.ts +9 -0
- package/packages/core/src/shared/ExtensionToggles.ts +130 -0
- package/packages/core/src/shared/FileEnumerator.ts +361 -0
- package/packages/core/src/shared/FileScanner.ts +57 -0
- package/packages/core/src/shared/FileWatch.ts +107 -0
- package/packages/core/src/shared/Format.ts +74 -0
- package/packages/core/src/shared/Fs.ts +91 -0
- package/packages/core/src/shared/FsErrors.ts +67 -0
- package/packages/core/src/shared/FuzzyMatcher.ts +71 -0
- package/packages/core/src/shared/Git.ts +89 -0
- package/packages/core/src/shared/GlobExclusions.ts +13 -0
- package/packages/core/src/shared/Http.ts +14 -0
- package/packages/core/src/shared/ImageMime.ts +36 -0
- package/packages/core/src/shared/Images.ts +357 -0
- package/packages/core/src/shared/Json.ts +19 -0
- package/packages/core/src/shared/Languages.ts +95 -0
- package/packages/core/src/shared/Levenshtein.ts +30 -0
- package/packages/core/src/shared/Lines.ts +79 -0
- package/packages/core/src/shared/McpClient.ts +444 -0
- package/packages/core/src/shared/OutputBudget.ts +84 -0
- package/packages/core/src/shared/PatchSummary.ts +40 -0
- package/packages/core/src/shared/Paths.ts +80 -0
- package/packages/core/src/shared/PimSettings.ts +128 -0
- package/packages/core/src/shared/PimVersion.ts +93 -0
- package/packages/core/src/shared/Pool.ts +26 -0
- package/packages/core/src/shared/Proc.ts +30 -0
- package/packages/core/src/shared/RateLimiter.ts +50 -0
- package/packages/core/src/shared/Renderer.ts +371 -0
- package/packages/core/src/shared/SafePath.ts +17 -0
- package/packages/core/src/shared/SearchRender.ts +82 -0
- package/packages/core/src/shared/SendFile.ts +23 -0
- package/packages/core/src/shared/SpillCache.ts +78 -0
- package/packages/core/src/shared/SubagentLogs.ts +102 -0
- package/packages/core/src/shared/Supervisor.ts +460 -0
- package/packages/core/src/shared/Sweeper.ts +32 -0
- package/packages/core/src/shared/Tools.ts +801 -0
- package/packages/core/src/shared/Updater.ts +193 -0
- package/packages/core/src/view/AnsiPainter.ts +227 -0
- package/packages/core/src/view/BodyRenderer.ts +102 -0
- package/packages/core/src/view/DiffBlocks.ts +52 -0
- package/packages/core/src/view/DiffLayout.ts +69 -0
- package/packages/core/src/view/MarkdownPainter.ts +314 -0
- package/packages/core/src/view/Painting.ts +182 -0
- package/packages/core/src/view/ViewBlock.ts +124 -0
- package/packages/daemon/src/Daemon.ts +63 -0
- package/packages/daemon/src/DaemonInstall.ts +115 -0
- package/packages/daemon/src/Surfaces.ts +66 -0
- package/packages/daemon/src/TelegramSurface.ts +30 -0
- package/packages/daemon/src/WebSurface.ts +57 -0
- package/packages/daemon/src/index.ts +37 -0
- package/packages/protocol/src/Command.ts +92 -0
- package/packages/protocol/src/ImageRoute.ts +23 -0
- package/packages/protocol/src/Protocol.ts +6 -0
- package/packages/protocol/src/ServerEvent.ts +251 -0
- package/packages/server/src/AttachmentEndpoint.ts +184 -0
- package/packages/server/src/ClientConnection.ts +265 -0
- package/packages/server/src/ImageEndpoint.ts +36 -0
- package/packages/server/src/ProbeClient.ts +312 -0
- package/packages/server/src/Reloader.ts +93 -0
- package/packages/server/src/SendFileTool.ts +88 -0
- package/packages/server/src/SessionCatalogue.ts +202 -0
- package/packages/server/src/SessionProjection.ts +194 -0
- package/packages/server/src/SessionStream.ts +601 -0
- package/packages/server/src/StaticClient.ts +95 -0
- package/packages/server/src/WebOptions.ts +72 -0
- package/packages/server/src/WsGateway.ts +563 -0
- package/packages/server/src/probe.ts +146 -0
- package/packages/telegram/src/Bot.ts +229 -0
- package/packages/telegram/src/Commands.ts +768 -0
- package/packages/telegram/src/Config.ts +148 -0
- package/packages/telegram/src/Markdown.ts +235 -0
- package/packages/telegram/src/Message.ts +152 -0
- package/packages/telegram/src/Renderer.ts +456 -0
- package/packages/telegram/src/SendFileSchema.ts +18 -0
- package/packages/telegram/src/SendFileTool.ts +65 -0
- package/packages/telegram/src/Session.ts +252 -0
- package/packages/telegram/src/SessionRegistry.ts +126 -0
- package/packages/telegram/src/TaskScheduler.ts +296 -0
- package/packages/telegram/src/TaskSchema.ts +88 -0
- package/packages/telegram/src/TaskStore.ts +67 -0
- package/packages/telegram/src/TaskTool.ts +208 -0
- package/packages/telegram/src/TelegramHtml.ts +288 -0
- package/packages/telegram/src/TypingIndicator.ts +43 -0
- package/packages/telegram/src/UpdateConfirm.ts +61 -0
- package/packages/tui/src/extensions/_init/index.ts +144 -0
- package/packages/tui/src/extensions/command-picker/index.ts +43 -0
- package/packages/tui/src/extensions/file-picker/index.ts +156 -0
- package/packages/tui/src/extensions/footer/index.ts +117 -0
- package/packages/tui/src/extensions/footer/powerline.ts +87 -0
- package/packages/tui/src/extensions/footer/segments.ts +202 -0
- package/packages/tui/src/extensions/pim/index.ts +89 -0
- package/packages/tui/src/extensions/session-lease/index.ts +248 -0
- package/packages/tui/src/extensions/tps/index.ts +118 -0
- package/packages/tui/src/extensions/working-indicator/index.ts +75 -0
- package/packages/tui/src/extensions/wrapProvider.ts +18 -0
- package/packages/tui/src/themes/pim-dark.json +84 -0
- package/packages/tui/src/themes/pim-light.json +84 -0
- package/packages/tui/src/themes/themeCliArgs.ts +20 -0
- package/packages/web/dist/client/apple-touch-icon.png +0 -0
- package/packages/web/dist/client/assets/bash-LmRjUKlR.js +1 -0
- package/packages/web/dist/client/assets/c-DBVbJFpC.js +1 -0
- package/packages/web/dist/client/assets/clojure-CR7dvLJ4.js +1 -0
- package/packages/web/dist/client/assets/cmake-CgP6gB4W.js +1 -0
- package/packages/web/dist/client/assets/commit-mono-latin-300-italic-B48RJMeK.woff +0 -0
- package/packages/web/dist/client/assets/commit-mono-latin-300-italic-DBq5bJCE.woff2 +0 -0
- package/packages/web/dist/client/assets/commit-mono-latin-400-italic-BXinMwCi.woff +0 -0
- package/packages/web/dist/client/assets/commit-mono-latin-400-italic-DjSHLl2N.woff2 +0 -0
- package/packages/web/dist/client/assets/commit-mono-latin-400-normal-s0S3qwFW.woff +0 -0
- package/packages/web/dist/client/assets/commit-mono-latin-400-normal-wzhe4RuD.woff2 +0 -0
- package/packages/web/dist/client/assets/commit-mono-latin-600-normal-BuQDXT0M.woff +0 -0
- package/packages/web/dist/client/assets/commit-mono-latin-600-normal-SwgSWHaV.woff2 +0 -0
- package/packages/web/dist/client/assets/commit-mono-latin-700-normal-1-GU0IUE.woff +0 -0
- package/packages/web/dist/client/assets/commit-mono-latin-700-normal-DU8mrtj2.woff2 +0 -0
- package/packages/web/dist/client/assets/core-CGdQx9la.js +3 -0
- package/packages/web/dist/client/assets/cpp-N2d8M7Nc.js +1 -0
- package/packages/web/dist/client/assets/csharp-yYt0Ikks.js +1 -0
- package/packages/web/dist/client/assets/css-D8M8GYaw.js +1 -0
- package/packages/web/dist/client/assets/diff-BhdZizse.js +1 -0
- package/packages/web/dist/client/assets/dockerfile-C0zufGsU.js +1 -0
- package/packages/web/dist/client/assets/elixir-DXIHlGsR.js +1 -0
- package/packages/web/dist/client/assets/erlang-BjYI8dhz.js +1 -0
- package/packages/web/dist/client/assets/go-CYstVVJV.js +1 -0
- package/packages/web/dist/client/assets/graphql-BHHhacaR.js +1 -0
- package/packages/web/dist/client/assets/haskell-DUkhXIda.js +1 -0
- package/packages/web/dist/client/assets/index-BMNwleWO.js +40 -0
- package/packages/web/dist/client/assets/index-CqjB2RPr.css +1 -0
- package/packages/web/dist/client/assets/ini-B1CroMsf.js +1 -0
- package/packages/web/dist/client/assets/java-Dde-Wnb4.js +1 -0
- package/packages/web/dist/client/assets/javascript-8AmHuH5O.js +1 -0
- package/packages/web/dist/client/assets/json-PmyKWHmm.js +1 -0
- package/packages/web/dist/client/assets/kotlin-DgSK8bu-.js +2 -0
- package/packages/web/dist/client/assets/less-Bp0mgmpG.js +1 -0
- package/packages/web/dist/client/assets/lua-1dXYaL-H.js +1 -0
- package/packages/web/dist/client/assets/makefile-BX-o3EIz.js +1 -0
- package/packages/web/dist/client/assets/markdown-D6FrnlqJ.js +1 -0
- package/packages/web/dist/client/assets/ocaml-B-P1Uyq8.js +1 -0
- package/packages/web/dist/client/assets/perl-CvqMZYuf.js +1 -0
- package/packages/web/dist/client/assets/php-DuZl5e-L.js +2 -0
- package/packages/web/dist/client/assets/powershell-BUe16zE4.js +1 -0
- package/packages/web/dist/client/assets/protobuf-DGOW1qT8.js +1 -0
- package/packages/web/dist/client/assets/python-gHmu6VPG.js +1 -0
- package/packages/web/dist/client/assets/r-DNWPCnjT.js +1 -0
- package/packages/web/dist/client/assets/ruby-BGpZ3p4_.js +1 -0
- package/packages/web/dist/client/assets/rust-BIKPVoi6.js +1 -0
- package/packages/web/dist/client/assets/scala-C9lP4FWY.js +1 -0
- package/packages/web/dist/client/assets/scss-gOyOKiXd.js +1 -0
- package/packages/web/dist/client/assets/sql-DWRKyvJ-.js +1 -0
- package/packages/web/dist/client/assets/swift-BTn9__hL.js +1 -0
- package/packages/web/dist/client/assets/typescript-CxkPLmSc.js +1 -0
- package/packages/web/dist/client/assets/vim-Dhhcn_CA.js +1 -0
- package/packages/web/dist/client/assets/xml-COl5yFSC.js +1 -0
- package/packages/web/dist/client/assets/yaml-DjOhhVLG.js +1 -0
- package/packages/web/dist/client/favicon.svg +1 -0
- package/packages/web/dist/client/icon-192.png +0 -0
- package/packages/web/dist/client/icon-512.png +0 -0
- package/packages/web/dist/client/icon-maskable.png +0 -0
- package/packages/web/dist/client/index.html +24 -0
- package/packages/web/dist/client/manifest.webmanifest +21 -0
- package/packages/web/dist/client/wordmark.svg +1 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { OutputBudget } from "./OutputBudget";
|
|
2
|
+
import { Paths } from "./Paths";
|
|
3
|
+
|
|
4
|
+
export const NO_MATCHES = "No matches.";
|
|
5
|
+
|
|
6
|
+
export type SearchPathFormat = "relative" | "absolute";
|
|
7
|
+
|
|
8
|
+
export type SearchPathOptions = {
|
|
9
|
+
readonly cwd: string;
|
|
10
|
+
readonly pathFormat: SearchPathFormat;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type SearchList = {
|
|
14
|
+
readonly body: string;
|
|
15
|
+
readonly totalItems: number;
|
|
16
|
+
readonly visibleItems: number;
|
|
17
|
+
readonly truncated: boolean;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type SubjectTitleOptions = {
|
|
21
|
+
readonly subject: string;
|
|
22
|
+
readonly path: string | undefined;
|
|
23
|
+
readonly suffix?: string | undefined;
|
|
24
|
+
readonly cwd: string;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
function formatPath(path: string, options: SearchPathOptions): string {
|
|
28
|
+
return options.pathFormat === "absolute"
|
|
29
|
+
? path
|
|
30
|
+
: Paths.displayRelative(path, options.cwd);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function subjectTitle(options: SubjectTitleOptions): string {
|
|
34
|
+
const resolved =
|
|
35
|
+
options.path === undefined
|
|
36
|
+
? undefined
|
|
37
|
+
: Paths.resolve(options.path, options.cwd);
|
|
38
|
+
const dir =
|
|
39
|
+
resolved === undefined || resolved === options.cwd
|
|
40
|
+
? undefined
|
|
41
|
+
: Paths.displayRelative(resolved, options.cwd);
|
|
42
|
+
const target = joinTarget(dir, options.suffix);
|
|
43
|
+
const location = target ? ` in ${target}` : "";
|
|
44
|
+
return `${options.subject}${location}`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function joinTarget(
|
|
48
|
+
dir: string | undefined,
|
|
49
|
+
suffix: string | undefined
|
|
50
|
+
): string | undefined {
|
|
51
|
+
if (suffix === undefined) {
|
|
52
|
+
return dir;
|
|
53
|
+
}
|
|
54
|
+
return dir === undefined ? suffix : `${dir}/${suffix}`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function capList(lines: readonly string[], headLimit: number): SearchList {
|
|
58
|
+
if (lines.length === 0) {
|
|
59
|
+
return {
|
|
60
|
+
body: NO_MATCHES,
|
|
61
|
+
totalItems: 0,
|
|
62
|
+
visibleItems: 0,
|
|
63
|
+
truncated: false,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const { visible } = OutputBudget.applyByteCap(lines.slice(0, headLimit));
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
body: visible.join("\n"),
|
|
71
|
+
totalItems: lines.length,
|
|
72
|
+
visibleItems: visible.length,
|
|
73
|
+
truncated: visible.length < lines.length,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export const SearchRender = {
|
|
78
|
+
NO_MATCHES,
|
|
79
|
+
formatPath,
|
|
80
|
+
subjectTitle,
|
|
81
|
+
capList,
|
|
82
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { FsErrors } from "./FsErrors";
|
|
2
|
+
import { Paths } from "./Paths";
|
|
3
|
+
|
|
4
|
+
const MAX_BYTES = 50 * 1024 * 1024;
|
|
5
|
+
|
|
6
|
+
async function validate(
|
|
7
|
+
rawPath: string,
|
|
8
|
+
cwd: string
|
|
9
|
+
): Promise<{ readonly path: string; readonly size: number }> {
|
|
10
|
+
const path = Paths.resolve(rawPath, cwd);
|
|
11
|
+
const st = await FsErrors.statOrThrow(path);
|
|
12
|
+
if (!st.isFile()) {
|
|
13
|
+
throw new Error(`${rawPath} is not a regular file.`);
|
|
14
|
+
}
|
|
15
|
+
if (st.size > MAX_BYTES) {
|
|
16
|
+
throw new Error(
|
|
17
|
+
`${rawPath} is ${st.size} bytes; max allowed is ${MAX_BYTES}.`
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
return { path, size: st.size };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const SendFile = { MAX_BYTES, validate };
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { readdirSync, statSync, unlinkSync } from "node:fs";
|
|
2
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { FsErrors } from "./FsErrors";
|
|
5
|
+
import { Paths } from "./Paths";
|
|
6
|
+
import { Sweeper } from "./Sweeper";
|
|
7
|
+
|
|
8
|
+
// UUIDv7-named spills and content-addressed ones, which share the TTL.
|
|
9
|
+
const SPILL_FILE_RE =
|
|
10
|
+
/^[a-z0-9]+-(?:[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|[0-9a-f]{64})\.[a-z0-9]+$/;
|
|
11
|
+
|
|
12
|
+
const TTL_MS = 7 * 24 * 60 * 60 * 1000;
|
|
13
|
+
const SWEEP_INTERVAL_MS = 60 * 60 * 1000;
|
|
14
|
+
|
|
15
|
+
function dir(): string {
|
|
16
|
+
return join(Paths.pimHomeDir(), "cache");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Writes under a caller-chosen name; an existing file of that name is kept, so content-addressed names re-write for free. */
|
|
20
|
+
async function writeNamed(
|
|
21
|
+
name: string,
|
|
22
|
+
data: string | Uint8Array
|
|
23
|
+
): Promise<string | null> {
|
|
24
|
+
const cacheDir = dir();
|
|
25
|
+
const path = join(cacheDir, name);
|
|
26
|
+
try {
|
|
27
|
+
await mkdir(cacheDir, { recursive: true, mode: 0o700 });
|
|
28
|
+
await writeFile(path, data, { flag: "wx", mode: 0o600 });
|
|
29
|
+
return path;
|
|
30
|
+
} catch (error) {
|
|
31
|
+
return FsErrors.code(error) === "EEXIST" ? path : null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function write(
|
|
36
|
+
prefix: string,
|
|
37
|
+
ext: string,
|
|
38
|
+
data: string | Uint8Array
|
|
39
|
+
): Promise<string | null> {
|
|
40
|
+
return writeNamed(`${prefix}-${Bun.randomUUIDv7()}.${ext}`, data);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function cleanup(cacheDir = dir(), now = Date.now()): void {
|
|
44
|
+
let entries: string[];
|
|
45
|
+
try {
|
|
46
|
+
entries = readdirSync(cacheDir);
|
|
47
|
+
} catch {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const cutoff = now - TTL_MS;
|
|
52
|
+
for (const name of entries) {
|
|
53
|
+
if (!SPILL_FILE_RE.test(name)) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
const path = join(cacheDir, name);
|
|
57
|
+
try {
|
|
58
|
+
const metadata = statSync(path);
|
|
59
|
+
if (metadata.isFile() && metadata.mtimeMs < cutoff) {
|
|
60
|
+
unlinkSync(path);
|
|
61
|
+
}
|
|
62
|
+
} catch {}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function installSweeper(): void {
|
|
67
|
+
Sweeper.install({ cleanup, intervalMs: SWEEP_INTERVAL_MS });
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export const SpillCache = {
|
|
71
|
+
TTL_MS,
|
|
72
|
+
SWEEP_INTERVAL_MS,
|
|
73
|
+
dir,
|
|
74
|
+
write,
|
|
75
|
+
writeNamed,
|
|
76
|
+
cleanup,
|
|
77
|
+
installSweeper,
|
|
78
|
+
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { readdirSync, rmSync, statSync } from "node:fs";
|
|
2
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { Paths } from "./Paths";
|
|
5
|
+
import { Sweeper } from "./Sweeper";
|
|
6
|
+
|
|
7
|
+
// The parent id names a directory; a call id is opaque provider data, hashed rather than trusted as a segment.
|
|
8
|
+
const ID_RE = /^[A-Za-z0-9_-]{1,128}$/;
|
|
9
|
+
// A call id arrives off the wire: bound what gets hashed.
|
|
10
|
+
const MAX_CALL_ID_LENGTH = 4096;
|
|
11
|
+
|
|
12
|
+
const TTL_MS = 30 * 24 * 60 * 60 * 1000;
|
|
13
|
+
const SWEEP_INTERVAL_MS = 60 * 60 * 1000;
|
|
14
|
+
|
|
15
|
+
// Must stay outside pi's sessions root, which `SessionRegistry` globs as the session catalogue.
|
|
16
|
+
function dir(): string {
|
|
17
|
+
return join(Paths.pimHomeDir(), "subagents");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function pathFor(parentSessionId: string, callId: string): string | null {
|
|
21
|
+
if (
|
|
22
|
+
!ID_RE.test(parentSessionId) ||
|
|
23
|
+
callId.length === 0 ||
|
|
24
|
+
callId.length > MAX_CALL_ID_LENGTH
|
|
25
|
+
) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
return join(
|
|
29
|
+
dir(),
|
|
30
|
+
parentSessionId,
|
|
31
|
+
`${Bun.SHA256.hash(callId, "hex")}.jsonl`
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Create the log empty with explicit modes: a file pi creates takes the process umask.
|
|
36
|
+
async function create(
|
|
37
|
+
parentSessionId: string,
|
|
38
|
+
callId: string
|
|
39
|
+
): Promise<string | null> {
|
|
40
|
+
const path = pathFor(parentSessionId, callId);
|
|
41
|
+
if (!path) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
await mkdir(dirname(path), { recursive: true, mode: 0o700 });
|
|
46
|
+
await writeFile(path, "", { flag: "wx", mode: 0o600 });
|
|
47
|
+
return path;
|
|
48
|
+
} catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Expire a parent's children together by newest mtime, or a long run loses its earlier subagents.
|
|
54
|
+
function cleanup(root = dir(), now = Date.now()): void {
|
|
55
|
+
let names: string[];
|
|
56
|
+
try {
|
|
57
|
+
names = readdirSync(root);
|
|
58
|
+
} catch {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const cutoff = now - TTL_MS;
|
|
63
|
+
for (const name of names) {
|
|
64
|
+
if (!ID_RE.test(name)) {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
const parentDir = join(root, name);
|
|
68
|
+
try {
|
|
69
|
+
if (!statSync(parentDir).isDirectory()) {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (newestMtime(parentDir) >= cutoff) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
rmSync(parentDir, { recursive: true, force: true });
|
|
76
|
+
} catch {}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function newestMtime(parentDir: string): number {
|
|
81
|
+
let newest = statSync(parentDir).mtimeMs;
|
|
82
|
+
for (const name of readdirSync(parentDir)) {
|
|
83
|
+
try {
|
|
84
|
+
newest = Math.max(newest, statSync(join(parentDir, name)).mtimeMs);
|
|
85
|
+
} catch {}
|
|
86
|
+
}
|
|
87
|
+
return newest;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function installSweeper(): void {
|
|
91
|
+
Sweeper.install({ cleanup, intervalMs: SWEEP_INTERVAL_MS });
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export const SubagentLogs = {
|
|
95
|
+
TTL_MS,
|
|
96
|
+
SWEEP_INTERVAL_MS,
|
|
97
|
+
dir,
|
|
98
|
+
pathFor,
|
|
99
|
+
create,
|
|
100
|
+
cleanup,
|
|
101
|
+
installSweeper,
|
|
102
|
+
};
|