@cat-factory/integrations 0.6.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/LICENSE +21 -0
- package/dist/index.d.ts +68 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +84 -0
- package/dist/index.js.map +1 -0
- package/dist/modules/datadog/DatadogClient.d.ts +54 -0
- package/dist/modules/datadog/DatadogClient.d.ts.map +1 -0
- package/dist/modules/datadog/DatadogClient.js +132 -0
- package/dist/modules/datadog/DatadogClient.js.map +1 -0
- package/dist/modules/datadog/DatadogReleaseHealthProvider.d.ts +30 -0
- package/dist/modules/datadog/DatadogReleaseHealthProvider.d.ts.map +1 -0
- package/dist/modules/datadog/DatadogReleaseHealthProvider.js +101 -0
- package/dist/modules/datadog/DatadogReleaseHealthProvider.js.map +1 -0
- package/dist/modules/datadog/datadog.logic.d.ts +37 -0
- package/dist/modules/datadog/datadog.logic.d.ts.map +1 -0
- package/dist/modules/datadog/datadog.logic.js +90 -0
- package/dist/modules/datadog/datadog.logic.js.map +1 -0
- package/dist/modules/documents/ConfluenceProvider.d.ts +29 -0
- package/dist/modules/documents/ConfluenceProvider.d.ts.map +1 -0
- package/dist/modules/documents/ConfluenceProvider.js +180 -0
- package/dist/modules/documents/ConfluenceProvider.js.map +1 -0
- package/dist/modules/documents/DocumentConnectionService.d.ts +30 -0
- package/dist/modules/documents/DocumentConnectionService.d.ts.map +1 -0
- package/dist/modules/documents/DocumentConnectionService.js +69 -0
- package/dist/modules/documents/DocumentConnectionService.js.map +1 -0
- package/dist/modules/documents/DocumentImportService.d.ts +34 -0
- package/dist/modules/documents/DocumentImportService.d.ts.map +1 -0
- package/dist/modules/documents/DocumentImportService.js +83 -0
- package/dist/modules/documents/DocumentImportService.js.map +1 -0
- package/dist/modules/documents/DocumentLinkService.d.ts +31 -0
- package/dist/modules/documents/DocumentLinkService.d.ts.map +1 -0
- package/dist/modules/documents/DocumentLinkService.js +75 -0
- package/dist/modules/documents/DocumentLinkService.js.map +1 -0
- package/dist/modules/documents/DocumentPlannerService.d.ts +23 -0
- package/dist/modules/documents/DocumentPlannerService.d.ts.map +1 -0
- package/dist/modules/documents/DocumentPlannerService.js +96 -0
- package/dist/modules/documents/DocumentPlannerService.js.map +1 -0
- package/dist/modules/documents/GitHubDocsProvider.d.ts +42 -0
- package/dist/modules/documents/GitHubDocsProvider.d.ts.map +1 -0
- package/dist/modules/documents/GitHubDocsProvider.js +86 -0
- package/dist/modules/documents/GitHubDocsProvider.js.map +1 -0
- package/dist/modules/documents/NotionProvider.d.ts +32 -0
- package/dist/modules/documents/NotionProvider.d.ts.map +1 -0
- package/dist/modules/documents/NotionProvider.js +221 -0
- package/dist/modules/documents/NotionProvider.js.map +1 -0
- package/dist/modules/documents/confluence.logic.d.ts +37 -0
- package/dist/modules/documents/confluence.logic.d.ts.map +1 -0
- package/dist/modules/documents/confluence.logic.js +133 -0
- package/dist/modules/documents/confluence.logic.js.map +1 -0
- package/dist/modules/documents/documents.logic.d.ts +22 -0
- package/dist/modules/documents/documents.logic.d.ts.map +1 -0
- package/dist/modules/documents/documents.logic.js +138 -0
- package/dist/modules/documents/documents.logic.js.map +1 -0
- package/dist/modules/documents/github-docs.logic.d.ts +52 -0
- package/dist/modules/documents/github-docs.logic.d.ts.map +1 -0
- package/dist/modules/documents/github-docs.logic.js +94 -0
- package/dist/modules/documents/github-docs.logic.js.map +1 -0
- package/dist/modules/documents/notion.logic.d.ts +31 -0
- package/dist/modules/documents/notion.logic.d.ts.map +1 -0
- package/dist/modules/documents/notion.logic.js +142 -0
- package/dist/modules/documents/notion.logic.js.map +1 -0
- package/dist/modules/email/EmailConnectionService.d.ts +34 -0
- package/dist/modules/email/EmailConnectionService.d.ts.map +1 -0
- package/dist/modules/email/EmailConnectionService.js +82 -0
- package/dist/modules/email/EmailConnectionService.js.map +1 -0
- package/dist/modules/email/adapters.d.ts +39 -0
- package/dist/modules/email/adapters.d.ts.map +1 -0
- package/dist/modules/email/adapters.js +79 -0
- package/dist/modules/email/adapters.js.map +1 -0
- package/dist/modules/environments/EnvironmentConnectionService.d.ts +42 -0
- package/dist/modules/environments/EnvironmentConnectionService.d.ts.map +1 -0
- package/dist/modules/environments/EnvironmentConnectionService.js +120 -0
- package/dist/modules/environments/EnvironmentConnectionService.js.map +1 -0
- package/dist/modules/environments/EnvironmentProvisioningService.d.ts +56 -0
- package/dist/modules/environments/EnvironmentProvisioningService.d.ts.map +1 -0
- package/dist/modules/environments/EnvironmentProvisioningService.js +153 -0
- package/dist/modules/environments/EnvironmentProvisioningService.js.map +1 -0
- package/dist/modules/environments/EnvironmentTeardownService.d.ts +24 -0
- package/dist/modules/environments/EnvironmentTeardownService.d.ts.map +1 -0
- package/dist/modules/environments/EnvironmentTeardownService.js +54 -0
- package/dist/modules/environments/EnvironmentTeardownService.js.map +1 -0
- package/dist/modules/environments/HttpEnvironmentProvider.d.ts +30 -0
- package/dist/modules/environments/HttpEnvironmentProvider.d.ts.map +1 -0
- package/dist/modules/environments/HttpEnvironmentProvider.js +316 -0
- package/dist/modules/environments/HttpEnvironmentProvider.js.map +1 -0
- package/dist/modules/environments/environments.logic.d.ts +50 -0
- package/dist/modules/environments/environments.logic.d.ts.map +1 -0
- package/dist/modules/environments/environments.logic.js +257 -0
- package/dist/modules/environments/environments.logic.js.map +1 -0
- package/dist/modules/github/GitHubInstallationService.d.ts +66 -0
- package/dist/modules/github/GitHubInstallationService.d.ts.map +1 -0
- package/dist/modules/github/GitHubInstallationService.js +143 -0
- package/dist/modules/github/GitHubInstallationService.js.map +1 -0
- package/dist/modules/github/GitHubService.d.ts +29 -0
- package/dist/modules/github/GitHubService.d.ts.map +1 -0
- package/dist/modules/github/GitHubService.js +61 -0
- package/dist/modules/github/GitHubService.js.map +1 -0
- package/dist/modules/github/GitHubSyncService.d.ts +97 -0
- package/dist/modules/github/GitHubSyncService.d.ts.map +1 -0
- package/dist/modules/github/GitHubSyncService.js +241 -0
- package/dist/modules/github/GitHubSyncService.js.map +1 -0
- package/dist/modules/github/RepoProvisioningService.d.ts +26 -0
- package/dist/modules/github/RepoProvisioningService.d.ts.map +1 -0
- package/dist/modules/github/RepoProvisioningService.js +36 -0
- package/dist/modules/github/RepoProvisioningService.js.map +1 -0
- package/dist/modules/github/WebhookService.d.ts +28 -0
- package/dist/modules/github/WebhookService.d.ts.map +1 -0
- package/dist/modules/github/WebhookService.js +156 -0
- package/dist/modules/github/WebhookService.js.map +1 -0
- package/dist/modules/github/projection.logic.d.ts +95 -0
- package/dist/modules/github/projection.logic.d.ts.map +1 -0
- package/dist/modules/github/projection.logic.js +94 -0
- package/dist/modules/github/projection.logic.js.map +1 -0
- package/dist/modules/github/provisioning.logic.d.ts +11 -0
- package/dist/modules/github/provisioning.logic.d.ts.map +1 -0
- package/dist/modules/github/provisioning.logic.js +18 -0
- package/dist/modules/github/provisioning.logic.js.map +1 -0
- package/dist/modules/incident/incident.logic.d.ts +16 -0
- package/dist/modules/incident/incident.logic.d.ts.map +1 -0
- package/dist/modules/incident/incident.logic.js +23 -0
- package/dist/modules/incident/incident.logic.js.map +1 -0
- package/dist/modules/incidentio/IncidentIoEnrichmentProvider.d.ts +26 -0
- package/dist/modules/incidentio/IncidentIoEnrichmentProvider.d.ts.map +1 -0
- package/dist/modules/incidentio/IncidentIoEnrichmentProvider.js +84 -0
- package/dist/modules/incidentio/IncidentIoEnrichmentProvider.js.map +1 -0
- package/dist/modules/pagerduty/PagerDutyEnrichmentProvider.d.ts +27 -0
- package/dist/modules/pagerduty/PagerDutyEnrichmentProvider.d.ts.map +1 -0
- package/dist/modules/pagerduty/PagerDutyEnrichmentProvider.js +65 -0
- package/dist/modules/pagerduty/PagerDutyEnrichmentProvider.js.map +1 -0
- package/dist/modules/providers/ApiKeyService.d.ts +73 -0
- package/dist/modules/providers/ApiKeyService.d.ts.map +1 -0
- package/dist/modules/providers/ApiKeyService.js +122 -0
- package/dist/modules/providers/ApiKeyService.js.map +1 -0
- package/dist/modules/providers/LocalModelEndpointService.d.ts +52 -0
- package/dist/modules/providers/LocalModelEndpointService.d.ts.map +1 -0
- package/dist/modules/providers/LocalModelEndpointService.js +131 -0
- package/dist/modules/providers/LocalModelEndpointService.js.map +1 -0
- package/dist/modules/providers/PersonalSubscriptionService.d.ts +94 -0
- package/dist/modules/providers/PersonalSubscriptionService.d.ts.map +1 -0
- package/dist/modules/providers/PersonalSubscriptionService.js +218 -0
- package/dist/modules/providers/PersonalSubscriptionService.js.map +1 -0
- package/dist/modules/providers/ProviderSubscriptionService.d.ts +75 -0
- package/dist/modules/providers/ProviderSubscriptionService.d.ts.map +1 -0
- package/dist/modules/providers/ProviderSubscriptionService.js +130 -0
- package/dist/modules/providers/ProviderSubscriptionService.js.map +1 -0
- package/dist/modules/providers/localModelUrl.d.ts +7 -0
- package/dist/modules/providers/localModelUrl.d.ts.map +1 -0
- package/dist/modules/providers/localModelUrl.js +67 -0
- package/dist/modules/providers/localModelUrl.js.map +1 -0
- package/dist/modules/providers/providers.logic.d.ts +23 -0
- package/dist/modules/providers/providers.logic.d.ts.map +1 -0
- package/dist/modules/providers/providers.logic.js +46 -0
- package/dist/modules/providers/providers.logic.js.map +1 -0
- package/dist/modules/runners/HttpRunnerPoolProvider.d.ts +51 -0
- package/dist/modules/runners/HttpRunnerPoolProvider.d.ts.map +1 -0
- package/dist/modules/runners/HttpRunnerPoolProvider.js +304 -0
- package/dist/modules/runners/HttpRunnerPoolProvider.js.map +1 -0
- package/dist/modules/runners/RunnerPoolConnectionService.d.ts +47 -0
- package/dist/modules/runners/RunnerPoolConnectionService.d.ts.map +1 -0
- package/dist/modules/runners/RunnerPoolConnectionService.js +98 -0
- package/dist/modules/runners/RunnerPoolConnectionService.js.map +1 -0
- package/dist/modules/runners/RunnerPoolTransport.d.ts +11 -0
- package/dist/modules/runners/RunnerPoolTransport.d.ts.map +1 -0
- package/dist/modules/runners/RunnerPoolTransport.js +61 -0
- package/dist/modules/runners/RunnerPoolTransport.js.map +1 -0
- package/dist/modules/runners/runners.logic.d.ts +16 -0
- package/dist/modules/runners/runners.logic.d.ts.map +1 -0
- package/dist/modules/runners/runners.logic.js +52 -0
- package/dist/modules/runners/runners.logic.js.map +1 -0
- package/dist/modules/slack/SlackApiClient.d.ts +67 -0
- package/dist/modules/slack/SlackApiClient.d.ts.map +1 -0
- package/dist/modules/slack/SlackApiClient.js +132 -0
- package/dist/modules/slack/SlackApiClient.js.map +1 -0
- package/dist/modules/slack/SlackConnectionService.d.ts +41 -0
- package/dist/modules/slack/SlackConnectionService.d.ts.map +1 -0
- package/dist/modules/slack/SlackConnectionService.js +136 -0
- package/dist/modules/slack/SlackConnectionService.js.map +1 -0
- package/dist/modules/slack/SlackMemberMappingService.d.ts +17 -0
- package/dist/modules/slack/SlackMemberMappingService.d.ts.map +1 -0
- package/dist/modules/slack/SlackMemberMappingService.js +28 -0
- package/dist/modules/slack/SlackMemberMappingService.js.map +1 -0
- package/dist/modules/slack/SlackNotificationChannel.d.ts +45 -0
- package/dist/modules/slack/SlackNotificationChannel.d.ts.map +1 -0
- package/dist/modules/slack/SlackNotificationChannel.js +84 -0
- package/dist/modules/slack/SlackNotificationChannel.js.map +1 -0
- package/dist/modules/slack/SlackSettingsService.d.ts +16 -0
- package/dist/modules/slack/SlackSettingsService.d.ts.map +1 -0
- package/dist/modules/slack/SlackSettingsService.js +41 -0
- package/dist/modules/slack/SlackSettingsService.js.map +1 -0
- package/dist/modules/slack/slack.logic.d.ts +55 -0
- package/dist/modules/slack/slack.logic.d.ts.map +1 -0
- package/dist/modules/slack/slack.logic.js +149 -0
- package/dist/modules/slack/slack.logic.js.map +1 -0
- package/dist/modules/tasks/GitHubIssuesProvider.d.ts +50 -0
- package/dist/modules/tasks/GitHubIssuesProvider.d.ts.map +1 -0
- package/dist/modules/tasks/GitHubIssuesProvider.js +92 -0
- package/dist/modules/tasks/GitHubIssuesProvider.js.map +1 -0
- package/dist/modules/tasks/JiraProvider.d.ts +29 -0
- package/dist/modules/tasks/JiraProvider.d.ts.map +1 -0
- package/dist/modules/tasks/JiraProvider.js +114 -0
- package/dist/modules/tasks/JiraProvider.js.map +1 -0
- package/dist/modules/tasks/TaskConnectionService.d.ts +30 -0
- package/dist/modules/tasks/TaskConnectionService.d.ts.map +1 -0
- package/dist/modules/tasks/TaskConnectionService.js +69 -0
- package/dist/modules/tasks/TaskConnectionService.js.map +1 -0
- package/dist/modules/tasks/TaskImportService.d.ts +34 -0
- package/dist/modules/tasks/TaskImportService.d.ts.map +1 -0
- package/dist/modules/tasks/TaskImportService.js +96 -0
- package/dist/modules/tasks/TaskImportService.js.map +1 -0
- package/dist/modules/tasks/TaskLinkService.d.ts +30 -0
- package/dist/modules/tasks/TaskLinkService.d.ts.map +1 -0
- package/dist/modules/tasks/TaskLinkService.js +56 -0
- package/dist/modules/tasks/TaskLinkService.js.map +1 -0
- package/dist/modules/tasks/github-issues.logic.d.ts +35 -0
- package/dist/modules/tasks/github-issues.logic.d.ts.map +1 -0
- package/dist/modules/tasks/github-issues.logic.js +67 -0
- package/dist/modules/tasks/github-issues.logic.js.map +1 -0
- package/dist/modules/tasks/jira.logic.d.ts +28 -0
- package/dist/modules/tasks/jira.logic.d.ts.map +1 -0
- package/dist/modules/tasks/jira.logic.js +151 -0
- package/dist/modules/tasks/jira.logic.js.map +1 -0
- package/dist/modules/tasks/tasks.logic.d.ts +12 -0
- package/dist/modules/tasks/tasks.logic.d.ts.map +1 -0
- package/dist/modules/tasks/tasks.logic.js +17 -0
- package/dist/modules/tasks/tasks.logic.js.map +1 -0
- package/dist/modules/tracker/TicketTrackerService.d.ts +45 -0
- package/dist/modules/tracker/TicketTrackerService.d.ts.map +1 -0
- package/dist/modules/tracker/TicketTrackerService.js +52 -0
- package/dist/modules/tracker/TicketTrackerService.js.map +1 -0
- package/dist/modules/tracker/base64.d.ts +2 -0
- package/dist/modules/tracker/base64.d.ts.map +1 -0
- package/dist/modules/tracker/base64.js +18 -0
- package/dist/modules/tracker/base64.js.map +1 -0
- package/dist/modules/tracker/github.create.logic.d.ts +16 -0
- package/dist/modules/tracker/github.create.logic.d.ts.map +1 -0
- package/dist/modules/tracker/github.create.logic.js +25 -0
- package/dist/modules/tracker/github.create.logic.js.map +1 -0
- package/dist/modules/tracker/jira.create.logic.d.ts +31 -0
- package/dist/modules/tracker/jira.create.logic.d.ts.map +1 -0
- package/dist/modules/tracker/jira.create.logic.js +59 -0
- package/dist/modules/tracker/jira.create.logic.js.map +1 -0
- package/package.json +36 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { ConflictError, ValidationError } from '@cat-factory/kernel';
|
|
2
|
+
import { requireWorkspace } from '@cat-factory/kernel';
|
|
3
|
+
function toConnection(record) {
|
|
4
|
+
return {
|
|
5
|
+
source: record.source,
|
|
6
|
+
label: record.label,
|
|
7
|
+
connectedAt: record.createdAt,
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export class TaskConnectionService {
|
|
11
|
+
deps;
|
|
12
|
+
constructor(deps) {
|
|
13
|
+
this.deps = deps;
|
|
14
|
+
}
|
|
15
|
+
/** The descriptors of every configured source (drives the connect UI). */
|
|
16
|
+
listSources() {
|
|
17
|
+
return this.deps.registry.list().map((p) => p.descriptor);
|
|
18
|
+
}
|
|
19
|
+
/** Resolve a provider for a source or throw if that source isn't configured. */
|
|
20
|
+
requireProvider(source) {
|
|
21
|
+
const provider = this.deps.registry.get(source);
|
|
22
|
+
if (!provider)
|
|
23
|
+
throw new ValidationError(`Unknown or unconfigured task source '${source}'`);
|
|
24
|
+
return provider;
|
|
25
|
+
}
|
|
26
|
+
/** Connect (or re-connect) a workspace to a task source. */
|
|
27
|
+
async connect(workspaceId, source, credentials) {
|
|
28
|
+
await requireWorkspace(this.deps.workspaceRepository, workspaceId);
|
|
29
|
+
const provider = this.requireProvider(source);
|
|
30
|
+
const normalized = provider.normalizeConnection(credentials);
|
|
31
|
+
const existing = await this.deps.taskConnectionRepository.getByWorkspace(workspaceId, source);
|
|
32
|
+
const record = {
|
|
33
|
+
workspaceId,
|
|
34
|
+
source,
|
|
35
|
+
credentials: normalized.credentials,
|
|
36
|
+
label: normalized.label,
|
|
37
|
+
createdAt: existing?.createdAt ?? this.deps.clock.now(),
|
|
38
|
+
deletedAt: null,
|
|
39
|
+
};
|
|
40
|
+
await this.deps.taskConnectionRepository.upsert(record);
|
|
41
|
+
return toConnection(record);
|
|
42
|
+
}
|
|
43
|
+
/** The workspace's current connection for a source, or null if not connected. */
|
|
44
|
+
async getConnection(workspaceId, source) {
|
|
45
|
+
const record = await this.deps.taskConnectionRepository.getByWorkspace(workspaceId, source);
|
|
46
|
+
return record ? toConnection(record) : null;
|
|
47
|
+
}
|
|
48
|
+
/** Every live connection the workspace holds, across sources. */
|
|
49
|
+
async listConnections(workspaceId) {
|
|
50
|
+
const records = await this.deps.taskConnectionRepository.listByWorkspace(workspaceId);
|
|
51
|
+
return records.map(toConnection);
|
|
52
|
+
}
|
|
53
|
+
/** Resolve the live connection (with credentials) or throw if not connected. */
|
|
54
|
+
async requireConnection(workspaceId, source) {
|
|
55
|
+
const record = await this.deps.taskConnectionRepository.getByWorkspace(workspaceId, source);
|
|
56
|
+
if (!record) {
|
|
57
|
+
throw new ConflictError(`Workspace '${workspaceId}' is not connected to ${source}`);
|
|
58
|
+
}
|
|
59
|
+
return record;
|
|
60
|
+
}
|
|
61
|
+
/** Disconnect a workspace from a source (tombstones the binding). */
|
|
62
|
+
async disconnect(workspaceId, source) {
|
|
63
|
+
const record = await this.deps.taskConnectionRepository.getByWorkspace(workspaceId, source);
|
|
64
|
+
if (!record)
|
|
65
|
+
return;
|
|
66
|
+
await this.deps.taskConnectionRepository.softDelete(workspaceId, source, this.deps.clock.now());
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=TaskConnectionService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TaskConnectionService.js","sourceRoot":"","sources":["../../../src/modules/tasks/TaskConnectionService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Clock } from '@cat-factory/kernel';
|
|
2
|
+
import type { TaskSourceRegistry } from '@cat-factory/kernel';
|
|
3
|
+
import type { TaskRecord, TaskRepository } from '@cat-factory/kernel';
|
|
4
|
+
import type { SourceTask, TaskSearchResult, TaskSourceKind } from '@cat-factory/kernel';
|
|
5
|
+
import type { WorkspaceRepository } from '@cat-factory/kernel';
|
|
6
|
+
import type { TaskConnectionService } from './TaskConnectionService.js';
|
|
7
|
+
export interface TaskImportServiceDependencies {
|
|
8
|
+
registry: TaskSourceRegistry;
|
|
9
|
+
taskRepository: TaskRepository;
|
|
10
|
+
connectionService: TaskConnectionService;
|
|
11
|
+
workspaceRepository: WorkspaceRepository;
|
|
12
|
+
clock: Clock;
|
|
13
|
+
}
|
|
14
|
+
/** Project a stored task record onto the wire shape (drops the tombstone). */
|
|
15
|
+
export declare function toSourceTask(record: TaskRecord): SourceTask;
|
|
16
|
+
export declare class TaskImportService {
|
|
17
|
+
private readonly deps;
|
|
18
|
+
constructor(deps: TaskImportServiceDependencies);
|
|
19
|
+
private requireProvider;
|
|
20
|
+
/** Fetch an issue (by key or URL) and upsert its projection; returns the issue. */
|
|
21
|
+
import(workspaceId: string, source: TaskSourceKind, ref: string): Promise<SourceTask>;
|
|
22
|
+
/**
|
|
23
|
+
* Search a tracker by free text, returning lean hits (not yet imported). The
|
|
24
|
+
* provider authenticates with the workspace's stored credentials and builds/
|
|
25
|
+
* parses the source-specific query. Throws if the source can't search (no
|
|
26
|
+
* provider `search`), so the controller can answer cleanly.
|
|
27
|
+
*/
|
|
28
|
+
search(workspaceId: string, source: TaskSourceKind, query: string): Promise<TaskSearchResult[]>;
|
|
29
|
+
/** Every issue imported into the workspace, across sources, as wire shapes. */
|
|
30
|
+
listTasks(workspaceId: string): Promise<SourceTask[]>;
|
|
31
|
+
/** Resolve a stored task record or throw if not imported. */
|
|
32
|
+
requireTask(workspaceId: string, source: TaskSourceKind, externalId: string): Promise<TaskRecord>;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=TaskImportService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TaskImportService.d.ts","sourceRoot":"","sources":["../../../src/modules/tasks/TaskImportService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAA;AAChD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAC7D,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AACrE,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAGvF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AAC9D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAA;AASvE,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,EAAE,kBAAkB,CAAA;IAC5B,cAAc,EAAE,cAAc,CAAA;IAC9B,iBAAiB,EAAE,qBAAqB,CAAA;IACxC,mBAAmB,EAAE,mBAAmB,CAAA;IACxC,KAAK,EAAE,KAAK,CAAA;CACb;AAED,8EAA8E;AAC9E,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,GAAG,UAAU,CAiB3D;AAED,qBAAa,iBAAiB;IAChB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAAjC,YAA6B,IAAI,EAAE,6BAA6B,EAAI;IAEpE,OAAO,CAAC,eAAe;IAMvB,mFAAmF;IAC7E,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAgC1F;IAED;;;;;OAKG;IACG,MAAM,CACV,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAQ7B;IAED,+EAA+E;IACzE,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAG1D;IAED,6DAA6D;IACvD,WAAW,CACf,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,cAAc,EACtB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,UAAU,CAAC,CAMrB;CACF"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { ValidationError } from '@cat-factory/kernel';
|
|
2
|
+
import { requireWorkspace } from '@cat-factory/kernel';
|
|
3
|
+
import { buildTaskExcerpt } from './tasks.logic.js';
|
|
4
|
+
/** Project a stored task record onto the wire shape (drops the tombstone). */
|
|
5
|
+
export function toSourceTask(record) {
|
|
6
|
+
return {
|
|
7
|
+
source: record.source,
|
|
8
|
+
externalId: record.externalId,
|
|
9
|
+
title: record.title,
|
|
10
|
+
url: record.url,
|
|
11
|
+
status: record.status,
|
|
12
|
+
type: record.type,
|
|
13
|
+
assignee: record.assignee,
|
|
14
|
+
priority: record.priority,
|
|
15
|
+
labels: record.labels,
|
|
16
|
+
description: record.description,
|
|
17
|
+
comments: record.comments,
|
|
18
|
+
excerpt: record.excerpt,
|
|
19
|
+
linkedBlockId: record.linkedBlockId,
|
|
20
|
+
syncedAt: record.syncedAt,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export class TaskImportService {
|
|
24
|
+
deps;
|
|
25
|
+
constructor(deps) {
|
|
26
|
+
this.deps = deps;
|
|
27
|
+
}
|
|
28
|
+
requireProvider(source) {
|
|
29
|
+
const provider = this.deps.registry.get(source);
|
|
30
|
+
if (!provider)
|
|
31
|
+
throw new ValidationError(`Unknown or unconfigured task source '${source}'`);
|
|
32
|
+
return provider;
|
|
33
|
+
}
|
|
34
|
+
/** Fetch an issue (by key or URL) and upsert its projection; returns the issue. */
|
|
35
|
+
async import(workspaceId, source, ref) {
|
|
36
|
+
await requireWorkspace(this.deps.workspaceRepository, workspaceId);
|
|
37
|
+
const provider = this.requireProvider(source);
|
|
38
|
+
const externalId = provider.parseRef(ref);
|
|
39
|
+
if (!externalId) {
|
|
40
|
+
throw new ValidationError(`Could not resolve a ${source} issue key from '${ref}'`);
|
|
41
|
+
}
|
|
42
|
+
const connection = await this.deps.connectionService.requireConnection(workspaceId, source);
|
|
43
|
+
const content = await provider.fetchTask(connection.credentials, externalId);
|
|
44
|
+
// Preserve any existing block link across a re-import.
|
|
45
|
+
const existing = await this.deps.taskRepository.get(workspaceId, source, content.externalId);
|
|
46
|
+
const record = {
|
|
47
|
+
workspaceId,
|
|
48
|
+
source,
|
|
49
|
+
externalId: content.externalId,
|
|
50
|
+
title: content.title,
|
|
51
|
+
url: content.url,
|
|
52
|
+
status: content.status,
|
|
53
|
+
type: content.type,
|
|
54
|
+
assignee: content.assignee,
|
|
55
|
+
priority: content.priority,
|
|
56
|
+
labels: content.labels,
|
|
57
|
+
description: content.description,
|
|
58
|
+
comments: content.comments,
|
|
59
|
+
excerpt: buildTaskExcerpt(content),
|
|
60
|
+
linkedBlockId: existing?.linkedBlockId ?? null,
|
|
61
|
+
syncedAt: this.deps.clock.now(),
|
|
62
|
+
deletedAt: null,
|
|
63
|
+
};
|
|
64
|
+
await this.deps.taskRepository.upsert(record);
|
|
65
|
+
return toSourceTask(record);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Search a tracker by free text, returning lean hits (not yet imported). The
|
|
69
|
+
* provider authenticates with the workspace's stored credentials and builds/
|
|
70
|
+
* parses the source-specific query. Throws if the source can't search (no
|
|
71
|
+
* provider `search`), so the controller can answer cleanly.
|
|
72
|
+
*/
|
|
73
|
+
async search(workspaceId, source, query) {
|
|
74
|
+
await requireWorkspace(this.deps.workspaceRepository, workspaceId);
|
|
75
|
+
const provider = this.requireProvider(source);
|
|
76
|
+
if (!provider.search) {
|
|
77
|
+
throw new ValidationError(`The ${source} source does not support search`);
|
|
78
|
+
}
|
|
79
|
+
const connection = await this.deps.connectionService.requireConnection(workspaceId, source);
|
|
80
|
+
return provider.search(connection.credentials, query, workspaceId);
|
|
81
|
+
}
|
|
82
|
+
/** Every issue imported into the workspace, across sources, as wire shapes. */
|
|
83
|
+
async listTasks(workspaceId) {
|
|
84
|
+
const records = await this.deps.taskRepository.listByWorkspace(workspaceId);
|
|
85
|
+
return records.map(toSourceTask);
|
|
86
|
+
}
|
|
87
|
+
/** Resolve a stored task record or throw if not imported. */
|
|
88
|
+
async requireTask(workspaceId, source, externalId) {
|
|
89
|
+
const record = await this.deps.taskRepository.get(workspaceId, source, externalId);
|
|
90
|
+
if (!record) {
|
|
91
|
+
throw new ValidationError(`${source} issue '${externalId}' has not been imported`);
|
|
92
|
+
}
|
|
93
|
+
return record;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=TaskImportService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TaskImportService.js","sourceRoot":"","sources":["../../../src/modules/tasks/TaskImportService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Block, SourceTask, TaskSourceKind } from '@cat-factory/kernel';
|
|
2
|
+
import type { BlockRepository } from '@cat-factory/kernel';
|
|
3
|
+
import type { BoardWritePort } from '@cat-factory/kernel';
|
|
4
|
+
import type { TaskRepository } from '@cat-factory/kernel';
|
|
5
|
+
export interface TaskLinkServiceDependencies {
|
|
6
|
+
boardService: BoardWritePort;
|
|
7
|
+
blockRepository: BlockRepository;
|
|
8
|
+
taskRepository: TaskRepository;
|
|
9
|
+
}
|
|
10
|
+
/** A board task created from an imported issue, plus the now-linked issue. */
|
|
11
|
+
export interface TaskFromIssue {
|
|
12
|
+
block: Block;
|
|
13
|
+
task: SourceTask;
|
|
14
|
+
}
|
|
15
|
+
export declare class TaskLinkService {
|
|
16
|
+
private readonly deps;
|
|
17
|
+
constructor(deps: TaskLinkServiceDependencies);
|
|
18
|
+
/** Attach an imported issue to a board block as extra agent context. */
|
|
19
|
+
linkToBlock(workspaceId: string, blockId: string, source: TaskSourceKind, externalId: string): Promise<SourceTask>;
|
|
20
|
+
/**
|
|
21
|
+
* Create a new board task from an already-imported issue, inside a container
|
|
22
|
+
* (service frame or module), and link the issue to the new task for context.
|
|
23
|
+
* The title/description are seeded from the issue; the issue stays the source
|
|
24
|
+
* of truth (re-importing refreshes it) and is fed to every agent step via the
|
|
25
|
+
* link. Reuses BoardService.addTask so scope/placement rules stay in one place.
|
|
26
|
+
* `createdBy` (the signed-in user) flows onto the new task for notification routing.
|
|
27
|
+
*/
|
|
28
|
+
createTaskFromIssue(workspaceId: string, containerId: string, source: TaskSourceKind, externalId: string, createdBy?: string | null): Promise<TaskFromIssue>;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=TaskLinkService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TaskLinkService.d.ts","sourceRoot":"","sources":["../../../src/modules/tasks/TaskLinkService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,UAAU,EAAc,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAExF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAUzD,MAAM,WAAW,2BAA2B;IAC1C,YAAY,EAAE,cAAc,CAAA;IAC5B,eAAe,EAAE,eAAe,CAAA;IAChC,cAAc,EAAE,cAAc,CAAA;CAC/B;AAED,8EAA8E;AAC9E,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,KAAK,CAAA;IACZ,IAAI,EAAE,UAAU,CAAA;CACjB;AAED,qBAAa,eAAe;IACd,OAAO,CAAC,QAAQ,CAAC,IAAI;IAAjC,YAA6B,IAAI,EAAE,2BAA2B,EAAI;IAElE,wEAAwE;IAClE,WAAW,CACf,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,cAAc,EACtB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,UAAU,CAAC,CAarB;IAED;;;;;;;OAOG;IACG,mBAAmB,CACvB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,cAAc,EACtB,UAAU,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GACxB,OAAO,CAAC,aAAa,CAAC,CAgCxB;CACF"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { assertFound, ConflictError } from '@cat-factory/kernel';
|
|
2
|
+
import { toSourceTask } from './TaskImportService.js';
|
|
3
|
+
export class TaskLinkService {
|
|
4
|
+
deps;
|
|
5
|
+
constructor(deps) {
|
|
6
|
+
this.deps = deps;
|
|
7
|
+
}
|
|
8
|
+
/** Attach an imported issue to a board block as extra agent context. */
|
|
9
|
+
async linkToBlock(workspaceId, blockId, source, externalId) {
|
|
10
|
+
const block = assertFound(await this.deps.blockRepository.get(workspaceId, blockId), 'Block', blockId);
|
|
11
|
+
const task = assertFound(await this.deps.taskRepository.get(workspaceId, source, externalId), 'Task', externalId);
|
|
12
|
+
await this.deps.taskRepository.linkBlock(workspaceId, source, externalId, block.id);
|
|
13
|
+
return toSourceTask({ ...task, linkedBlockId: block.id });
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Create a new board task from an already-imported issue, inside a container
|
|
17
|
+
* (service frame or module), and link the issue to the new task for context.
|
|
18
|
+
* The title/description are seeded from the issue; the issue stays the source
|
|
19
|
+
* of truth (re-importing refreshes it) and is fed to every agent step via the
|
|
20
|
+
* link. Reuses BoardService.addTask so scope/placement rules stay in one place.
|
|
21
|
+
* `createdBy` (the signed-in user) flows onto the new task for notification routing.
|
|
22
|
+
*/
|
|
23
|
+
async createTaskFromIssue(workspaceId, containerId, source, externalId, createdBy) {
|
|
24
|
+
const issue = assertFound(await this.deps.taskRepository.get(workspaceId, source, externalId), 'Task', externalId);
|
|
25
|
+
// An issue carries a single `linkedBlockId`, so creating a second task from it
|
|
26
|
+
// would silently re-point the link and orphan the first task's issue context.
|
|
27
|
+
// Refuse rather than lose the existing link (the issue is the source of truth).
|
|
28
|
+
if (issue.linkedBlockId) {
|
|
29
|
+
throw new ConflictError(`Issue ${externalId} is already linked to task ${issue.linkedBlockId}; unlink it first`);
|
|
30
|
+
}
|
|
31
|
+
// Resolve the container in the REQUEST workspace (like linkToBlock) so the new
|
|
32
|
+
// block and the issue projection share a workspace — the issue link is workspace-
|
|
33
|
+
// scoped, so creating the task in a service mounted from another workspace would
|
|
34
|
+
// leave the link unresolvable at execution time. A foreign/unknown container 404s.
|
|
35
|
+
assertFound(await this.deps.blockRepository.get(workspaceId, containerId), 'Block', containerId);
|
|
36
|
+
const block = await this.deps.boardService.addTask(workspaceId, containerId, {
|
|
37
|
+
title: issueTaskTitle(issue),
|
|
38
|
+
description: issueTaskDescription(issue),
|
|
39
|
+
}, createdBy ?? null);
|
|
40
|
+
// Link the issue to the new task so agents get the full issue (description,
|
|
41
|
+
// comments, metadata) as context — and the task carries the back-reference.
|
|
42
|
+
await this.deps.taskRepository.linkBlock(workspaceId, source, externalId, block.id);
|
|
43
|
+
return { block, task: toSourceTask({ ...issue, linkedBlockId: block.id }) };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/** Seed the new task's title from the issue (keyed for traceability). */
|
|
47
|
+
function issueTaskTitle(issue) {
|
|
48
|
+
return `${issue.externalId}: ${issue.title}`;
|
|
49
|
+
}
|
|
50
|
+
/** Seed the new task's description: a source reference line + the issue body. */
|
|
51
|
+
function issueTaskDescription(issue) {
|
|
52
|
+
const reference = `Imported from ${issue.url}`;
|
|
53
|
+
const body = issue.description.trim() || issue.excerpt.trim();
|
|
54
|
+
return body ? `${reference}\n\n${body}` : reference;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=TaskLinkService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TaskLinkService.js","sourceRoot":"","sources":["../../../src/modules/tasks/TaskLinkService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { TaskSourceDescriptor } from '@cat-factory/kernel';
|
|
2
|
+
/**
|
|
3
|
+
* What the connect UI renders. GitHub issues piggyback on the workspace's
|
|
4
|
+
* existing GitHub App installation, so there are NO credential fields — the
|
|
5
|
+
* connect form is just a confirmation, and `normalizeConnection` accepts an empty
|
|
6
|
+
* bag.
|
|
7
|
+
*/
|
|
8
|
+
export declare const GITHUB_ISSUES_DESCRIPTOR: TaskSourceDescriptor;
|
|
9
|
+
/** The canonical `owner/repo#number` external id for an issue's parts. */
|
|
10
|
+
export declare function githubIssueExternalId(id: GitHubIssueExternalId): string;
|
|
11
|
+
/**
|
|
12
|
+
* Resolve a GitHub issue reference from raw user input into the canonical
|
|
13
|
+
* `owner/repo#number` external id. Accepts:
|
|
14
|
+
* - a full issue URL: `https://github.com/octo/repo/issues/123`
|
|
15
|
+
* - the `octo/repo/issues/123` path form
|
|
16
|
+
* - the shorthand `octo/repo#123`
|
|
17
|
+
* Returns null when nothing parses. The owner/repo are kept verbatim (GitHub repo
|
|
18
|
+
* names are case-preserving); only surrounding whitespace is trimmed.
|
|
19
|
+
*/
|
|
20
|
+
export declare function parseGitHubIssueRef(input: string): string | null;
|
|
21
|
+
/** The parts of a GitHub issue external id (`owner/repo#number`). */
|
|
22
|
+
export interface GitHubIssueExternalId {
|
|
23
|
+
owner: string;
|
|
24
|
+
repo: string;
|
|
25
|
+
number: number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Split a stored `owner/repo#number` external id back into its parts. Returns
|
|
29
|
+
* null if the id is malformed (defensive — ids are produced by
|
|
30
|
+
* {@link parseGitHubIssueRef}, but a stale/hand-edited row should not throw).
|
|
31
|
+
*/
|
|
32
|
+
export declare function parseGitHubIssueExternalId(externalId: string): GitHubIssueExternalId | null;
|
|
33
|
+
/** The canonical web URL for an issue external id. */
|
|
34
|
+
export declare function githubIssueUrl(id: GitHubIssueExternalId): string;
|
|
35
|
+
//# sourceMappingURL=github-issues.logic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-issues.logic.d.ts","sourceRoot":"","sources":["../../../src/modules/tasks/github-issues.logic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAU/D;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,EAAE,oBAQtC,CAAA;AAED,0EAA0E;AAC1E,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,qBAAqB,GAAG,MAAM,CAEvE;AAMD;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAShE;AAED,qEAAqE;AACrE,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,MAAM,GAAG,qBAAqB,GAAG,IAAI,CAI3F;AAED,sDAAsD;AACtD,wBAAgB,cAAc,CAAC,EAAE,EAAE,qBAAqB,GAAG,MAAM,CAEhE"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// GitHub-issues task-source pure logic, kept out of the worker so it is
|
|
2
|
+
// unit-testable without a live API: parsing an issue reference out of user input
|
|
3
|
+
// and round-tripping the `owner/repo#number` external id the provider stores. The
|
|
4
|
+
// fetch itself lives in the worker's GitHubIssuesProvider, which reuses the
|
|
5
|
+
// workspace's installed GitHub App (so this source needs no credentials of its
|
|
6
|
+
// own — unlike Jira). GitHub issue bodies are already Markdown, so there is no
|
|
7
|
+
// body-conversion step here.
|
|
8
|
+
/**
|
|
9
|
+
* What the connect UI renders. GitHub issues piggyback on the workspace's
|
|
10
|
+
* existing GitHub App installation, so there are NO credential fields — the
|
|
11
|
+
* connect form is just a confirmation, and `normalizeConnection` accepts an empty
|
|
12
|
+
* bag.
|
|
13
|
+
*/
|
|
14
|
+
export const GITHUB_ISSUES_DESCRIPTOR = {
|
|
15
|
+
source: 'github',
|
|
16
|
+
label: 'GitHub Issues',
|
|
17
|
+
icon: 'i-lucide-github',
|
|
18
|
+
credentialFields: [],
|
|
19
|
+
refLabel: 'Issue URL or owner/repo#number',
|
|
20
|
+
refPlaceholder: 'octo/repo#123 or https://github.com/octo/repo/issues/123',
|
|
21
|
+
searchable: true,
|
|
22
|
+
};
|
|
23
|
+
/** The canonical `owner/repo#number` external id for an issue's parts. */
|
|
24
|
+
export function githubIssueExternalId(id) {
|
|
25
|
+
return `${id.owner}/${id.repo}#${id.number}`;
|
|
26
|
+
}
|
|
27
|
+
// A GitHub owner/repo segment: letters, digits, '.', '_' and '-'. Deliberately
|
|
28
|
+
// permissive but bounded so a stray path can't masquerade as a ref.
|
|
29
|
+
const SEG = '[A-Za-z0-9._-]+';
|
|
30
|
+
/**
|
|
31
|
+
* Resolve a GitHub issue reference from raw user input into the canonical
|
|
32
|
+
* `owner/repo#number` external id. Accepts:
|
|
33
|
+
* - a full issue URL: `https://github.com/octo/repo/issues/123`
|
|
34
|
+
* - the `octo/repo/issues/123` path form
|
|
35
|
+
* - the shorthand `octo/repo#123`
|
|
36
|
+
* Returns null when nothing parses. The owner/repo are kept verbatim (GitHub repo
|
|
37
|
+
* names are case-preserving); only surrounding whitespace is trimmed.
|
|
38
|
+
*/
|
|
39
|
+
export function parseGitHubIssueRef(input) {
|
|
40
|
+
const trimmed = input.trim();
|
|
41
|
+
const url = trimmed.match(new RegExp(`github\\.com/(${SEG})/(${SEG})/issues/(\\d+)`));
|
|
42
|
+
if (url)
|
|
43
|
+
return `${url[1]}/${url[2]}#${url[3]}`;
|
|
44
|
+
const path = trimmed.match(new RegExp(`^(${SEG})/(${SEG})/issues/(\\d+)$`));
|
|
45
|
+
if (path)
|
|
46
|
+
return `${path[1]}/${path[2]}#${path[3]}`;
|
|
47
|
+
const short = trimmed.match(new RegExp(`^(${SEG})/(${SEG})#(\\d+)$`));
|
|
48
|
+
if (short)
|
|
49
|
+
return `${short[1]}/${short[2]}#${short[3]}`;
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Split a stored `owner/repo#number` external id back into its parts. Returns
|
|
54
|
+
* null if the id is malformed (defensive — ids are produced by
|
|
55
|
+
* {@link parseGitHubIssueRef}, but a stale/hand-edited row should not throw).
|
|
56
|
+
*/
|
|
57
|
+
export function parseGitHubIssueExternalId(externalId) {
|
|
58
|
+
const m = externalId.match(new RegExp(`^(${SEG})/(${SEG})#(\\d+)$`));
|
|
59
|
+
if (!m)
|
|
60
|
+
return null;
|
|
61
|
+
return { owner: m[1], repo: m[2], number: Number(m[3]) };
|
|
62
|
+
}
|
|
63
|
+
/** The canonical web URL for an issue external id. */
|
|
64
|
+
export function githubIssueUrl(id) {
|
|
65
|
+
return `https://github.com/${id.owner}/${id.repo}/issues/${id.number}`;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=github-issues.logic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-issues.logic.js","sourceRoot":"","sources":["../../../src/modules/tasks/github-issues.logic.ts"],"names":[],"mappings":"AAEA,wEAAwE;AACxE,iFAAiF;AACjF,kFAAkF;AAClF,4EAA4E;AAC5E,+EAA+E;AAC/E,+EAA+E;AAC/E,6BAA6B;AAE7B;;;;;GAKG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAyB;IAC5D,MAAM,EAAE,QAAQ;IAChB,KAAK,EAAE,eAAe;IACtB,IAAI,EAAE,iBAAiB;IACvB,gBAAgB,EAAE,EAAE;IACpB,QAAQ,EAAE,gCAAgC;IAC1C,cAAc,EAAE,4DAA4D;IAC5E,UAAU,EAAE,IAAI;CACjB,CAAA;AAED,0EAA0E;AAC1E,MAAM,UAAU,qBAAqB,CAAC,EAAyB;IAC7D,OAAO,GAAG,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,MAAM,EAAE,CAAA;AAC9C,CAAC;AAED,+EAA+E;AAC/E,oEAAoE;AACpE,MAAM,GAAG,GAAG,iBAAiB,CAAA;AAE7B;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAa;IAC/C,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAA;IAC5B,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,iBAAiB,GAAG,MAAM,GAAG,iBAAiB,CAAC,CAAC,CAAA;IACrF,IAAI,GAAG;QAAE,OAAO,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,CAAA;IAC/C,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,KAAK,GAAG,MAAM,GAAG,kBAAkB,CAAC,CAAC,CAAA;IAC3E,IAAI,IAAI;QAAE,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAA;IACnD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,KAAK,GAAG,MAAM,GAAG,WAAW,CAAC,CAAC,CAAA;IACrE,IAAI,KAAK;QAAE,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,CAAA;IACvD,OAAO,IAAI,CAAA;AACb,CAAC;AASD;;;;GAIG;AACH,MAAM,UAAU,0BAA0B,CAAC,UAAkB;IAC3D,MAAM,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,KAAK,GAAG,MAAM,GAAG,WAAW,CAAC,CAAC,CAAA;IACpE,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAA;IACnB,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAE,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;AAC5D,CAAC;AAED,sDAAsD;AACtD,MAAM,UAAU,cAAc,CAAC,EAAyB;IACtD,OAAO,sBAAsB,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,EAAE,CAAA;AACxE,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { TaskSearchResult, TaskSourceDescriptor } from '@cat-factory/kernel';
|
|
2
|
+
/** What the connect UI renders, and which credentials the provider needs. */
|
|
3
|
+
export declare const JIRA_DESCRIPTOR: TaskSourceDescriptor;
|
|
4
|
+
/**
|
|
5
|
+
* Build the JQL for a free-text issue search: match the term across summary and
|
|
6
|
+
* body (`text ~`), newest first. A bare key typed into the box still matches via
|
|
7
|
+
* `text ~`, so the search box doubles as a quick "jump to issue".
|
|
8
|
+
*/
|
|
9
|
+
export declare function buildJiraSearchJql(query: string): string;
|
|
10
|
+
/** Map a Jira search response into lean hits; URLs are the canonical `/browse/KEY`. */
|
|
11
|
+
export declare function parseJiraSearchResults(json: unknown, base: string): TaskSearchResult[];
|
|
12
|
+
/**
|
|
13
|
+
* Resolve a Jira issue key from raw user input: a bare key (`PROJ-123`), a
|
|
14
|
+
* `/browse/PROJ-123` URL, or the `selectedIssue=PROJ-123` / `/issues/PROJ-123`
|
|
15
|
+
* board URL forms. The key is upper-cased (Jira keys are case-insensitive on
|
|
16
|
+
* input but canonically upper-case). Returns null when nothing parses.
|
|
17
|
+
*/
|
|
18
|
+
export declare function parseJiraRef(input: string): string | null;
|
|
19
|
+
/**
|
|
20
|
+
* Convert an Atlassian Document Format node tree into lightweight Markdown: the
|
|
21
|
+
* same `#`/`##`/`###` headings, `- ` list items and blank-line block boundaries
|
|
22
|
+
* the generic excerpt/prompt logic consumes. ADF is a JSON document
|
|
23
|
+
* (`{ type: 'doc', content: [...] }`); we walk it defensively so a missing or
|
|
24
|
+
* unexpected node yields '' rather than throwing — Jira fields are sometimes
|
|
25
|
+
* null or a plain string on older issues.
|
|
26
|
+
*/
|
|
27
|
+
export declare function adfToMarkdown(node: unknown): string;
|
|
28
|
+
//# sourceMappingURL=jira.logic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jira.logic.d.ts","sourceRoot":"","sources":["../../../src/modules/tasks/jira.logic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AASjF,6EAA6E;AAC7E,eAAO,MAAM,eAAe,EAAE,oBAuB7B,CAAA;AAOD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAExD;AASD,uFAAuF;AACvF,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAgBtF;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAWzD;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAUnD"}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
// Jira-specific pure logic, kept out of the worker so it is unit-testable
|
|
2
|
+
// without a live site: parsing an issue key out of user input and converting an
|
|
3
|
+
// issue's Atlassian Document Format (ADF) body into the lightweight Markdown the
|
|
4
|
+
// generic excerpt/prompt logic consumes. The base-URL guard is shared with
|
|
5
|
+
// Confluence (see ../../shared/atlassian.logic); the fetch itself lives in the
|
|
6
|
+
// worker's JiraProvider.
|
|
7
|
+
/** What the connect UI renders, and which credentials the provider needs. */
|
|
8
|
+
export const JIRA_DESCRIPTOR = {
|
|
9
|
+
source: 'jira',
|
|
10
|
+
label: 'Jira',
|
|
11
|
+
icon: 'i-lucide-square-check',
|
|
12
|
+
credentialFields: [
|
|
13
|
+
{
|
|
14
|
+
key: 'baseUrl',
|
|
15
|
+
label: 'Site URL',
|
|
16
|
+
placeholder: 'https://your-team.atlassian.net',
|
|
17
|
+
help: 'e.g. https://your-team.atlassian.net',
|
|
18
|
+
},
|
|
19
|
+
{ key: 'accountEmail', label: 'Account email', placeholder: 'you@company.com' },
|
|
20
|
+
{
|
|
21
|
+
key: 'apiToken',
|
|
22
|
+
label: 'API token',
|
|
23
|
+
secret: true,
|
|
24
|
+
placeholder: 'Paste a Jira API token',
|
|
25
|
+
help: 'Create one at id.atlassian.com → Security → API tokens',
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
refLabel: 'Issue key or URL',
|
|
29
|
+
refPlaceholder: 'PROJ-123 or https://…/browse/PROJ-123',
|
|
30
|
+
searchable: true,
|
|
31
|
+
};
|
|
32
|
+
/** Escape a user string for embedding inside a JQL double-quoted literal. */
|
|
33
|
+
function escapeJql(query) {
|
|
34
|
+
return query.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Build the JQL for a free-text issue search: match the term across summary and
|
|
38
|
+
* body (`text ~`), newest first. A bare key typed into the box still matches via
|
|
39
|
+
* `text ~`, so the search box doubles as a quick "jump to issue".
|
|
40
|
+
*/
|
|
41
|
+
export function buildJiraSearchJql(query) {
|
|
42
|
+
return `text ~ "${escapeJql(query.trim())}" ORDER BY updated DESC`;
|
|
43
|
+
}
|
|
44
|
+
/** Map a Jira search response into lean hits; URLs are the canonical `/browse/KEY`. */
|
|
45
|
+
export function parseJiraSearchResults(json, base) {
|
|
46
|
+
const body = (json ?? {});
|
|
47
|
+
const cleanBase = base.replace(/\/+$/, '');
|
|
48
|
+
const out = [];
|
|
49
|
+
for (const issue of Array.isArray(body.issues) ? body.issues : []) {
|
|
50
|
+
if (!issue.key)
|
|
51
|
+
continue;
|
|
52
|
+
out.push({
|
|
53
|
+
source: 'jira',
|
|
54
|
+
externalId: issue.key,
|
|
55
|
+
title: issue.fields?.summary ?? '(untitled)',
|
|
56
|
+
url: `${cleanBase}/browse/${issue.key}`,
|
|
57
|
+
status: issue.fields?.status?.name ?? '',
|
|
58
|
+
excerpt: '',
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Resolve a Jira issue key from raw user input: a bare key (`PROJ-123`), a
|
|
65
|
+
* `/browse/PROJ-123` URL, or the `selectedIssue=PROJ-123` / `/issues/PROJ-123`
|
|
66
|
+
* board URL forms. The key is upper-cased (Jira keys are case-insensitive on
|
|
67
|
+
* input but canonically upper-case). Returns null when nothing parses.
|
|
68
|
+
*/
|
|
69
|
+
export function parseJiraRef(input) {
|
|
70
|
+
const trimmed = input.trim();
|
|
71
|
+
const KEY = /[A-Za-z][A-Za-z0-9]+-\d+/;
|
|
72
|
+
if (new RegExp(`^${KEY.source}$`).test(trimmed))
|
|
73
|
+
return trimmed.toUpperCase();
|
|
74
|
+
const browse = trimmed.match(new RegExp(`/browse/(${KEY.source})`));
|
|
75
|
+
if (browse)
|
|
76
|
+
return browse[1].toUpperCase();
|
|
77
|
+
const selected = trimmed.match(new RegExp(`[?&]selectedIssue=(${KEY.source})`));
|
|
78
|
+
if (selected)
|
|
79
|
+
return selected[1].toUpperCase();
|
|
80
|
+
const issues = trimmed.match(new RegExp(`/issues/(${KEY.source})`));
|
|
81
|
+
if (issues)
|
|
82
|
+
return issues[1].toUpperCase();
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Convert an Atlassian Document Format node tree into lightweight Markdown: the
|
|
87
|
+
* same `#`/`##`/`###` headings, `- ` list items and blank-line block boundaries
|
|
88
|
+
* the generic excerpt/prompt logic consumes. ADF is a JSON document
|
|
89
|
+
* (`{ type: 'doc', content: [...] }`); we walk it defensively so a missing or
|
|
90
|
+
* unexpected node yields '' rather than throwing — Jira fields are sometimes
|
|
91
|
+
* null or a plain string on older issues.
|
|
92
|
+
*/
|
|
93
|
+
export function adfToMarkdown(node) {
|
|
94
|
+
if (node == null)
|
|
95
|
+
return '';
|
|
96
|
+
// Older issues / some fields come back as a plain string already.
|
|
97
|
+
if (typeof node === 'string')
|
|
98
|
+
return node.trim();
|
|
99
|
+
if (typeof node !== 'object')
|
|
100
|
+
return '';
|
|
101
|
+
const out = renderNode(node);
|
|
102
|
+
return out
|
|
103
|
+
.replace(/[ \t]+\n/g, '\n')
|
|
104
|
+
.replace(/\n{3,}/g, '\n\n')
|
|
105
|
+
.trim();
|
|
106
|
+
}
|
|
107
|
+
/** Render a node's children, concatenated. */
|
|
108
|
+
function renderChildren(node) {
|
|
109
|
+
if (!Array.isArray(node.content))
|
|
110
|
+
return '';
|
|
111
|
+
return node.content.map((child) => renderNode(child)).join('');
|
|
112
|
+
}
|
|
113
|
+
/** Render the inline text of a node (no block markers), for headings/list items. */
|
|
114
|
+
function renderInline(node) {
|
|
115
|
+
if (node.type === 'text')
|
|
116
|
+
return node.text ?? '';
|
|
117
|
+
if (node.type === 'hardBreak')
|
|
118
|
+
return ' ';
|
|
119
|
+
return Array.isArray(node.content)
|
|
120
|
+
? node.content.map((child) => renderInline(child)).join('')
|
|
121
|
+
: '';
|
|
122
|
+
}
|
|
123
|
+
function renderNode(node) {
|
|
124
|
+
if (typeof node !== 'object' || node === null)
|
|
125
|
+
return '';
|
|
126
|
+
switch (node.type) {
|
|
127
|
+
case 'text':
|
|
128
|
+
return node.text ?? '';
|
|
129
|
+
case 'hardBreak':
|
|
130
|
+
return '\n';
|
|
131
|
+
case 'paragraph':
|
|
132
|
+
return `${renderChildren(node)}\n\n`;
|
|
133
|
+
case 'heading': {
|
|
134
|
+
const level = Math.min(Math.max(Number(node.attrs?.level ?? 1) || 1, 1), 3);
|
|
135
|
+
return `${'#'.repeat(level)} ${renderInline(node).trim()}\n\n`;
|
|
136
|
+
}
|
|
137
|
+
case 'bulletList':
|
|
138
|
+
case 'orderedList':
|
|
139
|
+
return `${renderChildren(node)}\n`;
|
|
140
|
+
case 'listItem':
|
|
141
|
+
return `- ${renderInline(node).trim()}\n`;
|
|
142
|
+
case 'codeBlock':
|
|
143
|
+
return `\`\`\`\n${renderInline(node)}\n\`\`\`\n\n`;
|
|
144
|
+
case 'blockquote':
|
|
145
|
+
return renderChildren(node);
|
|
146
|
+
default:
|
|
147
|
+
// 'doc', 'mediaGroup', panels, tables, unknown marks: recurse into content.
|
|
148
|
+
return renderChildren(node);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=jira.logic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jira.logic.js","sourceRoot":"","sources":["../../../src/modules/tasks/jira.logic.ts"],"names":[],"mappings":"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"}
|