agent-relay-server 0.32.4 → 0.33.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/public/assets/{activity-DT1JGHnp.js → activity-B0_uE6Yh.js} +2 -2
- package/public/assets/{activity-DT1JGHnp.js.map → activity-B0_uE6Yh.js.map} +1 -1
- package/public/assets/{agent-profiles-CrMemMkZ.js → agent-profiles-Rwxrcf9F.js} +2 -2
- package/public/assets/{agent-profiles-CrMemMkZ.js.map → agent-profiles-Rwxrcf9F.js.map} +1 -1
- package/public/assets/{agents-Bl-rrgOy.js → agents-Dp1EXJc8.js} +2 -2
- package/public/assets/{agents-Bl-rrgOy.js.map → agents-Dp1EXJc8.js.map} +1 -1
- package/public/assets/{analytics-a663ak56.js → analytics-D5OT5ajj.js} +2 -2
- package/public/assets/{analytics-a663ak56.js.map → analytics-D5OT5ajj.js.map} +1 -1
- package/public/assets/automation-Dm6rXNxK.js +2 -0
- package/public/assets/{automation-CiaLThdO.js.map → automation-Dm6rXNxK.js.map} +1 -1
- package/public/assets/{branch-state-badge-D4ur3m3_.js → branch-state-badge-FX5Yww2s.js} +2 -2
- package/public/assets/{branch-state-badge-D4ur3m3_.js.map → branch-state-badge-FX5Yww2s.js.map} +1 -1
- package/public/assets/{channels-o9KLTHoK.js → channels--rdAiX17.js} +2 -2
- package/public/assets/{channels-o9KLTHoK.js.map → channels--rdAiX17.js.map} +1 -1
- package/public/assets/chat-JZAEDGfX.js +2 -0
- package/public/assets/chat-JZAEDGfX.js.map +1 -0
- package/public/assets/{connectors-CdC806mA.js → connectors-Bx4gzvNf.js} +2 -2
- package/public/assets/{connectors-CdC806mA.js.map → connectors-Bx4gzvNf.js.map} +1 -1
- package/public/assets/display-Bebqs1qu.js +3 -0
- package/public/assets/display-Bebqs1qu.js.map +1 -0
- package/public/assets/{formatted-body-impl-Ca74OAEH.js → formatted-body-impl-CVq4qHix.js} +2 -2
- package/public/assets/{formatted-body-impl-Ca74OAEH.js.map → formatted-body-impl-CVq4qHix.js.map} +1 -1
- package/public/assets/{index-C_33ymaw.js → index-BHRtR4q7.js} +8 -8
- package/public/assets/{index-C_33ymaw.js.map → index-BHRtR4q7.js.map} +1 -1
- package/public/assets/{insights-ClI68s39.js → insights-yJFgCa3o.js} +2 -2
- package/public/assets/{insights-ClI68s39.js.map → insights-yJFgCa3o.js.map} +1 -1
- package/public/assets/{integrations-1nxMizDY.js → integrations-k1HIONjo.js} +2 -2
- package/public/assets/{integrations-1nxMizDY.js.map → integrations-k1HIONjo.js.map} +1 -1
- package/public/assets/maintenance-CsoOFBXx.js +2 -0
- package/public/assets/{maintenance-DiFNzNPN.js.map → maintenance-CsoOFBXx.js.map} +1 -1
- package/public/assets/{managed-agents-Do3dKvfj.js → managed-agents-Q3HuVjGg.js} +2 -2
- package/public/assets/{managed-agents-Do3dKvfj.js.map → managed-agents-Q3HuVjGg.js.map} +1 -1
- package/public/assets/{markdown-preview-impl-CLA0J255.js → markdown-preview-impl-CnsMjrnu.js} +2 -2
- package/public/assets/{markdown-preview-impl-CLA0J255.js.map → markdown-preview-impl-CnsMjrnu.js.map} +1 -1
- package/public/assets/{memory-IjwqFzBd.js → memory-D3-K5eJS.js} +2 -2
- package/public/assets/{memory-IjwqFzBd.js.map → memory-D3-K5eJS.js.map} +1 -1
- package/public/assets/{messages-DjvWqHyn.js → messages-B4lCP5rS.js} +2 -2
- package/public/assets/{messages-DjvWqHyn.js.map → messages-B4lCP5rS.js.map} +1 -1
- package/public/assets/{orchestrators-D2IqDxDT.js → orchestrators-CRoZtLeQ.js} +2 -2
- package/public/assets/{orchestrators-D2IqDxDT.js.map → orchestrators-CRoZtLeQ.js.map} +1 -1
- package/public/assets/{overview-DKC3TbAh.js → overview-CxCU2fOF.js} +2 -2
- package/public/assets/{overview-DKC3TbAh.js.map → overview-CxCU2fOF.js.map} +1 -1
- package/public/assets/pairs-unqjPlmq.js +2 -0
- package/public/assets/{pairs-WpKCPE1n.js.map → pairs-unqjPlmq.js.map} +1 -1
- package/public/assets/{security-BF7ZtPQe.js → security-B7HhSYNy.js} +2 -2
- package/public/assets/{security-BF7ZtPQe.js.map → security-B7HhSYNy.js.map} +1 -1
- package/public/assets/{settings-CQnjrTa-.js → settings-B9NDhsAb.js} +2 -2
- package/public/assets/{settings-CQnjrTa-.js.map → settings-B9NDhsAb.js.map} +1 -1
- package/public/assets/store-DiSzYHj9.js +9 -0
- package/public/assets/{store-C9VcSo05.js.map → store-DiSzYHj9.js.map} +1 -1
- package/public/assets/{tasks-CbN_GSSb.js → tasks-CIQolvNm.js} +2 -2
- package/public/assets/{tasks-CbN_GSSb.js.map → tasks-CIQolvNm.js.map} +1 -1
- package/public/assets/{terminal-viewer-impl-BJRohThT.js → terminal-viewer-impl-DCifVqFR.js} +2 -2
- package/public/assets/{terminal-viewer-impl-BJRohThT.js.map → terminal-viewer-impl-DCifVqFR.js.map} +1 -1
- package/public/assets/{work-queue-C5xLBLmm.js → work-queue-Dr3c1V6O.js} +2 -2
- package/public/assets/{work-queue-C5xLBLmm.js.map → work-queue-Dr3c1V6O.js.map} +1 -1
- package/public/assets/{workspaces-D91H3wDX.js → workspaces-B1Jxop7h.js} +3 -3
- package/public/assets/{workspaces-D91H3wDX.js.map → workspaces-B1Jxop7h.js.map} +1 -1
- package/public/index.html +3 -3
- package/runner/src/adapter.ts +1 -1
- package/src/agent-lifecycle-events.ts +137 -0
- package/src/artifact-storage.ts +3 -5
- package/src/channel-target.ts +24 -0
- package/src/cli/_shared.ts +80 -0
- package/src/cli/agent-detect.ts +188 -0
- package/src/cli/agent-meta.ts +95 -0
- package/src/cli/context-probe.ts +88 -0
- package/src/cli/daemon.ts +111 -0
- package/src/cli/dev.ts +173 -0
- package/src/cli/index.ts +361 -0
- package/src/cli/introspect.ts +73 -0
- package/src/cli/memory.ts +37 -0
- package/src/cli/message.ts +201 -0
- package/src/cli/orchestrator.ts +227 -0
- package/src/cli/pair.ts +125 -0
- package/src/cli/provider.ts +209 -0
- package/src/cli/recipe.ts +110 -0
- package/src/cli/reply.ts +141 -0
- package/src/cli/setup.ts +57 -0
- package/src/cli/steward.ts +59 -0
- package/src/cli/token.ts +81 -0
- package/src/cli/upgrade.ts +193 -0
- package/src/cli/workspace.ts +215 -0
- package/src/cli.ts +4 -2718
- package/src/config-store.ts +10 -6
- package/src/db/activity.ts +194 -0
- package/src/db/agent-search.ts +174 -0
- package/src/db/agents.ts +551 -0
- package/src/db/artifacts.ts +342 -0
- package/src/db/channels.ts +576 -0
- package/src/db/connection.ts +71 -0
- package/src/db/delivery.ts +395 -0
- package/src/db/inbox.ts +249 -0
- package/src/db/index.ts +23 -0
- package/src/db/integrations.ts +339 -0
- package/src/db/mappers.ts +397 -0
- package/src/db/merge-lease.ts +160 -0
- package/src/db/message-reads.ts +304 -0
- package/src/db/messages.ts +434 -0
- package/src/db/migrations.ts +431 -0
- package/src/db/orchestrators.ts +358 -0
- package/src/db/pairs.ts +324 -0
- package/src/db/schema.ts +758 -0
- package/src/db/stats.ts +337 -0
- package/src/db/tasks.ts +407 -0
- package/src/db/workspaces.ts +440 -0
- package/src/db.ts +4 -5721
- package/src/maintenance.ts +4 -0
- package/src/mcp-errors.ts +7 -0
- package/src/mcp.ts +32 -34
- package/src/routes/agents-spawn.ts +9 -1
- package/src/routes/agents.ts +5 -0
- package/src/routes/commands.ts +15 -0
- package/src/routes/integrations.ts +6 -8
- package/src/spawn-targets.ts +159 -0
- package/src/utils.ts +16 -1
- package/public/assets/automation-CiaLThdO.js +0 -2
- package/public/assets/chat-5hvHZcAe.js +0 -2
- package/public/assets/chat-5hvHZcAe.js.map +0 -1
- package/public/assets/display-JI19Vc7L.js +0 -3
- package/public/assets/display-JI19Vc7L.js.map +0 -1
- package/public/assets/maintenance-DiFNzNPN.js +0 -2
- package/public/assets/pairs-WpKCPE1n.js +0 -2
- package/public/assets/store-C9VcSo05.js +0 -9
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
import { Database } from "bun:sqlite";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { isRecord, stringValue, isMechanicalMessageKind } from "agent-relay-sdk";
|
|
4
|
+
import { ORCHESTRATOR_PROTOCOL_VERSION, VERSION } from "../config.ts";
|
|
5
|
+
import { parseJson } from "../utils";
|
|
6
|
+
import { isLiveIsolatedWorkspace } from "../workspace-phase";
|
|
7
|
+
import {
|
|
8
|
+
CONTRACT_REQUIREMENTS,
|
|
9
|
+
contractCompatibility,
|
|
10
|
+
parseRuntimeCapabilities,
|
|
11
|
+
parseRuntimeContracts,
|
|
12
|
+
parseRuntimePackage,
|
|
13
|
+
type RuntimeContracts,
|
|
14
|
+
} from "../contracts";
|
|
15
|
+
import { STALE_TTL_MS, DAY_MS, CLAIM_LEASE_MS, POOL_CLAIM_LEASE_MS, WORKSPACE_MERGE_LEASE_MS } from "../config";
|
|
16
|
+
import { matchAgents } from "../agent-ref";
|
|
17
|
+
import { normalizeTags, parseStringArray } from "./agents.ts";
|
|
18
|
+
import { linkAttachmentRefs, validateAttachmentRefs } from "./artifacts.ts";
|
|
19
|
+
import { ValidationError, getDb } from "./connection.ts";
|
|
20
|
+
import { sendMessage } from "./messages.ts";
|
|
21
|
+
import { findOpenTaskByDedupe, getTask, insertTaskEvent, taskMessageBody } from "./tasks.ts";
|
|
22
|
+
import type {
|
|
23
|
+
AgentCard,
|
|
24
|
+
ActivityEvent,
|
|
25
|
+
ActivityEventInput,
|
|
26
|
+
AgentKind,
|
|
27
|
+
AgentSessionGuard,
|
|
28
|
+
Artifact,
|
|
29
|
+
ArtifactBlob,
|
|
30
|
+
ArtifactKind,
|
|
31
|
+
ArtifactLink,
|
|
32
|
+
ArtifactSensitivity,
|
|
33
|
+
ArtifactVisibility,
|
|
34
|
+
AttachmentRef,
|
|
35
|
+
ChannelBinding,
|
|
36
|
+
ChannelBindingMode,
|
|
37
|
+
ChannelRouteTarget,
|
|
38
|
+
ChatHistoryImport,
|
|
39
|
+
ChatHistoryImportEntry,
|
|
40
|
+
ChannelSummary,
|
|
41
|
+
ChannelTargetHealth,
|
|
42
|
+
CreatePairInput,
|
|
43
|
+
HealthCheck,
|
|
44
|
+
HealthReport,
|
|
45
|
+
ManagedAgent,
|
|
46
|
+
ManagedSessionExitDiagnostics,
|
|
47
|
+
Message,
|
|
48
|
+
MessageDeliveryAttempt,
|
|
49
|
+
MessageDeliveryStatus,
|
|
50
|
+
Orchestrator,
|
|
51
|
+
OrchestratorHealth,
|
|
52
|
+
OrchestratorRuntimeInput,
|
|
53
|
+
OrchestratorStatus,
|
|
54
|
+
OrchestratorUpgradeState,
|
|
55
|
+
PairActionInput,
|
|
56
|
+
PairMessageInput,
|
|
57
|
+
PairSession,
|
|
58
|
+
PairStatus,
|
|
59
|
+
RegisterAgentInput,
|
|
60
|
+
ReplyObligation,
|
|
61
|
+
RegisterOrchestratorInput,
|
|
62
|
+
SendMessageInput,
|
|
63
|
+
PollQuery,
|
|
64
|
+
SpawnApprovalMode,
|
|
65
|
+
SpawnProvider,
|
|
66
|
+
Task,
|
|
67
|
+
TaskEvent,
|
|
68
|
+
TaskSeverity,
|
|
69
|
+
TaskStatus,
|
|
70
|
+
IntegrationEventInput,
|
|
71
|
+
IntegrationSummary,
|
|
72
|
+
IntegrationTaskStats,
|
|
73
|
+
InboxDraft,
|
|
74
|
+
InboxState,
|
|
75
|
+
InboxThreadState,
|
|
76
|
+
ContextSnapshot,
|
|
77
|
+
ContextState,
|
|
78
|
+
ProviderCapabilities,
|
|
79
|
+
TaskStatusInput,
|
|
80
|
+
WorkspaceMetadata,
|
|
81
|
+
WorkspaceRecord,
|
|
82
|
+
WorkspaceStatus,
|
|
83
|
+
} from "../types";
|
|
84
|
+
|
|
85
|
+
export function ingestIntegrationEvent(input: IntegrationEventInput, integrationName: string): { task: Task; event: TaskEvent; created: boolean; message?: Message } {
|
|
86
|
+
const now = Date.now();
|
|
87
|
+
const source = input.source ?? integrationName;
|
|
88
|
+
const severity = input.severity ?? "info";
|
|
89
|
+
const eventType = input.type ?? "event";
|
|
90
|
+
const targetStatus = input.status === "resolved" ? "done" : input.status;
|
|
91
|
+
const attachmentRefs = input.attachments ?? [];
|
|
92
|
+
validateAttachmentRefs(attachmentRefs);
|
|
93
|
+
updateIntegrationObserved(source, now);
|
|
94
|
+
|
|
95
|
+
return getDb().transaction(() => {
|
|
96
|
+
const existing = input.dedupeKey ? findOpenTaskByDedupe(source, input.dedupeKey) : null;
|
|
97
|
+
let taskId: number;
|
|
98
|
+
let created = false;
|
|
99
|
+
|
|
100
|
+
if (existing) {
|
|
101
|
+
getDb().query(`
|
|
102
|
+
UPDATE tasks
|
|
103
|
+
SET title = ?, body = ?, severity = ?, target = ?, channel = ?, external_url = ?,
|
|
104
|
+
occurrence_count = occurrence_count + 1, metadata = ?, updated_at = ?, last_seen_at = ?,
|
|
105
|
+
status = CASE WHEN ? IS NULL THEN status ELSE ? END,
|
|
106
|
+
result = CASE WHEN ? IN ('done', 'failed', 'canceled') THEN ? ELSE result END
|
|
107
|
+
WHERE id = ?
|
|
108
|
+
`).run(
|
|
109
|
+
input.title,
|
|
110
|
+
input.body,
|
|
111
|
+
severity,
|
|
112
|
+
input.target,
|
|
113
|
+
input.channel ?? null,
|
|
114
|
+
input.externalUrl ?? null,
|
|
115
|
+
JSON.stringify(input.metadata ?? existing.metadata),
|
|
116
|
+
now,
|
|
117
|
+
now,
|
|
118
|
+
targetStatus ?? null,
|
|
119
|
+
targetStatus ?? null,
|
|
120
|
+
targetStatus ?? null,
|
|
121
|
+
input.body,
|
|
122
|
+
existing.id,
|
|
123
|
+
);
|
|
124
|
+
taskId = existing.id;
|
|
125
|
+
} else {
|
|
126
|
+
const result = getDb().query(`
|
|
127
|
+
INSERT INTO tasks (source, title, body, severity, status, target, channel, dedupe_key, external_url, metadata, created_at, updated_at, last_seen_at)
|
|
128
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
129
|
+
`).run(
|
|
130
|
+
source,
|
|
131
|
+
input.title,
|
|
132
|
+
input.body,
|
|
133
|
+
severity,
|
|
134
|
+
targetStatus ?? "open",
|
|
135
|
+
input.target,
|
|
136
|
+
input.channel ?? null,
|
|
137
|
+
input.dedupeKey ?? null,
|
|
138
|
+
input.externalUrl ?? null,
|
|
139
|
+
JSON.stringify(input.metadata ?? {}),
|
|
140
|
+
now,
|
|
141
|
+
now,
|
|
142
|
+
now,
|
|
143
|
+
);
|
|
144
|
+
taskId = Number(result.lastInsertRowid);
|
|
145
|
+
created = true;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const task = getTask(taskId)!;
|
|
149
|
+
linkAttachmentRefs("task", task.id, attachmentRefs, source);
|
|
150
|
+
const event = insertTaskEvent(taskId, {
|
|
151
|
+
source,
|
|
152
|
+
type: eventType,
|
|
153
|
+
severity,
|
|
154
|
+
title: input.title,
|
|
155
|
+
body: input.body,
|
|
156
|
+
metadata: input.metadata ?? {},
|
|
157
|
+
}, now);
|
|
158
|
+
|
|
159
|
+
let message: Message | undefined;
|
|
160
|
+
if (created && task.status === "open") {
|
|
161
|
+
message = sendMessage({
|
|
162
|
+
from: "system",
|
|
163
|
+
to: task.target,
|
|
164
|
+
kind: "task",
|
|
165
|
+
channel: task.channel,
|
|
166
|
+
subject: `[${task.severity}] ${task.title}`,
|
|
167
|
+
body: taskMessageBody(task),
|
|
168
|
+
claimable: true,
|
|
169
|
+
payload: {
|
|
170
|
+
taskId: task.id,
|
|
171
|
+
source: task.source,
|
|
172
|
+
title: task.title,
|
|
173
|
+
status: task.status,
|
|
174
|
+
severity: task.severity,
|
|
175
|
+
dedupeKey: task.dedupeKey ?? null,
|
|
176
|
+
externalUrl: task.externalUrl ?? null,
|
|
177
|
+
...(attachmentRefs.length ? { attachments: attachmentRefs } : {}),
|
|
178
|
+
},
|
|
179
|
+
});
|
|
180
|
+
getDb().query("UPDATE tasks SET message_id = ?, updated_at = ? WHERE id = ?").run(message.id, now, task.id);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return { task: getTask(taskId)!, event, created, message };
|
|
184
|
+
})();
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
export function listIntegrationTaskStats(): IntegrationTaskStats[] {
|
|
189
|
+
const rows = getDb().query(`
|
|
190
|
+
SELECT
|
|
191
|
+
source,
|
|
192
|
+
COUNT(*) AS tasks,
|
|
193
|
+
SUM(CASE WHEN status NOT IN ('done', 'failed', 'canceled') THEN 1 ELSE 0 END) AS open_tasks,
|
|
194
|
+
SUM(CASE WHEN status IN ('open', 'blocked') AND claimed_by IS NULL THEN 1 ELSE 0 END) AS waiting_tasks,
|
|
195
|
+
SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) AS failed_tasks,
|
|
196
|
+
MAX(last_seen_at) AS last_seen_at,
|
|
197
|
+
MAX(updated_at) AS last_updated_at
|
|
198
|
+
FROM tasks
|
|
199
|
+
GROUP BY source
|
|
200
|
+
ORDER BY last_seen_at DESC, source ASC
|
|
201
|
+
`).all() as any[];
|
|
202
|
+
|
|
203
|
+
return rows.map((row) => ({
|
|
204
|
+
source: row.source,
|
|
205
|
+
tasks: Number(row.tasks ?? 0),
|
|
206
|
+
openTasks: Number(row.open_tasks ?? 0),
|
|
207
|
+
waitingTasks: Number(row.waiting_tasks ?? 0),
|
|
208
|
+
failedTasks: Number(row.failed_tasks ?? 0),
|
|
209
|
+
lastSeenAt: row.last_seen_at ?? undefined,
|
|
210
|
+
lastUpdatedAt: row.last_updated_at ?? undefined,
|
|
211
|
+
}));
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export type IntegrationRegistryInput = {
|
|
215
|
+
name: string;
|
|
216
|
+
displayName?: string;
|
|
217
|
+
description?: string;
|
|
218
|
+
enabled?: boolean;
|
|
219
|
+
scopes?: string[];
|
|
220
|
+
targets?: string[];
|
|
221
|
+
channels?: string[];
|
|
222
|
+
type?: string;
|
|
223
|
+
icon?: string;
|
|
224
|
+
accentColor?: string;
|
|
225
|
+
tags?: string[];
|
|
226
|
+
homepageUrl?: string;
|
|
227
|
+
repositoryUrl?: string;
|
|
228
|
+
docsUrl?: string;
|
|
229
|
+
manifest?: Record<string, unknown>;
|
|
230
|
+
source?: "api" | "env" | "observed";
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
export function rowToIntegrationRegistry(row: any): Omit<IntegrationSummary, "configured" | "observed" | "callbackConfigured" | "rateLimit" | "taskStats"> & {
|
|
234
|
+
source: "api" | "env" | "observed";
|
|
235
|
+
lastEventAt?: number;
|
|
236
|
+
lastTaskAt?: number;
|
|
237
|
+
lastAuthSuccessAt?: number;
|
|
238
|
+
lastAuthFailureAt?: number;
|
|
239
|
+
} {
|
|
240
|
+
return {
|
|
241
|
+
name: row.name,
|
|
242
|
+
displayName: row.display_name ?? undefined,
|
|
243
|
+
description: row.description ?? undefined,
|
|
244
|
+
enabled: row.enabled !== 0,
|
|
245
|
+
scopes: parseStringArray(row.scopes),
|
|
246
|
+
targets: parseStringArray(row.targets),
|
|
247
|
+
channels: parseStringArray(row.channels),
|
|
248
|
+
type: row.type ?? undefined,
|
|
249
|
+
icon: row.icon ?? undefined,
|
|
250
|
+
accentColor: row.accent_color ?? undefined,
|
|
251
|
+
tags: parseStringArray(row.tags),
|
|
252
|
+
homepageUrl: row.homepage_url ?? undefined,
|
|
253
|
+
repositoryUrl: row.repository_url ?? undefined,
|
|
254
|
+
docsUrl: row.docs_url ?? undefined,
|
|
255
|
+
manifest: parseJson(row.manifest, {}),
|
|
256
|
+
source: row.source ?? "api",
|
|
257
|
+
lastEventAt: row.last_event_at ?? undefined,
|
|
258
|
+
lastTaskAt: row.last_task_at ?? undefined,
|
|
259
|
+
lastAuthSuccessAt: row.last_auth_success_at ?? undefined,
|
|
260
|
+
lastAuthFailureAt: row.last_auth_failure_at ?? undefined,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export function upsertIntegrationRegistry(input: IntegrationRegistryInput): ReturnType<typeof rowToIntegrationRegistry> {
|
|
265
|
+
const name = stringValue(input.name);
|
|
266
|
+
if (!name) throw new ValidationError("integration name required");
|
|
267
|
+
const now = Date.now();
|
|
268
|
+
getDb().query(`
|
|
269
|
+
INSERT INTO integration_registry (
|
|
270
|
+
name, display_name, description, enabled, scopes, targets, channels, type, icon, accent_color,
|
|
271
|
+
tags, homepage_url, repository_url, docs_url, manifest, source, created_at, updated_at
|
|
272
|
+
)
|
|
273
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
274
|
+
ON CONFLICT(name) DO UPDATE SET
|
|
275
|
+
display_name = coalesce(excluded.display_name, integration_registry.display_name),
|
|
276
|
+
description = coalesce(excluded.description, integration_registry.description),
|
|
277
|
+
enabled = CASE WHEN integration_registry.source = 'api' AND excluded.source IN ('env', 'observed') THEN integration_registry.enabled ELSE excluded.enabled END,
|
|
278
|
+
scopes = CASE WHEN excluded.scopes <> '[]' THEN excluded.scopes ELSE integration_registry.scopes END,
|
|
279
|
+
targets = CASE WHEN excluded.targets <> '[]' THEN excluded.targets ELSE integration_registry.targets END,
|
|
280
|
+
channels = CASE WHEN excluded.channels <> '[]' THEN excluded.channels ELSE integration_registry.channels END,
|
|
281
|
+
type = coalesce(excluded.type, integration_registry.type),
|
|
282
|
+
icon = coalesce(excluded.icon, integration_registry.icon),
|
|
283
|
+
accent_color = coalesce(excluded.accent_color, integration_registry.accent_color),
|
|
284
|
+
tags = CASE WHEN excluded.tags <> '[]' THEN excluded.tags ELSE integration_registry.tags END,
|
|
285
|
+
homepage_url = coalesce(excluded.homepage_url, integration_registry.homepage_url),
|
|
286
|
+
repository_url = coalesce(excluded.repository_url, integration_registry.repository_url),
|
|
287
|
+
docs_url = coalesce(excluded.docs_url, integration_registry.docs_url),
|
|
288
|
+
manifest = CASE WHEN excluded.manifest <> '{}' THEN excluded.manifest ELSE integration_registry.manifest END,
|
|
289
|
+
source = CASE WHEN integration_registry.source = 'api' AND excluded.source IN ('env', 'observed') THEN integration_registry.source ELSE excluded.source END,
|
|
290
|
+
updated_at = excluded.updated_at
|
|
291
|
+
`).run(
|
|
292
|
+
name,
|
|
293
|
+
input.displayName ?? null,
|
|
294
|
+
input.description ?? null,
|
|
295
|
+
input.enabled === false ? 0 : 1,
|
|
296
|
+
JSON.stringify(input.scopes ?? []),
|
|
297
|
+
JSON.stringify(input.targets ?? []),
|
|
298
|
+
JSON.stringify(input.channels ?? []),
|
|
299
|
+
input.type ?? null,
|
|
300
|
+
input.icon ?? null,
|
|
301
|
+
input.accentColor ?? null,
|
|
302
|
+
JSON.stringify(normalizeTags(input.tags)),
|
|
303
|
+
input.homepageUrl ?? null,
|
|
304
|
+
input.repositoryUrl ?? null,
|
|
305
|
+
input.docsUrl ?? null,
|
|
306
|
+
JSON.stringify(input.manifest ?? {}),
|
|
307
|
+
input.source ?? "api",
|
|
308
|
+
now,
|
|
309
|
+
now,
|
|
310
|
+
);
|
|
311
|
+
return getIntegrationRegistry(name)!;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export function getIntegrationRegistry(name: string): ReturnType<typeof rowToIntegrationRegistry> | null {
|
|
315
|
+
const row = getDb().query("SELECT * FROM integration_registry WHERE name = ?").get(name) as any;
|
|
316
|
+
return row ? rowToIntegrationRegistry(row) : null;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
export function listIntegrationRegistry(): ReturnType<typeof rowToIntegrationRegistry>[] {
|
|
320
|
+
return (getDb().query("SELECT * FROM integration_registry ORDER BY display_name COLLATE NOCASE, name COLLATE NOCASE").all() as any[]).map(rowToIntegrationRegistry);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
export function updateIntegrationObserved(name: string, eventAt: number): void {
|
|
324
|
+
const now = Date.now();
|
|
325
|
+
getDb().query(`
|
|
326
|
+
INSERT INTO integration_registry (name, enabled, scopes, targets, channels, tags, manifest, source, created_at, updated_at, last_event_at, last_task_at)
|
|
327
|
+
VALUES (?, 1, '[]', '[]', '[]', '[]', '{}', 'observed', ?, ?, ?, ?)
|
|
328
|
+
ON CONFLICT(name) DO UPDATE SET
|
|
329
|
+
last_event_at = max(coalesce(last_event_at, 0), excluded.last_event_at),
|
|
330
|
+
last_task_at = max(coalesce(last_task_at, 0), excluded.last_task_at),
|
|
331
|
+
updated_at = excluded.updated_at
|
|
332
|
+
`).run(name, now, now, eventAt, eventAt);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export function isIntegrationRegistryEnabled(name: string): boolean {
|
|
336
|
+
const registry = getIntegrationRegistry(name);
|
|
337
|
+
return registry?.enabled !== false;
|
|
338
|
+
}
|
|
339
|
+
|