wave-agent-sdk 1.2.0 → 1.3.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/dist/agent.d.ts +58 -4
- package/dist/agent.js +91 -19
- package/dist/builtin/index.js +2 -0
- package/dist/builtin/skills/settings.js +1 -12
- package/dist/builtin/skills/wave-daemon.d.ts +1 -0
- package/dist/builtin/skills/wave-daemon.js +194 -0
- package/dist/constants/images.d.ts +26 -0
- package/dist/constants/images.js +26 -0
- package/dist/constants/index.d.ts +16 -0
- package/dist/constants/index.js +16 -0
- package/dist/constants/memory.d.ts +26 -0
- package/dist/constants/memory.js +34 -0
- package/dist/constants/messages.d.ts +11 -0
- package/dist/constants/messages.js +11 -0
- package/dist/constants/plugins.d.ts +8 -0
- package/dist/constants/plugins.js +8 -0
- package/dist/constants/tools.d.ts +1 -0
- package/dist/constants/tools.js +1 -0
- package/dist/core/plugin.d.ts +53 -13
- package/dist/core/plugin.js +134 -26
- package/dist/core/session.d.ts +1 -1
- package/dist/core/session.js +1 -1
- package/dist/exec/catalog.d.ts +140 -0
- package/dist/exec/catalog.js +470 -0
- package/dist/exec/catalogAnnouncement.d.ts +89 -0
- package/dist/exec/catalogAnnouncement.js +293 -0
- package/dist/exec/constants.d.ts +51 -0
- package/dist/exec/constants.js +51 -0
- package/dist/exec/execRuntime.d.ts +55 -0
- package/dist/exec/execRuntime.js +217 -0
- package/dist/exec/workerSource.d.ts +28 -0
- package/dist/exec/workerSource.js +299 -0
- package/dist/host/index.d.ts +23 -0
- package/dist/host/index.js +23 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +6 -0
- package/dist/managers/MemoryRuleManager.d.ts +6 -0
- package/dist/managers/MemoryRuleManager.js +12 -0
- package/dist/managers/aiManager.d.ts +35 -1
- package/dist/managers/aiManager.js +190 -21
- package/dist/managers/backgroundTaskManager.js +14 -0
- package/dist/managers/hookManager.d.ts +13 -0
- package/dist/managers/hookManager.js +31 -4
- package/dist/managers/liveConfigManager.d.ts +33 -0
- package/dist/managers/liveConfigManager.js +103 -8
- package/dist/managers/lspManager.d.ts +9 -0
- package/dist/managers/lspManager.js +47 -18
- package/dist/managers/mcpManager.d.ts +45 -10
- package/dist/managers/mcpManager.js +103 -1
- package/dist/managers/messageManager.d.ts +48 -5
- package/dist/managers/messageManager.js +107 -21
- package/dist/managers/permissionManager.d.ts +40 -0
- package/dist/managers/permissionManager.js +63 -8
- package/dist/managers/pluginManager.d.ts +46 -2
- package/dist/managers/pluginManager.js +117 -11
- package/dist/managers/pluginScopeManager.d.ts +15 -2
- package/dist/managers/pluginScopeManager.js +20 -1
- package/dist/managers/skillManager.d.ts +19 -0
- package/dist/managers/skillManager.js +44 -0
- package/dist/managers/slashCommandManager.d.ts +10 -0
- package/dist/managers/slashCommandManager.js +35 -3
- package/dist/managers/subagentManager.d.ts +8 -0
- package/dist/managers/subagentManager.js +20 -0
- package/dist/managers/toolManager.d.ts +29 -3
- package/dist/managers/toolManager.js +87 -13
- package/dist/prompts/autoMemory.d.ts +9 -0
- package/dist/prompts/autoMemory.js +30 -31
- package/dist/prompts/autoMemoryExtraction.d.ts +4 -0
- package/dist/prompts/autoMemoryExtraction.js +8 -111
- package/dist/prompts/memoryTypes.d.ts +63 -0
- package/dist/prompts/memoryTypes.js +191 -0
- package/dist/services/GitService.d.ts +7 -0
- package/dist/services/GitService.js +23 -0
- package/dist/services/MarketplaceService.d.ts +101 -17
- package/dist/services/MarketplaceService.js +318 -119
- package/dist/services/artifactContent.d.ts +84 -0
- package/dist/services/artifactContent.js +204 -0
- package/dist/services/artifactSession.d.ts +6 -0
- package/dist/services/artifactSession.js +17 -0
- package/dist/services/autoMemoryService.js +5 -13
- package/dist/services/configurationService.d.ts +60 -9
- package/dist/services/configurationService.js +129 -54
- package/dist/services/contentSummarizer.d.ts +15 -0
- package/dist/services/contentSummarizer.js +45 -0
- package/dist/services/execAvailability.d.ts +9 -0
- package/dist/services/execAvailability.js +32 -0
- package/dist/services/fileWatcher.js +61 -6
- package/dist/services/initializationService.js +19 -15
- package/dist/services/interactionService.d.ts +9 -1
- package/dist/services/interactionService.js +28 -8
- package/dist/services/jsonlHandler.d.ts +84 -0
- package/dist/services/jsonlHandler.js +209 -14
- package/dist/services/memory.d.ts +3 -1
- package/dist/services/memory.js +13 -9
- package/dist/services/officialMarketplaceMirror.js +3 -2
- package/dist/services/pluginLoader.d.ts +12 -4
- package/dist/services/pluginLoader.js +38 -7
- package/dist/services/remoteSettingsService.js +16 -2
- package/dist/services/session.d.ts +74 -0
- package/dist/services/session.js +144 -3
- package/dist/services/sessionEntries.d.ts +2 -0
- package/dist/services/sessionEntries.js +20 -0
- package/dist/stdio/index.d.ts +3 -1
- package/dist/stdio/index.js +3 -1
- package/dist/stdio/notificationRouter.js +1 -0
- package/dist/stdio/stdioAgent.d.ts +14 -7
- package/dist/stdio/stdioAgent.js +19 -0
- package/dist/tools/artifactTool.js +406 -273
- package/dist/tools/bashTool.js +8 -6
- package/dist/tools/editTool.js +6 -3
- package/dist/tools/execTool.d.ts +2 -0
- package/dist/tools/execTool.js +165 -0
- package/dist/tools/grepTool.js +7 -1
- package/dist/tools/readTool.js +30 -2
- package/dist/tools/types.d.ts +34 -8
- package/dist/tools/webFetchTool.js +15 -166
- package/dist/tools/workflowTool.js +40 -8
- package/dist/tools/writeTool.js +6 -3
- package/dist/types/agent.d.ts +20 -5
- package/dist/types/configuration.d.ts +39 -1
- package/dist/types/marketplace.d.ts +40 -2
- package/dist/types/mcp.d.ts +39 -0
- package/dist/types/permissions.d.ts +22 -0
- package/dist/types/permissions.js +17 -0
- package/dist/types/plugins.d.ts +26 -2
- package/dist/types/skills.d.ts +15 -0
- package/dist/utils/constants.d.ts +10 -0
- package/dist/utils/constants.js +10 -0
- package/dist/utils/containerSetup.js +43 -0
- package/dist/utils/convertMessagesForAPI.d.ts +7 -1
- package/dist/utils/convertMessagesForAPI.js +64 -14
- package/dist/utils/fileChangeReminder.d.ts +20 -0
- package/dist/utils/fileChangeReminder.js +153 -0
- package/dist/utils/fileSearch.js +4 -3
- package/dist/utils/fileUtils.d.ts +33 -0
- package/dist/utils/fileUtils.js +81 -0
- package/dist/utils/frontmatterYaml.d.ts +33 -0
- package/dist/utils/frontmatterYaml.js +192 -0
- package/dist/utils/imageBudget.d.ts +85 -0
- package/dist/utils/imageBudget.js +109 -0
- package/dist/utils/imageDimensions.d.ts +83 -0
- package/dist/utils/imageDimensions.js +232 -0
- package/dist/utils/imageProcessor.d.ts +66 -0
- package/dist/utils/imageProcessor.js +84 -0
- package/dist/utils/imageRewrite.d.ts +29 -0
- package/dist/utils/imageRewrite.js +251 -0
- package/dist/utils/markdownParser.d.ts +5 -1
- package/dist/utils/markdownParser.js +9 -51
- package/dist/utils/mcpInstructions.d.ts +61 -0
- package/dist/utils/mcpInstructions.js +126 -0
- package/dist/utils/mcpUtils.d.ts +7 -0
- package/dist/utils/mcpUtils.js +11 -2
- package/dist/utils/memoryAge.d.ts +32 -0
- package/dist/utils/memoryAge.js +47 -0
- package/dist/utils/memoryEntrypoint.d.ts +20 -0
- package/dist/utils/memoryEntrypoint.js +49 -0
- package/dist/utils/memoryIndex.d.ts +30 -0
- package/dist/utils/memoryIndex.js +76 -0
- package/dist/utils/messageOperations.d.ts +6 -2
- package/dist/utils/messageOperations.js +40 -29
- package/dist/utils/nestedMemory.d.ts +22 -0
- package/dist/utils/nestedMemory.js +61 -0
- package/dist/utils/npmTarball.d.ts +19 -0
- package/dist/utils/npmTarball.js +92 -0
- package/dist/utils/pluginSource.d.ts +37 -0
- package/dist/utils/pluginSource.js +73 -0
- package/dist/utils/ripgrep.d.ts +18 -4
- package/dist/utils/ripgrep.js +56 -4
- package/dist/utils/runtimeDeps.d.ts +35 -0
- package/dist/utils/runtimeDeps.js +426 -0
- package/dist/utils/skillParser.js +22 -52
- package/dist/utils/subagentParser.js +39 -43
- package/dist/utils/userSettings.d.ts +90 -0
- package/dist/utils/userSettings.js +291 -0
- package/package.json +10 -7
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface NestedMemoryFile {
|
|
2
|
+
path: string;
|
|
3
|
+
content: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Collect the memory files that sit in the directories above a file the agent
|
|
7
|
+
* just read, outermost first.
|
|
8
|
+
*
|
|
9
|
+
* The root memory file is loaded eagerly into every request, so putting
|
|
10
|
+
* subdirectory-specific conventions there costs context on every turn; this
|
|
11
|
+
* pulls them in only once work actually reaches that subtree.
|
|
12
|
+
*
|
|
13
|
+
* Aligned with Claude Code's `nested_memory` attachment (attachments.ts
|
|
14
|
+
* `getDirectoriesToProcess` + `memoryFilesToAttachments`): the walk stops at
|
|
15
|
+
* the project root (the root's own memory file is already loaded), only
|
|
16
|
+
* directories inside the project are considered, and the order is root-ward
|
|
17
|
+
* first so the most specific file lands closest to the read.
|
|
18
|
+
*
|
|
19
|
+
* `loadedPaths` is the session-level dedup set, owned by the caller — it must
|
|
20
|
+
* outlive any read-state cache, or an eviction re-injects the same file.
|
|
21
|
+
*/
|
|
22
|
+
export declare function findNestedMemoryFiles(filePath: string, workdir: string, loadedPaths: Set<string>): Promise<NestedMemoryFile[]>;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import * as fs from "node:fs/promises";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { isPathInside } from "./pathSafety.js";
|
|
4
|
+
/**
|
|
5
|
+
* Memory file names to look for in a directory, in priority order:
|
|
6
|
+
* `AGENTS.md` is the convention, `CLAUDE.md` the compatibility fallback —
|
|
7
|
+
* the same order `MemoryService.readMemoryFile` uses for project memory.
|
|
8
|
+
*/
|
|
9
|
+
const NESTED_MEMORY_FILENAMES = ["AGENTS.md", "CLAUDE.md"];
|
|
10
|
+
/**
|
|
11
|
+
* Collect the memory files that sit in the directories above a file the agent
|
|
12
|
+
* just read, outermost first.
|
|
13
|
+
*
|
|
14
|
+
* The root memory file is loaded eagerly into every request, so putting
|
|
15
|
+
* subdirectory-specific conventions there costs context on every turn; this
|
|
16
|
+
* pulls them in only once work actually reaches that subtree.
|
|
17
|
+
*
|
|
18
|
+
* Aligned with Claude Code's `nested_memory` attachment (attachments.ts
|
|
19
|
+
* `getDirectoriesToProcess` + `memoryFilesToAttachments`): the walk stops at
|
|
20
|
+
* the project root (the root's own memory file is already loaded), only
|
|
21
|
+
* directories inside the project are considered, and the order is root-ward
|
|
22
|
+
* first so the most specific file lands closest to the read.
|
|
23
|
+
*
|
|
24
|
+
* `loadedPaths` is the session-level dedup set, owned by the caller — it must
|
|
25
|
+
* outlive any read-state cache, or an eviction re-injects the same file.
|
|
26
|
+
*/
|
|
27
|
+
export async function findNestedMemoryFiles(filePath, workdir, loadedPaths) {
|
|
28
|
+
const root = path.resolve(workdir);
|
|
29
|
+
const directories = [];
|
|
30
|
+
let current = path.dirname(path.resolve(filePath));
|
|
31
|
+
while (current !== root && isPathInside(current, root)) {
|
|
32
|
+
directories.push(current);
|
|
33
|
+
const parent = path.dirname(current);
|
|
34
|
+
if (parent === current)
|
|
35
|
+
break;
|
|
36
|
+
current = parent;
|
|
37
|
+
}
|
|
38
|
+
// Outermost (closest to the project root) first — Claude Code's order.
|
|
39
|
+
directories.reverse();
|
|
40
|
+
const memories = [];
|
|
41
|
+
for (const directory of directories) {
|
|
42
|
+
for (const filename of NESTED_MEMORY_FILENAMES) {
|
|
43
|
+
const candidate = path.join(directory, filename);
|
|
44
|
+
// One memory file per directory: a directory whose AGENTS.md was already
|
|
45
|
+
// injected must not fall through to its CLAUDE.md.
|
|
46
|
+
if (loadedPaths.has(candidate))
|
|
47
|
+
break;
|
|
48
|
+
try {
|
|
49
|
+
const content = await fs.readFile(candidate, "utf-8");
|
|
50
|
+
loadedPaths.add(candidate);
|
|
51
|
+
memories.push({ path: candidate, content });
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// Try the next candidate name; a missing AGENTS.md falls back to
|
|
56
|
+
// CLAUDE.md, and an unreadable file must not abort the whole chain.
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return memories;
|
|
61
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface TarballEntry {
|
|
2
|
+
/** Path relative to the package root (the leading `package/` is removed). */
|
|
3
|
+
path: string;
|
|
4
|
+
kind: "file" | "directory";
|
|
5
|
+
/** File contents; empty for directories. */
|
|
6
|
+
data: Uint8Array;
|
|
7
|
+
/**
|
|
8
|
+
* Unix permission bits from the header (e.g. `0o755`). Callers that only write
|
|
9
|
+
* files may ignore it, but an executable payload (the ripgrep binary) is
|
|
10
|
+
* unusable without the exec bit.
|
|
11
|
+
*/
|
|
12
|
+
mode: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Decompress and list an npm tarball. Directory entries are returned (empty
|
|
16
|
+
* `data`) so callers can recreate empty directories, but a caller that just
|
|
17
|
+
* writes files may ignore them.
|
|
18
|
+
*/
|
|
19
|
+
export declare function extractNpmTarball(tarball: Uint8Array): TarballEntry[];
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal npm-tarball reader: gunzip + ustar extraction, no dependencies.
|
|
3
|
+
*
|
|
4
|
+
* The runtime-dependency installer (`runtimeDeps.ts`) downloads packages
|
|
5
|
+
* straight from the registry instead of shelling out to npm, so it needs to
|
|
6
|
+
* turn a `.tgz` into files. Node ships gzip (`zlib`) but not tar, and pulling in
|
|
7
|
+
* a tar package would drag a dependency into the CLI bundle for one code path.
|
|
8
|
+
*
|
|
9
|
+
* Scope is deliberately narrow — this reads *npm* tarballs, not tar in general:
|
|
10
|
+
*
|
|
11
|
+
* - npm wraps every entry in a top-level `package/` directory; that wrapper is
|
|
12
|
+
* stripped, so returned paths are relative to the package root. Anything not
|
|
13
|
+
* under it is an unexpected layout and throws rather than guessing.
|
|
14
|
+
* - Regular files (`0`) and directories (`5`) are supported. Everything else —
|
|
15
|
+
* GNU long names (`L`), pax headers (`x`), symlinks (`2`) — throws: npm
|
|
16
|
+
* publishes none of them, and silently skipping an entry would leave a
|
|
17
|
+
* half-installed package that only fails later.
|
|
18
|
+
* - The 512-byte ustar `prefix` field is honoured, so paths beyond 100 bytes
|
|
19
|
+
* resolve correctly.
|
|
20
|
+
* - Permission bits are reported (`mode`), not applied — some packages ship an
|
|
21
|
+
* executable payload (`@vscode/ripgrep-*` holds `bin/rg` at `0o755`), and
|
|
22
|
+
* whoever writes the files has to set the exec bit itself.
|
|
23
|
+
*
|
|
24
|
+
* Tarball integrity (sha512 from the registry) is verified by the caller before
|
|
25
|
+
* extraction, so this module does not re-checksum headers.
|
|
26
|
+
*/
|
|
27
|
+
import { gunzipSync } from "node:zlib";
|
|
28
|
+
const BLOCK_SIZE = 512;
|
|
29
|
+
/** Read a NUL-terminated (or full-length) utf8 field out of a tar header. */
|
|
30
|
+
function readField(block, offset, length) {
|
|
31
|
+
let end = offset;
|
|
32
|
+
const limit = offset + length;
|
|
33
|
+
while (end < limit && block[end] !== 0)
|
|
34
|
+
end++;
|
|
35
|
+
return Buffer.from(block.subarray(offset, end)).toString("utf8");
|
|
36
|
+
}
|
|
37
|
+
/** Reject anything that could escape the target directory. */
|
|
38
|
+
function assertSafeRelativePath(path) {
|
|
39
|
+
if (path.startsWith("/") || path.split("/").some((seg) => seg === "..")) {
|
|
40
|
+
throw new Error(`Refusing unsafe path in npm tarball: ${path}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Decompress and list an npm tarball. Directory entries are returned (empty
|
|
45
|
+
* `data`) so callers can recreate empty directories, but a caller that just
|
|
46
|
+
* writes files may ignore them.
|
|
47
|
+
*/
|
|
48
|
+
export function extractNpmTarball(tarball) {
|
|
49
|
+
const tar = gunzipSync(tarball);
|
|
50
|
+
const entries = [];
|
|
51
|
+
let offset = 0;
|
|
52
|
+
while (offset + BLOCK_SIZE <= tar.length) {
|
|
53
|
+
const header = tar.subarray(offset, offset + BLOCK_SIZE);
|
|
54
|
+
offset += BLOCK_SIZE;
|
|
55
|
+
// A zeroed block marks the end of the archive (conventionally two of them).
|
|
56
|
+
if (header.every((byte) => byte === 0))
|
|
57
|
+
break;
|
|
58
|
+
const name = readField(header, 0, 100);
|
|
59
|
+
const size = Number.parseInt(readField(header, 124, 12).trim(), 8);
|
|
60
|
+
const mode = Number.parseInt(readField(header, 100, 8).trim(), 8);
|
|
61
|
+
const typeFlag = String.fromCharCode(header[156]);
|
|
62
|
+
const prefix = readField(header, 345, 155);
|
|
63
|
+
const fullPath = prefix ? `${prefix}/${name}` : name;
|
|
64
|
+
if (!Number.isInteger(size) || size < 0) {
|
|
65
|
+
throw new Error(`Corrupt npm tarball: bad size field for ${fullPath}`);
|
|
66
|
+
}
|
|
67
|
+
const data = tar.subarray(offset, offset + size);
|
|
68
|
+
if (data.length < size) {
|
|
69
|
+
throw new Error(`Corrupt npm tarball: truncated entry ${fullPath}`);
|
|
70
|
+
}
|
|
71
|
+
offset += Math.ceil(size / BLOCK_SIZE) * BLOCK_SIZE;
|
|
72
|
+
if (fullPath === "package")
|
|
73
|
+
continue; // the wrapper directory itself
|
|
74
|
+
if (!fullPath.startsWith("package/")) {
|
|
75
|
+
throw new Error(`Unexpected npm tarball layout: ${fullPath}`);
|
|
76
|
+
}
|
|
77
|
+
const path = fullPath.slice("package/".length);
|
|
78
|
+
if (path === "")
|
|
79
|
+
continue;
|
|
80
|
+
assertSafeRelativePath(path);
|
|
81
|
+
if (typeFlag === "5") {
|
|
82
|
+
entries.push({ path, kind: "directory", data: new Uint8Array(0), mode });
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (typeFlag === "0" || typeFlag === "\0") {
|
|
86
|
+
entries.push({ path, kind: "file", data: Buffer.from(data), mode });
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
throw new Error(`Unsupported tar entry type "${typeFlag}" in npm tarball: ${fullPath}`);
|
|
90
|
+
}
|
|
91
|
+
return entries;
|
|
92
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Marketplace plugin entry `source` parsing (spec plugin A-021).
|
|
3
|
+
*
|
|
4
|
+
* Two shapes are accepted:
|
|
5
|
+
* - **string** — a Git URL (`http://` / `https://` / `git@` / `ssh://`, optionally
|
|
6
|
+
* `url#ref`) or a path relative to the marketplace checkout.
|
|
7
|
+
* - **object** — `{"source":"url","url":…}` (whole repo) or
|
|
8
|
+
* `{"source":"git-subdir","url":…,"path":…}` (subdirectory of that repo), both
|
|
9
|
+
* optionally carrying `ref` (branch/tag) and `sha` (pinned commit, wins over `ref`).
|
|
10
|
+
*
|
|
11
|
+
* Tolerance is per-entry (spec plugin「兼容 Claude Code 生态的市场清单与插件」场景 4):
|
|
12
|
+
* an unrecognized shape yields `undefined` so only that entry fails, never the
|
|
13
|
+
* whole marketplace listing.
|
|
14
|
+
*/
|
|
15
|
+
export interface LocalPluginSource {
|
|
16
|
+
kind: "local";
|
|
17
|
+
/** Path relative to the marketplace checkout. */
|
|
18
|
+
path: string;
|
|
19
|
+
}
|
|
20
|
+
export interface GitPluginSource {
|
|
21
|
+
kind: "git";
|
|
22
|
+
url: string;
|
|
23
|
+
/** Branch or tag. */
|
|
24
|
+
ref?: string;
|
|
25
|
+
/** Pinned commit; when present it is checked out after cloning. */
|
|
26
|
+
sha?: string;
|
|
27
|
+
/** Subdirectory inside the cloned repository that holds the plugin. */
|
|
28
|
+
subdir?: string;
|
|
29
|
+
}
|
|
30
|
+
export type ParsedPluginSource = LocalPluginSource | GitPluginSource;
|
|
31
|
+
export declare function isGitUrl(value: string): boolean;
|
|
32
|
+
export declare function parsePluginSource(source: unknown): ParsedPluginSource | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* Renders an unrecognized `source` for error messages so install failures name
|
|
35
|
+
* the offending shape instead of hiding it (spec plugin A-021).
|
|
36
|
+
*/
|
|
37
|
+
export declare function describePluginSource(source: unknown): string;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Marketplace plugin entry `source` parsing (spec plugin A-021).
|
|
3
|
+
*
|
|
4
|
+
* Two shapes are accepted:
|
|
5
|
+
* - **string** — a Git URL (`http://` / `https://` / `git@` / `ssh://`, optionally
|
|
6
|
+
* `url#ref`) or a path relative to the marketplace checkout.
|
|
7
|
+
* - **object** — `{"source":"url","url":…}` (whole repo) or
|
|
8
|
+
* `{"source":"git-subdir","url":…,"path":…}` (subdirectory of that repo), both
|
|
9
|
+
* optionally carrying `ref` (branch/tag) and `sha` (pinned commit, wins over `ref`).
|
|
10
|
+
*
|
|
11
|
+
* Tolerance is per-entry (spec plugin「兼容 Claude Code 生态的市场清单与插件」场景 4):
|
|
12
|
+
* an unrecognized shape yields `undefined` so only that entry fails, never the
|
|
13
|
+
* whole marketplace listing.
|
|
14
|
+
*/
|
|
15
|
+
const GIT_URL_PREFIXES = ["http://", "https://", "git@", "ssh://"];
|
|
16
|
+
export function isGitUrl(value) {
|
|
17
|
+
return GIT_URL_PREFIXES.some((prefix) => value.startsWith(prefix));
|
|
18
|
+
}
|
|
19
|
+
export function parsePluginSource(source) {
|
|
20
|
+
if (typeof source === "string") {
|
|
21
|
+
return parseStringSource(source);
|
|
22
|
+
}
|
|
23
|
+
if (source && typeof source === "object") {
|
|
24
|
+
return parseObjectSource(source);
|
|
25
|
+
}
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
function parseStringSource(source) {
|
|
29
|
+
const trimmed = source.trim();
|
|
30
|
+
if (!trimmed)
|
|
31
|
+
return undefined;
|
|
32
|
+
if (!isGitUrl(trimmed)) {
|
|
33
|
+
return { kind: "local", path: trimmed };
|
|
34
|
+
}
|
|
35
|
+
const hashIndex = trimmed.indexOf("#");
|
|
36
|
+
if (hashIndex === -1) {
|
|
37
|
+
return { kind: "git", url: trimmed };
|
|
38
|
+
}
|
|
39
|
+
const url = trimmed.slice(0, hashIndex);
|
|
40
|
+
const ref = trimmed.slice(hashIndex + 1);
|
|
41
|
+
return { kind: "git", url, ref: ref || undefined };
|
|
42
|
+
}
|
|
43
|
+
function parseObjectSource(source) {
|
|
44
|
+
const declaration = source.source;
|
|
45
|
+
if (declaration !== "url" && declaration !== "git-subdir") {
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
const url = typeof source.url === "string" ? source.url.trim() : "";
|
|
49
|
+
if (!url)
|
|
50
|
+
return undefined;
|
|
51
|
+
const ref = typeof source.ref === "string" && source.ref ? source.ref : undefined;
|
|
52
|
+
const sha = typeof source.sha === "string" && source.sha ? source.sha : undefined;
|
|
53
|
+
if (declaration === "url") {
|
|
54
|
+
return { kind: "git", url, ref, sha };
|
|
55
|
+
}
|
|
56
|
+
const subdir = typeof source.path === "string" ? source.path.trim() : "";
|
|
57
|
+
if (!subdir)
|
|
58
|
+
return undefined;
|
|
59
|
+
return { kind: "git", url, ref, sha, subdir };
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Renders an unrecognized `source` for error messages so install failures name
|
|
63
|
+
* the offending shape instead of hiding it (spec plugin A-021).
|
|
64
|
+
*/
|
|
65
|
+
export function describePluginSource(source) {
|
|
66
|
+
if (typeof source === "string")
|
|
67
|
+
return JSON.stringify(source);
|
|
68
|
+
if (source && typeof source === "object") {
|
|
69
|
+
const json = JSON.stringify(source);
|
|
70
|
+
return json.length > 200 ? `${json.slice(0, 200)}…` : json;
|
|
71
|
+
}
|
|
72
|
+
return String(source);
|
|
73
|
+
}
|
package/dist/utils/ripgrep.d.ts
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Resolve the rg binary path through [requireFn], or `undefined` when it is not
|
|
3
|
+
* installed. `requireFn` is injectable so the installer can verify an install
|
|
4
|
+
* from the directory the CLI will actually load it from, and so tests can
|
|
5
|
+
* exercise both outcomes without the real package.
|
|
5
6
|
*/
|
|
6
|
-
export
|
|
7
|
+
export declare function resolveRipgrep(requireFn: NodeRequire): string | undefined;
|
|
8
|
+
/**
|
|
9
|
+
* Path to the ripgrep binary, or `undefined` when grep is unavailable (the Grep
|
|
10
|
+
* tool reports it, file search throws its own error). Memoised once resolved —
|
|
11
|
+
* callers on a hot path pay a string check after that.
|
|
12
|
+
*/
|
|
13
|
+
export declare function getRgPath(): string | undefined;
|
|
14
|
+
/**
|
|
15
|
+
* Forget the memoised path so the next {@link getRgPath} resolves again. Called
|
|
16
|
+
* by the runtime-dependency installer once rg is on disk — without it a process
|
|
17
|
+
* that asked for rg *before* the download finished would report grep as missing
|
|
18
|
+
* for its whole lifetime. Also used by tests.
|
|
19
|
+
*/
|
|
20
|
+
export declare function resetRipgrep(): void;
|
package/dist/utils/ripgrep.js
CHANGED
|
@@ -1,6 +1,58 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
1
2
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* Lazy access to the optional `@vscode/ripgrep` search dependency.
|
|
4
|
+
*
|
|
5
|
+
* ## Why a runtime require, and why not at module-evaluation time
|
|
6
|
+
*
|
|
7
|
+
* The wrapper resolves the platform-specific binary package
|
|
8
|
+
* (`@vscode/ripgrep-<platform>-<arch>`) while its module body evaluates and
|
|
9
|
+
* throws when that package is absent. A top-level import would therefore take
|
|
10
|
+
* down any host that loads the SDK barrel without the platform package — the
|
|
11
|
+
* desktop main process and the IDE extension hosts ship no such package and run
|
|
12
|
+
* grep in the CLI child process instead.
|
|
13
|
+
*
|
|
14
|
+
* Resolution is also *re-attempted* rather than frozen at module-evaluation
|
|
15
|
+
* time: the CLI installs this dependency itself, after its own module graph has
|
|
16
|
+
* been evaluated (see `utils/runtimeDeps.ts`), so an eager value would pin "grep
|
|
17
|
+
* is missing" for the whole first process. The installer calls
|
|
18
|
+
* {@link resetRipgrep} once the binary is on disk.
|
|
5
19
|
*/
|
|
6
|
-
|
|
20
|
+
let cached;
|
|
21
|
+
let resolutionAttempted = false;
|
|
22
|
+
/**
|
|
23
|
+
* Resolve the rg binary path through [requireFn], or `undefined` when it is not
|
|
24
|
+
* installed. `requireFn` is injectable so the installer can verify an install
|
|
25
|
+
* from the directory the CLI will actually load it from, and so tests can
|
|
26
|
+
* exercise both outcomes without the real package.
|
|
27
|
+
*/
|
|
28
|
+
export function resolveRipgrep(requireFn) {
|
|
29
|
+
try {
|
|
30
|
+
const loaded = requireFn("@vscode/ripgrep");
|
|
31
|
+
return typeof loaded?.rgPath === "string" ? loaded.rgPath : undefined;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Path to the ripgrep binary, or `undefined` when grep is unavailable (the Grep
|
|
39
|
+
* tool reports it, file search throws its own error). Memoised once resolved —
|
|
40
|
+
* callers on a hot path pay a string check after that.
|
|
41
|
+
*/
|
|
42
|
+
export function getRgPath() {
|
|
43
|
+
if (!resolutionAttempted) {
|
|
44
|
+
resolutionAttempted = true;
|
|
45
|
+
cached = resolveRipgrep(createRequire(import.meta.url));
|
|
46
|
+
}
|
|
47
|
+
return cached;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Forget the memoised path so the next {@link getRgPath} resolves again. Called
|
|
51
|
+
* by the runtime-dependency installer once rg is on disk — without it a process
|
|
52
|
+
* that asked for rg *before* the download finished would report grep as missing
|
|
53
|
+
* for its whole lifetime. Also used by tests.
|
|
54
|
+
*/
|
|
55
|
+
export function resetRipgrep() {
|
|
56
|
+
cached = undefined;
|
|
57
|
+
resolutionAttempted = false;
|
|
58
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** npm registry mirror for China users (same choice as the ripgrep download). */
|
|
2
|
+
export declare const RUNTIME_DEPS_REGISTRY = "https://registry.npmmirror.com";
|
|
3
|
+
export interface RuntimeDepsResult {
|
|
4
|
+
available: boolean;
|
|
5
|
+
reason?: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Whether this code runs from a CLI copy under the shared cli home.
|
|
9
|
+
*
|
|
10
|
+
* An npm-installed CLI (bundle under `<prefix>/lib/node_modules/wave-code/...`)
|
|
11
|
+
* must not self-install: Node would never look in `~/.wave/cli/node_modules`
|
|
12
|
+
* from there, and writing into a global npm prefix may lack permissions and
|
|
13
|
+
* gets pruned by the next `npm install`. Such a setup either already has the
|
|
14
|
+
* dependencies (the normal case) or degrades with an actionable warning.
|
|
15
|
+
*/
|
|
16
|
+
export declare function isManagedCliInstall(moduleUrl: string, cliHome?: string): boolean;
|
|
17
|
+
/** sha512/base64 check against the registry's `dist.integrity`. */
|
|
18
|
+
export declare function verifyIntegrity(buffer: Buffer, integrity: string): void;
|
|
19
|
+
/**
|
|
20
|
+
* Make sure the runtime dependencies are installed. Never throws: the long-lived
|
|
21
|
+
* CLI entry points await it before serving, so a first pasted image (or a first
|
|
22
|
+
* search) cannot race a download — the same reason the hosts used to block on
|
|
23
|
+
* ripgrep before spawning the CLI. A failed install is reported once and only
|
|
24
|
+
* degrades images and grep.
|
|
25
|
+
*
|
|
26
|
+
* The outcome is memoised for the process lifetime, failures included — one
|
|
27
|
+
* attempt per launch, so a broken network does not retry on every image.
|
|
28
|
+
*/
|
|
29
|
+
export declare function ensureRuntimeDeps(options?: {
|
|
30
|
+
moduleUrl?: string;
|
|
31
|
+
fetchImpl?: typeof fetch;
|
|
32
|
+
cliHome?: string;
|
|
33
|
+
}): Promise<RuntimeDepsResult>;
|
|
34
|
+
/** Forget the memoised outcome, and the settled result (tests). */
|
|
35
|
+
export declare function __resetRuntimeDepsForTesting(): void;
|