opencode-pilot 0.16.4 → 0.16.5
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/package.json +1 -1
- package/service/actions.js +31 -10
- package/service/repo-config.js +6 -1
- package/test/unit/repo-config.test.js +40 -0
package/package.json
CHANGED
package/service/actions.js
CHANGED
|
@@ -525,19 +525,40 @@ export async function createSessionViaApi(serverUrl, directory, prompt, options
|
|
|
525
525
|
messageBody.modelID = modelID;
|
|
526
526
|
}
|
|
527
527
|
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
528
|
+
// Use AbortController with timeout for the message POST
|
|
529
|
+
// The /session/{id}/message endpoint returns a chunked/streaming response
|
|
530
|
+
// that stays open until the agent completes. We only need to verify the
|
|
531
|
+
// request was accepted (2xx status), not wait for the full response.
|
|
532
|
+
const controller = new AbortController();
|
|
533
|
+
const timeoutId = setTimeout(() => controller.abort(), 10000); // 10 second timeout
|
|
533
534
|
|
|
534
|
-
|
|
535
|
-
const
|
|
536
|
-
|
|
535
|
+
try {
|
|
536
|
+
const messageResponse = await fetchFn(messageUrl.toString(), {
|
|
537
|
+
method: 'POST',
|
|
538
|
+
headers: { 'Content-Type': 'application/json' },
|
|
539
|
+
body: JSON.stringify(messageBody),
|
|
540
|
+
signal: controller.signal,
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
clearTimeout(timeoutId);
|
|
544
|
+
|
|
545
|
+
if (!messageResponse.ok) {
|
|
546
|
+
const errorText = await messageResponse.text();
|
|
547
|
+
throw new Error(`Failed to send message: ${messageResponse.status} ${errorText}`);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
debug(`createSessionViaApi: sent message to session ${session.id}`);
|
|
551
|
+
} catch (abortErr) {
|
|
552
|
+
clearTimeout(timeoutId);
|
|
553
|
+
// AbortError is expected - we intentionally abort after verifying the request started
|
|
554
|
+
// The server accepted our message, we just don't need to wait for the response
|
|
555
|
+
if (abortErr.name === 'AbortError') {
|
|
556
|
+
debug(`createSessionViaApi: message request started for session ${session.id} (response aborted as expected)`);
|
|
557
|
+
} else {
|
|
558
|
+
throw abortErr;
|
|
559
|
+
}
|
|
537
560
|
}
|
|
538
561
|
|
|
539
|
-
debug(`createSessionViaApi: sent message to session ${session.id}`);
|
|
540
|
-
|
|
541
562
|
return {
|
|
542
563
|
success: true,
|
|
543
564
|
sessionId: session.id,
|
package/service/repo-config.js
CHANGED
|
@@ -352,7 +352,12 @@ export function resolveRepoForItem(source, item) {
|
|
|
352
352
|
if (resolvedRepo) {
|
|
353
353
|
return source.repos.includes(resolvedRepo) ? [resolvedRepo] : [];
|
|
354
354
|
}
|
|
355
|
-
// No repo template -
|
|
355
|
+
// No repo template - if exactly one repo, use it as default
|
|
356
|
+
// (e.g., Linear issues don't have repo context, user explicitly configures one repo)
|
|
357
|
+
if (source.repos.length === 1) {
|
|
358
|
+
return source.repos;
|
|
359
|
+
}
|
|
360
|
+
// Multiple repos but can't match without item context
|
|
356
361
|
return [];
|
|
357
362
|
}
|
|
358
363
|
|
|
@@ -769,6 +769,46 @@ sources:
|
|
|
769
769
|
assert.deepStrictEqual(resolveRepoForItem(source, filteredItem), []);
|
|
770
770
|
});
|
|
771
771
|
|
|
772
|
+
test('single-repo allowlist uses repo as default when no template', async () => {
|
|
773
|
+
// Linear issues don't have repository context - when exactly one repo is configured,
|
|
774
|
+
// use it as the default for all items from that source
|
|
775
|
+
writeFileSync(configPath, `
|
|
776
|
+
sources:
|
|
777
|
+
- preset: linear/my-issues
|
|
778
|
+
repos:
|
|
779
|
+
- 0din-ai/odin
|
|
780
|
+
`);
|
|
781
|
+
|
|
782
|
+
const { loadRepoConfig, getSources, resolveRepoForItem } = await import('../../service/repo-config.js');
|
|
783
|
+
loadRepoConfig(configPath);
|
|
784
|
+
const source = getSources()[0];
|
|
785
|
+
|
|
786
|
+
// Linear items don't have repository field
|
|
787
|
+
const linearItem = { id: 'linear:abc123', title: 'Fix bug', state: { name: 'In Progress' } };
|
|
788
|
+
assert.deepStrictEqual(resolveRepoForItem(source, linearItem), ['0din-ai/odin'],
|
|
789
|
+
'single-repo allowlist should use repo as default');
|
|
790
|
+
});
|
|
791
|
+
|
|
792
|
+
test('multi-repo allowlist returns empty when no template match', async () => {
|
|
793
|
+
// With multiple repos and no way to determine which one, return empty
|
|
794
|
+
writeFileSync(configPath, `
|
|
795
|
+
sources:
|
|
796
|
+
- preset: linear/my-issues
|
|
797
|
+
repos:
|
|
798
|
+
- org/repo-a
|
|
799
|
+
- org/repo-b
|
|
800
|
+
`);
|
|
801
|
+
|
|
802
|
+
const { loadRepoConfig, getSources, resolveRepoForItem } = await import('../../service/repo-config.js');
|
|
803
|
+
loadRepoConfig(configPath);
|
|
804
|
+
const source = getSources()[0];
|
|
805
|
+
|
|
806
|
+
// Can't determine which of the 2 repos to use
|
|
807
|
+
const linearItem = { id: 'linear:abc123', title: 'Fix bug' };
|
|
808
|
+
assert.deepStrictEqual(resolveRepoForItem(source, linearItem), [],
|
|
809
|
+
'multi-repo allowlist should return empty when no template');
|
|
810
|
+
});
|
|
811
|
+
|
|
772
812
|
test('github presets include semantic session names', async () => {
|
|
773
813
|
writeFileSync(configPath, `
|
|
774
814
|
sources:
|