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,623 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* StandaloneClient - SudocodeClient implementation for standalone mode
|
|
3
|
-
*
|
|
4
|
-
* Accesses sudocode data directly via CLI operations and file watching.
|
|
5
|
-
* This is used when macro-agent runs independently without a sudocode server.
|
|
6
|
-
*
|
|
7
|
-
* @module task/backend/sudocode/standalone-client
|
|
8
|
-
* @see s-8472 Pluggable Task Backend Integration with Sudocode
|
|
9
|
-
* @see i-1ju3 7A.3: Implement StandaloneClient (standalone mode)
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { existsSync, mkdirSync } from "fs";
|
|
13
|
-
import { join } from "path";
|
|
14
|
-
|
|
15
|
-
import type {
|
|
16
|
-
SudocodeClient,
|
|
17
|
-
StandaloneClientConfig,
|
|
18
|
-
ListIssuesOptions,
|
|
19
|
-
ListSpecsOptions,
|
|
20
|
-
UpdateIssueInput,
|
|
21
|
-
FeedbackInput,
|
|
22
|
-
IssueChangeCallback,
|
|
23
|
-
IssueChangeEvent,
|
|
24
|
-
Unsubscribe,
|
|
25
|
-
} from "./client.js";
|
|
26
|
-
|
|
27
|
-
import type {
|
|
28
|
-
Issue,
|
|
29
|
-
Spec,
|
|
30
|
-
RelationshipType,
|
|
31
|
-
EntityType,
|
|
32
|
-
} from "@sudocode-ai/types";
|
|
33
|
-
|
|
34
|
-
import type { Database } from "better-sqlite3";
|
|
35
|
-
|
|
36
|
-
// Import CLI operations - these may throw if not available
|
|
37
|
-
// We use dynamic import to handle this gracefully
|
|
38
|
-
type SudocodeCliModule = typeof import("@sudocode-ai/cli");
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* StandaloneClient - CLI-based client for standalone mode
|
|
42
|
-
*
|
|
43
|
-
* Uses @sudocode-ai/cli operations directly against a SQLite database.
|
|
44
|
-
* Supports event subscriptions via polling since we don't have file watchers here.
|
|
45
|
-
*/
|
|
46
|
-
export class StandaloneClient implements SudocodeClient {
|
|
47
|
-
private config: StandaloneClientConfig;
|
|
48
|
-
private ready: boolean = false;
|
|
49
|
-
private db: Database | null = null;
|
|
50
|
-
private cli: SudocodeCliModule | null = null;
|
|
51
|
-
private changeCallbacks: Map<string | "*", Set<IssueChangeCallback>> =
|
|
52
|
-
new Map();
|
|
53
|
-
private pollTimer: ReturnType<typeof setInterval> | null = null;
|
|
54
|
-
private lastKnownIssues: Map<string, Issue> = new Map();
|
|
55
|
-
|
|
56
|
-
constructor(config: StandaloneClientConfig) {
|
|
57
|
-
this.config = {
|
|
58
|
-
projectPath: config.projectPath,
|
|
59
|
-
pollInterval: config.pollInterval ?? 5000,
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Initialize the client - must be called before other operations
|
|
65
|
-
*/
|
|
66
|
-
async init(): Promise<void> {
|
|
67
|
-
if (this.ready) return;
|
|
68
|
-
|
|
69
|
-
// Dynamically import CLI module
|
|
70
|
-
try {
|
|
71
|
-
this.cli = await import("@sudocode-ai/cli");
|
|
72
|
-
} catch (error) {
|
|
73
|
-
throw new Error(
|
|
74
|
-
`Failed to load @sudocode-ai/cli package: ${error instanceof Error ? error.message : String(error)}`
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Initialize database
|
|
79
|
-
const sudocodeDir = join(this.config.projectPath, ".sudocode");
|
|
80
|
-
const dbPath = join(sudocodeDir, "cache.db");
|
|
81
|
-
|
|
82
|
-
// Create .sudocode directory if it doesn't exist
|
|
83
|
-
if (!existsSync(sudocodeDir)) {
|
|
84
|
-
mkdirSync(sudocodeDir, { recursive: true });
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Initialize database (creates schema if needed)
|
|
88
|
-
this.db = this.cli.initDatabase({ path: dbPath });
|
|
89
|
-
|
|
90
|
-
this.ready = true;
|
|
91
|
-
|
|
92
|
-
// Load initial state for change detection
|
|
93
|
-
await this.refreshIssueCache();
|
|
94
|
-
|
|
95
|
-
// Start polling for changes if we have subscribers
|
|
96
|
-
this.startPollingIfNeeded();
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Internal: ensure client is initialized
|
|
101
|
-
*/
|
|
102
|
-
private ensureReady(): void {
|
|
103
|
-
if (!this.ready || !this.db || !this.cli) {
|
|
104
|
-
throw new Error(
|
|
105
|
-
"StandaloneClient not initialized. Call init() before using."
|
|
106
|
-
);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Internal: get the CLI module (type-safe helper)
|
|
112
|
-
*/
|
|
113
|
-
private getCli(): SudocodeCliModule {
|
|
114
|
-
this.ensureReady();
|
|
115
|
-
return this.cli!;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Internal: get the database (type-safe helper)
|
|
120
|
-
*/
|
|
121
|
-
private getDb(): Database {
|
|
122
|
-
this.ensureReady();
|
|
123
|
-
return this.db!;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// ─── Issue Operations ────────────────────────────────────────────────────────
|
|
127
|
-
|
|
128
|
-
async getIssue(id: string): Promise<Issue | null> {
|
|
129
|
-
this.ensureReady();
|
|
130
|
-
const issue = this.getCli().getIssue(this.getDb(), id);
|
|
131
|
-
return issue ?? null;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
async listIssues(filter?: ListIssuesOptions): Promise<Issue[]> {
|
|
135
|
-
this.ensureReady();
|
|
136
|
-
const cli = this.getCli();
|
|
137
|
-
const db = this.getDb();
|
|
138
|
-
|
|
139
|
-
if (filter?.search) {
|
|
140
|
-
// Use search function for text search
|
|
141
|
-
return cli.searchIssues(db, filter.search, {
|
|
142
|
-
status: filter.status,
|
|
143
|
-
priority: filter.priority,
|
|
144
|
-
archived: filter.archived,
|
|
145
|
-
limit: filter.limit,
|
|
146
|
-
}) as Issue[];
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
return cli.listIssues(db, {
|
|
150
|
-
status: filter?.status,
|
|
151
|
-
priority: filter?.priority,
|
|
152
|
-
archived: filter?.archived,
|
|
153
|
-
limit: filter?.limit,
|
|
154
|
-
}) as Issue[];
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
async getReadyIssues(): Promise<Issue[]> {
|
|
158
|
-
this.ensureReady();
|
|
159
|
-
return this.getCli().getReadyIssues(this.getDb()) as Issue[];
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
async updateIssue(id: string, updates: UpdateIssueInput): Promise<Issue> {
|
|
163
|
-
this.ensureReady();
|
|
164
|
-
const cli = this.getCli();
|
|
165
|
-
const db = this.getDb();
|
|
166
|
-
|
|
167
|
-
// Transform updates to match CLI's expected type (convert null to undefined)
|
|
168
|
-
const cliUpdates = {
|
|
169
|
-
...updates,
|
|
170
|
-
assignee: updates.assignee === null ? undefined : updates.assignee,
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
const previousIssue = cli.getIssue(db, id) as Issue | undefined;
|
|
174
|
-
const updated = cli.updateIssue(db, id, cliUpdates) as Issue;
|
|
175
|
-
|
|
176
|
-
// Emit change event
|
|
177
|
-
if (previousIssue) {
|
|
178
|
-
this.emitChangeEvent({
|
|
179
|
-
type: updates.status !== previousIssue.status ? "status_changed" : "updated",
|
|
180
|
-
issueId: id,
|
|
181
|
-
issue: updated,
|
|
182
|
-
previousIssue,
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
return updated;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// ─── Relationship Operations ─────────────────────────────────────────────────
|
|
190
|
-
|
|
191
|
-
async createLink(
|
|
192
|
-
from: string,
|
|
193
|
-
to: string,
|
|
194
|
-
type: RelationshipType
|
|
195
|
-
): Promise<void> {
|
|
196
|
-
this.ensureReady();
|
|
197
|
-
const cli = this.getCli();
|
|
198
|
-
const db = this.getDb();
|
|
199
|
-
|
|
200
|
-
const fromType = this.inferEntityType(from);
|
|
201
|
-
const toType = this.inferEntityType(to);
|
|
202
|
-
|
|
203
|
-
cli.addRelationship(db, {
|
|
204
|
-
from_id: from,
|
|
205
|
-
from_type: fromType,
|
|
206
|
-
to_id: to,
|
|
207
|
-
to_type: toType,
|
|
208
|
-
relationship_type: type,
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
// If this was a 'blocks' relationship between issues, emit events
|
|
212
|
-
if (type === "blocks" && fromType === "issue" && toType === "issue") {
|
|
213
|
-
const toIssue = cli.getIssue(db, to) as Issue | undefined;
|
|
214
|
-
if (toIssue && toIssue.status === "blocked") {
|
|
215
|
-
this.emitChangeEvent({
|
|
216
|
-
type: "blocked",
|
|
217
|
-
issueId: to,
|
|
218
|
-
issue: toIssue,
|
|
219
|
-
metadata: { blockerId: from },
|
|
220
|
-
});
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
async removeLink(
|
|
226
|
-
from: string,
|
|
227
|
-
to: string,
|
|
228
|
-
type: RelationshipType
|
|
229
|
-
): Promise<void> {
|
|
230
|
-
this.ensureReady();
|
|
231
|
-
const cli = this.getCli();
|
|
232
|
-
const db = this.getDb();
|
|
233
|
-
|
|
234
|
-
const fromType = this.inferEntityType(from);
|
|
235
|
-
const toType = this.inferEntityType(to);
|
|
236
|
-
|
|
237
|
-
cli.removeRelationship(db, from, fromType, to, toType, type);
|
|
238
|
-
|
|
239
|
-
// If this was a 'blocks' relationship, check if issue was unblocked
|
|
240
|
-
if (type === "blocks" && fromType === "issue" && toType === "issue") {
|
|
241
|
-
const toIssue = cli.getIssue(db, to) as Issue | undefined;
|
|
242
|
-
if (toIssue && toIssue.status !== "blocked") {
|
|
243
|
-
this.emitChangeEvent({
|
|
244
|
-
type: "unblocked",
|
|
245
|
-
issueId: to,
|
|
246
|
-
issue: toIssue,
|
|
247
|
-
metadata: { formerBlockerId: from },
|
|
248
|
-
});
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
async getBlockers(issueId: string): Promise<Issue[]> {
|
|
254
|
-
this.ensureReady();
|
|
255
|
-
const cli = this.getCli();
|
|
256
|
-
const db = this.getDb();
|
|
257
|
-
|
|
258
|
-
// Get incoming 'blocks' relationships (other issues that block this one)
|
|
259
|
-
const incomingBlocks = cli.getIncomingRelationships(
|
|
260
|
-
db,
|
|
261
|
-
issueId,
|
|
262
|
-
"issue",
|
|
263
|
-
"blocks"
|
|
264
|
-
);
|
|
265
|
-
|
|
266
|
-
// Get outgoing 'depends-on' relationships (this issue depends on others)
|
|
267
|
-
const outgoingDependsOn = cli.getOutgoingRelationships(
|
|
268
|
-
db,
|
|
269
|
-
issueId,
|
|
270
|
-
"issue",
|
|
271
|
-
"depends-on"
|
|
272
|
-
);
|
|
273
|
-
|
|
274
|
-
const blockerIds = new Set<string>();
|
|
275
|
-
|
|
276
|
-
// For 'blocks': from_id blocks issueId
|
|
277
|
-
for (const rel of incomingBlocks) {
|
|
278
|
-
if (rel.from_type === "issue") {
|
|
279
|
-
blockerIds.add(rel.from_id);
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// For 'depends-on': issueId depends-on to_id
|
|
284
|
-
for (const rel of outgoingDependsOn) {
|
|
285
|
-
if (rel.to_type === "issue") {
|
|
286
|
-
blockerIds.add(rel.to_id);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
const blockers: Issue[] = [];
|
|
291
|
-
for (const blockerId of blockerIds) {
|
|
292
|
-
const blocker = cli.getIssue(db, blockerId) as Issue | undefined;
|
|
293
|
-
if (blocker) {
|
|
294
|
-
blockers.push(blocker);
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
return blockers;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
async getBlocking(issueId: string): Promise<Issue[]> {
|
|
302
|
-
this.ensureReady();
|
|
303
|
-
const cli = this.getCli();
|
|
304
|
-
const db = this.getDb();
|
|
305
|
-
|
|
306
|
-
// Get outgoing 'blocks' relationships (this issue blocks others)
|
|
307
|
-
const outgoingBlocks = cli.getOutgoingRelationships(
|
|
308
|
-
db,
|
|
309
|
-
issueId,
|
|
310
|
-
"issue",
|
|
311
|
-
"blocks"
|
|
312
|
-
);
|
|
313
|
-
|
|
314
|
-
// Get incoming 'depends-on' relationships (others depend on this issue)
|
|
315
|
-
const incomingDependsOn = cli.getIncomingRelationships(
|
|
316
|
-
db,
|
|
317
|
-
issueId,
|
|
318
|
-
"issue",
|
|
319
|
-
"depends-on"
|
|
320
|
-
);
|
|
321
|
-
|
|
322
|
-
const blockedIds = new Set<string>();
|
|
323
|
-
|
|
324
|
-
// For 'blocks': issueId blocks to_id
|
|
325
|
-
for (const rel of outgoingBlocks) {
|
|
326
|
-
if (rel.to_type === "issue") {
|
|
327
|
-
blockedIds.add(rel.to_id);
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
// For 'depends-on': from_id depends-on issueId
|
|
332
|
-
for (const rel of incomingDependsOn) {
|
|
333
|
-
if (rel.from_type === "issue") {
|
|
334
|
-
blockedIds.add(rel.from_id);
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
const blocked: Issue[] = [];
|
|
339
|
-
for (const blockedId of blockedIds) {
|
|
340
|
-
const issue = cli.getIssue(db, blockedId) as Issue | undefined;
|
|
341
|
-
if (issue) {
|
|
342
|
-
blocked.push(issue);
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
return blocked;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
// ─── Spec Operations ─────────────────────────────────────────────────────────
|
|
350
|
-
|
|
351
|
-
async getSpec(id: string): Promise<Spec | null> {
|
|
352
|
-
this.ensureReady();
|
|
353
|
-
const spec = this.getCli().getSpec(this.getDb(), id);
|
|
354
|
-
return (spec as Spec) ?? null;
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
async listSpecs(filter?: ListSpecsOptions): Promise<Spec[]> {
|
|
358
|
-
this.ensureReady();
|
|
359
|
-
const cli = this.getCli();
|
|
360
|
-
const db = this.getDb();
|
|
361
|
-
|
|
362
|
-
if (filter?.search) {
|
|
363
|
-
return cli.searchSpecs(db, filter.search, {
|
|
364
|
-
limit: filter.limit,
|
|
365
|
-
}) as Spec[];
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
return cli.listSpecs(db, {
|
|
369
|
-
limit: filter?.limit,
|
|
370
|
-
}) as Spec[];
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
// ─── Feedback Operations ─────────────────────────────────────────────────────
|
|
374
|
-
|
|
375
|
-
async addFeedback(
|
|
376
|
-
fromIssueId: string | undefined,
|
|
377
|
-
toId: string,
|
|
378
|
-
feedback: FeedbackInput
|
|
379
|
-
): Promise<void> {
|
|
380
|
-
this.ensureReady();
|
|
381
|
-
const cli = this.getCli();
|
|
382
|
-
const db = this.getDb();
|
|
383
|
-
|
|
384
|
-
// Check if addFeedback is available
|
|
385
|
-
if (typeof (cli as unknown as Record<string, unknown>).addFeedback !== "function") {
|
|
386
|
-
// Fallback: feedback not supported in this CLI version
|
|
387
|
-
console.warn("Feedback operations not supported in this @sudocode-ai/cli version");
|
|
388
|
-
return;
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
// Call addFeedback if available
|
|
392
|
-
(cli as unknown as { addFeedback: (db: Database, input: unknown) => void }).addFeedback(db, {
|
|
393
|
-
from_id: fromIssueId,
|
|
394
|
-
to_id: toId,
|
|
395
|
-
type: feedback.type,
|
|
396
|
-
content: feedback.content,
|
|
397
|
-
agent: feedback.agent,
|
|
398
|
-
line: feedback.anchor?.line,
|
|
399
|
-
text: feedback.anchor?.text,
|
|
400
|
-
});
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
// ─── Event Subscription ──────────────────────────────────────────────────────
|
|
404
|
-
|
|
405
|
-
onIssueChange(callback: IssueChangeCallback): Unsubscribe;
|
|
406
|
-
onIssueChange(issueId: string, callback: IssueChangeCallback): Unsubscribe;
|
|
407
|
-
onIssueChange(
|
|
408
|
-
callbackOrId: IssueChangeCallback | string,
|
|
409
|
-
maybeCallback?: IssueChangeCallback
|
|
410
|
-
): Unsubscribe {
|
|
411
|
-
let key: string;
|
|
412
|
-
let callback: IssueChangeCallback;
|
|
413
|
-
|
|
414
|
-
if (typeof callbackOrId === "function") {
|
|
415
|
-
key = "*";
|
|
416
|
-
callback = callbackOrId;
|
|
417
|
-
} else {
|
|
418
|
-
key = callbackOrId;
|
|
419
|
-
callback = maybeCallback!;
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
if (!this.changeCallbacks.has(key)) {
|
|
423
|
-
this.changeCallbacks.set(key, new Set());
|
|
424
|
-
}
|
|
425
|
-
this.changeCallbacks.get(key)!.add(callback);
|
|
426
|
-
|
|
427
|
-
// Start polling if we have subscribers and not already polling
|
|
428
|
-
this.startPollingIfNeeded();
|
|
429
|
-
|
|
430
|
-
return () => {
|
|
431
|
-
const callbacks = this.changeCallbacks.get(key);
|
|
432
|
-
if (callbacks) {
|
|
433
|
-
callbacks.delete(callback);
|
|
434
|
-
if (callbacks.size === 0) {
|
|
435
|
-
this.changeCallbacks.delete(key);
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
// Stop polling if no more subscribers
|
|
440
|
-
this.stopPollingIfNotNeeded();
|
|
441
|
-
};
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
// ─── Lifecycle ───────────────────────────────────────────────────────────────
|
|
445
|
-
|
|
446
|
-
isReady(): boolean {
|
|
447
|
-
return this.ready;
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
close(): void {
|
|
451
|
-
this.ready = false;
|
|
452
|
-
|
|
453
|
-
// Stop polling
|
|
454
|
-
if (this.pollTimer) {
|
|
455
|
-
clearInterval(this.pollTimer);
|
|
456
|
-
this.pollTimer = null;
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
// Close database
|
|
460
|
-
if (this.db) {
|
|
461
|
-
this.db.close();
|
|
462
|
-
this.db = null;
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
// Clear callbacks
|
|
466
|
-
this.changeCallbacks.clear();
|
|
467
|
-
this.lastKnownIssues.clear();
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
// ─── Internal Helpers ────────────────────────────────────────────────────────
|
|
471
|
-
|
|
472
|
-
/**
|
|
473
|
-
* Infer entity type from ID prefix
|
|
474
|
-
*/
|
|
475
|
-
private inferEntityType(id: string): EntityType {
|
|
476
|
-
if (id.startsWith("i-")) return "issue";
|
|
477
|
-
if (id.startsWith("s-")) return "spec";
|
|
478
|
-
throw new Error(`Cannot infer entity type from ID: ${id}`);
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
/**
|
|
482
|
-
* Emit a change event to subscribers
|
|
483
|
-
*/
|
|
484
|
-
private emitChangeEvent(event: IssueChangeEvent): void {
|
|
485
|
-
// Notify specific subscribers
|
|
486
|
-
const specificCallbacks = this.changeCallbacks.get(event.issueId);
|
|
487
|
-
if (specificCallbacks) {
|
|
488
|
-
for (const callback of specificCallbacks) {
|
|
489
|
-
try {
|
|
490
|
-
callback(event);
|
|
491
|
-
} catch (error) {
|
|
492
|
-
console.error("Error in issue change callback:", error);
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
// Notify global subscribers
|
|
498
|
-
const globalCallbacks = this.changeCallbacks.get("*");
|
|
499
|
-
if (globalCallbacks) {
|
|
500
|
-
for (const callback of globalCallbacks) {
|
|
501
|
-
try {
|
|
502
|
-
callback(event);
|
|
503
|
-
} catch (error) {
|
|
504
|
-
console.error("Error in issue change callback:", error);
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
/**
|
|
511
|
-
* Refresh the internal issue cache for change detection
|
|
512
|
-
*/
|
|
513
|
-
private async refreshIssueCache(): Promise<void> {
|
|
514
|
-
if (!this.ready || !this.db || !this.cli) return;
|
|
515
|
-
|
|
516
|
-
const issues = this.cli.listIssues(this.db, { archived: false }) as Issue[];
|
|
517
|
-
this.lastKnownIssues.clear();
|
|
518
|
-
for (const issue of issues) {
|
|
519
|
-
this.lastKnownIssues.set(issue.id, issue);
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
/**
|
|
524
|
-
* Poll for changes and emit events
|
|
525
|
-
*/
|
|
526
|
-
private async pollForChanges(): Promise<void> {
|
|
527
|
-
if (!this.ready || !this.db || !this.cli) return;
|
|
528
|
-
|
|
529
|
-
const currentIssues = this.cli.listIssues(this.db, { archived: false }) as Issue[];
|
|
530
|
-
const currentMap = new Map<string, Issue>();
|
|
531
|
-
for (const issue of currentIssues) {
|
|
532
|
-
currentMap.set(issue.id, issue);
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
// Check for deleted issues
|
|
536
|
-
for (const [id, previousIssue] of this.lastKnownIssues) {
|
|
537
|
-
if (!currentMap.has(id)) {
|
|
538
|
-
this.emitChangeEvent({
|
|
539
|
-
type: "deleted",
|
|
540
|
-
issueId: id,
|
|
541
|
-
previousIssue,
|
|
542
|
-
});
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
// Check for new or changed issues
|
|
547
|
-
for (const [id, currentIssue] of currentMap) {
|
|
548
|
-
const previousIssue = this.lastKnownIssues.get(id);
|
|
549
|
-
|
|
550
|
-
if (!previousIssue) {
|
|
551
|
-
// New issue
|
|
552
|
-
this.emitChangeEvent({
|
|
553
|
-
type: "created",
|
|
554
|
-
issueId: id,
|
|
555
|
-
issue: currentIssue,
|
|
556
|
-
});
|
|
557
|
-
} else if (currentIssue.updated_at !== previousIssue.updated_at) {
|
|
558
|
-
// Changed issue - determine the type of change
|
|
559
|
-
let changeType: IssueChangeEvent["type"] = "updated";
|
|
560
|
-
|
|
561
|
-
if (currentIssue.status !== previousIssue.status) {
|
|
562
|
-
if (currentIssue.status === "blocked") {
|
|
563
|
-
changeType = "blocked";
|
|
564
|
-
} else if (previousIssue.status === "blocked") {
|
|
565
|
-
changeType = "unblocked";
|
|
566
|
-
} else {
|
|
567
|
-
changeType = "status_changed";
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
this.emitChangeEvent({
|
|
572
|
-
type: changeType,
|
|
573
|
-
issueId: id,
|
|
574
|
-
issue: currentIssue,
|
|
575
|
-
previousIssue,
|
|
576
|
-
});
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
// Update cache
|
|
581
|
-
this.lastKnownIssues = currentMap;
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
/**
|
|
585
|
-
* Start polling if needed
|
|
586
|
-
*/
|
|
587
|
-
private startPollingIfNeeded(): void {
|
|
588
|
-
if (this.pollTimer) return; // Already polling
|
|
589
|
-
if (this.changeCallbacks.size === 0) return; // No subscribers
|
|
590
|
-
if (!this.ready) return; // Not initialized
|
|
591
|
-
|
|
592
|
-
this.pollTimer = setInterval(
|
|
593
|
-
() => this.pollForChanges(),
|
|
594
|
-
this.config.pollInterval
|
|
595
|
-
);
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
/**
|
|
599
|
-
* Stop polling if not needed
|
|
600
|
-
*/
|
|
601
|
-
private stopPollingIfNotNeeded(): void {
|
|
602
|
-
if (this.changeCallbacks.size > 0) return; // Still have subscribers
|
|
603
|
-
|
|
604
|
-
if (this.pollTimer) {
|
|
605
|
-
clearInterval(this.pollTimer);
|
|
606
|
-
this.pollTimer = null;
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
/**
|
|
612
|
-
* Create and initialize a StandaloneClient
|
|
613
|
-
*
|
|
614
|
-
* @param config Client configuration
|
|
615
|
-
* @returns Initialized StandaloneClient
|
|
616
|
-
*/
|
|
617
|
-
export async function createStandaloneClient(
|
|
618
|
-
config: StandaloneClientConfig
|
|
619
|
-
): Promise<StandaloneClient> {
|
|
620
|
-
const client = new StandaloneClient(config);
|
|
621
|
-
await client.init();
|
|
622
|
-
return client;
|
|
623
|
-
}
|