devchain-cli 0.22.1 → 0.23.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/dist/drizzle/0077_familiar_morbius.sql +6 -0
- package/dist/drizzle/0078_preserve_legacy_connection_ownership.sql +180 -0
- package/dist/drizzle/0079_premium_forgotten_one.sql +18 -0
- package/dist/drizzle/0080_lethal_thunderbolt_ross.sql +49 -0
- package/dist/drizzle/0081_early_kronos.sql +58 -0
- package/dist/drizzle/0082_huge_black_queen.sql +14 -0
- package/dist/drizzle/meta/0077_snapshot.json +6957 -0
- package/dist/drizzle/meta/0078_snapshot.json +6957 -0
- package/dist/drizzle/meta/0079_snapshot.json +7093 -0
- package/dist/drizzle/meta/0080_snapshot.json +7241 -0
- package/dist/drizzle/meta/0081_snapshot.json +7482 -0
- package/dist/drizzle/meta/0082_snapshot.json +7571 -0
- package/dist/drizzle/meta/_journal.json +42 -0
- package/dist/node_modules/@devchain/shared/tsconfig.tsbuildinfo +1 -1
- package/dist/server/common/errors/error-types.d.ts +7 -0
- package/dist/server/common/errors/error-types.js +7 -1
- package/dist/server/common/errors/error-types.js.map +1 -1
- package/dist/server/modules/epic-time/controllers/agent-time-buffer.controller.d.ts +8 -0
- package/dist/server/modules/epic-time/controllers/agent-time-buffer.controller.js +67 -0
- package/dist/server/modules/epic-time/controllers/agent-time-buffer.controller.js.map +1 -0
- package/dist/server/modules/epic-time/controllers/external-estimate-log.controller.d.ts +12 -0
- package/dist/server/modules/epic-time/controllers/external-estimate-log.controller.js +322 -0
- package/dist/server/modules/epic-time/controllers/external-estimate-log.controller.js.map +1 -0
- package/dist/server/modules/epic-time/epic-time.module.js +14 -4
- package/dist/server/modules/epic-time/epic-time.module.js.map +1 -1
- package/dist/server/modules/epic-time/models/epic-time-daily-allocator.d.ts +30 -0
- package/dist/server/modules/epic-time/models/epic-time-daily-allocator.js +125 -0
- package/dist/server/modules/epic-time/models/epic-time-daily-allocator.js.map +1 -0
- package/dist/server/modules/epic-time/models/epic-time-local-day.d.ts +9 -0
- package/dist/server/modules/epic-time/models/epic-time-local-day.js +164 -0
- package/dist/server/modules/epic-time/models/epic-time-local-day.js.map +1 -0
- package/dist/server/modules/epic-time/models/epic-time.models.d.ts +138 -0
- package/dist/server/modules/epic-time/models/epic-time.models.js +2 -0
- package/dist/server/modules/epic-time/models/epic-time.models.js.map +1 -1
- package/dist/server/modules/epic-time/services/agent-time-accounting.service.d.ts +1 -0
- package/dist/server/modules/epic-time/services/agent-time-accounting.service.js +31 -8
- package/dist/server/modules/epic-time/services/agent-time-accounting.service.js.map +1 -1
- package/dist/server/modules/epic-time/services/epic-estimate-logging.service.d.ts +37 -0
- package/dist/server/modules/epic-time/services/epic-estimate-logging.service.js +716 -0
- package/dist/server/modules/epic-time/services/epic-estimate-logging.service.js.map +1 -0
- package/dist/server/modules/epic-time/services/epic-time.service.d.ts +12 -3
- package/dist/server/modules/epic-time/services/epic-time.service.js +155 -69
- package/dist/server/modules/epic-time/services/epic-time.service.js.map +1 -1
- package/dist/server/modules/epic-time/services/epic-time.store.d.ts +29 -3
- package/dist/server/modules/epic-time/services/epic-time.store.js +524 -40
- package/dist/server/modules/epic-time/services/epic-time.store.js.map +1 -1
- package/dist/server/modules/epics/controllers/epics.controller.d.ts +10 -1
- package/dist/server/modules/epics/controllers/epics.controller.js +108 -2
- package/dist/server/modules/epics/controllers/epics.controller.js.map +1 -1
- package/dist/server/modules/epics/epics.module.js +8 -2
- package/dist/server/modules/epics/epics.module.js.map +1 -1
- package/dist/server/modules/epics/models/epic-relations.models.d.ts +34 -0
- package/dist/server/modules/epics/models/epic-relations.models.js +16 -0
- package/dist/server/modules/epics/models/epic-relations.models.js.map +1 -0
- package/dist/server/modules/epics/services/epic-relation-target-resolver.d.ts +8 -0
- package/dist/server/modules/epics/services/epic-relation-target-resolver.js +37 -0
- package/dist/server/modules/epics/services/epic-relation-target-resolver.js.map +1 -0
- package/dist/server/modules/epics/services/epic-relations.service.d.ts +52 -0
- package/dist/server/modules/epics/services/epic-relations.service.js +135 -0
- package/dist/server/modules/epics/services/epic-relations.service.js.map +1 -0
- package/dist/server/modules/epics/services/epics.service.d.ts +9 -2
- package/dist/server/modules/epics/services/epics.service.js +115 -11
- package/dist/server/modules/epics/services/epics.service.js.map +1 -1
- package/dist/server/modules/events/catalog/broadcast-registry.js +16 -0
- package/dist/server/modules/events/catalog/broadcast-registry.js.map +1 -1
- package/dist/server/modules/events/catalog/epic.relations.invalidated.d.ts +12 -0
- package/dist/server/modules/events/catalog/epic.relations.invalidated.js +13 -0
- package/dist/server/modules/events/catalog/epic.relations.invalidated.js.map +1 -0
- package/dist/server/modules/events/catalog/epic.time.scope.invalidated.d.ts +12 -0
- package/dist/server/modules/events/catalog/epic.time.scope.invalidated.js +13 -0
- package/dist/server/modules/events/catalog/epic.time.scope.invalidated.js.map +1 -0
- package/dist/server/modules/events/catalog/index.d.ts +27 -4
- package/dist/server/modules/events/catalog/index.js +6 -0
- package/dist/server/modules/events/catalog/index.js.map +1 -1
- package/dist/server/modules/events/catalog/integration.connection.created.d.ts +4 -1
- package/dist/server/modules/events/catalog/integration.connection.created.js +5 -2
- package/dist/server/modules/events/catalog/integration.connection.created.js.map +1 -1
- package/dist/server/modules/events/catalog/integration.connection.deleted.d.ts +4 -1
- package/dist/server/modules/events/catalog/integration.connection.deleted.js +5 -2
- package/dist/server/modules/events/catalog/integration.connection.deleted.js.map +1 -1
- package/dist/server/modules/events/catalog/integration.connection.legacy.d.ts +85 -0
- package/dist/server/modules/events/catalog/integration.connection.legacy.js +41 -0
- package/dist/server/modules/events/catalog/integration.connection.legacy.js.map +1 -0
- package/dist/server/modules/events/catalog/integration.connection.updated.d.ts +4 -1
- package/dist/server/modules/events/catalog/integration.connection.updated.js +5 -2
- package/dist/server/modules/events/catalog/integration.connection.updated.js.map +1 -1
- package/dist/server/modules/events/services/committed-event-replay.d.ts +10 -0
- package/dist/server/modules/events/services/committed-event-replay.js +30 -0
- package/dist/server/modules/events/services/committed-event-replay.js.map +1 -0
- package/dist/server/modules/events/services/committed-event.store.d.ts +1 -0
- package/dist/server/modules/events/services/committed-event.store.js +12 -7
- package/dist/server/modules/events/services/committed-event.store.js.map +1 -1
- package/dist/server/modules/events/services/durable-event-registry.service.d.ts +9 -2
- package/dist/server/modules/events/services/durable-event-registry.service.js.map +1 -1
- package/dist/server/modules/external-integrations/adapters/clickup-external-task.provider.d.ts +7 -0
- package/dist/server/modules/external-integrations/adapters/clickup-external-task.provider.js +158 -50
- package/dist/server/modules/external-integrations/adapters/clickup-external-task.provider.js.map +1 -1
- package/dist/server/modules/external-integrations/adapters/jira-external-task.provider.d.ts +4 -0
- package/dist/server/modules/external-integrations/adapters/jira-external-task.provider.js +73 -33
- package/dist/server/modules/external-integrations/adapters/jira-external-task.provider.js.map +1 -1
- package/dist/server/modules/external-integrations/connections/integration-connections.controller.d.ts +6 -3
- package/dist/server/modules/external-integrations/connections/integration-connections.controller.js +67 -12
- package/dist/server/modules/external-integrations/connections/integration-connections.controller.js.map +1 -1
- package/dist/server/modules/external-integrations/connections/integration-connections.service.d.ts +35 -3
- package/dist/server/modules/external-integrations/connections/integration-connections.service.js +178 -17
- package/dist/server/modules/external-integrations/connections/integration-connections.service.js.map +1 -1
- package/dist/server/modules/external-integrations/connections/managed-subtask-sync.controller.d.ts +6 -3
- package/dist/server/modules/external-integrations/connections/managed-subtask-sync.controller.js +51 -11
- package/dist/server/modules/external-integrations/connections/managed-subtask-sync.controller.js.map +1 -1
- package/dist/server/modules/external-integrations/external-integrations.module.js +6 -1
- package/dist/server/modules/external-integrations/external-integrations.module.js.map +1 -1
- package/dist/server/modules/external-integrations/models/external-edit-session.models.d.ts +1 -0
- package/dist/server/modules/external-integrations/models/external-provider.models.d.ts +6 -2
- package/dist/server/modules/external-integrations/models/external-provider.models.js +2 -3
- package/dist/server/modules/external-integrations/models/external-provider.models.js.map +1 -1
- package/dist/server/modules/external-integrations/models/external-time-mutation.models.d.ts +33 -2
- package/dist/server/modules/external-integrations/models/external-time-mutation.models.js +14 -0
- package/dist/server/modules/external-integrations/models/external-time-mutation.models.js.map +1 -1
- package/dist/server/modules/external-integrations/my-work/external-edit-session.service.d.ts +10 -9
- package/dist/server/modules/external-integrations/my-work/external-edit-session.service.js +81 -46
- package/dist/server/modules/external-integrations/my-work/external-edit-session.service.js.map +1 -1
- package/dist/server/modules/external-integrations/my-work/external-my-work.controller.d.ts +22 -20
- package/dist/server/modules/external-integrations/my-work/external-my-work.controller.js +136 -64
- package/dist/server/modules/external-integrations/my-work/external-my-work.controller.js.map +1 -1
- package/dist/server/modules/external-integrations/my-work/external-my-work.openapi.d.ts +7 -0
- package/dist/server/modules/external-integrations/my-work/external-my-work.openapi.js +289 -4
- package/dist/server/modules/external-integrations/my-work/external-my-work.openapi.js.map +1 -1
- package/dist/server/modules/external-integrations/my-work/external-my-work.service.d.ts +12 -7
- package/dist/server/modules/external-integrations/my-work/external-my-work.service.js +114 -54
- package/dist/server/modules/external-integrations/my-work/external-my-work.service.js.map +1 -1
- package/dist/server/modules/external-integrations/my-work/external-time-mutation.service.d.ts +19 -6
- package/dist/server/modules/external-integrations/my-work/external-time-mutation.service.js +346 -46
- package/dist/server/modules/external-integrations/my-work/external-time-mutation.service.js.map +1 -1
- package/dist/server/modules/external-integrations/sessions/external-edit-session.store.d.ts +2 -1
- package/dist/server/modules/external-integrations/sessions/external-edit-session.store.js +3 -2
- package/dist/server/modules/external-integrations/sessions/external-edit-session.store.js.map +1 -1
- package/dist/server/modules/external-integrations/sessions/external-time-mutation.store.d.ts +6 -1
- package/dist/server/modules/external-integrations/sessions/external-time-mutation.store.js +19 -0
- package/dist/server/modules/external-integrations/sessions/external-time-mutation.store.js.map +1 -1
- package/dist/server/modules/external-integrations/sessions/provider-operation-gate.d.ts +15 -2
- package/dist/server/modules/external-integrations/sessions/provider-operation-gate.js +33 -6
- package/dist/server/modules/external-integrations/sessions/provider-operation-gate.js.map +1 -1
- package/dist/server/modules/external-integrations/subscribers/external-subtask-sync.subscriber.d.ts +13 -6
- package/dist/server/modules/external-integrations/subscribers/external-subtask-sync.subscriber.js +212 -75
- package/dist/server/modules/external-integrations/subscribers/external-subtask-sync.subscriber.js.map +1 -1
- package/dist/server/modules/external-integrations/subscribers/managed-subtask-content.d.ts +13 -4
- package/dist/server/modules/external-integrations/subscribers/managed-subtask-content.js +98 -0
- package/dist/server/modules/external-integrations/subscribers/managed-subtask-content.js.map +1 -1
- package/dist/server/modules/external-integrations/subscribers/managed-subtask-sync-health.service.d.ts +10 -4
- package/dist/server/modules/external-integrations/subscribers/managed-subtask-sync-health.service.js +57 -18
- package/dist/server/modules/external-integrations/subscribers/managed-subtask-sync-health.service.js.map +1 -1
- package/dist/server/modules/mcp/dtos/mcp.dto.d.ts +139 -0
- package/dist/server/modules/mcp/dtos/mcp.dto.js +41 -1
- package/dist/server/modules/mcp/dtos/mcp.dto.js.map +1 -1
- package/dist/server/modules/mcp/services/handlers/epic-context.d.ts +2 -0
- package/dist/server/modules/mcp/services/handlers/epic-tools.d.ts +4 -0
- package/dist/server/modules/mcp/services/handlers/epic-tools.js +275 -0
- package/dist/server/modules/mcp/services/handlers/epic-tools.js.map +1 -1
- package/dist/server/modules/mcp/services/mcp-tool-binding.registry.d.ts +2 -1
- package/dist/server/modules/mcp/services/mcp-tool-binding.registry.js +13 -8
- package/dist/server/modules/mcp/services/mcp-tool-binding.registry.js.map +1 -1
- package/dist/server/modules/mcp/services/testing/mcp-tool-binding-registry.fixture.d.ts +1 -1
- package/dist/server/modules/mcp/services/testing/mcp-tool-binding-registry.fixture.js +1 -1
- package/dist/server/modules/mcp/services/testing/mcp-tool-binding-registry.fixture.js.map +1 -1
- package/dist/server/modules/mcp/tool-descriptors/binding-types.d.ts +2 -0
- package/dist/server/modules/mcp/tool-descriptors/binding-types.js.map +1 -1
- package/dist/server/modules/mcp/tool-descriptors/epic.bindings.js +6 -0
- package/dist/server/modules/mcp/tool-descriptors/epic.bindings.js.map +1 -1
- package/dist/server/modules/mcp/tool-descriptors/epic.metadata.js +132 -0
- package/dist/server/modules/mcp/tool-descriptors/epic.metadata.js.map +1 -1
- package/dist/server/modules/projects/controllers/projects.controller.js +1 -4
- package/dist/server/modules/projects/controllers/projects.controller.js.map +1 -1
- package/dist/server/modules/projects/projects.module.js +2 -0
- package/dist/server/modules/projects/projects.module.js.map +1 -1
- package/dist/server/modules/projects/services/projects.service.d.ts +4 -1
- package/dist/server/modules/projects/services/projects.service.js +23 -2
- package/dist/server/modules/projects/services/projects.service.js.map +1 -1
- package/dist/server/modules/providers/adapters/claude.adapter.d.ts +4 -2
- package/dist/server/modules/providers/adapters/claude.adapter.js +31 -0
- package/dist/server/modules/providers/adapters/claude.adapter.js.map +1 -1
- package/dist/server/modules/providers/services/provider-mcp-ensure.service.d.ts +7 -0
- package/dist/server/modules/providers/services/provider-mcp-ensure.service.js +69 -4
- package/dist/server/modules/providers/services/provider-mcp-ensure.service.js.map +1 -1
- package/dist/server/modules/session-reader/data/pricing.json +452 -56
- package/dist/server/modules/session-reader/services/session-reader.service.d.ts +2 -0
- package/dist/server/modules/session-reader/services/session-reader.service.js +85 -9
- package/dist/server/modules/session-reader/services/session-reader.service.js.map +1 -1
- package/dist/server/modules/session-reader/services/transcript-path-validator.service.js +8 -2
- package/dist/server/modules/session-reader/services/transcript-path-validator.service.js.map +1 -1
- package/dist/server/modules/session-reader/services/transcript-watcher.service.js +44 -17
- package/dist/server/modules/session-reader/services/transcript-watcher.service.js.map +1 -1
- package/dist/server/modules/sessions/services/session-runtime/__test-utils__/pipeline-harness.d.ts +1 -0
- package/dist/server/modules/sessions/services/session-runtime/__test-utils__/pipeline-harness.js +1 -0
- package/dist/server/modules/sessions/services/session-runtime/__test-utils__/pipeline-harness.js.map +1 -1
- package/dist/server/modules/sessions/services/session-runtime/session-launch-pipeline.service.js +11 -1
- package/dist/server/modules/sessions/services/session-runtime/session-launch-pipeline.service.js.map +1 -1
- package/dist/server/modules/sessions/utils/claude-config.d.ts +4 -1
- package/dist/server/modules/sessions/utils/claude-config.js +265 -60
- package/dist/server/modules/sessions/utils/claude-config.js.map +1 -1
- package/dist/server/modules/storage/db/schema.d.ts +1109 -98
- package/dist/server/modules/storage/db/schema.js +141 -3
- package/dist/server/modules/storage/db/schema.js.map +1 -1
- package/dist/server/modules/storage/db/sqlite-json.d.ts +1 -0
- package/dist/server/modules/storage/db/sqlite-json.js +4 -0
- package/dist/server/modules/storage/db/sqlite-json.js.map +1 -1
- package/dist/server/modules/storage/interfaces/storage.interface.d.ts +58 -10
- package/dist/server/modules/storage/interfaces/storage.interface.js.map +1 -1
- package/dist/server/modules/storage/local/delegates/epic.delegate.d.ts +30 -5
- package/dist/server/modules/storage/local/delegates/epic.delegate.js +718 -14
- package/dist/server/modules/storage/local/delegates/epic.delegate.js.map +1 -1
- package/dist/server/modules/storage/local/delegates/external-estimate-log.delegate.d.ts +52 -0
- package/dist/server/modules/storage/local/delegates/external-estimate-log.delegate.js +659 -0
- package/dist/server/modules/storage/local/delegates/external-estimate-log.delegate.js.map +1 -0
- package/dist/server/modules/storage/local/delegates/external-managed-subtask.delegate.d.ts +2 -1
- package/dist/server/modules/storage/local/delegates/external-managed-subtask.delegate.js +17 -4
- package/dist/server/modules/storage/local/delegates/external-managed-subtask.delegate.js.map +1 -1
- package/dist/server/modules/storage/local/delegates/integration.delegate.d.ts +23 -8
- package/dist/server/modules/storage/local/delegates/integration.delegate.js +238 -52
- package/dist/server/modules/storage/local/delegates/integration.delegate.js.map +1 -1
- package/dist/server/modules/storage/local/delegates/project.delegate.d.ts +2 -0
- package/dist/server/modules/storage/local/delegates/project.delegate.js +180 -130
- package/dist/server/modules/storage/local/delegates/project.delegate.js.map +1 -1
- package/dist/server/modules/storage/local/local-storage.service.d.ts +45 -10
- package/dist/server/modules/storage/local/local-storage.service.js +99 -15
- package/dist/server/modules/storage/local/local-storage.service.js.map +1 -1
- package/dist/server/modules/storage/models/domain.models.d.ts +170 -0
- package/dist/server/modules/storage/models/domain.models.js +9 -0
- package/dist/server/modules/storage/models/domain.models.js.map +1 -1
- package/dist/server/templates/teams-dev.json +310 -300
- package/dist/server/tsconfig.tsbuildinfo +1 -1
- package/dist/server/ui/assets/{ExternalRichEditor-DMtnZIMZ.js → ExternalRichEditor-BG9SopK8.js} +1 -1
- package/dist/server/ui/assets/{ReviewDetailPage-BmyiM5IR.js → ReviewDetailPage-DNo7Qx3m.js} +1 -1
- package/dist/server/ui/assets/{ReviewsPage-CQ87BMwT.js → ReviewsPage-Cay-IObR.js} +1 -1
- package/dist/server/ui/assets/index-C1Fdqua-.js +1197 -0
- package/dist/server/ui/assets/index-CV8ydm7w.css +32 -0
- package/dist/server/ui/assets/{useReviewSubscription-DjbEUzt4.js → useReviewSubscription-sAAfh4k9.js} +1 -1
- package/dist/server/ui/index.html +2 -2
- package/dist/templates/teams-dev.json +310 -300
- package/package.json +16 -2
- package/dist/server/ui/assets/index-BX6Yze-A.js +0 -1177
- package/dist/server/ui/assets/index-qoQEv_u4.css +0 -32
package/dist/server/modules/external-integrations/subscribers/external-subtask-sync.subscriber.js
CHANGED
|
@@ -70,24 +70,26 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
|
|
|
70
70
|
}
|
|
71
71
|
else if (event.name === 'integration.connection.created' ||
|
|
72
72
|
event.name === 'integration.connection.updated') {
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
results =
|
|
74
|
+
'projectId' in event.payload
|
|
75
|
+
? await this.reconcileProjectConnection(event.payload)
|
|
76
|
+
: await this.reconcileLegacyConnection(event.payload.connectionId, event.payload.provider);
|
|
75
77
|
}
|
|
76
78
|
else if (event.name === 'integration.connection.deleted') {
|
|
77
|
-
|
|
78
|
-
results = await this.projectDisconnected(provider);
|
|
79
|
+
results = await this.connectionDisconnected(event.payload.provider, event.payload.connectionId);
|
|
79
80
|
}
|
|
80
81
|
const retryCount = results.filter((result) => result.outcome === 'retry').length;
|
|
81
82
|
if (retryCount > 0) {
|
|
82
83
|
throw new Error(`Managed subtask reconciliation has ${retryCount} retryable projection(s).`);
|
|
83
84
|
}
|
|
84
85
|
}
|
|
85
|
-
async reconcileEpic(epicId, providerFilter) {
|
|
86
|
+
async reconcileEpic(epicId, providerFilter, connectionFilter) {
|
|
86
87
|
const allExisting = await this.storage.listExternalManagedSubtaskLinksForEpicSnapshot(epicId);
|
|
87
|
-
const existing = allExisting.filter((row) => !providerFilter || row.provider === providerFilter)
|
|
88
|
+
const existing = allExisting.filter((row) => (!providerFilter || row.provider === providerFilter) &&
|
|
89
|
+
(!connectionFilter || row.connectionIdSnapshot === connectionFilter.id));
|
|
88
90
|
const epic = await this.loadEpicOrNull(epicId);
|
|
89
91
|
const desired = epic?.parentId
|
|
90
|
-
? await this.desiredProjections(epic, providerFilter)
|
|
92
|
+
? await this.desiredProjections(epic, providerFilter, connectionFilter)
|
|
91
93
|
: new Map();
|
|
92
94
|
const results = [];
|
|
93
95
|
const obsolete = existing.filter((row) => !desired.has(row.parentSourceLinkIdSnapshot));
|
|
@@ -182,50 +184,118 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
|
|
|
182
184
|
}
|
|
183
185
|
return results;
|
|
184
186
|
}
|
|
185
|
-
async
|
|
187
|
+
async reconcileProjectConnection(input) {
|
|
188
|
+
const connection = await this.storage.getIntegrationConnectionById(input.connectionId);
|
|
189
|
+
if (!connection ||
|
|
190
|
+
connection.projectId !== input.projectId ||
|
|
191
|
+
connection.provider !== input.provider) {
|
|
192
|
+
return [];
|
|
193
|
+
}
|
|
194
|
+
return this.reconcileConnection(connection);
|
|
195
|
+
}
|
|
196
|
+
async reconcileLegacyConnection(legacySourceConnectionId, provider) {
|
|
197
|
+
const connections = (await this.storage.listIntegrationConnectionsByLegacySourceConnectionId(legacySourceConnectionId)).filter((connection) => connection.provider === provider);
|
|
198
|
+
const results = [];
|
|
199
|
+
for (const connection of connections) {
|
|
200
|
+
results.push(...(await this.reconcileConnection(connection)));
|
|
201
|
+
}
|
|
202
|
+
return results;
|
|
203
|
+
}
|
|
204
|
+
async reconcileConnection(connection) {
|
|
205
|
+
const results = [];
|
|
206
|
+
const existing = (await this.storage.listExternalManagedSubtaskLinksByConnection(connection.id)).filter((row) => row.provider === connection.provider);
|
|
207
|
+
const processedEpicIds = new Set();
|
|
208
|
+
for (const row of existing) {
|
|
209
|
+
if (processedEpicIds.has(row.epicIdSnapshot)) {
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
processedEpicIds.add(row.epicIdSnapshot);
|
|
213
|
+
results.push(...(await this.reconcileEpic(row.epicIdSnapshot, connection.provider, connection)));
|
|
214
|
+
}
|
|
215
|
+
if (!connection.projectId) {
|
|
216
|
+
return results;
|
|
217
|
+
}
|
|
218
|
+
let offset = 0;
|
|
219
|
+
while (true) {
|
|
220
|
+
const page = await this.storage.listProjectEpics(connection.projectId, {
|
|
221
|
+
type: 'all',
|
|
222
|
+
limit: 500,
|
|
223
|
+
offset,
|
|
224
|
+
});
|
|
225
|
+
for (const epic of page.items) {
|
|
226
|
+
if (epic.parentId && !processedEpicIds.has(epic.id)) {
|
|
227
|
+
processedEpicIds.add(epic.id);
|
|
228
|
+
results.push(...(await this.reconcileEpic(epic.id, connection.provider, connection)));
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
offset += page.items.length;
|
|
232
|
+
if (page.items.length === 0 || offset >= page.total) {
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return results;
|
|
237
|
+
}
|
|
238
|
+
async reconcileManagedLink(id, expectedConnectionId) {
|
|
239
|
+
return this.withProjectionLock(id, () => this.reconcileManagedLinkUnlocked(id, expectedConnectionId));
|
|
240
|
+
}
|
|
241
|
+
async verifyManagedLink(id, expectedConnectionId) {
|
|
186
242
|
return this.withProjectionLock(id, async () => {
|
|
187
243
|
const row = await this.storage.getExternalManagedSubtaskLink(id);
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
244
|
+
this.assertExpectedConnection(row, expectedConnectionId);
|
|
245
|
+
if (row.operationPhase === 'dispatch_admitted' ||
|
|
246
|
+
row.operationPhase === 'outcome_unknown' ||
|
|
247
|
+
row.operationPhase === 'needs_attention') {
|
|
248
|
+
await this.storage.updateExternalManagedSubtaskLink(id, {
|
|
249
|
+
operationPhase: 'outcome_unknown',
|
|
250
|
+
retryAt: null,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
return this.reconcileManagedLinkUnlocked(id, expectedConnectionId);
|
|
193
254
|
});
|
|
194
255
|
}
|
|
195
|
-
async
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
row
|
|
199
|
-
|
|
256
|
+
async retryManagedLink(id, expectedConnectionId) {
|
|
257
|
+
return this.withProjectionLock(id, async () => {
|
|
258
|
+
const row = await this.storage.getExternalManagedSubtaskLink(id);
|
|
259
|
+
this.assertExpectedConnection(row, expectedConnectionId);
|
|
260
|
+
const decision = (0, managed_subtask_recovery_policy_1.managedSubtaskRetryDecision)(row);
|
|
261
|
+
if (!decision.allowed) {
|
|
262
|
+
throw new error_types_1.ConflictError(decision.reason === 'verification_required'
|
|
263
|
+
? 'Unknown managed-subtask outcomes must be verified before retry.'
|
|
264
|
+
: 'Managed-subtask state does not allow Retry.', {
|
|
265
|
+
managedLinkId: id,
|
|
266
|
+
reason: decision.reason,
|
|
267
|
+
});
|
|
268
|
+
}
|
|
200
269
|
await this.storage.updateExternalManagedSubtaskLink(id, {
|
|
201
|
-
operationPhase: '
|
|
270
|
+
operationPhase: 'pre_dispatch',
|
|
271
|
+
safeErrorCode: null,
|
|
202
272
|
retryAt: null,
|
|
203
273
|
});
|
|
204
|
-
|
|
205
|
-
|
|
274
|
+
return this.reconcileManagedLinkUnlocked(id, expectedConnectionId);
|
|
275
|
+
});
|
|
206
276
|
}
|
|
207
|
-
async
|
|
277
|
+
async reconcileManagedLinkUnlocked(id, expectedConnectionId) {
|
|
208
278
|
const row = await this.storage.getExternalManagedSubtaskLink(id);
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
operationPhase: 'pre_dispatch',
|
|
220
|
-
safeErrorCode: null,
|
|
221
|
-
retryAt: null,
|
|
222
|
-
});
|
|
223
|
-
return this.reconcileManagedLink(id);
|
|
279
|
+
this.assertExpectedConnection(row, expectedConnectionId);
|
|
280
|
+
const [epic, connection] = await Promise.all([
|
|
281
|
+
this.loadEpicOrNull(row.epicIdSnapshot),
|
|
282
|
+
this.storage.getIntegrationConnectionById(row.connectionIdSnapshot),
|
|
283
|
+
]);
|
|
284
|
+
const allowUnassignedConnection = Boolean(expectedConnectionId && !connection?.projectId);
|
|
285
|
+
const desired = epic?.parentId && connection
|
|
286
|
+
? await this.desiredProjections(epic, row.provider, connection, allowUnassignedConnection)
|
|
287
|
+
: new Map();
|
|
288
|
+
return this.reconcileExisting(row, epic, desired.get(row.parentSourceLinkIdSnapshot) ?? null, allowUnassignedConnection);
|
|
224
289
|
}
|
|
225
|
-
async reconcileExisting(original, epic, desired) {
|
|
290
|
+
async reconcileExisting(original, epic, desired, allowUnassignedConnection = false) {
|
|
226
291
|
let row = await this.storage.getExternalManagedSubtaskLink(original.id);
|
|
227
|
-
const connection = await this.storage.
|
|
228
|
-
if (!connection
|
|
292
|
+
const connection = await this.storage.getIntegrationConnectionById(row.connectionIdSnapshot);
|
|
293
|
+
if (!connection ||
|
|
294
|
+
connection.provider !== row.provider ||
|
|
295
|
+
(epic &&
|
|
296
|
+
connection.projectId !== epic.projectId &&
|
|
297
|
+
!(allowUnassignedConnection && connection.projectId === null)) ||
|
|
298
|
+
!connection.subtaskSyncEnabled) {
|
|
229
299
|
return this.result(row, 'paused', connection ? 'sync_disabled' : 'connection_missing');
|
|
230
300
|
}
|
|
231
301
|
row = await this.refreshConnectionFence(row, connection);
|
|
@@ -235,6 +305,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
|
|
|
235
305
|
row.safeErrorCode?.startsWith('provider_') === true));
|
|
236
306
|
if ((!epic || !desired) && safeAbsentProjection) {
|
|
237
307
|
await this.storage.removeExternalManagedSubtaskLink(row.id);
|
|
308
|
+
await this.publishEpicTimeScopeHint(row.epicIdSnapshot);
|
|
238
309
|
return this.result(row, 'deleted');
|
|
239
310
|
}
|
|
240
311
|
if (row.operationPhase === 'needs_attention' || row.tombstoneState === 'orphan_risk') {
|
|
@@ -308,6 +379,19 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
|
|
|
308
379
|
}
|
|
309
380
|
return this.updateProjection(row, epic, desired);
|
|
310
381
|
}
|
|
382
|
+
async publishEpicTimeScopeHint(epicId) {
|
|
383
|
+
if (!epicId)
|
|
384
|
+
return;
|
|
385
|
+
try {
|
|
386
|
+
const epic = await this.storage.getEpic(epicId);
|
|
387
|
+
const project = await this.storage.getProject(epic.projectId);
|
|
388
|
+
await this.events.publish('epic.time.scope.invalidated', {
|
|
389
|
+
workspaceId: project.workspaceId,
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
catch {
|
|
393
|
+
}
|
|
394
|
+
}
|
|
311
395
|
async createProjection(row, epic, desired) {
|
|
312
396
|
const admission = await this.admitMutation(row);
|
|
313
397
|
if (!admission) {
|
|
@@ -317,8 +401,8 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
|
|
|
317
401
|
const snapshot = await admission.capability.create(admission.credentials, admission.context, {
|
|
318
402
|
parentRemoteTaskId: row.parentRemoteTaskId,
|
|
319
403
|
ownershipToken: row.ownershipToken,
|
|
320
|
-
title:
|
|
321
|
-
description:
|
|
404
|
+
title: desired.content.title,
|
|
405
|
+
description: desired.content.description,
|
|
322
406
|
});
|
|
323
407
|
if (!(await this.fenceStillCurrent(admission.connection))) {
|
|
324
408
|
await this.markUnknown(row, 'connection_changed_after_dispatch');
|
|
@@ -333,6 +417,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
|
|
|
333
417
|
confirmedFingerprint: row.desiredFingerprint,
|
|
334
418
|
sourceSnapshot: this.sourceSnapshot(row, desired, snapshot, admission.credentials),
|
|
335
419
|
});
|
|
420
|
+
await this.publishEpicTimeScopeHint(confirmed.managedLink.epicIdSnapshot);
|
|
336
421
|
return this.result(confirmed.managedLink, 'confirmed');
|
|
337
422
|
}
|
|
338
423
|
catch (error) {
|
|
@@ -356,8 +441,8 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
|
|
|
356
441
|
remoteTaskId: row.remoteTaskId,
|
|
357
442
|
expectedParentRemoteTaskId: row.parentRemoteTaskId,
|
|
358
443
|
ownershipToken: row.ownershipToken,
|
|
359
|
-
title:
|
|
360
|
-
description:
|
|
444
|
+
title: desired.content.title,
|
|
445
|
+
description: desired.content.description,
|
|
361
446
|
});
|
|
362
447
|
const snapshot = await admission.capability.readExact(admission.credentials, admission.context, row.remoteTaskId);
|
|
363
448
|
if (!(await this.fenceStillCurrent(admission.connection))) {
|
|
@@ -368,7 +453,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
|
|
|
368
453
|
return this.keepUnknownForRetry(row, 'remote_task_missing_after_update');
|
|
369
454
|
}
|
|
370
455
|
this.assertOwnedSnapshot(row, snapshot);
|
|
371
|
-
if (!(0, managed_subtask_content_1.managedSubtaskContentMatches)(row.provider, snapshot,
|
|
456
|
+
if (!(0, managed_subtask_content_1.managedSubtaskContentMatches)(row.provider, snapshot, desired.content)) {
|
|
372
457
|
return this.keepUnknownForRetry(row, 'remote_verification_mismatch');
|
|
373
458
|
}
|
|
374
459
|
const confirmed = await this.storage.confirmExternalManagedSubtaskLink({
|
|
@@ -379,6 +464,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
|
|
|
379
464
|
confirmedFingerprint: row.desiredFingerprint,
|
|
380
465
|
sourceSnapshot: this.sourceSnapshot(row, desired, snapshot, admission.credentials),
|
|
381
466
|
});
|
|
467
|
+
await this.publishEpicTimeScopeHint(confirmed.managedLink.epicIdSnapshot);
|
|
382
468
|
return this.result(confirmed.managedLink, 'confirmed');
|
|
383
469
|
}
|
|
384
470
|
catch (error) {
|
|
@@ -388,6 +474,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
|
|
|
388
474
|
async deleteProjection(row) {
|
|
389
475
|
if (!row.remoteTaskId) {
|
|
390
476
|
await this.storage.removeExternalManagedSubtaskLink(row.id);
|
|
477
|
+
await this.publishEpicTimeScopeHint(row.epicIdSnapshot);
|
|
391
478
|
return this.result(row, 'deleted');
|
|
392
479
|
}
|
|
393
480
|
const remoteTaskId = row.remoteTaskId;
|
|
@@ -419,6 +506,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
|
|
|
419
506
|
return this.result(row, 'retry', 'connection_changed_after_dispatch');
|
|
420
507
|
}
|
|
421
508
|
await this.storage.removeExternalManagedSubtaskLink(row.id);
|
|
509
|
+
await this.publishEpicTimeScopeHint(row.epicIdSnapshot);
|
|
422
510
|
return this.result(row, 'deleted');
|
|
423
511
|
}
|
|
424
512
|
catch (error) {
|
|
@@ -429,7 +517,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
|
|
|
429
517
|
if (!row.remoteTaskId) {
|
|
430
518
|
return this.resolveUnknownCreate(row, epic, desired);
|
|
431
519
|
}
|
|
432
|
-
const access = await this.captureReadAccess(row
|
|
520
|
+
const access = await this.captureReadAccess(row);
|
|
433
521
|
if (!access) {
|
|
434
522
|
return this.result(row, 'retry', 'verification_unavailable');
|
|
435
523
|
}
|
|
@@ -442,7 +530,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
|
|
|
442
530
|
return this.markNeedsAttention(row, 'remote_task_missing_after_unknown_update');
|
|
443
531
|
}
|
|
444
532
|
this.assertOwnedSnapshot(row, snapshot);
|
|
445
|
-
if ((0, managed_subtask_content_1.managedSubtaskContentMatches)(row.provider, snapshot,
|
|
533
|
+
if ((0, managed_subtask_content_1.managedSubtaskContentMatches)(row.provider, snapshot, desired.content)) {
|
|
446
534
|
const confirmed = await this.storage.confirmExternalManagedSubtaskLink({
|
|
447
535
|
managedLinkId: row.id,
|
|
448
536
|
remoteTaskId: snapshot.remoteTaskId,
|
|
@@ -451,6 +539,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
|
|
|
451
539
|
confirmedFingerprint: row.desiredFingerprint,
|
|
452
540
|
sourceSnapshot: this.sourceSnapshot(row, desired, snapshot, access.credentials),
|
|
453
541
|
});
|
|
542
|
+
await this.publishEpicTimeScopeHint(confirmed.managedLink.epicIdSnapshot);
|
|
454
543
|
return this.result(confirmed.managedLink, 'confirmed');
|
|
455
544
|
}
|
|
456
545
|
const retryable = await this.storage.updateExternalManagedSubtaskLink(row.id, {
|
|
@@ -468,7 +557,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
|
|
|
468
557
|
}
|
|
469
558
|
}
|
|
470
559
|
async resolveUnknownCreate(row, epic, desired) {
|
|
471
|
-
const access = await this.captureReadAccess(row
|
|
560
|
+
const access = await this.captureReadAccess(row);
|
|
472
561
|
if (!access) {
|
|
473
562
|
return this.result(row, 'retry', 'verification_unavailable');
|
|
474
563
|
}
|
|
@@ -485,7 +574,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
|
|
|
485
574
|
if (matches.length === 1) {
|
|
486
575
|
const snapshot = matches[0];
|
|
487
576
|
this.assertOwnedSnapshot(row, snapshot);
|
|
488
|
-
adoptedMatchesDesired = (0, managed_subtask_content_1.managedSubtaskContentMatches)(row.provider, snapshot,
|
|
577
|
+
adoptedMatchesDesired = (0, managed_subtask_content_1.managedSubtaskContentMatches)(row.provider, snapshot, desired.content);
|
|
489
578
|
adoptedFingerprint = adoptedMatchesDesired
|
|
490
579
|
? row.desiredFingerprint
|
|
491
580
|
: this.projectionFingerprint(snapshot.title, snapshot.description, row.parentSourceLinkIdSnapshot, row.parentRemoteTaskId);
|
|
@@ -497,6 +586,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
|
|
|
497
586
|
confirmedFingerprint: adoptedFingerprint,
|
|
498
587
|
sourceSnapshot: this.sourceSnapshot(row, desired, snapshot, access.credentials),
|
|
499
588
|
});
|
|
589
|
+
await this.publishEpicTimeScopeHint(confirmed.managedLink.epicIdSnapshot);
|
|
500
590
|
adopted = confirmed.managedLink;
|
|
501
591
|
}
|
|
502
592
|
if (!adopted && matches.length === 0 && children.complete) {
|
|
@@ -520,7 +610,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
|
|
|
520
610
|
return this.updateProjection(adopted, epic, desired);
|
|
521
611
|
}
|
|
522
612
|
async resolveUnknownCreateForRemoval(row, tombstoneState) {
|
|
523
|
-
const access = await this.captureReadAccess(row
|
|
613
|
+
const access = await this.captureReadAccess(row);
|
|
524
614
|
if (!access) {
|
|
525
615
|
return this.result(row, 'retry', 'verification_unavailable');
|
|
526
616
|
}
|
|
@@ -533,6 +623,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
|
|
|
533
623
|
item.parentRemoteTaskId === row.parentRemoteTaskId);
|
|
534
624
|
if (matches.length === 0 && children.complete) {
|
|
535
625
|
await this.storage.removeExternalManagedSubtaskLink(row.id);
|
|
626
|
+
await this.publishEpicTimeScopeHint(row.epicIdSnapshot);
|
|
536
627
|
return this.result(row, 'deleted');
|
|
537
628
|
}
|
|
538
629
|
if (matches.length !== 1) {
|
|
@@ -561,9 +652,10 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
|
|
|
561
652
|
async resolveUnknownDelete(row) {
|
|
562
653
|
if (!row.remoteTaskId) {
|
|
563
654
|
await this.storage.removeExternalManagedSubtaskLink(row.id);
|
|
655
|
+
await this.publishEpicTimeScopeHint(row.epicIdSnapshot);
|
|
564
656
|
return this.result(row, 'deleted');
|
|
565
657
|
}
|
|
566
|
-
const access = await this.captureReadAccess(row
|
|
658
|
+
const access = await this.captureReadAccess(row);
|
|
567
659
|
if (!access) {
|
|
568
660
|
return this.result(row, 'retry', 'verification_unavailable');
|
|
569
661
|
}
|
|
@@ -574,6 +666,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
|
|
|
574
666
|
}
|
|
575
667
|
if (!snapshot) {
|
|
576
668
|
await this.storage.removeExternalManagedSubtaskLink(row.id);
|
|
669
|
+
await this.publishEpicTimeScopeHint(row.epicIdSnapshot);
|
|
577
670
|
return this.result(row, 'deleted');
|
|
578
671
|
}
|
|
579
672
|
this.assertOwnedSnapshot(row, snapshot);
|
|
@@ -595,7 +688,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
|
|
|
595
688
|
if (!row.remoteTaskId) {
|
|
596
689
|
return this.createProjection(row, epic, desired);
|
|
597
690
|
}
|
|
598
|
-
const access = await this.captureReadAccess(row
|
|
691
|
+
const access = await this.captureReadAccess(row);
|
|
599
692
|
if (!access) {
|
|
600
693
|
return this.result(row, 'retry', 'verification_unavailable');
|
|
601
694
|
}
|
|
@@ -616,6 +709,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
|
|
|
616
709
|
confirmedFingerprint: row.desiredFingerprint,
|
|
617
710
|
sourceSnapshot: this.sourceSnapshot(row, desired, snapshot, access.credentials),
|
|
618
711
|
});
|
|
712
|
+
await this.publishEpicTimeScopeHint(repaired.managedLink.epicIdSnapshot);
|
|
619
713
|
return this.result(repaired.managedLink, 'confirmed', 'recognition_repaired');
|
|
620
714
|
}
|
|
621
715
|
catch (error) {
|
|
@@ -629,7 +723,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
|
|
|
629
723
|
if (!row.remoteTaskId) {
|
|
630
724
|
return this.markNeedsAttention(row, 'remote_identity_missing');
|
|
631
725
|
}
|
|
632
|
-
const access = await this.captureReadAccess(row
|
|
726
|
+
const access = await this.captureReadAccess(row);
|
|
633
727
|
if (!access) {
|
|
634
728
|
return this.result(row, 'retry', 'ownership_verification_unavailable');
|
|
635
729
|
}
|
|
@@ -652,22 +746,29 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
|
|
|
652
746
|
}
|
|
653
747
|
}
|
|
654
748
|
async admitMutation(row) {
|
|
749
|
+
const scopedConnection = await this.storage.getIntegrationConnectionById(row.connectionIdSnapshot);
|
|
750
|
+
if (!scopedConnection || scopedConnection.provider !== row.provider) {
|
|
751
|
+
return null;
|
|
752
|
+
}
|
|
655
753
|
try {
|
|
656
|
-
return await this.providerGate.run(
|
|
657
|
-
const connection = await this.storage.
|
|
754
|
+
return await this.providerGate.run(this.connectionOperationScope(scopedConnection), async () => {
|
|
755
|
+
const connection = await this.storage.getIntegrationConnectionById(row.connectionIdSnapshot);
|
|
658
756
|
if (!connection?.subtaskSyncEnabled ||
|
|
757
|
+
connection.projectId !== scopedConnection.projectId ||
|
|
659
758
|
connection.id !== row.connectionIdSnapshot ||
|
|
660
759
|
connection.generation !== row.connectionGeneration ||
|
|
661
760
|
connection.syncSettingRevision !== row.syncSettingRevision) {
|
|
662
761
|
return null;
|
|
663
762
|
}
|
|
664
763
|
const adapter = this.providers.get(row.provider);
|
|
665
|
-
const credentials = await this.storage.
|
|
666
|
-
const rechecked = await this.storage.
|
|
764
|
+
const credentials = await this.storage.getIntegrationConnectionCredentialsById(row.connectionIdSnapshot);
|
|
765
|
+
const rechecked = await this.storage.getIntegrationConnectionById(row.connectionIdSnapshot);
|
|
667
766
|
if (!adapter.subtaskSync ||
|
|
668
767
|
!credentials ||
|
|
669
768
|
credentials.provider !== row.provider ||
|
|
670
769
|
!rechecked?.subtaskSyncEnabled ||
|
|
770
|
+
rechecked.projectId !== connection.projectId ||
|
|
771
|
+
rechecked.provider !== row.provider ||
|
|
671
772
|
rechecked.id !== connection.id ||
|
|
672
773
|
rechecked.generation !== connection.generation ||
|
|
673
774
|
rechecked.syncSettingRevision !== connection.syncSettingRevision) {
|
|
@@ -698,18 +799,26 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
|
|
|
698
799
|
throw error;
|
|
699
800
|
}
|
|
700
801
|
}
|
|
701
|
-
async captureReadAccess(
|
|
802
|
+
async captureReadAccess(row) {
|
|
803
|
+
const scopedConnection = await this.storage.getIntegrationConnectionById(row.connectionIdSnapshot);
|
|
804
|
+
if (!scopedConnection || scopedConnection.provider !== row.provider) {
|
|
805
|
+
return null;
|
|
806
|
+
}
|
|
702
807
|
try {
|
|
703
|
-
return await this.providerGate.run(
|
|
704
|
-
const connection = await this.storage.
|
|
705
|
-
const adapter = this.providers.get(provider);
|
|
706
|
-
const credentials = await this.storage.
|
|
707
|
-
const rechecked = await this.storage.
|
|
708
|
-
if (
|
|
808
|
+
return await this.providerGate.run(this.connectionOperationScope(scopedConnection), async () => {
|
|
809
|
+
const connection = await this.storage.getIntegrationConnectionById(row.connectionIdSnapshot);
|
|
810
|
+
const adapter = this.providers.get(row.provider);
|
|
811
|
+
const credentials = await this.storage.getIntegrationConnectionCredentialsById(row.connectionIdSnapshot);
|
|
812
|
+
const rechecked = await this.storage.getIntegrationConnectionById(row.connectionIdSnapshot);
|
|
813
|
+
if (connection?.provider !== row.provider ||
|
|
814
|
+
connection.projectId !== scopedConnection.projectId ||
|
|
815
|
+
!connection.subtaskSyncEnabled ||
|
|
709
816
|
!adapter.subtaskSync ||
|
|
710
817
|
!credentials ||
|
|
711
|
-
credentials.provider !== provider ||
|
|
818
|
+
credentials.provider !== row.provider ||
|
|
712
819
|
!rechecked?.subtaskSyncEnabled ||
|
|
820
|
+
rechecked.projectId !== connection.projectId ||
|
|
821
|
+
rechecked.provider !== row.provider ||
|
|
713
822
|
rechecked.id !== connection.id ||
|
|
714
823
|
rechecked.generation !== connection.generation ||
|
|
715
824
|
rechecked.syncSettingRevision !== connection.syncSettingRevision) {
|
|
@@ -743,7 +852,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
|
|
|
743
852
|
syncSettingRevision: connection.syncSettingRevision,
|
|
744
853
|
});
|
|
745
854
|
}
|
|
746
|
-
const access = await this.captureReadAccess(row
|
|
855
|
+
const access = await this.captureReadAccess(row);
|
|
747
856
|
if (!access) {
|
|
748
857
|
return this.storage.updateExternalManagedSubtaskLink(row.id, {
|
|
749
858
|
operationPhase: 'pre_dispatch',
|
|
@@ -792,18 +901,35 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
|
|
|
792
901
|
});
|
|
793
902
|
}
|
|
794
903
|
}
|
|
795
|
-
async desiredProjections(epic, providerFilter) {
|
|
904
|
+
async desiredProjections(epic, providerFilter, connectionFilter, allowUnassignedConnection = false) {
|
|
796
905
|
const result = new Map();
|
|
797
906
|
if (!epic.parentId) {
|
|
798
907
|
return result;
|
|
799
908
|
}
|
|
909
|
+
const workspaceProjects = await this.storage.getProjectWorkspaceSnapshot(epic.projectId);
|
|
910
|
+
const project = workspaceProjects.find((candidate) => candidate.id === epic.projectId);
|
|
911
|
+
if (!project) {
|
|
912
|
+
throw new error_types_1.NotFoundError('Project', epic.projectId);
|
|
913
|
+
}
|
|
914
|
+
const content = (0, managed_subtask_content_1.redactManagedSubtaskContent)(epic, project.rootPath, undefined, {
|
|
915
|
+
owningProjectId: project.id,
|
|
916
|
+
projects: workspaceProjects,
|
|
917
|
+
});
|
|
800
918
|
const sources = await this.storage.listExternalTaskLinksForEpic(epic.parentId);
|
|
801
919
|
for (const source of sources) {
|
|
802
920
|
if (providerFilter && source.provider !== providerFilter) {
|
|
803
921
|
continue;
|
|
804
922
|
}
|
|
805
|
-
|
|
923
|
+
if (connectionFilter && source.connectionId !== connectionFilter.id) {
|
|
924
|
+
continue;
|
|
925
|
+
}
|
|
926
|
+
let connection = connectionFilter ?? null;
|
|
927
|
+
if (!connection && source.connectionId) {
|
|
928
|
+
connection = await this.storage.getIntegrationConnectionById(source.connectionId);
|
|
929
|
+
}
|
|
806
930
|
if (!connection?.subtaskSyncEnabled ||
|
|
931
|
+
(connection.projectId !== epic.projectId &&
|
|
932
|
+
!(allowUnassignedConnection && connection.projectId === null)) ||
|
|
807
933
|
source.connectionId !== connection.id ||
|
|
808
934
|
!this.providers.get(source.provider).subtaskSync) {
|
|
809
935
|
continue;
|
|
@@ -814,10 +940,11 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
|
|
|
814
940
|
}
|
|
815
941
|
result.set(source.id, {
|
|
816
942
|
epic,
|
|
943
|
+
content,
|
|
817
944
|
parentSource: source,
|
|
818
945
|
connection,
|
|
819
946
|
workAreaRemoteId,
|
|
820
|
-
desiredFingerprint: this.desiredFingerprint(
|
|
947
|
+
desiredFingerprint: this.desiredFingerprint(content, source),
|
|
821
948
|
});
|
|
822
949
|
}
|
|
823
950
|
return result;
|
|
@@ -836,7 +963,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
|
|
|
836
963
|
parentRemoteTaskId: desired.parentSource.remoteTaskId,
|
|
837
964
|
connectionGeneration: desired.connection.generation,
|
|
838
965
|
syncSettingRevision: desired.connection.syncSettingRevision,
|
|
839
|
-
ownershipToken: (0, node_crypto_1.randomUUID)(),
|
|
966
|
+
ownershipToken: (0, node_crypto_1.randomUUID)().slice(0, 8),
|
|
840
967
|
desiredVersion: desired.epic.version,
|
|
841
968
|
desiredFingerprint: desired.desiredFingerprint,
|
|
842
969
|
});
|
|
@@ -849,8 +976,8 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
|
|
|
849
976
|
throw error;
|
|
850
977
|
}
|
|
851
978
|
}
|
|
852
|
-
async
|
|
853
|
-
const rows = await this.storage.
|
|
979
|
+
async connectionDisconnected(provider, connectionIdSnapshot) {
|
|
980
|
+
const rows = (await this.storage.listExternalManagedSubtaskLinksByConnection(connectionIdSnapshot)).filter((row) => row.provider === provider);
|
|
854
981
|
return Promise.all(rows.map(async (row) => {
|
|
855
982
|
const updated = await this.storage.updateExternalManagedSubtaskLink(row.id, {
|
|
856
983
|
safeErrorCode: 'connection_missing',
|
|
@@ -983,7 +1110,7 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
|
|
|
983
1110
|
: 'ownership_mismatch';
|
|
984
1111
|
}
|
|
985
1112
|
async fenceStillCurrent(expected) {
|
|
986
|
-
const current = await this.storage.
|
|
1113
|
+
const current = await this.storage.getIntegrationConnectionById(expected.id);
|
|
987
1114
|
return (current?.subtaskSyncEnabled === true &&
|
|
988
1115
|
current.id === expected.id &&
|
|
989
1116
|
current.generation === expected.generation &&
|
|
@@ -1015,8 +1142,8 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
|
|
|
1015
1142
|
const candidate = `${new URL(credentials.siteUrl).origin}/browse/${encodeURIComponent(snapshot.remoteKey)}`;
|
|
1016
1143
|
return (0, external_task_source_1.normalizeExternalTaskSourceUrl)(provider, candidate);
|
|
1017
1144
|
}
|
|
1018
|
-
desiredFingerprint(
|
|
1019
|
-
return this.projectionFingerprint(
|
|
1145
|
+
desiredFingerprint(content, parentSource) {
|
|
1146
|
+
return this.projectionFingerprint(content.title, content.description, parentSource.id, parentSource.remoteTaskId);
|
|
1020
1147
|
}
|
|
1021
1148
|
projectionFingerprint(title, description, parentSourceLinkId, parentRemoteTaskId) {
|
|
1022
1149
|
return (0, node_crypto_1.createHash)('sha256')
|
|
@@ -1038,9 +1165,19 @@ let ExternalSubtaskSyncSubscriber = class ExternalSubtaskSyncSubscriber {
|
|
|
1038
1165
|
connectionGeneration: connection.generation,
|
|
1039
1166
|
};
|
|
1040
1167
|
}
|
|
1168
|
+
connectionOperationScope(connection) {
|
|
1169
|
+
return connection.projectId
|
|
1170
|
+
? { projectId: connection.projectId, provider: connection.provider }
|
|
1171
|
+
: { connectionId: connection.id, provider: connection.provider };
|
|
1172
|
+
}
|
|
1041
1173
|
retryIsInFuture(retryAt) {
|
|
1042
1174
|
return Boolean(retryAt && Date.parse(retryAt) > Date.now());
|
|
1043
1175
|
}
|
|
1176
|
+
assertExpectedConnection(row, expectedConnectionId) {
|
|
1177
|
+
if (expectedConnectionId && row.connectionIdSnapshot !== expectedConnectionId) {
|
|
1178
|
+
throw new error_types_1.NotFoundError('Managed subtask link', row.id);
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1044
1181
|
async loadEpicOrNull(id) {
|
|
1045
1182
|
try {
|
|
1046
1183
|
return await this.storage.getEpic(id);
|