opencode-pilot 0.24.8 → 0.24.9
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.
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
class OpencodePilot < Formula
|
|
2
2
|
desc "Automation daemon for OpenCode - polls GitHub/Linear issues and spawns sessions"
|
|
3
3
|
homepage "https://github.com/athal7/opencode-pilot"
|
|
4
|
-
url "https://github.com/athal7/opencode-pilot/archive/refs/tags/v0.24.
|
|
5
|
-
sha256 "
|
|
4
|
+
url "https://github.com/athal7/opencode-pilot/archive/refs/tags/v0.24.8.tar.gz"
|
|
5
|
+
sha256 "23b550fd74fee8f2fd8c60c8358f67065ee77af921fdc3bc2ff26a6cfef68ae6"
|
|
6
6
|
license "MIT"
|
|
7
7
|
|
|
8
8
|
depends_on "node"
|
package/package.json
CHANGED
package/service/actions.js
CHANGED
|
@@ -639,26 +639,17 @@ export async function sendMessageToSession(serverUrl, sessionId, directory, prom
|
|
|
639
639
|
* @param {string} directory - Working directory to match
|
|
640
640
|
* @param {object} [options] - Options
|
|
641
641
|
* @param {function} [options.fetch] - Custom fetch function (for testing)
|
|
642
|
-
* @param {string} [options.projectDirectory] - Project directory for session lookup
|
|
643
|
-
* (used instead of directory when provided, so sessions created with the project
|
|
644
|
-
* directory are found instead of old worktree-scoped sessions)
|
|
645
642
|
* @returns {Promise<object|null>} Session to reuse, or null
|
|
646
643
|
*/
|
|
647
644
|
export async function findReusableSession(serverUrl, directory, options = {}) {
|
|
648
|
-
// Use project directory for session lookup when available.
|
|
649
|
-
// Sessions created with v0.24.7+ are scoped to the project directory,
|
|
650
|
-
// while messages use the worktree directory. Query the project directory
|
|
651
|
-
// to find correctly-scoped sessions.
|
|
652
|
-
const lookupDirectory = options.projectDirectory || directory;
|
|
653
|
-
|
|
654
645
|
// Get sessions for this directory
|
|
655
646
|
const sessions = await listSessions(serverUrl, {
|
|
656
|
-
directory
|
|
647
|
+
directory,
|
|
657
648
|
fetch: options.fetch
|
|
658
649
|
});
|
|
659
650
|
|
|
660
651
|
if (sessions.length === 0) {
|
|
661
|
-
debug(`findReusableSession: no sessions found for ${
|
|
652
|
+
debug(`findReusableSession: no sessions found for ${directory}`);
|
|
662
653
|
return null;
|
|
663
654
|
}
|
|
664
655
|
|
|
@@ -666,11 +657,11 @@ export async function findReusableSession(serverUrl, directory, options = {}) {
|
|
|
666
657
|
const activeSessions = sessions.filter(s => !isSessionArchived(s));
|
|
667
658
|
|
|
668
659
|
if (activeSessions.length === 0) {
|
|
669
|
-
debug(`findReusableSession: all ${sessions.length} sessions are archived for ${
|
|
660
|
+
debug(`findReusableSession: all ${sessions.length} sessions are archived for ${directory}`);
|
|
670
661
|
return null;
|
|
671
662
|
}
|
|
672
663
|
|
|
673
|
-
debug(`findReusableSession: found ${activeSessions.length} active sessions for ${
|
|
664
|
+
debug(`findReusableSession: found ${activeSessions.length} active sessions for ${directory}`);
|
|
674
665
|
|
|
675
666
|
// Get statuses to prefer idle sessions
|
|
676
667
|
const statuses = await getSessionStatuses(serverUrl, { fetch: options.fetch });
|
|
@@ -891,14 +882,16 @@ async function executeInDirectory(serverUrl, cwd, item, config, options = {}, pr
|
|
|
891
882
|
}
|
|
892
883
|
}
|
|
893
884
|
|
|
894
|
-
// Check if we should try to reuse an existing session
|
|
885
|
+
// Check if we should try to reuse an existing session.
|
|
886
|
+
// Skip reuse when working in a worktree (projectDirectory differs from cwd),
|
|
887
|
+
// because querying by worktree dir finds old sessions with projectID "global",
|
|
888
|
+
// and querying by project dir finds unrelated sessions for other PRs.
|
|
889
|
+
// Each worktree should get its own correctly-scoped session.
|
|
895
890
|
const reuseActiveSession = config.reuse_active_session !== false; // default true
|
|
891
|
+
const inWorktree = projectDirectory && projectDirectory !== cwd;
|
|
896
892
|
|
|
897
|
-
if (reuseActiveSession && !options.dryRun) {
|
|
898
|
-
const existingSession = await findReusableSession(serverUrl, cwd, {
|
|
899
|
-
fetch: options.fetch,
|
|
900
|
-
projectDirectory: projectDirectory,
|
|
901
|
-
});
|
|
893
|
+
if (reuseActiveSession && !inWorktree && !options.dryRun) {
|
|
894
|
+
const existingSession = await findReusableSession(serverUrl, cwd, { fetch: options.fetch });
|
|
902
895
|
|
|
903
896
|
if (existingSession) {
|
|
904
897
|
debug(`executeInDirectory: found reusable session ${existingSession.id} for ${cwd}`);
|
|
@@ -622,14 +622,15 @@ describe("integration: worktree creation with worktree_name", () => {
|
|
|
622
622
|
assert.strictEqual(sessionDirectory, "/proj", "Session should be scoped to project directory");
|
|
623
623
|
});
|
|
624
624
|
|
|
625
|
-
it("session reuse
|
|
626
|
-
//
|
|
627
|
-
//
|
|
628
|
-
//
|
|
629
|
-
//
|
|
625
|
+
it("skips session reuse when working in a worktree", async () => {
|
|
626
|
+
// When working in a worktree (projectDirectory differs from cwd), session
|
|
627
|
+
// reuse is skipped entirely. Querying by worktree dir finds old sessions
|
|
628
|
+
// with projectID "global", querying by project dir finds unrelated sessions
|
|
629
|
+
// for other PRs. Each worktree should get its own correctly-scoped session.
|
|
630
630
|
|
|
631
|
-
let
|
|
631
|
+
let sessionListQueried = false;
|
|
632
632
|
let sessionCreated = false;
|
|
633
|
+
let sessionCreateDirectory = null;
|
|
633
634
|
|
|
634
635
|
const existingWorktreeDir = "/worktree/calm-wizard";
|
|
635
636
|
|
|
@@ -643,25 +644,16 @@ describe("integration: worktree creation with worktree_name", () => {
|
|
|
643
644
|
"GET /experimental/worktree": () => ({
|
|
644
645
|
body: [existingWorktreeDir],
|
|
645
646
|
}),
|
|
646
|
-
"GET /session": (
|
|
647
|
-
|
|
648
|
-
// Return a session ONLY if queried by project directory
|
|
649
|
-
if (req.directory === "/proj") {
|
|
650
|
-
return {
|
|
651
|
-
body: [{ id: "ses_proj_scoped", directory: "/proj", time: { created: 1000, updated: 2000 } }],
|
|
652
|
-
};
|
|
653
|
-
}
|
|
654
|
-
// Old worktree-scoped sessions should NOT be found when querying by project dir
|
|
647
|
+
"GET /session": () => {
|
|
648
|
+
sessionListQueried = true;
|
|
655
649
|
return { body: [] };
|
|
656
650
|
},
|
|
657
651
|
"GET /session/status": () => ({ body: {} }),
|
|
658
652
|
"POST /session": (req) => {
|
|
659
653
|
sessionCreated = true;
|
|
654
|
+
sessionCreateDirectory = req.query?.directory;
|
|
660
655
|
return { body: { id: "ses_new" } };
|
|
661
656
|
},
|
|
662
|
-
"PATCH /session/ses_proj_scoped": () => ({ body: {} }),
|
|
663
|
-
"POST /session/ses_proj_scoped/message": () => ({ body: { success: true } }),
|
|
664
|
-
"POST /session/ses_proj_scoped/command": () => ({ body: { success: true } }),
|
|
665
657
|
"PATCH /session/ses_new": () => ({ body: {} }),
|
|
666
658
|
"POST /session/ses_new/message": () => ({ body: { success: true } }),
|
|
667
659
|
"POST /session/ses_new/command": () => ({ body: { success: true } }),
|
|
@@ -679,12 +671,13 @@ describe("integration: worktree creation with worktree_name", () => {
|
|
|
679
671
|
);
|
|
680
672
|
|
|
681
673
|
assert.ok(result.success, "Action should succeed");
|
|
682
|
-
//
|
|
683
|
-
assert.strictEqual(
|
|
684
|
-
"
|
|
685
|
-
// Should
|
|
686
|
-
assert.
|
|
687
|
-
assert.strictEqual(
|
|
674
|
+
// Should NOT query for existing sessions when in a worktree
|
|
675
|
+
assert.strictEqual(sessionListQueried, false,
|
|
676
|
+
"Should skip session reuse entirely when in a worktree");
|
|
677
|
+
// Should create a new session scoped to the project directory
|
|
678
|
+
assert.ok(sessionCreated, "Should create a new session");
|
|
679
|
+
assert.strictEqual(sessionCreateDirectory, "/proj",
|
|
680
|
+
"New session should be scoped to project directory");
|
|
688
681
|
});
|
|
689
682
|
});
|
|
690
683
|
|
|
@@ -2147,60 +2147,6 @@ Check for bugs and security issues.`;
|
|
|
2147
2147
|
assert.strictEqual(result.id, 'ses_new');
|
|
2148
2148
|
});
|
|
2149
2149
|
|
|
2150
|
-
test('uses projectDirectory for session lookup when provided', async () => {
|
|
2151
|
-
const { findReusableSession } = await import('../../service/actions.js');
|
|
2152
|
-
|
|
2153
|
-
let queriedDirectory = null;
|
|
2154
|
-
const mockFetch = async (url) => {
|
|
2155
|
-
if (url.includes('/session/status')) {
|
|
2156
|
-
return { ok: true, json: async () => ({}) };
|
|
2157
|
-
}
|
|
2158
|
-
// Capture the directory used in the session query
|
|
2159
|
-
const urlObj = new URL(url);
|
|
2160
|
-
queriedDirectory = urlObj.searchParams.get('directory');
|
|
2161
|
-
return {
|
|
2162
|
-
ok: true,
|
|
2163
|
-
json: async () => [
|
|
2164
|
-
{ id: 'ses_proj', time: { created: 1000, updated: 2000 } },
|
|
2165
|
-
],
|
|
2166
|
-
};
|
|
2167
|
-
};
|
|
2168
|
-
|
|
2169
|
-
const result = await findReusableSession('http://localhost:4096', '/worktree/pr-415', {
|
|
2170
|
-
fetch: mockFetch,
|
|
2171
|
-
projectDirectory: '/home/user/code/odin',
|
|
2172
|
-
});
|
|
2173
|
-
|
|
2174
|
-
assert.strictEqual(result.id, 'ses_proj');
|
|
2175
|
-
assert.strictEqual(queriedDirectory, '/home/user/code/odin',
|
|
2176
|
-
'Should query sessions using projectDirectory, not the worktree directory');
|
|
2177
|
-
});
|
|
2178
|
-
|
|
2179
|
-
test('falls back to directory when projectDirectory not provided', async () => {
|
|
2180
|
-
const { findReusableSession } = await import('../../service/actions.js');
|
|
2181
|
-
|
|
2182
|
-
let queriedDirectory = null;
|
|
2183
|
-
const mockFetch = async (url) => {
|
|
2184
|
-
if (url.includes('/session/status')) {
|
|
2185
|
-
return { ok: true, json: async () => ({}) };
|
|
2186
|
-
}
|
|
2187
|
-
const urlObj = new URL(url);
|
|
2188
|
-
queriedDirectory = urlObj.searchParams.get('directory');
|
|
2189
|
-
return {
|
|
2190
|
-
ok: true,
|
|
2191
|
-
json: async () => [
|
|
2192
|
-
{ id: 'ses_1', time: { created: 1000, updated: 2000 } },
|
|
2193
|
-
],
|
|
2194
|
-
};
|
|
2195
|
-
};
|
|
2196
|
-
|
|
2197
|
-
const result = await findReusableSession('http://localhost:4096', '/test/dir', { fetch: mockFetch });
|
|
2198
|
-
|
|
2199
|
-
assert.strictEqual(result.id, 'ses_1');
|
|
2200
|
-
assert.strictEqual(queriedDirectory, '/test/dir',
|
|
2201
|
-
'Should query sessions using directory when projectDirectory not provided');
|
|
2202
|
-
});
|
|
2203
|
-
|
|
2204
2150
|
test('falls back to busy session when no idle available', async () => {
|
|
2205
2151
|
const { findReusableSession } = await import('../../service/actions.js');
|
|
2206
2152
|
|