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.
Files changed (175) hide show
  1. package/dist/agent.d.ts +58 -4
  2. package/dist/agent.js +91 -19
  3. package/dist/builtin/index.js +2 -0
  4. package/dist/builtin/skills/settings.js +1 -12
  5. package/dist/builtin/skills/wave-daemon.d.ts +1 -0
  6. package/dist/builtin/skills/wave-daemon.js +194 -0
  7. package/dist/constants/images.d.ts +26 -0
  8. package/dist/constants/images.js +26 -0
  9. package/dist/constants/index.d.ts +16 -0
  10. package/dist/constants/index.js +16 -0
  11. package/dist/constants/memory.d.ts +26 -0
  12. package/dist/constants/memory.js +34 -0
  13. package/dist/constants/messages.d.ts +11 -0
  14. package/dist/constants/messages.js +11 -0
  15. package/dist/constants/plugins.d.ts +8 -0
  16. package/dist/constants/plugins.js +8 -0
  17. package/dist/constants/tools.d.ts +1 -0
  18. package/dist/constants/tools.js +1 -0
  19. package/dist/core/plugin.d.ts +53 -13
  20. package/dist/core/plugin.js +134 -26
  21. package/dist/core/session.d.ts +1 -1
  22. package/dist/core/session.js +1 -1
  23. package/dist/exec/catalog.d.ts +140 -0
  24. package/dist/exec/catalog.js +470 -0
  25. package/dist/exec/catalogAnnouncement.d.ts +89 -0
  26. package/dist/exec/catalogAnnouncement.js +293 -0
  27. package/dist/exec/constants.d.ts +51 -0
  28. package/dist/exec/constants.js +51 -0
  29. package/dist/exec/execRuntime.d.ts +55 -0
  30. package/dist/exec/execRuntime.js +217 -0
  31. package/dist/exec/workerSource.d.ts +28 -0
  32. package/dist/exec/workerSource.js +299 -0
  33. package/dist/host/index.d.ts +23 -0
  34. package/dist/host/index.js +23 -0
  35. package/dist/index.d.ts +5 -0
  36. package/dist/index.js +6 -0
  37. package/dist/managers/MemoryRuleManager.d.ts +6 -0
  38. package/dist/managers/MemoryRuleManager.js +12 -0
  39. package/dist/managers/aiManager.d.ts +35 -1
  40. package/dist/managers/aiManager.js +190 -21
  41. package/dist/managers/backgroundTaskManager.js +14 -0
  42. package/dist/managers/hookManager.d.ts +13 -0
  43. package/dist/managers/hookManager.js +31 -4
  44. package/dist/managers/liveConfigManager.d.ts +33 -0
  45. package/dist/managers/liveConfigManager.js +103 -8
  46. package/dist/managers/lspManager.d.ts +9 -0
  47. package/dist/managers/lspManager.js +47 -18
  48. package/dist/managers/mcpManager.d.ts +45 -10
  49. package/dist/managers/mcpManager.js +103 -1
  50. package/dist/managers/messageManager.d.ts +48 -5
  51. package/dist/managers/messageManager.js +107 -21
  52. package/dist/managers/permissionManager.d.ts +40 -0
  53. package/dist/managers/permissionManager.js +63 -8
  54. package/dist/managers/pluginManager.d.ts +46 -2
  55. package/dist/managers/pluginManager.js +117 -11
  56. package/dist/managers/pluginScopeManager.d.ts +15 -2
  57. package/dist/managers/pluginScopeManager.js +20 -1
  58. package/dist/managers/skillManager.d.ts +19 -0
  59. package/dist/managers/skillManager.js +44 -0
  60. package/dist/managers/slashCommandManager.d.ts +10 -0
  61. package/dist/managers/slashCommandManager.js +35 -3
  62. package/dist/managers/subagentManager.d.ts +8 -0
  63. package/dist/managers/subagentManager.js +20 -0
  64. package/dist/managers/toolManager.d.ts +29 -3
  65. package/dist/managers/toolManager.js +87 -13
  66. package/dist/prompts/autoMemory.d.ts +9 -0
  67. package/dist/prompts/autoMemory.js +30 -31
  68. package/dist/prompts/autoMemoryExtraction.d.ts +4 -0
  69. package/dist/prompts/autoMemoryExtraction.js +8 -111
  70. package/dist/prompts/memoryTypes.d.ts +63 -0
  71. package/dist/prompts/memoryTypes.js +191 -0
  72. package/dist/services/GitService.d.ts +7 -0
  73. package/dist/services/GitService.js +23 -0
  74. package/dist/services/MarketplaceService.d.ts +101 -17
  75. package/dist/services/MarketplaceService.js +318 -119
  76. package/dist/services/artifactContent.d.ts +84 -0
  77. package/dist/services/artifactContent.js +204 -0
  78. package/dist/services/artifactSession.d.ts +6 -0
  79. package/dist/services/artifactSession.js +17 -0
  80. package/dist/services/autoMemoryService.js +5 -13
  81. package/dist/services/configurationService.d.ts +60 -9
  82. package/dist/services/configurationService.js +129 -54
  83. package/dist/services/contentSummarizer.d.ts +15 -0
  84. package/dist/services/contentSummarizer.js +45 -0
  85. package/dist/services/execAvailability.d.ts +9 -0
  86. package/dist/services/execAvailability.js +32 -0
  87. package/dist/services/fileWatcher.js +61 -6
  88. package/dist/services/initializationService.js +19 -15
  89. package/dist/services/interactionService.d.ts +9 -1
  90. package/dist/services/interactionService.js +28 -8
  91. package/dist/services/jsonlHandler.d.ts +84 -0
  92. package/dist/services/jsonlHandler.js +209 -14
  93. package/dist/services/memory.d.ts +3 -1
  94. package/dist/services/memory.js +13 -9
  95. package/dist/services/officialMarketplaceMirror.js +3 -2
  96. package/dist/services/pluginLoader.d.ts +12 -4
  97. package/dist/services/pluginLoader.js +38 -7
  98. package/dist/services/remoteSettingsService.js +16 -2
  99. package/dist/services/session.d.ts +74 -0
  100. package/dist/services/session.js +144 -3
  101. package/dist/services/sessionEntries.d.ts +2 -0
  102. package/dist/services/sessionEntries.js +20 -0
  103. package/dist/stdio/index.d.ts +3 -1
  104. package/dist/stdio/index.js +3 -1
  105. package/dist/stdio/notificationRouter.js +1 -0
  106. package/dist/stdio/stdioAgent.d.ts +14 -7
  107. package/dist/stdio/stdioAgent.js +19 -0
  108. package/dist/tools/artifactTool.js +406 -273
  109. package/dist/tools/bashTool.js +8 -6
  110. package/dist/tools/editTool.js +6 -3
  111. package/dist/tools/execTool.d.ts +2 -0
  112. package/dist/tools/execTool.js +165 -0
  113. package/dist/tools/grepTool.js +7 -1
  114. package/dist/tools/readTool.js +30 -2
  115. package/dist/tools/types.d.ts +34 -8
  116. package/dist/tools/webFetchTool.js +15 -166
  117. package/dist/tools/workflowTool.js +40 -8
  118. package/dist/tools/writeTool.js +6 -3
  119. package/dist/types/agent.d.ts +20 -5
  120. package/dist/types/configuration.d.ts +39 -1
  121. package/dist/types/marketplace.d.ts +40 -2
  122. package/dist/types/mcp.d.ts +39 -0
  123. package/dist/types/permissions.d.ts +22 -0
  124. package/dist/types/permissions.js +17 -0
  125. package/dist/types/plugins.d.ts +26 -2
  126. package/dist/types/skills.d.ts +15 -0
  127. package/dist/utils/constants.d.ts +10 -0
  128. package/dist/utils/constants.js +10 -0
  129. package/dist/utils/containerSetup.js +43 -0
  130. package/dist/utils/convertMessagesForAPI.d.ts +7 -1
  131. package/dist/utils/convertMessagesForAPI.js +64 -14
  132. package/dist/utils/fileChangeReminder.d.ts +20 -0
  133. package/dist/utils/fileChangeReminder.js +153 -0
  134. package/dist/utils/fileSearch.js +4 -3
  135. package/dist/utils/fileUtils.d.ts +33 -0
  136. package/dist/utils/fileUtils.js +81 -0
  137. package/dist/utils/frontmatterYaml.d.ts +33 -0
  138. package/dist/utils/frontmatterYaml.js +192 -0
  139. package/dist/utils/imageBudget.d.ts +85 -0
  140. package/dist/utils/imageBudget.js +109 -0
  141. package/dist/utils/imageDimensions.d.ts +83 -0
  142. package/dist/utils/imageDimensions.js +232 -0
  143. package/dist/utils/imageProcessor.d.ts +66 -0
  144. package/dist/utils/imageProcessor.js +84 -0
  145. package/dist/utils/imageRewrite.d.ts +29 -0
  146. package/dist/utils/imageRewrite.js +251 -0
  147. package/dist/utils/markdownParser.d.ts +5 -1
  148. package/dist/utils/markdownParser.js +9 -51
  149. package/dist/utils/mcpInstructions.d.ts +61 -0
  150. package/dist/utils/mcpInstructions.js +126 -0
  151. package/dist/utils/mcpUtils.d.ts +7 -0
  152. package/dist/utils/mcpUtils.js +11 -2
  153. package/dist/utils/memoryAge.d.ts +32 -0
  154. package/dist/utils/memoryAge.js +47 -0
  155. package/dist/utils/memoryEntrypoint.d.ts +20 -0
  156. package/dist/utils/memoryEntrypoint.js +49 -0
  157. package/dist/utils/memoryIndex.d.ts +30 -0
  158. package/dist/utils/memoryIndex.js +76 -0
  159. package/dist/utils/messageOperations.d.ts +6 -2
  160. package/dist/utils/messageOperations.js +40 -29
  161. package/dist/utils/nestedMemory.d.ts +22 -0
  162. package/dist/utils/nestedMemory.js +61 -0
  163. package/dist/utils/npmTarball.d.ts +19 -0
  164. package/dist/utils/npmTarball.js +92 -0
  165. package/dist/utils/pluginSource.d.ts +37 -0
  166. package/dist/utils/pluginSource.js +73 -0
  167. package/dist/utils/ripgrep.d.ts +18 -4
  168. package/dist/utils/ripgrep.js +56 -4
  169. package/dist/utils/runtimeDeps.d.ts +35 -0
  170. package/dist/utils/runtimeDeps.js +426 -0
  171. package/dist/utils/skillParser.js +22 -52
  172. package/dist/utils/subagentParser.js +39 -43
  173. package/dist/utils/userSettings.d.ts +90 -0
  174. package/dist/utils/userSettings.js +291 -0
  175. 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
+ }
@@ -1,6 +1,20 @@
1
1
  /**
2
- * Path to the ripgrep binary, provided by the @vscode/ripgrep dependency.
3
- * The wrapper resolves the platform-specific binary package at import time
4
- * via optionalDependencies + os/cpu filtering.
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 { rgPath } from "@vscode/ripgrep";
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;
@@ -1,6 +1,58 @@
1
+ import { createRequire } from "node:module";
1
2
  /**
2
- * Path to the ripgrep binary, provided by the @vscode/ripgrep dependency.
3
- * The wrapper resolves the platform-specific binary package at import time
4
- * via optionalDependencies + os/cpu filtering.
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
- export { rgPath } from "@vscode/ripgrep";
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;