@oh-my-pi/pi-coding-agent 15.3.2 → 15.4.2
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/CHANGELOG.md +110 -0
- package/dist/types/cli/file-processor.d.ts +1 -1
- package/dist/types/config/settings-schema.d.ts +45 -3
- package/dist/types/config/settings.d.ts +1 -1
- package/dist/types/debug/raw-sse.d.ts +2 -0
- package/dist/types/edit/file-read-cache.d.ts +15 -4
- package/dist/types/edit/index.d.ts +3 -8
- package/dist/types/edit/renderer.d.ts +1 -2
- package/dist/types/eval/__tests__/shared-executors.test.d.ts +1 -0
- package/dist/types/eval/js/shared/local-module-loader.d.ts +16 -0
- package/dist/types/eval/js/shared/rewrite-imports.d.ts +4 -0
- package/dist/types/eval/js/shared/runtime.d.ts +14 -8
- package/dist/types/eval/py/executor.d.ts +1 -2
- package/dist/types/eval/py/kernel.d.ts +6 -0
- package/dist/types/eval/py/tool-bridge.d.ts +1 -5
- package/dist/types/eval/session-id.d.ts +3 -0
- package/dist/types/extensibility/extensions/types.d.ts +1 -3
- package/dist/types/hashline/anchors.d.ts +15 -9
- package/dist/types/hashline/constants.d.ts +0 -2
- package/dist/types/hashline/diff.d.ts +1 -2
- package/dist/types/hashline/executor.d.ts +52 -0
- package/dist/types/hashline/hash.d.ts +44 -93
- package/dist/types/hashline/index.d.ts +2 -1
- package/dist/types/hashline/input.d.ts +2 -9
- package/dist/types/hashline/recovery.d.ts +3 -9
- package/dist/types/hashline/tokenizer.d.ts +91 -0
- package/dist/types/hashline/types.d.ts +5 -7
- package/dist/types/modes/components/extensions/types.d.ts +0 -4
- package/dist/types/modes/types.d.ts +1 -0
- package/dist/types/modes/utils/ui-helpers.d.ts +1 -0
- package/dist/types/sdk.d.ts +2 -0
- package/dist/types/session/agent-session.d.ts +11 -15
- package/dist/types/session/agent-storage.d.ts +11 -10
- package/dist/types/slash-commands/acp-builtins.d.ts +3 -3
- package/dist/types/slash-commands/types.d.ts +0 -5
- package/dist/types/task/executor.d.ts +2 -0
- package/dist/types/tool-discovery/tool-index.d.ts +0 -50
- package/dist/types/tools/index.d.ts +2 -8
- package/dist/types/tools/match-line-format.d.ts +4 -4
- package/dist/types/tools/output-schema-validator.d.ts +64 -0
- package/dist/types/tools/review.d.ts +13 -0
- package/dist/types/tools/search-tool-bm25.d.ts +1 -1
- package/dist/types/tools/search.d.ts +4 -3
- package/dist/types/utils/edit-mode.d.ts +1 -1
- package/dist/types/web/kagi.d.ts +4 -2
- package/dist/types/web/parallel.d.ts +4 -3
- package/dist/types/web/scrapers/types.d.ts +2 -1
- package/dist/types/web/search/index.d.ts +12 -4
- package/dist/types/web/search/provider.d.ts +2 -1
- package/dist/types/web/search/providers/anthropic.d.ts +9 -4
- package/dist/types/web/search/providers/base.d.ts +34 -2
- package/dist/types/web/search/providers/brave.d.ts +8 -1
- package/dist/types/web/search/providers/codex.d.ts +13 -9
- package/dist/types/web/search/providers/exa.d.ts +10 -1
- package/dist/types/web/search/providers/gemini.d.ts +20 -23
- package/dist/types/web/search/providers/jina.d.ts +2 -1
- package/dist/types/web/search/providers/kagi.d.ts +4 -1
- package/dist/types/web/search/providers/kimi.d.ts +10 -1
- package/dist/types/web/search/providers/parallel.d.ts +3 -2
- package/dist/types/web/search/providers/perplexity.d.ts +5 -2
- package/dist/types/web/search/providers/searxng.d.ts +2 -1
- package/dist/types/web/search/providers/synthetic.d.ts +5 -8
- package/dist/types/web/search/providers/tavily.d.ts +11 -4
- package/dist/types/web/search/providers/utils.d.ts +8 -6
- package/dist/types/web/search/providers/zai.d.ts +12 -3
- package/package.json +7 -7
- package/src/cli/file-processor.ts +12 -2
- package/src/cli.ts +0 -8
- package/src/commands/commit.ts +8 -8
- package/src/config/prompt-templates.ts +6 -6
- package/src/config/settings-schema.ts +47 -3
- package/src/config/settings.ts +5 -5
- package/src/debug/raw-sse.ts +68 -3
- package/src/edit/file-read-cache.ts +68 -25
- package/src/edit/index.ts +6 -37
- package/src/edit/renderer.ts +9 -47
- package/src/edit/streaming.ts +43 -56
- package/src/eval/__tests__/shared-executors.test.ts +520 -0
- package/src/eval/js/context-manager.ts +64 -53
- package/src/eval/js/shared/local-module-loader.ts +265 -0
- package/src/eval/js/shared/prelude.txt +4 -0
- package/src/eval/js/shared/rewrite-imports.ts +85 -0
- package/src/eval/js/shared/runtime.ts +129 -86
- package/src/eval/js/worker-core.ts +23 -38
- package/src/eval/py/executor.ts +155 -84
- package/src/eval/py/kernel.ts +10 -1
- package/src/eval/py/prelude.py +22 -24
- package/src/eval/py/runner.py +203 -85
- package/src/eval/py/tool-bridge.ts +17 -10
- package/src/eval/session-id.ts +8 -0
- package/src/exec/bash-executor.ts +27 -16
- package/src/extensibility/extensions/runner.ts +0 -1
- package/src/extensibility/extensions/types.ts +1 -3
- package/src/hashline/anchors.ts +56 -65
- package/src/hashline/apply.ts +29 -31
- package/src/hashline/constants.ts +0 -3
- package/src/hashline/diff-preview.ts +4 -5
- package/src/hashline/diff.ts +30 -4
- package/src/hashline/execute.ts +91 -26
- package/src/hashline/executor.ts +239 -0
- package/src/hashline/grammar.lark +12 -10
- package/src/hashline/hash.ts +69 -114
- package/src/hashline/index.ts +2 -1
- package/src/hashline/input.ts +48 -41
- package/src/hashline/prefixes.ts +21 -11
- package/src/hashline/recovery.ts +63 -71
- package/src/hashline/stream.ts +2 -2
- package/src/hashline/tokenizer.ts +467 -0
- package/src/hashline/types.ts +6 -8
- package/src/internal-urls/docs-index.generated.ts +7 -7
- package/src/modes/components/extensions/types.ts +0 -5
- package/src/modes/components/session-observer-overlay.ts +11 -2
- package/src/modes/components/settings-selector.ts +10 -1
- package/src/modes/components/tree-selector.ts +10 -2
- package/src/modes/controllers/command-controller.ts +1 -3
- package/src/modes/controllers/extension-ui-controller.ts +10 -11
- package/src/modes/controllers/selector-controller.ts +5 -5
- package/src/modes/theme/theme.ts +4 -2
- package/src/modes/types.ts +4 -1
- package/src/modes/utils/ui-helpers.ts +4 -0
- package/src/prompts/agents/explore.md +1 -1
- package/src/prompts/tools/ast-edit.md +1 -1
- package/src/prompts/tools/ast-grep.md +1 -1
- package/src/prompts/tools/eval.md +1 -1
- package/src/prompts/tools/hashline.md +73 -94
- package/src/prompts/tools/read.md +4 -4
- package/src/prompts/tools/search.md +3 -3
- package/src/sdk.ts +33 -26
- package/src/session/agent-session.ts +59 -66
- package/src/session/agent-storage.ts +13 -14
- package/src/slash-commands/acp-builtins.ts +3 -3
- package/src/slash-commands/types.ts +0 -6
- package/src/task/executor.ts +26 -57
- package/src/task/index.ts +8 -4
- package/src/tool-discovery/tool-index.ts +0 -134
- package/src/tools/ast-edit.ts +36 -13
- package/src/tools/ast-grep.ts +45 -4
- package/src/tools/browser/tab-worker.ts +3 -2
- package/src/tools/eval.ts +2 -1
- package/src/tools/fetch.ts +23 -14
- package/src/tools/index.ts +2 -8
- package/src/tools/irc.ts +59 -5
- package/src/tools/match-line-format.ts +5 -7
- package/src/tools/output-schema-validator.ts +132 -0
- package/src/tools/read.ts +142 -31
- package/src/tools/review.ts +23 -0
- package/src/tools/search-tool-bm25.ts +3 -30
- package/src/tools/search.ts +48 -16
- package/src/tools/write.ts +3 -3
- package/src/tools/yield.ts +32 -41
- package/src/utils/edit-mode.ts +1 -2
- package/src/utils/file-mentions.ts +2 -2
- package/src/web/kagi.ts +15 -6
- package/src/web/parallel.ts +9 -6
- package/src/web/scrapers/types.ts +7 -1
- package/src/web/scrapers/youtube.ts +13 -7
- package/src/web/search/index.ts +37 -11
- package/src/web/search/provider.ts +5 -3
- package/src/web/search/providers/anthropic.ts +30 -21
- package/src/web/search/providers/base.ts +35 -2
- package/src/web/search/providers/brave.ts +4 -4
- package/src/web/search/providers/codex.ts +118 -89
- package/src/web/search/providers/exa.ts +3 -2
- package/src/web/search/providers/gemini.ts +58 -155
- package/src/web/search/providers/jina.ts +4 -4
- package/src/web/search/providers/kagi.ts +17 -11
- package/src/web/search/providers/kimi.ts +29 -13
- package/src/web/search/providers/parallel.ts +171 -23
- package/src/web/search/providers/perplexity.ts +38 -37
- package/src/web/search/providers/searxng.ts +3 -1
- package/src/web/search/providers/synthetic.ts +16 -19
- package/src/web/search/providers/tavily.ts +23 -18
- package/src/web/search/providers/utils.ts +11 -17
- package/src/web/search/providers/zai.ts +16 -8
- package/dist/types/hashline/parser.d.ts +0 -7
- package/dist/types/mcp/discoverable-tool-metadata.d.ts +0 -7
- package/dist/types/tools/vim.d.ts +0 -58
- package/dist/types/vim/buffer.d.ts +0 -41
- package/dist/types/vim/commands.d.ts +0 -6
- package/dist/types/vim/engine.d.ts +0 -47
- package/dist/types/vim/parser.d.ts +0 -3
- package/dist/types/vim/render.d.ts +0 -25
- package/dist/types/vim/types.d.ts +0 -182
- package/src/hashline/parser.ts +0 -246
- package/src/mcp/discoverable-tool-metadata.ts +0 -24
- package/src/prompts/tools/vim.md +0 -98
- package/src/tools/vim.ts +0 -949
- package/src/vim/buffer.ts +0 -309
- package/src/vim/commands.ts +0 -382
- package/src/vim/engine.ts +0 -2409
- package/src/vim/parser.ts +0 -134
- package/src/vim/render.ts +0 -252
- package/src/vim/types.ts +0 -197
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
|
+
import * as vm from "node:vm";
|
|
6
|
+
import { collectModuleSourceSpecifiers, stripTypeScriptSyntax } from "./rewrite-imports";
|
|
7
|
+
|
|
8
|
+
interface LocalModuleEntry {
|
|
9
|
+
version: number;
|
|
10
|
+
identifier: string;
|
|
11
|
+
module: vm.SourceTextModule;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type LocalImportResolution = { mode: "local"; value: unknown } | { mode: "external"; target: string };
|
|
15
|
+
|
|
16
|
+
const LOCAL_MODULE_EXTENSIONS = new Set([".js", ".jsx", ".mjs", ".ts", ".tsx", ".mts"]);
|
|
17
|
+
|
|
18
|
+
export class LocalModuleLoader {
|
|
19
|
+
#context: vm.Context;
|
|
20
|
+
#sessionTag: string;
|
|
21
|
+
#moduleMtimes = new Map<string, number>();
|
|
22
|
+
#moduleDeps = new Map<string, Set<string>>();
|
|
23
|
+
#moduleParents = new Map<string, Set<string>>();
|
|
24
|
+
#moduleVersions = new Map<string, number>();
|
|
25
|
+
#moduleEntries = new Map<string, LocalModuleEntry>();
|
|
26
|
+
#moduleBuilds = new Map<string, Promise<LocalModuleEntry>>();
|
|
27
|
+
#externalModules = new Map<string, Promise<vm.Module>>();
|
|
28
|
+
#requireCache = new Map<string, NodeJS.Require>();
|
|
29
|
+
|
|
30
|
+
constructor(sessionId: string) {
|
|
31
|
+
this.#context = vm.createContext(globalThis);
|
|
32
|
+
this.#sessionTag = Bun.hash(sessionId).toString(16);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async resolveForRun(cwd: string, source: string): Promise<LocalImportResolution> {
|
|
36
|
+
this.#refreshTrackedLocalModules();
|
|
37
|
+
return await this.#resolveFromBase(cwd, source);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async resolveForModule(moduleUrl: string, source: string, cwd: string): Promise<LocalImportResolution> {
|
|
41
|
+
this.#refreshTrackedLocalModules();
|
|
42
|
+
const modulePath = this.filenameForUrl(moduleUrl);
|
|
43
|
+
const baseDir = modulePath ? path.dirname(modulePath) : cwd;
|
|
44
|
+
return await this.#resolveFromBase(baseDir, source);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
requireForFile(moduleUrlOrPath: string | undefined, cwd: string): NodeJS.Require {
|
|
48
|
+
const basePath = this.filenameForUrl(moduleUrlOrPath) ?? path.join(cwd, "[eval]");
|
|
49
|
+
let cached = this.#requireCache.get(basePath);
|
|
50
|
+
if (!cached) {
|
|
51
|
+
cached = buildRequire(basePath);
|
|
52
|
+
this.#requireCache.set(basePath, cached);
|
|
53
|
+
}
|
|
54
|
+
return cached;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
filenameForUrl(moduleUrlOrPath: string | undefined): string | null {
|
|
58
|
+
if (!moduleUrlOrPath) return null;
|
|
59
|
+
if (moduleUrlOrPath.startsWith("file://")) return fileURLToPath(moduleUrlOrPath);
|
|
60
|
+
return path.isAbsolute(moduleUrlOrPath) ? moduleUrlOrPath : null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
dirnameForUrl(moduleUrlOrPath: string | undefined, cwd: string): string {
|
|
64
|
+
const filename = this.filenameForUrl(moduleUrlOrPath);
|
|
65
|
+
return filename ? path.dirname(filename) : cwd;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async #resolveFromBase(baseDir: string, source: string): Promise<LocalImportResolution> {
|
|
69
|
+
const resolved = resolveImportSpecifier(baseDir, source);
|
|
70
|
+
if (isLocalPathSpecifier(source) && isManagedLocalModulePath(resolved)) {
|
|
71
|
+
const entry = await this.#ensureLocalModule(resolved);
|
|
72
|
+
return { mode: "local", value: entry.module.namespace };
|
|
73
|
+
}
|
|
74
|
+
return { mode: "external", target: normalizeImportTarget(resolved) };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async #ensureLocalModule(modulePath: string): Promise<LocalModuleEntry> {
|
|
78
|
+
const existing = this.#moduleEntries.get(modulePath);
|
|
79
|
+
if (existing) return existing;
|
|
80
|
+
const building = this.#moduleBuilds.get(modulePath);
|
|
81
|
+
if (building) return await building;
|
|
82
|
+
const buildPromise = this.#buildLocalModule(modulePath).finally(() => {
|
|
83
|
+
if (this.#moduleBuilds.get(modulePath) === buildPromise) this.#moduleBuilds.delete(modulePath);
|
|
84
|
+
});
|
|
85
|
+
this.#moduleBuilds.set(modulePath, buildPromise);
|
|
86
|
+
return await buildPromise;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async #buildLocalModule(modulePath: string): Promise<LocalModuleEntry> {
|
|
90
|
+
const rawSource = fs.readFileSync(modulePath, "utf8");
|
|
91
|
+
const stripped = stripTypeScriptSyntax(rawSource);
|
|
92
|
+
const moduleDir = path.dirname(modulePath);
|
|
93
|
+
const localDeps = new Set<string>();
|
|
94
|
+
for (const specifier of collectModuleSourceSpecifiers(stripped)) {
|
|
95
|
+
const resolved = resolveImportSpecifier(moduleDir, specifier);
|
|
96
|
+
if (isLocalPathSpecifier(specifier) && isManagedLocalModulePath(resolved)) {
|
|
97
|
+
localDeps.add(resolved);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
this.#setModuleDependencies(modulePath, localDeps);
|
|
101
|
+
this.#moduleMtimes.set(modulePath, fs.statSync(modulePath).mtimeMs);
|
|
102
|
+
const version = this.#moduleVersions.get(modulePath) ?? 1;
|
|
103
|
+
this.#moduleVersions.set(modulePath, version);
|
|
104
|
+
const fileUrl = pathToFileURL(modulePath).href;
|
|
105
|
+
const identifier = `${fileUrl}?omp-session=${this.#sessionTag}&v=${version}`;
|
|
106
|
+
const wrappedSource = buildModuleSource(stripped, modulePath);
|
|
107
|
+
const module = new vm.SourceTextModule(wrappedSource, {
|
|
108
|
+
context: this.#context,
|
|
109
|
+
identifier,
|
|
110
|
+
initializeImportMeta: meta => {
|
|
111
|
+
(meta as { url?: string; path?: string; dir?: string }).url = fileUrl;
|
|
112
|
+
(meta as { url?: string; path?: string; dir?: string }).path = modulePath;
|
|
113
|
+
(meta as { url?: string; path?: string; dir?: string }).dir = moduleDir;
|
|
114
|
+
},
|
|
115
|
+
importModuleDynamically: async specifier => {
|
|
116
|
+
return await this.#resolveLinkedModule(modulePath, String(specifier));
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
const entry: LocalModuleEntry = { version, identifier, module };
|
|
120
|
+
this.#moduleEntries.set(modulePath, entry);
|
|
121
|
+
try {
|
|
122
|
+
await module.link(async specifier => await this.#resolveLinkedModule(modulePath, specifier));
|
|
123
|
+
await module.evaluate();
|
|
124
|
+
return entry;
|
|
125
|
+
} catch (error) {
|
|
126
|
+
this.#moduleEntries.delete(modulePath);
|
|
127
|
+
throw error;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async #resolveLinkedModule(referrerPath: string, specifier: string): Promise<vm.Module> {
|
|
132
|
+
const baseDir = path.dirname(referrerPath);
|
|
133
|
+
const resolved = resolveImportSpecifier(baseDir, specifier);
|
|
134
|
+
if (isLocalPathSpecifier(specifier) && isManagedLocalModulePath(resolved)) {
|
|
135
|
+
return (await this.#ensureLocalModule(resolved)).module;
|
|
136
|
+
}
|
|
137
|
+
return await this.#ensureExternalModule(normalizeImportTarget(resolved));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async #ensureExternalModule(target: string): Promise<vm.Module> {
|
|
141
|
+
const existing = this.#externalModules.get(target);
|
|
142
|
+
if (existing) return await existing;
|
|
143
|
+
const loadPromise = (async () => {
|
|
144
|
+
const namespace = await import(target);
|
|
145
|
+
const exportNames = Object.keys(namespace);
|
|
146
|
+
const module = new vm.SyntheticModule(
|
|
147
|
+
exportNames,
|
|
148
|
+
function () {
|
|
149
|
+
for (const name of exportNames) {
|
|
150
|
+
this.setExport(name, namespace[name as keyof typeof namespace]);
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
{ context: this.#context, identifier: target },
|
|
154
|
+
);
|
|
155
|
+
await module.link(() => {
|
|
156
|
+
throw new Error("Synthetic external modules have no dependencies");
|
|
157
|
+
});
|
|
158
|
+
await module.evaluate();
|
|
159
|
+
return module;
|
|
160
|
+
})();
|
|
161
|
+
this.#externalModules.set(target, loadPromise);
|
|
162
|
+
try {
|
|
163
|
+
return await loadPromise;
|
|
164
|
+
} catch (error) {
|
|
165
|
+
if (this.#externalModules.get(target) === loadPromise) this.#externalModules.delete(target);
|
|
166
|
+
throw error;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
#refreshTrackedLocalModules(): void {
|
|
171
|
+
const changed: string[] = [];
|
|
172
|
+
for (const [modulePath, previousMtime] of this.#moduleMtimes.entries()) {
|
|
173
|
+
let nextMtime: number | undefined;
|
|
174
|
+
try {
|
|
175
|
+
nextMtime = fs.statSync(modulePath).mtimeMs;
|
|
176
|
+
} catch {
|
|
177
|
+
nextMtime = undefined;
|
|
178
|
+
}
|
|
179
|
+
if (nextMtime === previousMtime) continue;
|
|
180
|
+
if (nextMtime === undefined) this.#moduleMtimes.delete(modulePath);
|
|
181
|
+
else this.#moduleMtimes.set(modulePath, nextMtime);
|
|
182
|
+
changed.push(modulePath);
|
|
183
|
+
}
|
|
184
|
+
for (const modulePath of changed) {
|
|
185
|
+
this.#invalidateModuleAndParents(modulePath, new Set());
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
#invalidateModuleAndParents(modulePath: string, seen: Set<string>): void {
|
|
190
|
+
if (seen.has(modulePath)) return;
|
|
191
|
+
seen.add(modulePath);
|
|
192
|
+
this.#moduleEntries.delete(modulePath);
|
|
193
|
+
this.#moduleBuilds.delete(modulePath);
|
|
194
|
+
this.#moduleVersions.set(modulePath, (this.#moduleVersions.get(modulePath) ?? 1) + 1);
|
|
195
|
+
const parents = [...(this.#moduleParents.get(modulePath) ?? [])];
|
|
196
|
+
for (const parent of parents) this.#invalidateModuleAndParents(parent, seen);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
#setModuleDependencies(modulePath: string, deps: Set<string>): void {
|
|
200
|
+
const previousDeps = this.#moduleDeps.get(modulePath);
|
|
201
|
+
if (previousDeps) {
|
|
202
|
+
for (const dep of previousDeps) {
|
|
203
|
+
const parents = this.#moduleParents.get(dep);
|
|
204
|
+
if (!parents) continue;
|
|
205
|
+
parents.delete(modulePath);
|
|
206
|
+
if (parents.size === 0) this.#moduleParents.delete(dep);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
this.#moduleDeps.set(modulePath, new Set(deps));
|
|
210
|
+
for (const dep of deps) {
|
|
211
|
+
const parents = this.#moduleParents.get(dep) ?? new Set<string>();
|
|
212
|
+
parents.add(modulePath);
|
|
213
|
+
this.#moduleParents.set(dep, parents);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function buildRequire(fromPath: string): NodeJS.Require {
|
|
219
|
+
const basePath = path.extname(fromPath) ? fromPath : path.join(fromPath, "[eval]");
|
|
220
|
+
return createRequire(pathToFileURL(basePath).href);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function buildModuleSource(source: string, modulePath: string): string {
|
|
224
|
+
const moduleDir = path.dirname(modulePath);
|
|
225
|
+
return [
|
|
226
|
+
`const require = globalThis.__omp_get_require__(${JSON.stringify(pathToFileURL(modulePath).href)});`,
|
|
227
|
+
`const __filename = ${JSON.stringify(modulePath)};`,
|
|
228
|
+
`const __dirname = ${JSON.stringify(moduleDir)};`,
|
|
229
|
+
source,
|
|
230
|
+
].join("\n");
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function resolveImportSpecifier(cwd: string, source: string): string {
|
|
234
|
+
if (/^[a-z][a-z0-9+.-]*:/i.test(source)) return source;
|
|
235
|
+
try {
|
|
236
|
+
return Bun.resolveSync(source, cwd);
|
|
237
|
+
} catch {
|
|
238
|
+
return source;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function isLocalPathSpecifier(source: string): boolean {
|
|
243
|
+
return (
|
|
244
|
+
source.startsWith("./") ||
|
|
245
|
+
source.startsWith("../") ||
|
|
246
|
+
source === "." ||
|
|
247
|
+
source === ".." ||
|
|
248
|
+
source.startsWith("/") ||
|
|
249
|
+
source.startsWith("~/") ||
|
|
250
|
+
/^[a-zA-Z]:[\\/]/.test(source)
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function isManagedLocalModulePath(target: string): boolean {
|
|
255
|
+
return (
|
|
256
|
+
path.isAbsolute(target) &&
|
|
257
|
+
LOCAL_MODULE_EXTENSIONS.has(path.extname(target)) &&
|
|
258
|
+
!target.includes(`${path.sep}node_modules${path.sep}`)
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function normalizeImportTarget(target: string): string {
|
|
263
|
+
if (path.isAbsolute(target)) return pathToFileURL(target).href;
|
|
264
|
+
return target;
|
|
265
|
+
}
|
|
@@ -51,6 +51,10 @@ if (!globalThis.__omp_js_prelude_loaded__) {
|
|
|
51
51
|
warn: (...args) => globalThis.__omp_log__("warn", ...formatArgs(args)),
|
|
52
52
|
error: (...args) => globalThis.__omp_log__("error", ...formatArgs(args)),
|
|
53
53
|
debug: (...args) => globalThis.__omp_log__("debug", ...formatArgs(args)),
|
|
54
|
+
table: (data, columns) =>
|
|
55
|
+
columns === undefined
|
|
56
|
+
? globalThis.__omp_table__(data)
|
|
57
|
+
: globalThis.__omp_table__(data, columns),
|
|
54
58
|
};
|
|
55
59
|
|
|
56
60
|
globalThis.console = consoleBridge;
|
|
@@ -57,6 +57,10 @@ type BabelExpressionStatement = {
|
|
|
57
57
|
};
|
|
58
58
|
|
|
59
59
|
type BabelProgramNode = BabelImportDeclaration | BabelLexicalDecl | BabelExpressionStatement | { type: string };
|
|
60
|
+
type BabelModuleSourceDeclaration = {
|
|
61
|
+
type: "ImportDeclaration" | "ExportNamedDeclaration" | "ExportAllDeclaration";
|
|
62
|
+
source?: { value: string; start: number; end: number } | null;
|
|
63
|
+
};
|
|
60
64
|
|
|
61
65
|
type BabelNode = { type: string; start: number; end: number; [key: string]: unknown };
|
|
62
66
|
|
|
@@ -189,6 +193,84 @@ export function rewriteImports(code: string): string {
|
|
|
189
193
|
}
|
|
190
194
|
return result;
|
|
191
195
|
}
|
|
196
|
+
export function collectModuleSourceSpecifiers(code: string): string[] {
|
|
197
|
+
const ast = parseProgram(code);
|
|
198
|
+
if (!ast) return [];
|
|
199
|
+
const sources: string[] = [];
|
|
200
|
+
for (const node of ast.program.body) {
|
|
201
|
+
if (
|
|
202
|
+
(node.type === "ImportDeclaration" ||
|
|
203
|
+
node.type === "ExportNamedDeclaration" ||
|
|
204
|
+
node.type === "ExportAllDeclaration") &&
|
|
205
|
+
typeof (node as BabelModuleSourceDeclaration).source?.value === "string"
|
|
206
|
+
) {
|
|
207
|
+
sources.push((node as BabelModuleSourceDeclaration).source!.value);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return sources;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export function rewriteModuleSourceSpecifiers(code: string, replacer: (source: string) => string): string {
|
|
214
|
+
const ast = parseProgram(code);
|
|
215
|
+
if (!ast) return code;
|
|
216
|
+
|
|
217
|
+
type Edit = { start: number; end: number; text: string };
|
|
218
|
+
const edits: Edit[] = [];
|
|
219
|
+
|
|
220
|
+
for (const node of ast.program.body) {
|
|
221
|
+
if (
|
|
222
|
+
node.type !== "ImportDeclaration" &&
|
|
223
|
+
node.type !== "ExportNamedDeclaration" &&
|
|
224
|
+
node.type !== "ExportAllDeclaration"
|
|
225
|
+
) {
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
const source = (node as BabelModuleSourceDeclaration).source;
|
|
229
|
+
if (!source || typeof source.value !== "string") continue;
|
|
230
|
+
const next = replacer(source.value);
|
|
231
|
+
if (next === source.value) continue;
|
|
232
|
+
edits.push({ start: source.start, end: source.end, text: JSON.stringify(next) });
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (edits.length === 0) return code;
|
|
236
|
+
edits.sort((a, b) => b.start - a.start);
|
|
237
|
+
let result = code;
|
|
238
|
+
for (const edit of edits) {
|
|
239
|
+
result = result.slice(0, edit.start) + edit.text + result.slice(edit.end);
|
|
240
|
+
}
|
|
241
|
+
return result;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export function rewriteDynamicImports(code: string, callee = "__omp_import__"): string {
|
|
245
|
+
if (!code.includes("import")) return code;
|
|
246
|
+
const ast = parseProgram(code);
|
|
247
|
+
if (!ast) return code;
|
|
248
|
+
|
|
249
|
+
type Edit = { start: number; end: number; text: string };
|
|
250
|
+
const edits: Edit[] = [];
|
|
251
|
+
walkNodes(ast, node => {
|
|
252
|
+
if (node.type !== "CallExpression") return;
|
|
253
|
+
const call = node as unknown as { callee?: { type?: string; start?: number; end?: number } };
|
|
254
|
+
const callCallee = call.callee;
|
|
255
|
+
if (
|
|
256
|
+
!callCallee ||
|
|
257
|
+
callCallee.type !== "Import" ||
|
|
258
|
+
typeof callCallee.start !== "number" ||
|
|
259
|
+
typeof callCallee.end !== "number"
|
|
260
|
+
) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
edits.push({ start: callCallee.start, end: callCallee.end, text: callee });
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
if (edits.length === 0) return code;
|
|
267
|
+
edits.sort((a, b) => b.start - a.start);
|
|
268
|
+
let result = code;
|
|
269
|
+
for (const edit of edits) {
|
|
270
|
+
result = result.slice(0, edit.start) + edit.text + result.slice(edit.end);
|
|
271
|
+
}
|
|
272
|
+
return result;
|
|
273
|
+
}
|
|
192
274
|
|
|
193
275
|
function collectBindingNames(pattern: unknown, names: string[]): void {
|
|
194
276
|
if (!pattern || typeof pattern !== "object") return;
|
|
@@ -390,6 +472,9 @@ function stripTypeScript(code: string): string {
|
|
|
390
472
|
return code;
|
|
391
473
|
}
|
|
392
474
|
}
|
|
475
|
+
export function stripTypeScriptSyntax(code: string): string {
|
|
476
|
+
return stripTypeScript(code);
|
|
477
|
+
}
|
|
393
478
|
|
|
394
479
|
// Heuristic: any of the obvious TS-only tokens. Plain JS using `as` only inside strings
|
|
395
480
|
// won't match because we require a leading word boundary plus a colon/keyword neighbor.
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
import { Console } from "node:console";
|
|
1
3
|
import * as fs from "node:fs";
|
|
2
4
|
import { createRequire } from "node:module";
|
|
3
5
|
import * as path from "node:path";
|
|
4
|
-
import {
|
|
6
|
+
import { Writable } from "node:stream";
|
|
5
7
|
import * as util from "node:util";
|
|
6
8
|
|
|
7
9
|
import { logger } from "@oh-my-pi/pi-utils";
|
|
8
10
|
|
|
9
|
-
import { ToolError } from "../../../tools/tool-errors";
|
|
10
11
|
import { createHelpers, type HelperBundle } from "./helpers";
|
|
11
12
|
import { awaitMaybePromise, indirectEval } from "./indirect-eval";
|
|
13
|
+
import { LocalModuleLoader } from "./local-module-loader";
|
|
12
14
|
import { JAVASCRIPT_PRELUDE_SOURCE } from "./prelude";
|
|
13
15
|
import { wrapCode } from "./rewrite-imports";
|
|
14
16
|
import type { JsDisplayOutput, JsStatusEvent } from "./types";
|
|
15
17
|
|
|
16
18
|
/**
|
|
17
|
-
* Per-run callbacks.
|
|
18
|
-
*
|
|
19
|
-
* makes status/display/tool calls reject with an error — useful for guarding against
|
|
20
|
-
* helpers being invoked outside a run window.
|
|
19
|
+
* Per-run callbacks. Runtime globals resolve these from AsyncLocalStorage so
|
|
20
|
+
* overlapping async cells can route output/tool calls back to their own run.
|
|
21
21
|
*/
|
|
22
22
|
export interface RuntimeHooks {
|
|
23
23
|
onText(chunk: string): void;
|
|
@@ -25,11 +25,17 @@ export interface RuntimeHooks {
|
|
|
25
25
|
callTool(name: string, args: unknown): Promise<unknown>;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
export interface RunContext {
|
|
29
|
+
runId: string;
|
|
30
|
+
hooks: RuntimeHooks;
|
|
31
|
+
cwd: string;
|
|
32
|
+
finalExpressionSet: boolean;
|
|
33
|
+
finalExpressionValue: unknown;
|
|
34
|
+
}
|
|
35
|
+
|
|
28
36
|
export interface RuntimeOptions {
|
|
29
37
|
initialCwd: string;
|
|
30
38
|
sessionId: string;
|
|
31
|
-
/** Resolve hooks for the run currently in flight, or `null` if nothing is active. */
|
|
32
|
-
getHooks(): RuntimeHooks | null;
|
|
33
39
|
/**
|
|
34
40
|
* Extra globals installed alongside `__omp_helpers__` / prelude. Use for stable, lifetime-
|
|
35
41
|
* of-the-worker bindings (e.g. browser's `page`, `browser`). Per-run scope should be set
|
|
@@ -118,19 +124,18 @@ export class JsRuntime {
|
|
|
118
124
|
#cwd: string;
|
|
119
125
|
readonly sessionId: string;
|
|
120
126
|
#env: Map<string, string>;
|
|
121
|
-
#
|
|
122
|
-
#
|
|
123
|
-
#finalExpressionValue: unknown;
|
|
127
|
+
#als = new AsyncLocalStorage<RunContext>();
|
|
128
|
+
#moduleLoader: LocalModuleLoader;
|
|
124
129
|
|
|
125
130
|
constructor(opts: RuntimeOptions) {
|
|
126
131
|
this.#cwd = opts.initialCwd;
|
|
127
132
|
this.sessionId = opts.sessionId;
|
|
128
133
|
this.#env = new Map();
|
|
129
|
-
this.#
|
|
134
|
+
this.#moduleLoader = new LocalModuleLoader(this.sessionId);
|
|
130
135
|
this.helpers = createHelpers({
|
|
131
|
-
cwd: () => this.#
|
|
136
|
+
cwd: () => this.#activeCwd(),
|
|
132
137
|
env: this.#env,
|
|
133
|
-
emitStatus: event => this.#
|
|
138
|
+
emitStatus: event => this.#activeHooks("emitStatus")?.onDisplay({ type: "status", event }),
|
|
134
139
|
});
|
|
135
140
|
this.#install(opts.extraGlobals);
|
|
136
141
|
}
|
|
@@ -154,29 +159,42 @@ export class JsRuntime {
|
|
|
154
159
|
Object.assign(globalThis, scope);
|
|
155
160
|
}
|
|
156
161
|
|
|
157
|
-
async run(
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
162
|
+
async run(
|
|
163
|
+
code: string,
|
|
164
|
+
filename: string | undefined,
|
|
165
|
+
hooks: RuntimeHooks,
|
|
166
|
+
options: { runId?: string; cwd?: string } = {},
|
|
167
|
+
): Promise<unknown> {
|
|
168
|
+
const context: RunContext = {
|
|
169
|
+
runId: options.runId ?? crypto.randomUUID(),
|
|
170
|
+
hooks,
|
|
171
|
+
cwd: options.cwd ?? this.#cwd,
|
|
172
|
+
finalExpressionSet: false,
|
|
173
|
+
finalExpressionValue: undefined,
|
|
174
|
+
};
|
|
175
|
+
return await this.#als.run(context, async () => {
|
|
176
|
+
const wrapped = wrapCode(code);
|
|
177
|
+
const value = indirectEval(wrapped.source, filename);
|
|
178
|
+
if (wrapped.finalExpressionReturned) {
|
|
179
|
+
const awaited = await awaitMaybePromise(value);
|
|
180
|
+
if (context.finalExpressionSet) {
|
|
181
|
+
const finalValue = context.finalExpressionValue;
|
|
182
|
+
context.finalExpressionSet = false;
|
|
183
|
+
context.finalExpressionValue = undefined;
|
|
184
|
+
return await awaitMaybePromise(finalValue);
|
|
185
|
+
}
|
|
186
|
+
return awaited;
|
|
170
187
|
}
|
|
171
|
-
return
|
|
172
|
-
}
|
|
173
|
-
return await awaitMaybePromise(value);
|
|
188
|
+
return await awaitMaybePromise(value);
|
|
189
|
+
});
|
|
174
190
|
}
|
|
175
191
|
|
|
176
|
-
displayValue(value: unknown): void {
|
|
192
|
+
displayValue(value: unknown, hooks: RuntimeHooks | undefined = this.#als.getStore()?.hooks): void {
|
|
177
193
|
if (value === undefined) return;
|
|
178
|
-
|
|
179
|
-
|
|
194
|
+
if (!hooks) {
|
|
195
|
+
logger.warn("js runtime display called outside an active run");
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
180
198
|
if (value && typeof value === "object") {
|
|
181
199
|
const record = value as Record<string, unknown>;
|
|
182
200
|
if (record.type === "image" && typeof record.mimeType === "string") {
|
|
@@ -207,45 +225,108 @@ export class JsRuntime {
|
|
|
207
225
|
hooks.onText(`${String(value)}\n`);
|
|
208
226
|
}
|
|
209
227
|
|
|
228
|
+
#activeCwd(): string {
|
|
229
|
+
return this.#als.getStore()?.cwd ?? this.#cwd;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
#activeHooks(action: string): RuntimeHooks | undefined {
|
|
233
|
+
const hooks = this.#als.getStore()?.hooks;
|
|
234
|
+
if (!hooks) {
|
|
235
|
+
logger.warn("js runtime helper called outside an active run", { action });
|
|
236
|
+
}
|
|
237
|
+
return hooks;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
#activeRequire(moduleUrlOrPath?: string): NodeJS.Require {
|
|
241
|
+
return this.#moduleLoader.requireForFile(moduleUrlOrPath, this.#activeCwd());
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
#moduleFilename(moduleUrlOrPath?: string): string {
|
|
245
|
+
return this.#moduleLoader.filenameForUrl(moduleUrlOrPath) ?? path.join(this.#activeCwd(), "[eval]");
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
#moduleDirname(moduleUrlOrPath?: string): string {
|
|
249
|
+
return this.#moduleLoader.dirnameForUrl(moduleUrlOrPath, this.#activeCwd());
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
#buildDynamicRequire(): NodeJS.Require {
|
|
253
|
+
const dynamicRequire = ((id: string) => this.#activeRequire()(id)) as NodeJS.Require;
|
|
254
|
+
const resolve = ((id: string, options?: { paths?: string[] }) =>
|
|
255
|
+
this.#activeRequire().resolve(id, options)) as NodeJS.Require["resolve"] & {
|
|
256
|
+
paths(request: string): string[] | null;
|
|
257
|
+
};
|
|
258
|
+
resolve.paths = request => this.#activeRequire().resolve.paths(request);
|
|
259
|
+
Object.defineProperties(dynamicRequire, {
|
|
260
|
+
resolve: { value: resolve, configurable: true },
|
|
261
|
+
cache: { get: () => this.#activeRequire().cache, configurable: true },
|
|
262
|
+
extensions: { get: () => this.#activeRequire().extensions, configurable: true },
|
|
263
|
+
main: { get: () => this.#activeRequire().main, configurable: true },
|
|
264
|
+
});
|
|
265
|
+
return dynamicRequire;
|
|
266
|
+
}
|
|
267
|
+
|
|
210
268
|
#install(extraGlobals: Record<string, unknown> | undefined): void {
|
|
211
269
|
const injected: Record<string, unknown> = {
|
|
212
270
|
__omp_session__: { cwd: this.#cwd, sessionId: this.sessionId },
|
|
213
271
|
__omp_helpers__: this.helpers,
|
|
214
272
|
__omp_call_tool__: async (name: string, args: unknown) => {
|
|
215
|
-
const hooks = this.#
|
|
216
|
-
if (!hooks)
|
|
273
|
+
const hooks = this.#activeHooks("tool");
|
|
274
|
+
if (!hooks) return undefined;
|
|
217
275
|
return await hooks.callTool(name, args);
|
|
218
276
|
},
|
|
219
277
|
__omp_import__: async (source: string, options?: ImportCallOptions) => {
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
278
|
+
const resolved = await this.#moduleLoader.resolveForRun(this.#activeCwd(), source);
|
|
279
|
+
if (resolved.mode === "local") return resolved.value;
|
|
280
|
+
const target = resolved.target;
|
|
281
|
+
return options !== undefined ? await import(target, options) : await import(target);
|
|
282
|
+
},
|
|
283
|
+
__omp_import_from__: async (moduleUrl: string, source: string, options?: ImportCallOptions) => {
|
|
284
|
+
const resolved = await this.#moduleLoader.resolveForModule(moduleUrl, source, this.#activeCwd());
|
|
285
|
+
if (resolved.mode === "local") return resolved.value;
|
|
286
|
+
const target = resolved.target;
|
|
228
287
|
return options !== undefined ? await import(target, options) : await import(target);
|
|
229
288
|
},
|
|
289
|
+
__omp_get_require__: (moduleUrl?: string) => this.#activeRequire(moduleUrl),
|
|
290
|
+
__omp_get_filename__: (moduleUrl?: string) => this.#moduleFilename(moduleUrl),
|
|
291
|
+
__omp_get_dirname__: (moduleUrl?: string) => this.#moduleDirname(moduleUrl),
|
|
230
292
|
__omp_emit_status__: (op: string, data: Record<string, unknown> = {}) => {
|
|
231
293
|
const event: JsStatusEvent = { op, ...data };
|
|
232
|
-
this.#
|
|
294
|
+
this.#activeHooks("emitStatus")?.onDisplay({ type: "status", event });
|
|
233
295
|
},
|
|
234
296
|
__omp_log__: (level: string, ...args: unknown[]) => {
|
|
235
297
|
const prefix = level === "error" ? "[error] " : level === "warn" ? "[warn] " : "";
|
|
236
298
|
const text = `${prefix}${formatConsoleArgs(args)}`;
|
|
237
|
-
this.#
|
|
299
|
+
this.#activeHooks("log")?.onText(text.endsWith("\n") ? text : `${text}\n`);
|
|
300
|
+
},
|
|
301
|
+
__omp_table__: (...args: unknown[]) => {
|
|
302
|
+
const hooks = this.#activeHooks("table");
|
|
303
|
+
if (!hooks) return;
|
|
304
|
+
let buffer = "";
|
|
305
|
+
const stream = new Writable({
|
|
306
|
+
write(chunk, _enc, cb) {
|
|
307
|
+
buffer += typeof chunk === "string" ? chunk : (chunk as Buffer).toString("utf8");
|
|
308
|
+
cb();
|
|
309
|
+
},
|
|
310
|
+
});
|
|
311
|
+
const tableConsole = new Console({ stdout: stream, colorMode: false });
|
|
312
|
+
(tableConsole.table as (...a: unknown[]) => void)(...args);
|
|
313
|
+
hooks.onText(buffer.endsWith("\n") ? buffer : `${buffer}\n`);
|
|
238
314
|
},
|
|
239
315
|
__omp_display__: (value: unknown) => this.displayValue(value),
|
|
240
316
|
__omp_set_final_expr__: (value: unknown) => {
|
|
241
|
-
|
|
242
|
-
|
|
317
|
+
const context = this.#als.getStore();
|
|
318
|
+
if (!context) {
|
|
319
|
+
logger.warn("js runtime final expression set outside an active run");
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
context.finalExpressionSet = true;
|
|
323
|
+
context.finalExpressionValue = value;
|
|
243
324
|
},
|
|
244
325
|
webcrypto: crypto,
|
|
245
326
|
// `process` is intentionally not overridden — user code gets the host worker's real
|
|
246
327
|
// `process` object. Subsetting it caused segfaults in workers that share state with
|
|
247
328
|
// puppeteer/worker_threads internals.
|
|
248
|
-
require:
|
|
329
|
+
require: this.#buildDynamicRequire(),
|
|
249
330
|
createRequire,
|
|
250
331
|
fs,
|
|
251
332
|
};
|
|
@@ -261,41 +342,3 @@ function formatConsoleArgs(args: unknown[]): string {
|
|
|
261
342
|
.map(arg => (typeof arg === "string" ? arg : util.inspect(arg, { depth: 6, colors: false, breakLength: 120 })))
|
|
262
343
|
.join(" ");
|
|
263
344
|
}
|
|
264
|
-
|
|
265
|
-
function buildRequire(cwd: string): NodeJS.Require {
|
|
266
|
-
return createRequire(pathToFileURL(path.join(cwd, "[eval]")).href);
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* Resolve an import specifier emitted by `rewriteImports` against the active session
|
|
271
|
-
* cwd. Relative paths (`./`, `../`, `/`) and bare specifiers (`pkg`, `@scope/pkg`) both go
|
|
272
|
-
* through `Bun.resolveSync` rooted at the cwd so user-pasted ESM behaves as if it lived in
|
|
273
|
-
* the project — not next to the worker module. URL-like specifiers (`file://`, `data:`,
|
|
274
|
-
* `node:`, `http:`) are passed through unchanged.
|
|
275
|
-
*/
|
|
276
|
-
function resolveImportSpecifier(cwd: string, source: string): string {
|
|
277
|
-
if (/^[a-z][a-z0-9+.-]*:/i.test(source)) return source;
|
|
278
|
-
try {
|
|
279
|
-
return Bun.resolveSync(source, cwd);
|
|
280
|
-
} catch {
|
|
281
|
-
return source;
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* Returns true when the original specifier is a relative or absolute filesystem path
|
|
287
|
-
* (i.e. user-owned source the agent is iterating on). Bare specifiers and URL schemes
|
|
288
|
-
* are excluded — `node:` built-ins cannot be reloaded, and busting bare packages would
|
|
289
|
-
* defeat module identity for every cell while bringing no editing benefit.
|
|
290
|
-
*/
|
|
291
|
-
function isLocalPathSpecifier(source: string): boolean {
|
|
292
|
-
return (
|
|
293
|
-
source.startsWith("./") ||
|
|
294
|
-
source.startsWith("../") ||
|
|
295
|
-
source === "." ||
|
|
296
|
-
source === ".." ||
|
|
297
|
-
source.startsWith("/") ||
|
|
298
|
-
source.startsWith("~/") ||
|
|
299
|
-
/^[a-zA-Z]:[\\/]/.test(source)
|
|
300
|
-
);
|
|
301
|
-
}
|