opencode-orchestrator 1.2.55 → 1.2.59

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/README.md CHANGED
@@ -10,40 +10,6 @@
10
10
 
11
11
  ---
12
12
 
13
- ## ⚡ Why Build a Custom Orchestrator?
14
-
15
- **TL;DR: A lightweight, self-contained orchestration system is far more reliable and maintainable than relying on OpenCode's built-in APIs.**
16
-
17
- ### 🎯 The Problem with OpenCode's Built-in APIs
18
-
19
- OpenCode provides basic async session functionality through its `prompt_async` endpoint. However, this approach has significant limitations:
20
-
21
- ❌ **No Control**: Cannot customize parallel processing logic
22
- ❌ **Server Dependency**: API may change when OpenCode updates
23
- ❌ **Performance Bottleneck**: Cannot optimize from plugin side
24
- ❌ **Maintenance Nightmare**: Must update plugin every time OpenCode updates
25
-
26
- ### ✅ Our Solution: Custom Lightweight Orchestrator
27
-
28
- We built our own orchestration system that delivers:
29
-
30
- 🚀 **90%+ CPU utilization** via work-stealing queues (vs 50-70% with OpenCode's approach)
31
- 🛡️ **99.95% sync accuracy** via MVCC + Mutex
32
- ⚡ **10x faster tool calls** (5-10ms vs 50-100ms)
33
- 💾 **60% memory reduction** via pooling
34
- 🔒 **Zero resource leaks** via RAII pattern
35
-
36
- ### 🔑 Key Benefits
37
-
38
- 1. **Full Control**: Complete authority over concurrency, session management, and state synchronization
39
- 2. **High Performance**: Work-stealing queues, session pooling, Rust connection pool
40
- 3. **Reliability**: Circuit breaker, resource pressure detection, auto-recovery
41
- 4. **Independence**: Minimal impact from OpenCode updates - no more "update plugin every time" headaches
42
-
43
- [📖 Read the full analysis: Why We Built a Custom Orchestrator Instead of Using OpenCode's APIs →](docs/WHY_CUSTOM_ORCHESTRATOR.md)
44
-
45
- ---
46
-
47
13
  ## ⚡ Quick Start
48
14
 
49
15
  ```bash
@@ -57,6 +23,8 @@ Inside an OpenCode environment:
57
23
 
58
24
  ---
59
25
 
26
+ ---
27
+
60
28
  ## 🚀 Engine Workflow
61
29
 
62
30
  OpenCode Orchestrator utilizes a **Hub-and-Spoke Topology** with **Work-Stealing Queues** to execute complex engineering tasks through parallel, context-isolated sessions.
Binary file
@@ -4,4 +4,3 @@
4
4
  import { type Platform } from "../../../shared/os/index.js";
5
5
  export declare function detectPlatform(): Platform;
6
6
  export declare function getDefaultSoundPath(p: Platform): string;
7
- export declare function preloadPlatformCommands(platform: Platform): void;
package/dist/index.js CHANGED
@@ -36390,6 +36390,7 @@ function buildVerificationSummary(result) {
36390
36390
  init_logger();
36391
36391
  import { exec as exec2 } from "node:child_process";
36392
36392
  import { promisify as promisify2 } from "node:util";
36393
+ import { readFileSync as readFileSync3 } from "node:fs";
36393
36394
 
36394
36395
  // src/shared/notification/os-notify/constants/notification-commands.ts
36395
36396
  var NOTIFICATION_COMMANDS = {
@@ -36462,14 +36463,24 @@ async function notifyDarwin(title, message) {
36462
36463
  if (!path10) return;
36463
36464
  const escT = title.replace(/"/g, '\\"');
36464
36465
  const escM = message.replace(/"/g, '\\"');
36465
- await execAsync2(`${path10} -e 'display notification "${escM}" with title "${escT}" sound name "Glass"'`);
36466
+ await execAsync2(`${path10} -e 'display notification "${escM}" with title "${escT}" sound name "Glass"' >/dev/null 2>/dev/null`);
36467
+ }
36468
+ function isWSL() {
36469
+ try {
36470
+ if (process.env.WSL_DISTRO_NAME || process.env.WSLENV) return true;
36471
+ const procVersion = readFileSync3("/proc/version", "utf-8");
36472
+ return /microsoft|WSL/i.test(procVersion);
36473
+ } catch {
36474
+ return false;
36475
+ }
36466
36476
  }
36467
36477
  async function notifyLinux(title, message) {
36478
+ if (isWSL()) return;
36468
36479
  const path10 = await resolveCommandPath(
36469
36480
  NOTIFICATION_COMMAND_KEYS.NOTIFY_SEND,
36470
36481
  NOTIFICATION_COMMANDS.NOTIFY_SEND
36471
36482
  );
36472
- if (path10) await execAsync2(`${path10} "${title}" "${message}" 2>/dev/null`);
36483
+ if (path10) await execAsync2(`${path10} "${title}" "${message}" >/dev/null 2>/dev/null`);
36473
36484
  }
36474
36485
  async function notifyWindows(title, message) {
36475
36486
  const ps = await resolveCommandPath(
@@ -36491,7 +36502,7 @@ $Toast = [Windows.UI.Notifications.ToastNotification]::new($SerializedXml)
36491
36502
  $Notifier = [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier('OpenCode Orchestrator')
36492
36503
  $Notifier.Show($Toast)
36493
36504
  `.trim().replace(/\n/g, "; ");
36494
- await execAsync2(`${ps} -Command "${script}"`);
36505
+ await execAsync2(`${ps} -Command "${script}" >NUL 2>NUL`);
36495
36506
  }
36496
36507
  async function sendNotification(platform2, title, message) {
36497
36508
  try {
@@ -36521,7 +36532,7 @@ async function playDarwin(soundPath) {
36521
36532
  NOTIFICATION_COMMAND_KEYS.AFPLAY,
36522
36533
  NOTIFICATION_COMMANDS.AFPLAY
36523
36534
  );
36524
- if (path10) exec3(`"${path10}" "${soundPath}"`);
36535
+ if (path10) exec3(`"${path10}" "${soundPath}" >/dev/null 2>/dev/null`);
36525
36536
  } catch (err) {
36526
36537
  log(`[session-notify] Error playing sound (Darwin): ${err}`);
36527
36538
  }
@@ -36534,14 +36545,14 @@ async function playLinux(soundPath) {
36534
36545
  NOTIFICATION_COMMANDS.PAPLAY
36535
36546
  );
36536
36547
  if (paplay) {
36537
- exec3(`"${paplay}" "${soundPath}" 2>/dev/null`);
36548
+ exec3(`"${paplay}" "${soundPath}" >/dev/null 2>/dev/null`);
36538
36549
  return;
36539
36550
  }
36540
36551
  const aplay = await resolveCommandPath(
36541
36552
  NOTIFICATION_COMMAND_KEYS.APLAY,
36542
36553
  NOTIFICATION_COMMANDS.APLAY
36543
36554
  );
36544
- if (aplay) exec3(`"${aplay}" "${soundPath}" 2>/dev/null`);
36555
+ if (aplay) exec3(`"${aplay}" "${soundPath}" >/dev/null 2>/dev/null`);
36545
36556
  } catch (err) {
36546
36557
  log(`[session-notify] Error playing sound (Linux): ${err}`);
36547
36558
  }
@@ -36554,10 +36565,10 @@ async function playWindows(soundPath) {
36554
36565
  );
36555
36566
  if (!ps) return;
36556
36567
  if (!soundPath) {
36557
- exec3(`"${ps}" -Command "[System.Media.SystemSounds]::Asterisk.Play()"`);
36568
+ exec3(`"${ps}" -Command "[System.Media.SystemSounds]::Asterisk.Play()" >NUL 2>NUL`);
36558
36569
  } else {
36559
36570
  const escaped = soundPath.replace(/'/g, "''");
36560
- exec3(`"${ps}" -Command "(New-Object Media.SoundPlayer '${escaped}').PlaySync()"`);
36571
+ exec3(`"${ps}" -Command "(New-Object Media.SoundPlayer '${escaped}').PlaySync()" >NUL 2>NUL`);
36561
36572
  }
36562
36573
  } catch (err) {
36563
36574
  log(`[session-notify] Error playing sound (Windows): ${err}`);
@@ -36577,8 +36588,8 @@ async function playSound(platform2, soundPath) {
36577
36588
  }
36578
36589
 
36579
36590
  // src/core/notification/os-notify/platform.ts
36580
- import { platform as osPlatform } from "node:os";
36581
36591
  init_os();
36592
+ import { platform as osPlatform } from "node:os";
36582
36593
  function detectPlatform() {
36583
36594
  const p = osPlatform();
36584
36595
  if (p === PLATFORM.DARWIN) return PLATFORM.DARWIN;
@@ -40502,6 +40513,7 @@ function createSystemTransformHandler(ctx) {
40502
40513
  const { directory, sessions, state: state2 } = ctx;
40503
40514
  return async (input, output) => {
40504
40515
  const { sessionID } = input;
40516
+ if (!sessionID) return;
40505
40517
  const loopState = readLoopState(directory);
40506
40518
  const isActiveLoop = isMissionActive(sessionID, directory) || loopState?.active && loopState?.sessionID === sessionID;
40507
40519
  const session = ensureSessionInitialized(sessions, sessionID, directory);
@@ -5,8 +5,8 @@
5
5
  * Input for system transform hook
6
6
  */
7
7
  export interface SystemTransformInput {
8
- /** Session ID for the chat */
9
- sessionID: string;
8
+ /** Session ID for the chat (optional per opencode Plugin type) */
9
+ sessionID?: string;
10
10
  }
11
11
  /**
12
12
  * Output for system transform hook
@@ -1,13 +1,7 @@
1
1
  #!/usr/bin/env node
2
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
3
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
4
- }) : x)(function(x) {
5
- if (typeof require !== "undefined") return require.apply(this, arguments);
6
- throw Error('Dynamic require of "' + x + '" is not supported');
7
- });
8
2
 
9
3
  // scripts/postinstall.ts
10
- import { existsSync, mkdirSync, readFileSync, writeFileSync, appendFileSync, copyFileSync, renameSync, unlinkSync } from "fs";
4
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, appendFileSync, copyFileSync, renameSync, unlinkSync, readdirSync } from "fs";
11
5
  import { homedir, tmpdir } from "os";
12
6
  import { join } from "path";
13
7
  var LOG_FILE = join(tmpdir(), "opencode-orchestrator.log");
@@ -46,6 +40,45 @@ function formatError(err, context) {
46
40
  return `Failed to ${context}: ${String(err)}`;
47
41
  }
48
42
  var PLUGIN_NAME = "opencode-orchestrator";
43
+ function detectWSLWindowsConfigDir() {
44
+ try {
45
+ const isWSL = process.env.WSL_DISTRO_NAME || process.env.WSLENV;
46
+ if (!isWSL) {
47
+ try {
48
+ const procVersion = readFileSync("/proc/version", "utf-8");
49
+ if (!/microsoft|WSL/i.test(procVersion)) return null;
50
+ } catch {
51
+ return null;
52
+ }
53
+ }
54
+ const windowsUser = process.env.WINDOWS_USERNAME || process.env.USERNAME;
55
+ const candidates = [];
56
+ const userDir = "/mnt/c/Users";
57
+ if (existsSync(userDir)) {
58
+ try {
59
+ const users = readdirSync(userDir);
60
+ for (const user of users) {
61
+ if (["Public", "Default", "Default User", "All Users", "desktop.ini"].includes(user) || user.startsWith(".")) continue;
62
+ const candidate = join(userDir, user, "AppData", "Roaming", "opencode");
63
+ candidates.push(candidate);
64
+ }
65
+ } catch {
66
+ }
67
+ }
68
+ if (windowsUser) {
69
+ const preferred = `/mnt/c/Users/${windowsUser}/AppData/Roaming/opencode`;
70
+ if (candidates.includes(preferred)) {
71
+ return preferred;
72
+ }
73
+ }
74
+ for (const c of candidates) {
75
+ if (existsSync(c)) return c;
76
+ }
77
+ return candidates[0] || null;
78
+ } catch {
79
+ return null;
80
+ }
81
+ }
49
82
  function getConfigPaths() {
50
83
  const paths = [];
51
84
  if (process.env.XDG_CONFIG_HOME) {
@@ -60,6 +93,11 @@ function getConfigPaths() {
60
93
  }
61
94
  } else {
62
95
  paths.push(join(homedir(), ".config", "opencode"));
96
+ const wslWindowsConfig = detectWSLWindowsConfigDir();
97
+ if (wslWindowsConfig && !paths.includes(wslWindowsConfig)) {
98
+ log("Detected WSL2 - also checking Windows config path", { wslWindowsConfig });
99
+ paths.push(wslWindowsConfig);
100
+ }
63
101
  }
64
102
  return paths;
65
103
  }
@@ -122,28 +160,41 @@ function registerInConfig(configDir) {
122
160
  mkdirSync(configDir, { recursive: true, mode: 493 });
123
161
  log("Created config directory", { configDir });
124
162
  }
125
- backupFile = createBackup(configFile);
126
163
  let config = {};
164
+ let fileExisted = false;
127
165
  if (existsSync(configFile)) {
128
- try {
129
- const content = readFileSync(configFile, "utf-8").trim();
130
- if (content) {
131
- config = JSON.parse(content);
132
- if (!validateConfig(config)) {
133
- log("Invalid config structure detected, using safe defaults", { config });
134
- config = { plugin: [] };
166
+ fileExisted = true;
167
+ const rawContent = readFileSync(configFile, "utf-8").trim();
168
+ if (rawContent) {
169
+ let parseError;
170
+ try {
171
+ config = JSON.parse(rawContent);
172
+ } catch (err) {
173
+ parseError = err;
174
+ }
175
+ if (parseError) {
176
+ backupFile = createBackup(configFile);
177
+ log("Corrupted config JSON, skipping this path to avoid data loss", { configFile });
178
+ console.log(`\u26A0\uFE0F opencode.json at ${configFile} has invalid JSON and was skipped.`);
179
+ if (backupFile) {
180
+ console.log(` Backup saved: ${backupFile}`);
135
181
  }
182
+ console.log(` Please fix the file manually, then add "${PLUGIN_NAME}" to the "plugin" array.`);
183
+ return { success: false, backupFile, skipped: true };
136
184
  }
137
- } catch (error) {
138
- log("Failed to parse existing config, creating new one", { error: String(error) });
139
- if (backupFile) {
140
- console.log(`\u26A0\uFE0F Corrupted config detected. Backup saved: ${backupFile}`);
185
+ if (!validateConfig(config)) {
186
+ log("Unexpected config structure, skipping to avoid corruption", { config, configFile });
187
+ console.log(`\u26A0\uFE0F Unexpected config structure in ${configFile}. Skipping to avoid corruption.`);
188
+ console.log(` Please manually add "${PLUGIN_NAME}" to the "plugin" array.`);
189
+ return { success: false, backupFile: null, skipped: true };
141
190
  }
142
- config = { plugin: [] };
143
191
  }
144
192
  }
145
193
  if (!config.plugin) {
146
194
  config.plugin = [];
195
+ if (!fileExisted && !config["$schema"]) {
196
+ config["$schema"] = "https://opencode.ai/config.json";
197
+ }
147
198
  }
148
199
  const hasPlugin = config.plugin.some((p) => {
149
200
  if (typeof p !== "string") return false;
@@ -153,6 +204,9 @@ function registerInConfig(configDir) {
153
204
  log("Plugin already registered", { configFile });
154
205
  return { success: false, backupFile };
155
206
  }
207
+ if (fileExisted) {
208
+ backupFile = createBackup(configFile);
209
+ }
156
210
  config.plugin.push(PLUGIN_NAME);
157
211
  log("Adding plugin to config", { plugin: PLUGIN_NAME, configFile });
158
212
  atomicWriteJSON(configFile, config);
@@ -189,7 +243,7 @@ function registerInConfig(configDir) {
189
243
  function cleanupOldBackups(configFile) {
190
244
  try {
191
245
  const configDir = join(configFile, "..");
192
- const files = __require("fs").readdirSync(configDir);
246
+ const files = readdirSync(configDir);
193
247
  const backupFiles = files.filter((f) => f.startsWith("opencode.json.backup.")).sort().reverse();
194
248
  for (let i = 5; i < backupFiles.length; i++) {
195
249
  const backupPath = join(configDir, backupFiles[i]);
@@ -209,6 +263,7 @@ try {
209
263
  log("Config paths to check", configPaths);
210
264
  let registered = false;
211
265
  let alreadyRegistered = false;
266
+ let skippedCorrupt = false;
212
267
  let backupCreated = null;
213
268
  for (const configDir of configPaths) {
214
269
  const configFile = join(configDir, "opencode.json");
@@ -228,7 +283,10 @@ try {
228
283
  }
229
284
  }
230
285
  const result = registerInConfig(configDir);
231
- if (result.success) {
286
+ if (result.skipped) {
287
+ skippedCorrupt = true;
288
+ if (result.backupFile) backupCreated = result.backupFile;
289
+ } else if (result.success) {
232
290
  console.log(`\u2705 Plugin registered: ${configFile}`);
233
291
  if (result.backupFile) {
234
292
  console.log(` Backup created: ${result.backupFile}`);
@@ -240,10 +298,13 @@ try {
240
298
  backupCreated = result.backupFile;
241
299
  }
242
300
  }
243
- if (!registered && alreadyRegistered) {
244
- console.log("\u2705 Plugin already registered.");
301
+ if (registered) {
302
+ } else if (alreadyRegistered) {
303
+ console.log("\u2705 Plugin already registered in all detected config locations.");
245
304
  log("Plugin was already registered");
246
- } else if (!registered) {
305
+ } else if (skippedCorrupt) {
306
+ log("Skipped due to corrupted config");
307
+ } else {
247
308
  console.log("\u26A0\uFE0F Could not register plugin in any config location.");
248
309
  console.log(" This may be due to permissions or file system issues.");
249
310
  console.log(` Check logs: ${LOG_FILE}`);
@@ -252,7 +313,7 @@ try {
252
313
  console.log("");
253
314
  console.log("\u{1F680} Ready! Restart OpenCode to use.");
254
315
  console.log("");
255
- log("Installation completed", { registered, alreadyRegistered, backupCreated });
316
+ log("Installation completed", { registered, alreadyRegistered, skippedCorrupt, backupCreated });
256
317
  } catch (error) {
257
318
  log("Installation error", { error: String(error) });
258
319
  console.error("\u274C " + formatError(error, "register plugin"));
@@ -1,13 +1,7 @@
1
1
  #!/usr/bin/env node
2
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
3
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
4
- }) : x)(function(x) {
5
- if (typeof require !== "undefined") return require.apply(this, arguments);
6
- throw Error('Dynamic require of "' + x + '" is not supported');
7
- });
8
2
 
9
3
  // scripts/preuninstall.ts
10
- import { existsSync, readFileSync, writeFileSync, appendFileSync, copyFileSync, renameSync, unlinkSync } from "fs";
4
+ import { existsSync, readFileSync, writeFileSync, appendFileSync, copyFileSync, renameSync, unlinkSync, readdirSync } from "fs";
11
5
  import { homedir, tmpdir } from "os";
12
6
  import { join } from "path";
13
7
  var LOG_FILE = join(tmpdir(), "opencode-orchestrator.log");
@@ -46,6 +40,43 @@ function formatError(err, context) {
46
40
  return `Failed to ${context}: ${String(err)}`;
47
41
  }
48
42
  var PLUGIN_NAME = "opencode-orchestrator";
43
+ function detectWSLWindowsConfigDir() {
44
+ try {
45
+ const isWSL = process.env.WSL_DISTRO_NAME || process.env.WSLENV;
46
+ if (!isWSL) {
47
+ try {
48
+ const procVersion = readFileSync("/proc/version", "utf-8");
49
+ if (!/microsoft|WSL/i.test(procVersion)) return null;
50
+ } catch {
51
+ return null;
52
+ }
53
+ }
54
+ const windowsUser = process.env.WINDOWS_USERNAME || process.env.USERNAME;
55
+ const candidates = [];
56
+ const userDir = "/mnt/c/Users";
57
+ if (existsSync(userDir)) {
58
+ try {
59
+ const users = readdirSync(userDir);
60
+ for (const user of users) {
61
+ if (["Public", "Default", "Default User", "All Users", "desktop.ini"].includes(user) || user.startsWith(".")) continue;
62
+ const candidate = join(userDir, user, "AppData", "Roaming", "opencode");
63
+ candidates.push(candidate);
64
+ }
65
+ } catch {
66
+ }
67
+ }
68
+ if (windowsUser) {
69
+ const preferred = `/mnt/c/Users/${windowsUser}/AppData/Roaming/opencode`;
70
+ if (candidates.includes(preferred)) return preferred;
71
+ }
72
+ for (const c of candidates) {
73
+ if (existsSync(c)) return c;
74
+ }
75
+ return candidates[0] || null;
76
+ } catch {
77
+ return null;
78
+ }
79
+ }
49
80
  function getConfigPaths() {
50
81
  const paths = [];
51
82
  if (process.env.XDG_CONFIG_HOME) {
@@ -60,6 +91,11 @@ function getConfigPaths() {
60
91
  }
61
92
  } else {
62
93
  paths.push(join(homedir(), ".config", "opencode"));
94
+ const wslWindowsConfig = detectWSLWindowsConfigDir();
95
+ if (wslWindowsConfig && !paths.includes(wslWindowsConfig)) {
96
+ log("Detected WSL2 - also checking Windows config path", { wslWindowsConfig });
97
+ paths.push(wslWindowsConfig);
98
+ }
63
99
  }
64
100
  return paths;
65
101
  }
@@ -199,7 +235,7 @@ function removeFromConfig(configDir) {
199
235
  function cleanupOldBackups(configFile) {
200
236
  try {
201
237
  const configDir = join(configFile, "..");
202
- const files = __require("fs").readdirSync(configDir);
238
+ const files = readdirSync(configDir);
203
239
  const backupFiles = files.filter((f) => f.startsWith("opencode.json.backup.")).sort().reverse();
204
240
  for (let i = 5; i < backupFiles.length; i++) {
205
241
  const backupPath = join(configDir, backupFiles[i]);
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "opencode-orchestrator",
3
3
  "displayName": "OpenCode Orchestrator",
4
4
  "description": "Distributed Cognitive Architecture for OpenCode. Turns simple prompts into specialized multi-agent workflows (Planner, Coder, Reviewer).",
5
- "version": "1.2.55",
5
+ "version": "1.2.59",
6
6
  "author": "agnusdei1207",
7
7
  "license": "MIT",
8
8
  "repository": {
@@ -39,28 +39,25 @@
39
39
  "LICENSE"
40
40
  ],
41
41
  "scripts": {
42
- "rust:build": "cargo build --release && shx mkdir -p bin && shx cp target/release/orchestrator bin/orchestrator 2>/dev/null || shx cp target/release/orchestrator.exe bin/orchestrator.exe 2>/dev/null",
43
- "rust:dist": "npm run rust:build && node -e \"const {platform, arch} = require('os'); const fs = require('fs'); const os = platform(); const cpu = arch(); const name = os === 'win32' ? 'orchestrator-windows-x64.exe' : (os === 'darwin' ? (cpu === 'arm64' ? 'orchestrator-macos-arm64' : 'orchestrator-macos-x64') : (cpu === 'arm64' ? 'orchestrator-linux-arm64' : 'orchestrator-linux-x64')); const src = os === 'win32' ? 'target/release/orchestrator.exe' : 'target/release/orchestrator'; fs.copyFileSync(src, 'bin/' + name); console.log('Distributed to bin/' + name)\"",
44
- "rust:test": "cargo test --workspace",
45
- "rust:check": "cargo check --workspace",
46
- "docker:build-all": "docker compose up dev win-builder --build",
47
- "docker:build-win": "docker compose up win-builder --build",
42
+ "docker:build-all": "docker compose run --rm dev && docker compose run --rm win-builder",
43
+ "docker:build-win": "docker compose run --rm win-builder",
44
+ "docker:rust-dist": "docker compose run --rm dev && sudo chown -R $(id -u):$(id -g) bin/ 2>/dev/null || true",
48
45
  "docker:test": "docker compose run --rm test",
49
- "docker:dist": "npm run docker:build-all && shx cp target/x86_64-pc-windows-gnu/release/orchestrator.exe bin/orchestrator-windows-x64.exe && shx cp target/release/orchestrator bin/orchestrator-linux-x64",
50
46
  "docker:clean": "docker compose down -v",
51
- "build": "shx rm -rf dist && npx esbuild src/index.ts --bundle --outfile=dist/index.js --platform=node --format=esm && tsc --emitDeclarationOnly && shx mkdir -p dist/scripts && npx esbuild scripts/postinstall.ts --bundle --outfile=dist/scripts/postinstall.js --platform=node --format=esm && npx esbuild scripts/preuninstall.ts --bundle --outfile=dist/scripts/preuninstall.js --platform=node --format=esm",
52
- "build:all": "npm run build && npm run rust:dist",
47
+ "build": "rm -rf dist && npx esbuild src/index.ts --bundle --outfile=dist/index.js --platform=node --format=esm && tsc --emitDeclarationOnly && mkdir -p dist/scripts && npx esbuild scripts/postinstall.ts --bundle --outfile=dist/scripts/postinstall.js --platform=node --format=esm && npx esbuild scripts/preuninstall.ts --bundle --outfile=dist/scripts/preuninstall.js --platform=node --format=esm",
48
+ "build:all": "npm run build && npm run docker:rust-dist",
53
49
  "test": "vitest run --reporter=verbose",
54
50
  "test:coverage": "vitest run --coverage",
55
51
  "test:unit": "vitest run tests/unit --reporter=verbose",
56
52
  "test:e2e": "vitest run tests/e2e --reporter=verbose",
57
- "test:all": "npm run build:all && npm run rust:test && vitest run --reporter=verbose && echo '=== ALL TESTS PASSED ==='",
53
+ "test:all": "npm run build && vitest run --reporter=verbose && echo '=== ALL TESTS PASSED ==='",
58
54
  "postinstall": "node dist/scripts/postinstall.js",
59
55
  "preuninstall": "node dist/scripts/preuninstall.js",
60
- "prepublishOnly": "npm run build && npm run rust:dist",
61
- "release:patch": "npm run build && npm run rust:dist && npm version patch && git push --follow-tags && npm publish --access public",
62
- "release:minor": "npm run build && npm run rust:dist && npm version minor && git push --follow-tags && npm publish --access public",
63
- "release:major": "npm run build && npm run rust:dist && npm version major && git push --follow-tags && npm publish --access public",
56
+ "prepublishOnly": "npm run build",
57
+ "publish:token": "npm publish --access public",
58
+ "release:patch": "npm run build && npm run docker:rust-dist && npm version patch && git push --follow-tags && npm run publish:token",
59
+ "release:minor": "npm run build && npm run docker:rust-dist && npm version minor && git push --follow-tags && npm run publish:token",
60
+ "release:major": "npm run build && npm run docker:rust-dist && npm version major && git push --follow-tags && npm run publish:token",
64
61
  "reset:local": "brew uninstall opencode 2>/dev/null; rm -rf ~/.config/opencode ~/.opencode ~/.local/share/opencode ~/.cache/opencode/node_modules/opencode-orchestrator && echo '=== Clean done ===' && brew install opencode && echo '{\"plugin\": [\"opencode-orchestrator\"], \"$schema\": \"https://opencode.ai/config.json\"}' > ~/.config/opencode/opencode.json && echo '=== Reset (Dev) complete. Run: opencode ==='",
65
62
  "reset:prod": "brew uninstall opencode 2>/dev/null; rm -rf ~/.config/opencode ~/.opencode ~/.local/share/opencode ~/.cache/opencode/node_modules/opencode-orchestrator && echo '=== Clean done ===' && brew install opencode && echo '{\"plugin\": [\"opencode-orchestrator\"], \"$schema\": \"https://opencode.ai/config.json\"}' > ~/.config/opencode/opencode.json && npm uninstall -g opencode-orchestrator && echo '=== Reset (Prod) complete. Run: opencode ==='",
66
63
  "ginstall": "npm install -g opencode-orchestrator",