opencode-swarm 7.0.2 → 7.0.3
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/cli/index.js +890 -548
- package/dist/config/index.d.ts +1 -1
- package/dist/config/loader.d.ts +8 -0
- package/dist/hooks/__tests__/repo-graph-builder.test.d.ts +12 -0
- package/dist/hooks/repo-graph-builder.d.ts +13 -1
- package/dist/index.js +1828 -1331
- package/dist/tools/__tests__/repo-graph-walk.test.d.ts +16 -0
- package/dist/tools/repo-graph.d.ts +16 -2
- package/dist/utils/__tests__/bun-compat.test.d.ts +11 -0
- package/dist/utils/__tests__/timeout.test.d.ts +12 -0
- package/dist/utils/bun-compat.d.ts +106 -0
- package/dist/utils/timeout.d.ts +23 -0
- package/package.json +3 -2
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression coverage for the repo-graph walker (issue #704).
|
|
3
|
+
*
|
|
4
|
+
* Each test installs a fresh fixture under a tmp dir and asserts the walker
|
|
5
|
+
* cannot be tricked into:
|
|
6
|
+
* - infinite recursion via symlink cycles,
|
|
7
|
+
* - exceeding the wall-clock budget on a slow filesystem,
|
|
8
|
+
* - exceeding the file cap during traversal (vs. post-truncation),
|
|
9
|
+
* - scanning a refused top-level workspace root.
|
|
10
|
+
*
|
|
11
|
+
* Symlink-loop coverage is POSIX-only; the test bails on Windows because
|
|
12
|
+
* creating a directory symlink there requires Developer Mode. The walker's
|
|
13
|
+
* cycle defense itself is platform-agnostic — see `seenRealPaths` in
|
|
14
|
+
* src/tools/repo-graph.ts.
|
|
15
|
+
*/
|
|
16
|
+
export {};
|
|
@@ -206,10 +206,24 @@ export declare function saveIfDirty(workspace: string): Promise<void>;
|
|
|
206
206
|
* @returns Complete RepoGraph with nodes and edges
|
|
207
207
|
* @throws Error if workspace validation fails
|
|
208
208
|
*/
|
|
209
|
-
export
|
|
209
|
+
export interface BuildWorkspaceGraphOptions {
|
|
210
210
|
maxFileSizeBytes?: number;
|
|
211
211
|
maxFiles?: number;
|
|
212
|
-
|
|
212
|
+
walkBudgetMs?: number;
|
|
213
|
+
followSymlinks?: boolean;
|
|
214
|
+
}
|
|
215
|
+
export declare function buildWorkspaceGraph(workspaceRoot: string, options?: BuildWorkspaceGraphOptions): RepoGraph;
|
|
216
|
+
/**
|
|
217
|
+
* Async, event-loop-safe variant of `buildWorkspaceGraph`. The traversal
|
|
218
|
+
* yields between batches and uses async fs primitives, so callers can run
|
|
219
|
+
* this from plugin init without freezing the host while a large workspace
|
|
220
|
+
* is scanned. The per-file processing remains sync — it is CPU-bound symbol
|
|
221
|
+
* extraction, and the existing per-file caps already prevent runaway work.
|
|
222
|
+
*
|
|
223
|
+
* Returned shape matches `buildWorkspaceGraph`. Same homedir guard, same
|
|
224
|
+
* bounded walk behavior, same deterministic file order.
|
|
225
|
+
*/
|
|
226
|
+
export declare function buildWorkspaceGraphAsync(workspaceRoot: string, options?: BuildWorkspaceGraphOptions): Promise<RepoGraph>;
|
|
213
227
|
/**
|
|
214
228
|
* Incrementally update the graph for a set of changed files.
|
|
215
229
|
* Re-scans only the specified files, updates their nodes and edges,
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bun-compat shim tests (issue #704).
|
|
3
|
+
*
|
|
4
|
+
* Each public surface is exercised against the live runtime. When running
|
|
5
|
+
* under Bun the shim delegates to the native `Bun.*` primitives; when running
|
|
6
|
+
* under Node the shim's fallback path is exercised. The test only asserts the
|
|
7
|
+
* observable contract (text equality, written byte count, exit code parity)
|
|
8
|
+
* — it does not lock in implementation details that legitimately differ
|
|
9
|
+
* between the two paths.
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timeout-helper tests (issue #704).
|
|
3
|
+
*
|
|
4
|
+
* The plugin init path uses `withTimeout` to bound the snapshot rehydration
|
|
5
|
+
* read so a slow filesystem cannot pin the host's `await server(...)`. The
|
|
6
|
+
* helper must:
|
|
7
|
+
* - resolve to the racer's value when the racer wins,
|
|
8
|
+
* - reject with the supplied error when the deadline elapses,
|
|
9
|
+
* - clear its timer in `finally` (no leak that holds the loop open),
|
|
10
|
+
* - never throw synchronously.
|
|
11
|
+
*/
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime-portability shim for the small set of `Bun.*` APIs we depend on.
|
|
3
|
+
*
|
|
4
|
+
* Why this exists: the plugin entry (`src/index.ts`) is bundled with
|
|
5
|
+
* `--target node`, but the source tree calls `Bun.file`, `Bun.write`,
|
|
6
|
+
* `Bun.spawn`, `Bun.spawnSync`, and `Bun.hash` directly. OpenCode's plugin
|
|
7
|
+
* host explicitly supports running plugins under Node (its own `PluginInput`
|
|
8
|
+
* uses `$: typeof Bun === "undefined" ? undefined : Bun.$`). On the OpenCode
|
|
9
|
+
* Desktop sidecar, plugins may execute under Node — every direct `Bun.*`
|
|
10
|
+
* reference would throw `ReferenceError: Bun is not defined`.
|
|
11
|
+
*
|
|
12
|
+
* This module funnels all such calls through a small set of helpers that
|
|
13
|
+
* detect the runtime once and dispatch to either the Bun primitive or a
|
|
14
|
+
* Node fallback. The fallbacks are deliberately small — they implement
|
|
15
|
+
* exactly the surface our callers use, no more.
|
|
16
|
+
*
|
|
17
|
+
* Cross-platform notes:
|
|
18
|
+
* - `bunWrite` performs an atomic write via temp+rename on the Node path,
|
|
19
|
+
* mirroring Bun's atomic semantics. Includes a Windows EEXIST retry loop
|
|
20
|
+
* because rename can race with file-handle release on Windows.
|
|
21
|
+
* - `bunSpawn` and `bunSpawnSync` use `node:child_process` and translate
|
|
22
|
+
* Bun's option shape into Node's. Stdout/stderr capture is wired so
|
|
23
|
+
* callers see the same `text()`/`stdout` shape regardless of runtime.
|
|
24
|
+
* - `bunHash` uses Node's `xxhash` via `Bun.hash`'s default algorithm when
|
|
25
|
+
* present and falls back to a stable djb2-derived 32-bit hash on Node.
|
|
26
|
+
*/
|
|
27
|
+
/**
|
|
28
|
+
* Whether the current runtime is Bun. Cached at first call — every subsequent
|
|
29
|
+
* call is a single property access.
|
|
30
|
+
*/
|
|
31
|
+
export declare function isBun(): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Bun.file / fs read shim. Returns an object exposing the subset of
|
|
34
|
+
* `BunFile` methods used in this codebase: `text()`, `arrayBuffer()`,
|
|
35
|
+
* `exists()`, and `size`.
|
|
36
|
+
*
|
|
37
|
+
* On Bun, this is a thin wrapper around `Bun.file()` so callers see
|
|
38
|
+
* identical semantics. On Node, the methods read the file lazily.
|
|
39
|
+
*/
|
|
40
|
+
export interface BunCompatFile {
|
|
41
|
+
text(): Promise<string>;
|
|
42
|
+
arrayBuffer(): Promise<ArrayBuffer>;
|
|
43
|
+
exists(): Promise<boolean>;
|
|
44
|
+
readonly size: number;
|
|
45
|
+
}
|
|
46
|
+
export declare function bunFile(filePath: string): BunCompatFile;
|
|
47
|
+
/**
|
|
48
|
+
* Atomic file write. On Bun this delegates to `Bun.write`. On Node we write
|
|
49
|
+
* to a temp file in the same directory and rename atomically — the same
|
|
50
|
+
* semantics every existing call site already expects via Bun.write.
|
|
51
|
+
*/
|
|
52
|
+
export declare function bunWrite(filePath: string, data: string | Uint8Array | ArrayBuffer | ArrayBufferView): Promise<number>;
|
|
53
|
+
/**
|
|
54
|
+
* Stable 32-bit hash. Bun's `Bun.hash` uses xxHash64 by default; on Node we
|
|
55
|
+
* fall back to a 32-bit djb2 hash — identical hashes are NOT guaranteed
|
|
56
|
+
* across runtimes, so callers should not rely on cross-runtime hash equality
|
|
57
|
+
* (no current caller does — every `Bun.hash` use is in-process state keying
|
|
58
|
+
* or a same-runtime cache key).
|
|
59
|
+
*/
|
|
60
|
+
export declare function bunHash(input: string | ArrayBufferView | ArrayBuffer): bigint;
|
|
61
|
+
/**
|
|
62
|
+
* Process spawn. Bun's `Bun.spawn` returns an object with `exited`, `stdout`,
|
|
63
|
+
* `stderr` etc. We expose a minimal compatible surface that callers actually
|
|
64
|
+
* use: `exited`, `exitCode`, and `stdout`/`stderr` as `ReadableStream`-like
|
|
65
|
+
* objects with `text()` and `bytes()` methods.
|
|
66
|
+
*/
|
|
67
|
+
export interface BunCompatSpawnOptions {
|
|
68
|
+
cwd?: string;
|
|
69
|
+
env?: Record<string, string | undefined>;
|
|
70
|
+
stdin?: 'inherit' | 'ignore' | 'pipe';
|
|
71
|
+
stdout?: 'inherit' | 'ignore' | 'pipe';
|
|
72
|
+
stderr?: 'inherit' | 'ignore' | 'pipe';
|
|
73
|
+
timeout?: number;
|
|
74
|
+
}
|
|
75
|
+
export interface BunCompatStream {
|
|
76
|
+
text(): Promise<string>;
|
|
77
|
+
bytes(): Promise<Uint8Array>;
|
|
78
|
+
/**
|
|
79
|
+
* Returns a Web ReadableStream reader for incremental, bounded
|
|
80
|
+
* consumption — matches the Bun runtime's `proc.stdout.getReader()`
|
|
81
|
+
* shape, used by the test-runner's `readBoundedStream` to cap memory
|
|
82
|
+
* for multi-GB test output.
|
|
83
|
+
*/
|
|
84
|
+
getReader(): ReadableStreamDefaultReader<Uint8Array>;
|
|
85
|
+
}
|
|
86
|
+
export interface BunCompatSubprocess {
|
|
87
|
+
readonly stdout: BunCompatStream;
|
|
88
|
+
readonly stderr: BunCompatStream;
|
|
89
|
+
readonly exited: Promise<number>;
|
|
90
|
+
exitCode: number | null;
|
|
91
|
+
kill(signal?: NodeJS.Signals | number): void;
|
|
92
|
+
}
|
|
93
|
+
export declare function bunSpawn(cmd: string[], options?: BunCompatSpawnOptions): BunCompatSubprocess;
|
|
94
|
+
export interface BunCompatSyncResult {
|
|
95
|
+
stdout: Uint8Array;
|
|
96
|
+
stderr: Uint8Array;
|
|
97
|
+
exitCode: number;
|
|
98
|
+
success: boolean;
|
|
99
|
+
}
|
|
100
|
+
export declare function bunSpawnSync(cmd: string[] | {
|
|
101
|
+
cmd: string[];
|
|
102
|
+
cwd?: string;
|
|
103
|
+
env?: Record<string, string | undefined>;
|
|
104
|
+
stdin?: string | Uint8Array;
|
|
105
|
+
timeout?: number;
|
|
106
|
+
}, options?: BunCompatSpawnOptions): BunCompatSyncResult;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timeout primitives used by the plugin init path.
|
|
3
|
+
*
|
|
4
|
+
* Hard rule: every timer scheduled here must call `unref()` so it never holds
|
|
5
|
+
* the process open, and every Promise.race must clear the timer in `finally`
|
|
6
|
+
* so it does not leak the timer reference after the racer settles.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Race a promise against a timeout. The timer is cleared in `finally` so the
|
|
10
|
+
* Node event loop is not pinned open after the race resolves. The returned
|
|
11
|
+
* promise resolves to the racer's value, or rejects with the supplied
|
|
12
|
+
* `timeoutError` if the deadline elapses first.
|
|
13
|
+
*
|
|
14
|
+
* @param promise Long-running operation to race.
|
|
15
|
+
* @param ms Deadline in milliseconds.
|
|
16
|
+
* @param timeoutError Error thrown when the deadline elapses.
|
|
17
|
+
*/
|
|
18
|
+
export declare function withTimeout<T>(promise: Promise<T>, ms: number, timeoutError: Error): Promise<T>;
|
|
19
|
+
/**
|
|
20
|
+
* Yield to the macrotask queue. Works under both Node and Bun runtimes,
|
|
21
|
+
* unlike `setImmediate` which is Node-only.
|
|
22
|
+
*/
|
|
23
|
+
export declare function yieldToEventLoop(): Promise<void>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-swarm",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.3",
|
|
4
4
|
"description": "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -45,7 +45,8 @@
|
|
|
45
45
|
"format": "biome format . --write",
|
|
46
46
|
"check": "biome check --write .",
|
|
47
47
|
"dev": "bun run build && opencode",
|
|
48
|
-
"prepublishOnly": "bun run build"
|
|
48
|
+
"prepublishOnly": "bun run build",
|
|
49
|
+
"repro:704": "node scripts/repro-704.mjs"
|
|
49
50
|
},
|
|
50
51
|
"dependencies": {
|
|
51
52
|
"@opencode-ai/plugin": "^1.1.53",
|