macro-agent 0.1.0 → 0.1.1
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/.claude/settings.local.json +3 -1
- package/.sudocode/specs.jsonl +4 -0
- package/CLAUDE.md +16 -14
- package/README.md +11 -29
- package/dist/acp/macro-agent.d.ts +15 -0
- package/dist/acp/macro-agent.d.ts.map +1 -1
- package/dist/acp/macro-agent.js +131 -35
- package/dist/acp/macro-agent.js.map +1 -1
- package/dist/acp/types.d.ts +32 -1
- package/dist/acp/types.d.ts.map +1 -1
- package/dist/acp/types.js.map +1 -1
- package/dist/agent/agent-manager.d.ts +65 -1
- package/dist/agent/agent-manager.d.ts.map +1 -1
- package/dist/agent/agent-manager.js +464 -183
- package/dist/agent/agent-manager.js.map +1 -1
- package/dist/agent/types.d.ts +1 -1
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/api/server.d.ts +3 -0
- package/dist/api/server.d.ts.map +1 -1
- package/dist/api/server.js +37 -6
- package/dist/api/server.js.map +1 -1
- package/dist/auth/index.d.ts +2 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +2 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/token.d.ts +41 -0
- package/dist/auth/token.d.ts.map +1 -0
- package/dist/auth/token.js +73 -0
- package/dist/auth/token.js.map +1 -0
- package/dist/cli/acp.d.ts +2 -23
- package/dist/cli/acp.d.ts.map +1 -1
- package/dist/cli/acp.js +127 -61
- package/dist/cli/acp.js.map +1 -1
- package/dist/cli/index.js +147 -15
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/mcp.d.ts +6 -0
- package/dist/cli/mcp.d.ts.map +1 -1
- package/dist/cli/mcp.js +268 -181
- package/dist/cli/mcp.js.map +1 -1
- package/dist/cli/parse-args.d.ts +20 -0
- package/dist/cli/parse-args.d.ts.map +1 -0
- package/dist/cli/parse-args.js +43 -0
- package/dist/cli/parse-args.js.map +1 -0
- package/dist/cli/stable-instance-id.d.ts +8 -0
- package/dist/cli/stable-instance-id.d.ts.map +1 -0
- package/dist/cli/stable-instance-id.js +14 -0
- package/dist/cli/stable-instance-id.js.map +1 -0
- package/dist/config/project-config.d.ts +74 -7
- package/dist/config/project-config.d.ts.map +1 -1
- package/dist/config/project-config.js +123 -20
- package/dist/config/project-config.js.map +1 -1
- package/dist/map/adapter/acp-over-map.d.ts +17 -0
- package/dist/map/adapter/acp-over-map.d.ts.map +1 -1
- package/dist/map/adapter/acp-over-map.js +384 -23
- package/dist/map/adapter/acp-over-map.js.map +1 -1
- package/dist/map/adapter/connection-manager.d.ts.map +1 -1
- package/dist/map/adapter/connection-manager.js +3 -0
- package/dist/map/adapter/connection-manager.js.map +1 -1
- package/dist/map/adapter/event-log.d.ts +87 -0
- package/dist/map/adapter/event-log.d.ts.map +1 -0
- package/dist/map/adapter/event-log.js +122 -0
- package/dist/map/adapter/event-log.js.map +1 -0
- package/dist/map/adapter/event-translator.js +6 -6
- package/dist/map/adapter/event-translator.js.map +1 -1
- package/dist/map/adapter/extensions/agent-lifecycle.d.ts +82 -0
- package/dist/map/adapter/extensions/agent-lifecycle.d.ts.map +1 -0
- package/dist/map/adapter/extensions/agent-lifecycle.js +164 -0
- package/dist/map/adapter/extensions/agent-lifecycle.js.map +1 -0
- package/dist/map/adapter/extensions/index.d.ts +10 -1
- package/dist/map/adapter/extensions/index.d.ts.map +1 -1
- package/dist/map/adapter/extensions/index.js +34 -0
- package/dist/map/adapter/extensions/index.js.map +1 -1
- package/dist/map/adapter/extensions/mcp-bridge.d.ts +57 -0
- package/dist/map/adapter/extensions/mcp-bridge.d.ts.map +1 -0
- package/dist/map/adapter/extensions/mcp-bridge.js +745 -0
- package/dist/map/adapter/extensions/mcp-bridge.js.map +1 -0
- package/dist/map/adapter/extensions/rename.d.ts +29 -0
- package/dist/map/adapter/extensions/rename.d.ts.map +1 -0
- package/dist/map/adapter/extensions/rename.js +49 -0
- package/dist/map/adapter/extensions/rename.js.map +1 -0
- package/dist/map/adapter/extensions/task.d.ts.map +1 -1
- package/dist/map/adapter/extensions/task.js +10 -0
- package/dist/map/adapter/extensions/task.js.map +1 -1
- package/dist/map/adapter/extensions/update-metadata.d.ts +29 -0
- package/dist/map/adapter/extensions/update-metadata.d.ts.map +1 -0
- package/dist/map/adapter/extensions/update-metadata.js +67 -0
- package/dist/map/adapter/extensions/update-metadata.js.map +1 -0
- package/dist/map/adapter/index.d.ts +2 -1
- package/dist/map/adapter/index.d.ts.map +1 -1
- package/dist/map/adapter/index.js +8 -2
- package/dist/map/adapter/index.js.map +1 -1
- package/dist/map/adapter/interface.d.ts +2 -0
- package/dist/map/adapter/interface.d.ts.map +1 -1
- package/dist/map/adapter/map-adapter.d.ts +3 -0
- package/dist/map/adapter/map-adapter.d.ts.map +1 -1
- package/dist/map/adapter/map-adapter.js +258 -35
- package/dist/map/adapter/map-adapter.js.map +1 -1
- package/dist/map/adapter/subscription-manager.d.ts.map +1 -1
- package/dist/map/adapter/subscription-manager.js +5 -1
- package/dist/map/adapter/subscription-manager.js.map +1 -1
- package/dist/map/adapter/types.d.ts +2 -0
- package/dist/map/adapter/types.d.ts.map +1 -1
- package/dist/mcp/map-client.d.ts +39 -0
- package/dist/mcp/map-client.d.ts.map +1 -0
- package/dist/mcp/map-client.js +129 -0
- package/dist/mcp/map-client.js.map +1 -0
- package/dist/mcp/mcp-server.d.ts +14 -0
- package/dist/mcp/mcp-server.d.ts.map +1 -1
- package/dist/mcp/mcp-server.js +113 -85
- package/dist/mcp/mcp-server.js.map +1 -1
- package/dist/mcp/types.d.ts +9 -1
- package/dist/mcp/types.d.ts.map +1 -1
- package/dist/mcp/types.js.map +1 -1
- package/dist/metrics/metrics.js +1 -1
- package/dist/metrics/metrics.js.map +1 -1
- package/dist/roles/capabilities.d.ts +3 -1
- package/dist/roles/capabilities.d.ts.map +1 -1
- package/dist/roles/capabilities.js +17 -7
- package/dist/roles/capabilities.js.map +1 -1
- package/dist/roles/config-loader.d.ts +6 -6
- package/dist/roles/config-loader.d.ts.map +1 -1
- package/dist/roles/config-loader.js +6 -6
- package/dist/roles/config-loader.js.map +1 -1
- package/dist/roles/registry.d.ts +2 -2
- package/dist/roles/registry.js +2 -2
- package/dist/server/combined-server.d.ts +20 -0
- package/dist/server/combined-server.d.ts.map +1 -1
- package/dist/server/combined-server.js +107 -8
- package/dist/server/combined-server.js.map +1 -1
- package/dist/store/event-store.d.ts +2 -1
- package/dist/store/event-store.d.ts.map +1 -1
- package/dist/store/event-store.js +69 -20
- package/dist/store/event-store.js.map +1 -1
- package/dist/store/types/agents.d.ts +18 -0
- package/dist/store/types/agents.d.ts.map +1 -1
- package/dist/store/types/events.d.ts +1 -1
- package/dist/store/types/events.d.ts.map +1 -1
- package/dist/task/backend/index.d.ts +47 -29
- package/dist/task/backend/index.d.ts.map +1 -1
- package/dist/task/backend/index.js +109 -71
- package/dist/task/backend/index.js.map +1 -1
- package/dist/task/backend/memory.d.ts +1 -0
- package/dist/task/backend/memory.d.ts.map +1 -1
- package/dist/task/backend/memory.js +3 -0
- package/dist/task/backend/memory.js.map +1 -1
- package/dist/task/backend/opentasks/backend.d.ts +140 -0
- package/dist/task/backend/opentasks/backend.d.ts.map +1 -0
- package/dist/task/backend/opentasks/backend.js +1023 -0
- package/dist/task/backend/opentasks/backend.js.map +1 -0
- package/dist/task/backend/opentasks/client.d.ts +337 -0
- package/dist/task/backend/opentasks/client.d.ts.map +1 -0
- package/dist/task/backend/opentasks/client.js +225 -0
- package/dist/task/backend/opentasks/client.js.map +1 -0
- package/dist/task/backend/opentasks/daemon-manager.d.ts +89 -0
- package/dist/task/backend/opentasks/daemon-manager.d.ts.map +1 -0
- package/dist/task/backend/opentasks/daemon-manager.js +195 -0
- package/dist/task/backend/opentasks/daemon-manager.js.map +1 -0
- package/dist/task/backend/opentasks/index.d.ts +21 -0
- package/dist/task/backend/opentasks/index.d.ts.map +1 -0
- package/dist/task/backend/opentasks/index.js +21 -0
- package/dist/task/backend/opentasks/index.js.map +1 -0
- package/dist/task/backend/opentasks/mapping.d.ts +48 -0
- package/dist/task/backend/opentasks/mapping.d.ts.map +1 -0
- package/dist/task/backend/opentasks/mapping.js +77 -0
- package/dist/task/backend/opentasks/mapping.js.map +1 -0
- package/dist/task/backend/types.d.ts +33 -53
- package/dist/task/backend/types.d.ts.map +1 -1
- package/dist/task/backend/types.js +7 -11
- package/dist/task/backend/types.js.map +1 -1
- package/dist/task/backend/unified-tool-provider.d.ts +57 -0
- package/dist/task/backend/unified-tool-provider.d.ts.map +1 -0
- package/dist/task/backend/unified-tool-provider.js +623 -0
- package/dist/task/backend/unified-tool-provider.js.map +1 -0
- package/dist/teams/team-loader.d.ts +2 -2
- package/dist/teams/team-loader.js +3 -3
- package/dist/teams/team-loader.js.map +1 -1
- package/dist/teams/team-runtime.d.ts.map +1 -1
- package/dist/teams/team-runtime.js +2 -0
- package/dist/teams/team-runtime.js.map +1 -1
- package/docs/architecture.md +7 -6
- package/docs/configuration.md +26 -62
- package/docs/implementation-details.md +5 -5
- package/docs/implementation-summary.md +17 -17
- package/docs/plan-self-driving-support.md +4 -4
- package/docs/spec-self-driving-support.md +10 -10
- package/docs/team-templates.md +2 -2
- package/docs/teams.md +3 -3
- package/docs/troubleshooting.md +10 -11
- package/package.json +6 -4
- package/src/__tests__/e2e/agent-spawn-visibility.e2e.test.ts +761 -0
- package/src/__tests__/e2e/full-agent-conflict-resolution.e2e.test.ts +2 -2
- package/src/__tests__/e2e/mcp-thin-client-bridge.e2e.test.ts +304 -0
- package/src/__tests__/e2e/mcp-tools-available.e2e.test.ts +324 -0
- package/src/__tests__/e2e/multi-agent.e2e.test.ts +5 -5
- package/src/__tests__/e2e/spawn-session-streaming.e2e.test.ts +563 -0
- package/src/acp/__tests__/integration.test.ts +56 -31
- package/src/acp/__tests__/macro-agent.test.ts +16 -7
- package/src/acp/macro-agent.ts +170 -36
- package/src/acp/types.ts +46 -1
- package/src/agent/__tests__/agent-manager.test.ts +228 -2
- package/src/agent/agent-manager.ts +714 -261
- package/src/agent/types.ts +3 -1
- package/src/api/server.ts +41 -7
- package/src/auth/__tests__/token.test.ts +100 -0
- package/src/auth/index.ts +1 -0
- package/src/auth/token.ts +82 -0
- package/src/cli/__tests__/acp.test.ts +1 -1
- package/src/cli/__tests__/stable-instance-id.test.ts +1 -1
- package/src/cli/acp.ts +130 -72
- package/src/cli/index.ts +120 -14
- package/src/cli/mcp.ts +311 -207
- package/src/cli/parse-args.ts +54 -0
- package/src/cli/stable-instance-id.ts +14 -0
- package/src/config/project-config.ts +190 -27
- package/src/lifecycle/__tests__/cascade-termination.test.ts +1 -1
- package/src/map/adapter/__tests__/acp-over-map-cancel.test.ts +22 -4
- package/src/map/adapter/__tests__/acp-over-map-getmodels.test.ts +355 -0
- package/src/map/adapter/__tests__/acp-over-map-history.test.ts +263 -0
- package/src/map/adapter/__tests__/acp-over-map-persistence.e2e.test.ts +1 -1
- package/src/map/adapter/__tests__/event-broadcast.test.ts +420 -0
- package/src/map/adapter/__tests__/event-log.test.ts +527 -0
- package/src/map/adapter/__tests__/event-translator.test.ts +3 -3
- package/src/map/adapter/__tests__/extensions.test.ts +408 -0
- package/src/map/adapter/__tests__/map-adapter.test.ts +99 -0
- package/src/map/adapter/__tests__/mcp-bridge.test.ts +1187 -0
- package/src/map/adapter/__tests__/multi-client-broadcast.test.ts +711 -0
- package/src/map/adapter/__tests__/websocket-integration.test.ts +218 -0
- package/src/map/adapter/acp-over-map.ts +678 -66
- package/src/map/adapter/connection-manager.ts +3 -0
- package/src/map/adapter/event-log.ts +208 -0
- package/src/map/adapter/event-translator.ts +6 -6
- package/src/map/adapter/extensions/agent-lifecycle.ts +267 -0
- package/src/map/adapter/extensions/index.ts +60 -0
- package/src/map/adapter/extensions/mcp-bridge.ts +995 -0
- package/src/map/adapter/extensions/task.ts +11 -0
- package/src/map/adapter/extensions/update-metadata.ts +126 -0
- package/src/map/adapter/index.ts +28 -0
- package/src/map/adapter/interface.ts +2 -0
- package/src/map/adapter/map-adapter.ts +312 -47
- package/src/map/adapter/subscription-manager.ts +5 -1
- package/src/map/adapter/types.ts +2 -0
- package/src/mcp/__tests__/map-client.test.ts +386 -0
- package/src/mcp/__tests__/mcp-server-thin-client.test.ts +368 -0
- package/src/mcp/__tests__/mcp-server.test.ts +100 -1
- package/src/mcp/map-client.ts +177 -0
- package/src/mcp/mcp-server.ts +191 -100
- package/src/mcp/types.ts +6 -1
- package/src/metrics/metrics.ts +1 -1
- package/src/monitor/__tests__/stale-agent-flow.integration.test.ts +1 -1
- package/src/roles/__tests__/config-loader.test.ts +7 -7
- package/src/roles/capabilities.ts +17 -7
- package/src/roles/config-loader.ts +6 -6
- package/src/roles/registry.ts +2 -2
- package/src/server/__tests__/combined-server.test.ts +94 -21
- package/src/server/combined-server.ts +189 -33
- package/src/steering/__tests__/steering-integration.test.ts +1 -1
- package/src/store/__tests__/event-store.test.ts +196 -1
- package/src/store/__tests__/instance.test.ts +3 -3
- package/src/store/event-store.ts +80 -21
- package/src/store/types/agents.ts +15 -0
- package/src/store/types/events.ts +1 -1
- package/src/task/backend/__tests__/create-task-backend.test.ts +225 -0
- package/src/task/backend/__tests__/e2e/unified-tool-provider-opentasks.e2e.test.ts +524 -0
- package/src/task/backend/__tests__/unified-tool-provider.test.ts +579 -0
- package/src/task/backend/index.ts +156 -106
- package/src/task/backend/memory.ts +4 -0
- package/src/task/backend/opentasks/__tests__/backend.test.ts +968 -0
- package/src/task/backend/opentasks/__tests__/daemon-manager.test.ts +406 -0
- package/src/task/backend/opentasks/__tests__/mapping.test.ts +84 -0
- package/src/task/backend/opentasks/__tests__/opentasks-backend.e2e.test.ts +1338 -0
- package/src/task/backend/opentasks/backend.ts +1323 -0
- package/src/task/backend/opentasks/client.ts +652 -0
- package/src/task/backend/opentasks/daemon-manager.ts +253 -0
- package/src/task/backend/opentasks/index.ts +69 -0
- package/src/task/backend/opentasks/mapping.ts +94 -0
- package/src/task/backend/types.ts +42 -66
- package/src/task/backend/unified-tool-provider.ts +779 -0
- package/src/teams/__tests__/cross-subsystem.integration.test.ts +1 -1
- package/src/teams/team-loader.ts +3 -3
- package/src/teams/team-runtime.ts +2 -0
- package/test_fixtures/README.md +2 -3
- package/test_fixtures/fixtures/index.ts +0 -3
- package/test_fixtures/fixtures/projects/project-with-specs.ts +7 -149
- package/test_fixtures/fixtures/repos/index.ts +1 -3
- package/test_fixtures/fixtures/repos/temp-repo-factory.ts +0 -116
- package/test_fixtures/fixtures/repos/types.ts +0 -11
- package/test_fixtures/harness/__tests__/fixtures.test.ts +10 -102
- package/test_fixtures/harness/__tests__/temp-repo-and-simulator.test.ts +0 -33
- package/test_fixtures/harness/simulator/agent-simulator.ts +4 -4
- package/vitest.config.ts +1 -1
- package/vitest.e2e.config.ts +1 -1
- package/vitest.setup.ts +1 -30
- package/.macro-agent/teams/self-driving/prompts/grinder.md +0 -27
- package/.macro-agent/teams/self-driving/prompts/judge.md +0 -27
- package/.macro-agent/teams/self-driving/prompts/planner.md +0 -33
- package/.macro-agent/teams/self-driving/roles/grinder.yaml +0 -17
- package/.macro-agent/teams/self-driving/roles/judge.yaml +0 -24
- package/.macro-agent/teams/self-driving/roles/planner.yaml +0 -18
- package/.macro-agent/teams/self-driving/team.yaml +0 -103
- package/.macro-agent/teams/structured/prompts/developer.md +0 -26
- package/.macro-agent/teams/structured/prompts/lead.md +0 -25
- package/.macro-agent/teams/structured/prompts/reviewer.md +0 -24
- package/.macro-agent/teams/structured/roles/developer.yaml +0 -12
- package/.macro-agent/teams/structured/roles/lead.yaml +0 -11
- package/.macro-agent/teams/structured/roles/reviewer.yaml +0 -19
- package/.macro-agent/teams/structured/team.yaml +0 -89
- package/docs/sudocode-integration.md +0 -383
- package/src/task/backend/__tests__/backend-parity.test.ts +0 -451
- package/src/task/backend/__tests__/tool-provider-edge-cases.test.ts +0 -430
- package/src/task/backend/__tests__/tool-provider.test.ts +0 -983
- package/src/task/backend/sudocode/__tests__/backend-edge-cases.test.ts +0 -575
- package/src/task/backend/sudocode/__tests__/backend.test.ts +0 -1194
- package/src/task/backend/sudocode/__tests__/client-integration.test.ts +0 -418
- package/src/task/backend/sudocode/__tests__/client.test.ts +0 -345
- package/src/task/backend/sudocode/__tests__/e2e/backend.e2e.test.ts +0 -753
- package/src/task/backend/sudocode/__tests__/e2e/server-client.e2e.test.ts +0 -680
- package/src/task/backend/sudocode/__tests__/e2e-workflow.test.ts +0 -666
- package/src/task/backend/sudocode/__tests__/integration/standalone-client.integration.test.ts +0 -396
- package/src/task/backend/sudocode/__tests__/integration/sudocode-cli.integration.test.ts +0 -328
- package/src/task/backend/sudocode/__tests__/integration/test-utils.ts +0 -175
- package/src/task/backend/sudocode/__tests__/mapping-edge-cases.test.ts +0 -265
- package/src/task/backend/sudocode/__tests__/server-client.test.ts +0 -675
- package/src/task/backend/sudocode/__tests__/sync-policy-edge-cases.test.ts +0 -521
- package/src/task/backend/sudocode/__tests__/sync-policy.test.ts +0 -519
- package/src/task/backend/sudocode/__tests__/tools.test.ts +0 -471
- package/src/task/backend/sudocode/backend.ts +0 -1237
- package/src/task/backend/sudocode/client.ts +0 -515
- package/src/task/backend/sudocode/index.ts +0 -120
- package/src/task/backend/sudocode/mapping.ts +0 -93
- package/src/task/backend/sudocode/server-client.ts +0 -522
- package/src/task/backend/sudocode/standalone-client.ts +0 -623
- package/src/task/backend/sudocode/sync-policy.ts +0 -387
- package/src/task/backend/sudocode/tools.ts +0 -896
- package/src/task/backend/tool-provider.ts +0 -506
- package/test_fixtures/fixtures/sudocode/index.ts +0 -29
- package/test_fixtures/fixtures/sudocode/issues.ts +0 -185
- package/test_fixtures/fixtures/sudocode/specs.ts +0 -159
|
@@ -1,418 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SudocodeClient Integration Tests
|
|
3
|
-
*
|
|
4
|
-
* Tests for client integration edge cases and potential bugs.
|
|
5
|
-
*
|
|
6
|
-
* @module task/backend/sudocode/__tests__/client-integration.test
|
|
7
|
-
* @see s-8472 Pluggable Task Backend Integration
|
|
8
|
-
* @see s-1zcx Multi-Agent Orchestration Testing Strategy
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
12
|
-
import { mkdtempSync, rmSync, existsSync } from "fs";
|
|
13
|
-
import { tmpdir } from "os";
|
|
14
|
-
import { join } from "path";
|
|
15
|
-
import {
|
|
16
|
-
checkServerHealth,
|
|
17
|
-
createSudocodeClient,
|
|
18
|
-
type SudocodeClientConfig,
|
|
19
|
-
type IssueChangeEvent,
|
|
20
|
-
} from "../client.js";
|
|
21
|
-
import { createStandaloneClient, StandaloneClient } from "../standalone-client.js";
|
|
22
|
-
|
|
23
|
-
// Mock fetch for server health checks
|
|
24
|
-
const mockFetch = vi.fn();
|
|
25
|
-
vi.stubGlobal("fetch", mockFetch);
|
|
26
|
-
|
|
27
|
-
// Mock WebSocket
|
|
28
|
-
class MockWebSocket {
|
|
29
|
-
static instances: MockWebSocket[] = [];
|
|
30
|
-
onopen: (() => void) | null = null;
|
|
31
|
-
onclose: (() => void) | null = null;
|
|
32
|
-
onerror: ((error: unknown) => void) | null = null;
|
|
33
|
-
onmessage: ((event: { data: string }) => void) | null = null;
|
|
34
|
-
readyState = 0;
|
|
35
|
-
|
|
36
|
-
constructor(public url: string) {
|
|
37
|
-
MockWebSocket.instances.push(this);
|
|
38
|
-
setTimeout(() => {
|
|
39
|
-
this.readyState = 1;
|
|
40
|
-
if (this.onopen) this.onopen();
|
|
41
|
-
}, 0);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
close() {
|
|
45
|
-
this.readyState = 3;
|
|
46
|
-
if (this.onclose) this.onclose();
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
send(_data: string) {
|
|
50
|
-
// Mock send
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
static clearInstances() {
|
|
54
|
-
MockWebSocket.instances = [];
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
static simulateMessage(data: unknown) {
|
|
58
|
-
for (const ws of MockWebSocket.instances) {
|
|
59
|
-
if (ws.onmessage) {
|
|
60
|
-
ws.onmessage({ data: JSON.stringify(data) });
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
vi.stubGlobal("WebSocket", MockWebSocket);
|
|
67
|
-
|
|
68
|
-
describe("Client Integration", () => {
|
|
69
|
-
let tmpDir: string;
|
|
70
|
-
|
|
71
|
-
beforeEach(() => {
|
|
72
|
-
vi.clearAllMocks();
|
|
73
|
-
MockWebSocket.clearInstances();
|
|
74
|
-
tmpDir = mkdtempSync(join(tmpdir(), "sudocode-client-test-"));
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
afterEach(() => {
|
|
78
|
-
vi.restoreAllMocks();
|
|
79
|
-
if (existsSync(tmpDir)) {
|
|
80
|
-
rmSync(tmpDir, { recursive: true, force: true });
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
describe("StandaloneClient initialization", () => {
|
|
85
|
-
it("should create .sudocode directory if it doesn't exist", async () => {
|
|
86
|
-
const client = await createStandaloneClient({ projectPath: tmpDir });
|
|
87
|
-
|
|
88
|
-
try {
|
|
89
|
-
expect(existsSync(join(tmpDir, ".sudocode"))).toBe(true);
|
|
90
|
-
expect(existsSync(join(tmpDir, ".sudocode", "cache.db"))).toBe(true);
|
|
91
|
-
} finally {
|
|
92
|
-
client.close();
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it("should throw if init is not called on raw StandaloneClient", async () => {
|
|
97
|
-
const client = new StandaloneClient({ projectPath: tmpDir });
|
|
98
|
-
|
|
99
|
-
// Operations should fail before init
|
|
100
|
-
await expect(client.getIssue("i-test")).rejects.toThrow("not initialized");
|
|
101
|
-
|
|
102
|
-
client.close();
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
it("should handle double init gracefully", async () => {
|
|
106
|
-
const client = new StandaloneClient({ projectPath: tmpDir });
|
|
107
|
-
await client.init();
|
|
108
|
-
await client.init(); // Second init should be a no-op
|
|
109
|
-
|
|
110
|
-
expect(client.isReady()).toBe(true);
|
|
111
|
-
client.close();
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it("should handle close before init", () => {
|
|
115
|
-
const client = new StandaloneClient({ projectPath: tmpDir });
|
|
116
|
-
// Should not throw
|
|
117
|
-
expect(() => client.close()).not.toThrow();
|
|
118
|
-
expect(client.isReady()).toBe(false);
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
describe("StandaloneClient issue operations", () => {
|
|
123
|
-
let client: StandaloneClient;
|
|
124
|
-
|
|
125
|
-
beforeEach(async () => {
|
|
126
|
-
client = await createStandaloneClient({ projectPath: tmpDir });
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
afterEach(() => {
|
|
130
|
-
client.close();
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
it("should return null for non-existent issue", async () => {
|
|
134
|
-
const issue = await client.getIssue("i-nonexistent");
|
|
135
|
-
expect(issue).toBeNull();
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
it("should list issues with empty database", async () => {
|
|
139
|
-
const issues = await client.listIssues();
|
|
140
|
-
expect(issues).toEqual([]);
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
it("should list ready issues with empty database", async () => {
|
|
144
|
-
const ready = await client.getReadyIssues();
|
|
145
|
-
expect(ready).toEqual([]);
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
it("should throw when updating non-existent issue", async () => {
|
|
149
|
-
await expect(
|
|
150
|
-
client.updateIssue("i-nonexistent", { title: "Updated" })
|
|
151
|
-
).rejects.toThrow();
|
|
152
|
-
});
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
describe("StandaloneClient spec operations", () => {
|
|
156
|
-
let client: StandaloneClient;
|
|
157
|
-
|
|
158
|
-
beforeEach(async () => {
|
|
159
|
-
client = await createStandaloneClient({ projectPath: tmpDir });
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
afterEach(() => {
|
|
163
|
-
client.close();
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
it("should return null for non-existent spec", async () => {
|
|
167
|
-
const spec = await client.getSpec("s-nonexistent");
|
|
168
|
-
expect(spec).toBeNull();
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
it("should list specs with empty database", async () => {
|
|
172
|
-
const specs = await client.listSpecs();
|
|
173
|
-
expect(specs).toEqual([]);
|
|
174
|
-
});
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
describe("StandaloneClient relationship operations", () => {
|
|
178
|
-
let client: StandaloneClient;
|
|
179
|
-
|
|
180
|
-
beforeEach(async () => {
|
|
181
|
-
client = await createStandaloneClient({ projectPath: tmpDir });
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
afterEach(() => {
|
|
185
|
-
client.close();
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
it("should return empty array for blockers of non-existent issue", async () => {
|
|
189
|
-
const blockers = await client.getBlockers("i-nonexistent");
|
|
190
|
-
expect(blockers).toEqual([]);
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
it("should return empty array for blocking of non-existent issue", async () => {
|
|
194
|
-
const blocking = await client.getBlocking("i-nonexistent");
|
|
195
|
-
expect(blocking).toEqual([]);
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
it("should infer entity type from ID prefix", async () => {
|
|
199
|
-
// This tests the internal inferEntityType function indirectly
|
|
200
|
-
// by trying to create links with different ID prefixes
|
|
201
|
-
|
|
202
|
-
// Should not throw for valid prefixes (even if entities don't exist)
|
|
203
|
-
// The operation may fail for other reasons but not for entity type inference
|
|
204
|
-
await expect(
|
|
205
|
-
client.createLink("i-from", "i-to", "blocks")
|
|
206
|
-
).rejects.toThrow(); // Will throw because entities don't exist, but not for type inference
|
|
207
|
-
|
|
208
|
-
await expect(
|
|
209
|
-
client.createLink("s-from", "i-to", "implements")
|
|
210
|
-
).rejects.toThrow();
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
it("should throw for invalid entity ID prefix", async () => {
|
|
214
|
-
await expect(
|
|
215
|
-
client.createLink("x-invalid", "i-to", "blocks")
|
|
216
|
-
).rejects.toThrow("Cannot infer entity type");
|
|
217
|
-
});
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
describe("StandaloneClient event subscriptions", () => {
|
|
221
|
-
let client: StandaloneClient;
|
|
222
|
-
|
|
223
|
-
beforeEach(async () => {
|
|
224
|
-
client = await createStandaloneClient({
|
|
225
|
-
projectPath: tmpDir,
|
|
226
|
-
pollInterval: 100, // Short interval for testing
|
|
227
|
-
});
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
afterEach(() => {
|
|
231
|
-
client.close();
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
it("should subscribe to global issue changes", async () => {
|
|
235
|
-
const events: IssueChangeEvent[] = [];
|
|
236
|
-
const unsubscribe = client.onIssueChange((event) => {
|
|
237
|
-
events.push(event);
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
// Unsubscribe should work
|
|
241
|
-
unsubscribe();
|
|
242
|
-
expect(typeof unsubscribe).toBe("function");
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
it("should subscribe to specific issue changes", async () => {
|
|
246
|
-
const events: IssueChangeEvent[] = [];
|
|
247
|
-
const unsubscribe = client.onIssueChange("i-specific", (event) => {
|
|
248
|
-
events.push(event);
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
unsubscribe();
|
|
252
|
-
expect(typeof unsubscribe).toBe("function");
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
it("should stop polling when all subscribers unsubscribe", async () => {
|
|
256
|
-
const unsub1 = client.onIssueChange(() => {});
|
|
257
|
-
const unsub2 = client.onIssueChange(() => {});
|
|
258
|
-
|
|
259
|
-
unsub1();
|
|
260
|
-
// Polling should still be active (unsub2 still subscribed)
|
|
261
|
-
|
|
262
|
-
unsub2();
|
|
263
|
-
// Now polling should stop
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
it("should handle callback errors gracefully", async () => {
|
|
267
|
-
const goodEvents: IssueChangeEvent[] = [];
|
|
268
|
-
|
|
269
|
-
// Subscribe with a callback that throws
|
|
270
|
-
client.onIssueChange(() => {
|
|
271
|
-
throw new Error("Callback error");
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
// Subscribe with a good callback
|
|
275
|
-
client.onIssueChange((event) => {
|
|
276
|
-
goodEvents.push(event);
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
// Trigger an event manually (this tests error handling in emitChangeEvent)
|
|
280
|
-
// The good callback should still receive events
|
|
281
|
-
});
|
|
282
|
-
});
|
|
283
|
-
|
|
284
|
-
describe("Client factory auto mode", () => {
|
|
285
|
-
it("should timeout properly when server is slow", async () => {
|
|
286
|
-
// Make fetch return a promise that respects abort signal
|
|
287
|
-
// fetch signature is fetch(url, options) where options.signal is the AbortSignal
|
|
288
|
-
mockFetch.mockImplementation((_url: string, options?: { signal?: AbortSignal }) => {
|
|
289
|
-
return new Promise((resolve, reject) => {
|
|
290
|
-
const timeout = setTimeout(() => resolve({ ok: true }), 5000);
|
|
291
|
-
if (options?.signal) {
|
|
292
|
-
options.signal.addEventListener("abort", () => {
|
|
293
|
-
clearTimeout(timeout);
|
|
294
|
-
reject(new Error("Aborted"));
|
|
295
|
-
});
|
|
296
|
-
}
|
|
297
|
-
});
|
|
298
|
-
});
|
|
299
|
-
|
|
300
|
-
const config: SudocodeClientConfig = {
|
|
301
|
-
mode: "auto",
|
|
302
|
-
projectPath: tmpDir,
|
|
303
|
-
autoDetect: {
|
|
304
|
-
timeout: 100, // Very short timeout
|
|
305
|
-
preferManaged: true,
|
|
306
|
-
},
|
|
307
|
-
};
|
|
308
|
-
|
|
309
|
-
const client = await createSudocodeClient(config);
|
|
310
|
-
|
|
311
|
-
try {
|
|
312
|
-
// Should have fallen back to standalone due to timeout
|
|
313
|
-
expect(MockWebSocket.instances).toHaveLength(0);
|
|
314
|
-
expect(client.isReady()).toBe(true);
|
|
315
|
-
} finally {
|
|
316
|
-
client.close();
|
|
317
|
-
}
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
it("should use custom server URL for health check", async () => {
|
|
321
|
-
mockFetch.mockResolvedValueOnce({ ok: true });
|
|
322
|
-
|
|
323
|
-
const config: SudocodeClientConfig = {
|
|
324
|
-
mode: "auto",
|
|
325
|
-
autoDetect: {
|
|
326
|
-
serverUrl: "http://custom-server:9999",
|
|
327
|
-
},
|
|
328
|
-
};
|
|
329
|
-
|
|
330
|
-
const client = await createSudocodeClient(config);
|
|
331
|
-
|
|
332
|
-
try {
|
|
333
|
-
expect(mockFetch).toHaveBeenCalledWith(
|
|
334
|
-
"http://custom-server:9999/health",
|
|
335
|
-
expect.anything()
|
|
336
|
-
);
|
|
337
|
-
} finally {
|
|
338
|
-
client.close();
|
|
339
|
-
}
|
|
340
|
-
});
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
describe("checkServerHealth edge cases", () => {
|
|
344
|
-
it("should handle network errors", async () => {
|
|
345
|
-
mockFetch.mockRejectedValueOnce(new Error("ECONNREFUSED"));
|
|
346
|
-
|
|
347
|
-
const result = await checkServerHealth("http://localhost:3001");
|
|
348
|
-
expect(result).toBe(false);
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
it("should handle 500 response", async () => {
|
|
352
|
-
mockFetch.mockResolvedValueOnce({ ok: false, status: 500 });
|
|
353
|
-
|
|
354
|
-
const result = await checkServerHealth("http://localhost:3001");
|
|
355
|
-
expect(result).toBe(false);
|
|
356
|
-
});
|
|
357
|
-
|
|
358
|
-
it("should handle 404 response", async () => {
|
|
359
|
-
mockFetch.mockResolvedValueOnce({ ok: false, status: 404 });
|
|
360
|
-
|
|
361
|
-
const result = await checkServerHealth("http://localhost:3001");
|
|
362
|
-
expect(result).toBe(false);
|
|
363
|
-
});
|
|
364
|
-
});
|
|
365
|
-
|
|
366
|
-
describe("StandaloneClient feedback operations", () => {
|
|
367
|
-
let client: StandaloneClient;
|
|
368
|
-
|
|
369
|
-
beforeEach(async () => {
|
|
370
|
-
client = await createStandaloneClient({ projectPath: tmpDir });
|
|
371
|
-
});
|
|
372
|
-
|
|
373
|
-
afterEach(() => {
|
|
374
|
-
client.close();
|
|
375
|
-
});
|
|
376
|
-
|
|
377
|
-
it("should handle addFeedback gracefully even if not supported", async () => {
|
|
378
|
-
// addFeedback should not throw even if the CLI doesn't support it
|
|
379
|
-
await expect(
|
|
380
|
-
client.addFeedback("i-from", "s-to", {
|
|
381
|
-
type: "comment",
|
|
382
|
-
content: "Test feedback",
|
|
383
|
-
})
|
|
384
|
-
).resolves.not.toThrow();
|
|
385
|
-
});
|
|
386
|
-
});
|
|
387
|
-
|
|
388
|
-
describe("StandaloneClient lifecycle", () => {
|
|
389
|
-
it("should clean up resources on close", async () => {
|
|
390
|
-
const client = await createStandaloneClient({
|
|
391
|
-
projectPath: tmpDir,
|
|
392
|
-
pollInterval: 100,
|
|
393
|
-
});
|
|
394
|
-
|
|
395
|
-
// Subscribe to start polling
|
|
396
|
-
client.onIssueChange(() => {});
|
|
397
|
-
|
|
398
|
-
expect(client.isReady()).toBe(true);
|
|
399
|
-
|
|
400
|
-
client.close();
|
|
401
|
-
|
|
402
|
-
expect(client.isReady()).toBe(false);
|
|
403
|
-
|
|
404
|
-
// Operations should fail after close
|
|
405
|
-
await expect(client.getIssue("i-test")).rejects.toThrow("not initialized");
|
|
406
|
-
});
|
|
407
|
-
|
|
408
|
-
it("should handle multiple close calls", async () => {
|
|
409
|
-
const client = await createStandaloneClient({ projectPath: tmpDir });
|
|
410
|
-
|
|
411
|
-
client.close();
|
|
412
|
-
client.close(); // Should not throw
|
|
413
|
-
client.close(); // Should not throw
|
|
414
|
-
|
|
415
|
-
expect(client.isReady()).toBe(false);
|
|
416
|
-
});
|
|
417
|
-
});
|
|
418
|
-
});
|