opencode-pilot 0.9.2 → 0.10.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/README.md +1 -0
- package/examples/config.yaml +5 -0
- package/package.json +1 -1
- package/service/actions.js +37 -11
- package/service/repo-config.js +9 -0
- package/test/unit/actions.test.js +3 -3
package/README.md
CHANGED
|
@@ -45,6 +45,7 @@ See [examples/config.yaml](examples/config.yaml) for a complete example with all
|
|
|
45
45
|
|
|
46
46
|
### Key Sections
|
|
47
47
|
|
|
48
|
+
- **`server_port`** - Preferred OpenCode server port (e.g., `4096`). When multiple OpenCode instances are running, pilot attaches sessions to this port.
|
|
48
49
|
- **`defaults`** - Default values applied to all sources
|
|
49
50
|
- **`sources`** - What to poll (presets, shorthand, or full config)
|
|
50
51
|
- **`tools`** - Field mappings to normalize different MCP APIs
|
package/examples/config.yaml
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
# Example config.yaml for opencode-pilot
|
|
2
2
|
# Copy to ~/.config/opencode-pilot/config.yaml
|
|
3
3
|
|
|
4
|
+
# Preferred OpenCode server port for attaching sessions
|
|
5
|
+
# When multiple OpenCode instances are running, pilot will attach new sessions
|
|
6
|
+
# to this port. If not set, pilot discovers servers automatically.
|
|
7
|
+
# server_port: 4096
|
|
8
|
+
|
|
4
9
|
defaults:
|
|
5
10
|
agent: plan
|
|
6
11
|
prompt: default
|
package/package.json
CHANGED
package/service/actions.js
CHANGED
|
@@ -9,6 +9,7 @@ import { spawn, execSync } from "child_process";
|
|
|
9
9
|
import { readFileSync, existsSync } from "fs";
|
|
10
10
|
import { debug } from "./logger.js";
|
|
11
11
|
import { getNestedValue } from "./utils.js";
|
|
12
|
+
import { getServerPort } from "./repo-config.js";
|
|
12
13
|
import path from "path";
|
|
13
14
|
import os from "os";
|
|
14
15
|
|
|
@@ -99,23 +100,27 @@ function isServerHealthy(project) {
|
|
|
99
100
|
* Discover a running opencode server that matches the target directory
|
|
100
101
|
*
|
|
101
102
|
* Queries all running opencode servers and finds the best match based on:
|
|
102
|
-
* 1.
|
|
103
|
-
* 2. Exact
|
|
104
|
-
* 3.
|
|
103
|
+
* 1. Configured server_port (highest priority if set and healthy)
|
|
104
|
+
* 2. Exact sandbox match
|
|
105
|
+
* 3. Exact worktree match
|
|
106
|
+
* 4. Target is subdirectory of worktree
|
|
107
|
+
* 5. Global server (worktree="/") as fallback
|
|
105
108
|
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
+
* Global servers are used as a fallback when no project-specific match is found,
|
|
110
|
+
* since OpenCode Desktop may be connected to a global server that can display
|
|
111
|
+
* sessions for any project.
|
|
109
112
|
*
|
|
110
113
|
* @param {string} targetDir - The directory we want to work in
|
|
111
114
|
* @param {object} [options] - Options for testing/mocking
|
|
112
115
|
* @param {function} [options.getPorts] - Function to get server ports
|
|
113
116
|
* @param {function} [options.fetch] - Function to fetch URLs
|
|
117
|
+
* @param {number} [options.preferredPort] - Preferred port to use (overrides config)
|
|
114
118
|
* @returns {Promise<string|null>} Server URL (e.g., "http://localhost:4096") or null
|
|
115
119
|
*/
|
|
116
120
|
export async function discoverOpencodeServer(targetDir, options = {}) {
|
|
117
121
|
const getPorts = options.getPorts || getOpencodePorts;
|
|
118
122
|
const fetchFn = options.fetch || fetch;
|
|
123
|
+
const preferredPort = options.preferredPort ?? getServerPort();
|
|
119
124
|
|
|
120
125
|
const ports = await getPorts();
|
|
121
126
|
if (ports.length === 0) {
|
|
@@ -123,10 +128,28 @@ export async function discoverOpencodeServer(targetDir, options = {}) {
|
|
|
123
128
|
return null;
|
|
124
129
|
}
|
|
125
130
|
|
|
126
|
-
debug(`discoverOpencodeServer: checking ${ports.length} servers for ${targetDir}`);
|
|
131
|
+
debug(`discoverOpencodeServer: checking ${ports.length} servers for ${targetDir}, preferredPort=${preferredPort}`);
|
|
132
|
+
|
|
133
|
+
// If preferred port is configured and running, check it first
|
|
134
|
+
if (preferredPort && ports.includes(preferredPort)) {
|
|
135
|
+
const url = `http://localhost:${preferredPort}`;
|
|
136
|
+
try {
|
|
137
|
+
const response = await fetchFn(`${url}/project/current`);
|
|
138
|
+
if (response.ok) {
|
|
139
|
+
const project = await response.json();
|
|
140
|
+
if (isServerHealthy(project)) {
|
|
141
|
+
debug(`discoverOpencodeServer: using preferred port ${preferredPort}`);
|
|
142
|
+
return url;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
} catch (err) {
|
|
146
|
+
debug(`discoverOpencodeServer: preferred port ${preferredPort} error: ${err.message}`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
127
149
|
|
|
128
150
|
let bestMatch = null;
|
|
129
151
|
let bestScore = 0;
|
|
152
|
+
let globalServer = null;
|
|
130
153
|
|
|
131
154
|
for (const port of ports) {
|
|
132
155
|
const url = `http://localhost:${port}`;
|
|
@@ -148,9 +171,10 @@ export async function discoverOpencodeServer(targetDir, options = {}) {
|
|
|
148
171
|
const worktree = project.worktree || '/';
|
|
149
172
|
const sandboxes = project.sandboxes || [];
|
|
150
173
|
|
|
151
|
-
//
|
|
174
|
+
// Track global server as fallback (but prefer project-specific matches)
|
|
152
175
|
if (worktree === '/') {
|
|
153
|
-
debug(`discoverOpencodeServer: ${url} is global project,
|
|
176
|
+
debug(`discoverOpencodeServer: ${url} is global project, tracking as fallback`);
|
|
177
|
+
globalServer = url;
|
|
154
178
|
continue;
|
|
155
179
|
}
|
|
156
180
|
|
|
@@ -166,8 +190,10 @@ export async function discoverOpencodeServer(targetDir, options = {}) {
|
|
|
166
190
|
}
|
|
167
191
|
}
|
|
168
192
|
|
|
169
|
-
|
|
170
|
-
|
|
193
|
+
// Use project-specific match if found, otherwise fall back to global server
|
|
194
|
+
const result = bestMatch || globalServer;
|
|
195
|
+
debug(`discoverOpencodeServer: best match=${bestMatch} score=${bestScore}, global=${globalServer}, using=${result}`);
|
|
196
|
+
return result;
|
|
171
197
|
}
|
|
172
198
|
|
|
173
199
|
// Default templates directory
|
package/service/repo-config.js
CHANGED
|
@@ -310,6 +310,15 @@ export function getCleanupTtlDays() {
|
|
|
310
310
|
return config?.cleanup?.ttl_days ?? 30;
|
|
311
311
|
}
|
|
312
312
|
|
|
313
|
+
/**
|
|
314
|
+
* Get preferred OpenCode server port from config
|
|
315
|
+
* @returns {number|null} Port number or null if not configured
|
|
316
|
+
*/
|
|
317
|
+
export function getServerPort() {
|
|
318
|
+
const config = getRawConfig();
|
|
319
|
+
return config?.server_port ?? null;
|
|
320
|
+
}
|
|
321
|
+
|
|
313
322
|
/**
|
|
314
323
|
* Clear config cache (for testing)
|
|
315
324
|
*/
|
|
@@ -394,7 +394,7 @@ describe('actions.js', () => {
|
|
|
394
394
|
assert.strictEqual(result, 'http://localhost:4000');
|
|
395
395
|
});
|
|
396
396
|
|
|
397
|
-
test('
|
|
397
|
+
test('uses global project server as fallback when no specific match', async () => {
|
|
398
398
|
const { discoverOpencodeServer } = await import('../../service/actions.js');
|
|
399
399
|
|
|
400
400
|
const mockPorts = async () => [3000];
|
|
@@ -406,13 +406,13 @@ describe('actions.js', () => {
|
|
|
406
406
|
return { ok: false };
|
|
407
407
|
};
|
|
408
408
|
|
|
409
|
-
// Global servers should be
|
|
409
|
+
// Global servers should be used as fallback when no project-specific match
|
|
410
410
|
const result = await discoverOpencodeServer('/Users/test/random/path', {
|
|
411
411
|
getPorts: mockPorts,
|
|
412
412
|
fetch: mockFetch
|
|
413
413
|
});
|
|
414
414
|
|
|
415
|
-
assert.strictEqual(result,
|
|
415
|
+
assert.strictEqual(result, 'http://localhost:3000');
|
|
416
416
|
});
|
|
417
417
|
|
|
418
418
|
test('returns null when fetch fails for all servers', async () => {
|