@agent-native/core 0.22.45 → 0.24.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/a2a/artifact-response.js +1 -1
- package/dist/a2a/artifact-response.js.map +1 -1
- package/dist/agent/engine/builder-engine.d.ts.map +1 -1
- package/dist/agent/engine/builder-engine.js +5 -6
- package/dist/agent/engine/builder-engine.js.map +1 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +12 -4
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts +9 -2
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +9 -2
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/cli/app-skill.d.ts +139 -0
- package/dist/cli/app-skill.d.ts.map +1 -0
- package/dist/cli/app-skill.js +960 -0
- package/dist/cli/app-skill.js.map +1 -0
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +13 -4
- package/dist/cli/create.js.map +1 -1
- package/dist/cli/index.js +24 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/skills.d.ts +39 -0
- package/dist/cli/skills.d.ts.map +1 -0
- package/dist/cli/skills.js +363 -0
- package/dist/cli/skills.js.map +1 -0
- package/dist/cli/templates-meta.d.ts.map +1 -1
- package/dist/cli/templates-meta.js +9 -6
- package/dist/cli/templates-meta.js.map +1 -1
- package/dist/cli/workspace-dev.d.ts.map +1 -1
- package/dist/cli/workspace-dev.js +103 -9
- package/dist/cli/workspace-dev.js.map +1 -1
- package/dist/client/AgentPanel.d.ts +2 -0
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +2 -2
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +13 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +66 -22
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +17 -1
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +70 -10
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/extensions/ExtensionViewer.d.ts.map +1 -1
- package/dist/client/extensions/ExtensionViewer.js +157 -2
- package/dist/client/extensions/ExtensionViewer.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts +6 -0
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +9 -2
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/client/use-chat-threads.d.ts +8 -1
- package/dist/client/use-chat-threads.d.ts.map +1 -1
- package/dist/client/use-chat-threads.js +37 -7
- package/dist/client/use-chat-threads.js.map +1 -1
- package/dist/client/use-chat-threads.spec.js +59 -0
- package/dist/client/use-chat-threads.spec.js.map +1 -1
- package/dist/deploy/workspace-deploy.js +6 -0
- package/dist/deploy/workspace-deploy.js.map +1 -1
- package/dist/extensions/actions.d.ts.map +1 -1
- package/dist/extensions/actions.js +112 -2
- package/dist/extensions/actions.js.map +1 -1
- package/dist/extensions/routes.d.ts.map +1 -1
- package/dist/extensions/routes.js +37 -2
- package/dist/extensions/routes.js.map +1 -1
- package/dist/extensions/schema.d.ts +275 -0
- package/dist/extensions/schema.d.ts.map +1 -1
- package/dist/extensions/schema.js +53 -1
- package/dist/extensions/schema.js.map +1 -1
- package/dist/extensions/store.d.ts +40 -0
- package/dist/extensions/store.d.ts.map +1 -1
- package/dist/extensions/store.js +367 -3
- package/dist/extensions/store.js.map +1 -1
- package/dist/mcp-client/index.d.ts +1 -1
- package/dist/mcp-client/index.d.ts.map +1 -1
- package/dist/mcp-client/index.js +1 -1
- package/dist/mcp-client/index.js.map +1 -1
- package/dist/mcp-client/routes.d.ts +1 -0
- package/dist/mcp-client/routes.d.ts.map +1 -1
- package/dist/mcp-client/routes.js +52 -0
- package/dist/mcp-client/routes.js.map +1 -1
- package/dist/mcp-client/workspace-servers.d.ts +15 -0
- package/dist/mcp-client/workspace-servers.d.ts.map +1 -0
- package/dist/mcp-client/workspace-servers.js +297 -0
- package/dist/mcp-client/workspace-servers.js.map +1 -0
- package/dist/resources/handlers.d.ts.map +1 -1
- package/dist/resources/handlers.js +38 -25
- package/dist/resources/handlers.js.map +1 -1
- package/dist/resources/store.d.ts +11 -3
- package/dist/resources/store.d.ts.map +1 -1
- package/dist/resources/store.js +220 -9
- package/dist/resources/store.js.map +1 -1
- package/dist/scripts/call-agent.js +1 -1
- package/dist/scripts/call-agent.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +25 -6
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-discovery.d.ts.map +1 -1
- package/dist/server/agent-discovery.js +34 -9
- package/dist/server/agent-discovery.js.map +1 -1
- package/dist/server/auth-marketing.d.ts.map +1 -1
- package/dist/server/auth-marketing.js +8 -5
- package/dist/server/auth-marketing.js.map +1 -1
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +6 -2
- package/dist/server/auth.js.map +1 -1
- package/dist/templates/default/AGENTS.md +12 -4
- package/dist/templates/default/DEVELOPING.md +7 -5
- package/dist/templates/workspace-core/AGENTS.md +7 -0
- package/dist/templates/workspace-root/AGENTS.md +6 -0
- package/docs/content/creating-templates.md +14 -9
- package/docs/content/database.md +44 -17
- package/docs/content/deployment.md +15 -7
- package/docs/content/dispatch.md +7 -1
- package/docs/content/embedding-sdk.md +79 -0
- package/docs/content/extensions.md +5 -0
- package/docs/content/key-concepts.md +15 -17
- package/docs/content/mcp-clients.md +30 -0
- package/docs/content/multi-app-workspace.md +3 -2
- package/docs/content/multi-tenancy.md +4 -4
- package/docs/content/server.md +10 -7
- package/docs/content/skills-guide.md +75 -0
- package/docs/content/template-analytics.md +1 -1
- package/docs/content/template-assets.md +130 -0
- package/docs/content/template-dispatch.md +3 -2
- package/docs/content/template-slides.md +2 -2
- package/docs/content/workspace-management.md +2 -2
- package/docs/content/workspace.md +11 -9
- package/package.json +1 -1
- package/src/templates/default/AGENTS.md +12 -4
- package/src/templates/default/DEVELOPING.md +7 -5
- package/src/templates/workspace-core/AGENTS.md +7 -0
- package/src/templates/workspace-root/AGENTS.md +6 -0
- package/docs/content/template-images.md +0 -55
package/dist/extensions/store.js
CHANGED
|
@@ -7,10 +7,15 @@ import { recordChange } from "../server/poll.js";
|
|
|
7
7
|
import { accessFilter, assertAccess, resolveAccess, ForbiddenError, } from "../sharing/access.js";
|
|
8
8
|
import { getRequestUserEmail, getRequestOrgId, } from "../server/request-context.js";
|
|
9
9
|
import { registerShareableResource } from "../sharing/registry.js";
|
|
10
|
-
import { extensions, extensionHides, extensionShares, EXTENSIONS_CREATE_SQL, EXTENSIONS_CREATE_SQL_PG, EXTENSION_SHARES_CREATE_SQL, EXTENSION_SHARES_CREATE_SQL_PG, EXTENSION_DATA_CREATE_SQL, EXTENSION_DATA_CREATE_SQL_PG, EXTENSION_DATA_ITEM_INDEX_SQL, EXTENSION_DATA_ITEM_INDEX_SQL_PG, EXTENSION_DATA_DROP_OLD_INDEX_SQL, EXTENSION_DATA_DROP_OLD_INDEX_SQL_PG, EXTENSIONS_OWNER_INDEX_SQL, EXTENSIONS_ORG_INDEX_SQL, EXTENSIONS_UPDATED_INDEX_SQL, EXTENSION_SHARES_RESOURCE_INDEX_SQL, EXTENSION_HIDES_CREATE_SQL, EXTENSION_HIDES_CREATE_SQL_PG, EXTENSION_HIDES_UNIQUE_INDEX_SQL, EXTENSION_HIDES_OWNER_INDEX_SQL, EXTENSION_CONSENTS_CREATE_SQL, EXTENSION_CONSENTS_CREATE_SQL_PG, EXTENSION_CONSENTS_VIEWER_INDEX_SQL, } from "./schema.js";
|
|
10
|
+
import { extensions, extensionHides, extensionShares, extensionHistory, EXTENSIONS_CREATE_SQL, EXTENSIONS_CREATE_SQL_PG, EXTENSION_SHARES_CREATE_SQL, EXTENSION_SHARES_CREATE_SQL_PG, EXTENSION_DATA_CREATE_SQL, EXTENSION_DATA_CREATE_SQL_PG, EXTENSION_DATA_ITEM_INDEX_SQL, EXTENSION_DATA_ITEM_INDEX_SQL_PG, EXTENSION_DATA_DROP_OLD_INDEX_SQL, EXTENSION_DATA_DROP_OLD_INDEX_SQL_PG, EXTENSIONS_OWNER_INDEX_SQL, EXTENSIONS_ORG_INDEX_SQL, EXTENSIONS_UPDATED_INDEX_SQL, EXTENSION_SHARES_RESOURCE_INDEX_SQL, EXTENSION_HIDES_CREATE_SQL, EXTENSION_HIDES_CREATE_SQL_PG, EXTENSION_HIDES_UNIQUE_INDEX_SQL, EXTENSION_HIDES_OWNER_INDEX_SQL, EXTENSION_HISTORY_CREATE_SQL, EXTENSION_HISTORY_CREATE_SQL_PG, EXTENSION_HISTORY_VERSION_INDEX_SQL, EXTENSION_HISTORY_CREATED_INDEX_SQL, EXTENSION_CONSENTS_CREATE_SQL, EXTENSION_CONSENTS_CREATE_SQL_PG, EXTENSION_CONSENTS_VIEWER_INDEX_SQL, } from "./schema.js";
|
|
11
11
|
import { EXTENSION_CHANGE_MARKER_KEY, extensionChangeMarkerSession, extensionChangeMarkerValue, } from "./change-marker.js";
|
|
12
12
|
import { applyExtensionContentUpdate, } from "./content-patch.js";
|
|
13
|
-
const getDb = createGetDb({
|
|
13
|
+
const getDb = createGetDb({
|
|
14
|
+
extensions,
|
|
15
|
+
extensionShares,
|
|
16
|
+
extensionHides,
|
|
17
|
+
extensionHistory,
|
|
18
|
+
});
|
|
14
19
|
let _initPromise;
|
|
15
20
|
export async function ensureExtensionsTables() {
|
|
16
21
|
if (!_initPromise) {
|
|
@@ -34,6 +39,9 @@ export async function ensureExtensionsTables() {
|
|
|
34
39
|
await retryOnDdlRace(() => client.execute(pg ? EXTENSION_HIDES_CREATE_SQL_PG : EXTENSION_HIDES_CREATE_SQL));
|
|
35
40
|
await retryOnDdlRace(() => client.execute(EXTENSION_HIDES_UNIQUE_INDEX_SQL));
|
|
36
41
|
await retryOnDdlRace(() => client.execute(EXTENSION_HIDES_OWNER_INDEX_SQL));
|
|
42
|
+
await retryOnDdlRace(() => client.execute(pg ? EXTENSION_HISTORY_CREATE_SQL_PG : EXTENSION_HISTORY_CREATE_SQL));
|
|
43
|
+
await retryOnDdlRace(() => client.execute(EXTENSION_HISTORY_VERSION_INDEX_SQL));
|
|
44
|
+
await retryOnDdlRace(() => client.execute(EXTENSION_HISTORY_CREATED_INDEX_SQL));
|
|
37
45
|
// tool_consents was introduced for an audit-C1 per-viewer consent
|
|
38
46
|
// gate that we removed once we settled on intra-org trust as the
|
|
39
47
|
// baseline. The table is kept (additive — never drop) so deploys
|
|
@@ -214,6 +222,256 @@ export async function notifyExtensionChangeForResource(id, beforeTargets = []) {
|
|
|
214
222
|
...(await extensionChangeTargetsForId(id)),
|
|
215
223
|
]);
|
|
216
224
|
}
|
|
225
|
+
function extensionHistoryEntryFromRaw(row, includeContent) {
|
|
226
|
+
const content = row.content ?? "";
|
|
227
|
+
const visibility = normalizeVisibility(row.visibility);
|
|
228
|
+
return {
|
|
229
|
+
id: row.id,
|
|
230
|
+
extensionId: String(row.tool_id ?? row.extensionId ?? ""),
|
|
231
|
+
version: Number(row.version) || 1,
|
|
232
|
+
operation: row.operation,
|
|
233
|
+
summary: row.summary ?? "",
|
|
234
|
+
name: row.name,
|
|
235
|
+
description: row.description ?? "",
|
|
236
|
+
...(includeContent ? { content } : {}),
|
|
237
|
+
icon: row.icon ?? null,
|
|
238
|
+
actorEmail: row.actor_email ?? row.actorEmail ?? null,
|
|
239
|
+
ownerEmail: row.owner_email ?? row.ownerEmail ?? "",
|
|
240
|
+
orgId: row.org_id ?? row.orgId ?? null,
|
|
241
|
+
visibility,
|
|
242
|
+
createdAt: row.created_at ?? row.createdAt ?? new Date(0).toISOString(),
|
|
243
|
+
persisted: true,
|
|
244
|
+
contentLength: content.length,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
function extensionHistoryEntryFromExtension(row, includeContent) {
|
|
248
|
+
return {
|
|
249
|
+
id: `current:${row.id}`,
|
|
250
|
+
extensionId: row.id,
|
|
251
|
+
version: 1,
|
|
252
|
+
operation: "baseline",
|
|
253
|
+
summary: "Current version",
|
|
254
|
+
name: row.name,
|
|
255
|
+
description: row.description,
|
|
256
|
+
...(includeContent ? { content: row.content } : {}),
|
|
257
|
+
icon: row.icon,
|
|
258
|
+
actorEmail: null,
|
|
259
|
+
ownerEmail: row.ownerEmail,
|
|
260
|
+
orgId: row.orgId,
|
|
261
|
+
visibility: row.visibility,
|
|
262
|
+
createdAt: row.updatedAt,
|
|
263
|
+
persisted: false,
|
|
264
|
+
contentLength: row.content.length,
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
function normalizeVisibility(value) {
|
|
268
|
+
return value === "org" || value === "public" ? value : "private";
|
|
269
|
+
}
|
|
270
|
+
function currentActorEmail() {
|
|
271
|
+
return getRequestUserEmail() ?? null;
|
|
272
|
+
}
|
|
273
|
+
function clampHistoryLimit(value) {
|
|
274
|
+
const limit = Number(value ?? 50);
|
|
275
|
+
if (!Number.isFinite(limit))
|
|
276
|
+
return 50;
|
|
277
|
+
return Math.min(Math.max(1, Math.floor(limit)), 100);
|
|
278
|
+
}
|
|
279
|
+
async function historyVersionCount(extensionId) {
|
|
280
|
+
const result = await getDbExec().execute({
|
|
281
|
+
sql: `SELECT MAX(version) AS version FROM tool_history WHERE tool_id = ?`,
|
|
282
|
+
args: [extensionId],
|
|
283
|
+
});
|
|
284
|
+
const value = result.rows?.[0]?.version;
|
|
285
|
+
const version = Number(value ?? 0);
|
|
286
|
+
return Number.isFinite(version) ? version : 0;
|
|
287
|
+
}
|
|
288
|
+
async function hasExtensionHistory(extensionId) {
|
|
289
|
+
const result = await getDbExec().execute({
|
|
290
|
+
sql: `SELECT id FROM tool_history WHERE tool_id = ? LIMIT 1`,
|
|
291
|
+
args: [extensionId],
|
|
292
|
+
});
|
|
293
|
+
return (result.rows?.length ?? 0) > 0;
|
|
294
|
+
}
|
|
295
|
+
async function recordExtensionHistorySnapshot(row, operation, summary) {
|
|
296
|
+
const version = (await historyVersionCount(row.id)) + 1;
|
|
297
|
+
const now = new Date().toISOString();
|
|
298
|
+
const historyId = randomUUID();
|
|
299
|
+
await getDbExec().execute({
|
|
300
|
+
sql: `INSERT INTO tool_history (
|
|
301
|
+
id, tool_id, version, operation, summary, name, description, content,
|
|
302
|
+
icon, actor_email, owner_email, org_id, visibility, created_at
|
|
303
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
304
|
+
args: [
|
|
305
|
+
historyId,
|
|
306
|
+
row.id,
|
|
307
|
+
version,
|
|
308
|
+
operation,
|
|
309
|
+
summary,
|
|
310
|
+
row.name,
|
|
311
|
+
row.description,
|
|
312
|
+
row.content,
|
|
313
|
+
row.icon,
|
|
314
|
+
currentActorEmail(),
|
|
315
|
+
row.ownerEmail,
|
|
316
|
+
row.orgId,
|
|
317
|
+
row.visibility,
|
|
318
|
+
now,
|
|
319
|
+
],
|
|
320
|
+
});
|
|
321
|
+
return {
|
|
322
|
+
id: historyId,
|
|
323
|
+
extensionId: row.id,
|
|
324
|
+
version,
|
|
325
|
+
operation,
|
|
326
|
+
summary,
|
|
327
|
+
name: row.name,
|
|
328
|
+
description: row.description,
|
|
329
|
+
content: row.content,
|
|
330
|
+
icon: row.icon,
|
|
331
|
+
actorEmail: currentActorEmail(),
|
|
332
|
+
ownerEmail: row.ownerEmail,
|
|
333
|
+
orgId: row.orgId,
|
|
334
|
+
visibility: row.visibility,
|
|
335
|
+
createdAt: now,
|
|
336
|
+
persisted: true,
|
|
337
|
+
contentLength: row.content.length,
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
async function ensureExtensionHistoryBaseline(row) {
|
|
341
|
+
if (await hasExtensionHistory(row.id))
|
|
342
|
+
return;
|
|
343
|
+
await recordExtensionHistorySnapshot(row, "baseline", "Saved starting version");
|
|
344
|
+
}
|
|
345
|
+
function summarizeMetadataChange(before, after) {
|
|
346
|
+
const changes = [];
|
|
347
|
+
if (before.name !== after.name) {
|
|
348
|
+
changes.push(`Renamed from "${before.name}" to "${after.name}"`);
|
|
349
|
+
}
|
|
350
|
+
if (before.description !== after.description) {
|
|
351
|
+
changes.push("Updated description");
|
|
352
|
+
}
|
|
353
|
+
if (before.icon !== after.icon) {
|
|
354
|
+
changes.push("Updated icon");
|
|
355
|
+
}
|
|
356
|
+
if (before.visibility !== after.visibility) {
|
|
357
|
+
changes.push(`Changed visibility to ${after.visibility}`);
|
|
358
|
+
}
|
|
359
|
+
return changes.join("; ") || "Updated details";
|
|
360
|
+
}
|
|
361
|
+
function summarizeContentChange(beforeContent, afterContent) {
|
|
362
|
+
const stats = diffStats(createLineDiff(beforeContent, afterContent));
|
|
363
|
+
if (!stats.changed)
|
|
364
|
+
return "Saved content";
|
|
365
|
+
return `Updated content (+${stats.addedLines} -${stats.deletedLines} lines)`;
|
|
366
|
+
}
|
|
367
|
+
function parseHistoryVersion(version) {
|
|
368
|
+
const parsed = Number(version);
|
|
369
|
+
if (!Number.isInteger(parsed) || parsed < 1)
|
|
370
|
+
return null;
|
|
371
|
+
return parsed;
|
|
372
|
+
}
|
|
373
|
+
async function getPersistedHistoryEntry(extensionId, version, includeContent) {
|
|
374
|
+
const result = await getDbExec().execute({
|
|
375
|
+
sql: `SELECT id, tool_id, version, operation, summary, name, description,
|
|
376
|
+
content, icon, actor_email, owner_email, org_id, visibility, created_at
|
|
377
|
+
FROM tool_history
|
|
378
|
+
WHERE tool_id = ? AND version = ?
|
|
379
|
+
LIMIT 1`,
|
|
380
|
+
args: [extensionId, version],
|
|
381
|
+
});
|
|
382
|
+
const row = result.rows?.[0];
|
|
383
|
+
return row ? extensionHistoryEntryFromRaw(row, includeContent) : null;
|
|
384
|
+
}
|
|
385
|
+
function splitLines(text) {
|
|
386
|
+
if (!text)
|
|
387
|
+
return [];
|
|
388
|
+
const parts = text.split("\n");
|
|
389
|
+
return parts
|
|
390
|
+
.map((line, index) => (index < parts.length - 1 ? `${line}\n` : line))
|
|
391
|
+
.filter((line) => line.length > 0);
|
|
392
|
+
}
|
|
393
|
+
function createLineDiff(beforeText, afterText) {
|
|
394
|
+
const before = splitLines(beforeText);
|
|
395
|
+
const after = splitLines(afterText);
|
|
396
|
+
if (before.length === 0 && after.length === 0)
|
|
397
|
+
return [];
|
|
398
|
+
const cells = before.length * after.length;
|
|
399
|
+
if (cells > 40_000)
|
|
400
|
+
return createBoundaryDiff(before, after);
|
|
401
|
+
const dp = Array.from({ length: before.length + 1 }, () => Array(after.length + 1).fill(0));
|
|
402
|
+
for (let i = before.length - 1; i >= 0; i -= 1) {
|
|
403
|
+
for (let j = after.length - 1; j >= 0; j -= 1) {
|
|
404
|
+
dp[i][j] =
|
|
405
|
+
before[i] === after[j]
|
|
406
|
+
? dp[i + 1][j + 1] + 1
|
|
407
|
+
: Math.max(dp[i + 1][j], dp[i][j + 1]);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
const diff = [];
|
|
411
|
+
let i = 0;
|
|
412
|
+
let j = 0;
|
|
413
|
+
while (i < before.length && j < after.length) {
|
|
414
|
+
if (before[i] === after[j]) {
|
|
415
|
+
diff.push({ type: "equal", text: before[i] });
|
|
416
|
+
i += 1;
|
|
417
|
+
j += 1;
|
|
418
|
+
}
|
|
419
|
+
else if (dp[i + 1][j] >= dp[i][j + 1]) {
|
|
420
|
+
diff.push({ type: "delete", text: before[i] });
|
|
421
|
+
i += 1;
|
|
422
|
+
}
|
|
423
|
+
else {
|
|
424
|
+
diff.push({ type: "insert", text: after[j] });
|
|
425
|
+
j += 1;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
while (i < before.length) {
|
|
429
|
+
diff.push({ type: "delete", text: before[i] });
|
|
430
|
+
i += 1;
|
|
431
|
+
}
|
|
432
|
+
while (j < after.length) {
|
|
433
|
+
diff.push({ type: "insert", text: after[j] });
|
|
434
|
+
j += 1;
|
|
435
|
+
}
|
|
436
|
+
return diff;
|
|
437
|
+
}
|
|
438
|
+
function createBoundaryDiff(before, after) {
|
|
439
|
+
let prefix = 0;
|
|
440
|
+
while (prefix < before.length &&
|
|
441
|
+
prefix < after.length &&
|
|
442
|
+
before[prefix] === after[prefix]) {
|
|
443
|
+
prefix += 1;
|
|
444
|
+
}
|
|
445
|
+
let suffix = 0;
|
|
446
|
+
while (suffix + prefix < before.length &&
|
|
447
|
+
suffix + prefix < after.length &&
|
|
448
|
+
before[before.length - 1 - suffix] === after[after.length - 1 - suffix]) {
|
|
449
|
+
suffix += 1;
|
|
450
|
+
}
|
|
451
|
+
return [
|
|
452
|
+
...before
|
|
453
|
+
.slice(0, prefix)
|
|
454
|
+
.map((text) => ({ type: "equal", text })),
|
|
455
|
+
...before
|
|
456
|
+
.slice(prefix, before.length - suffix)
|
|
457
|
+
.map((text) => ({ type: "delete", text })),
|
|
458
|
+
...after
|
|
459
|
+
.slice(prefix, after.length - suffix)
|
|
460
|
+
.map((text) => ({ type: "insert", text })),
|
|
461
|
+
...before
|
|
462
|
+
.slice(before.length - suffix)
|
|
463
|
+
.map((text) => ({ type: "equal", text })),
|
|
464
|
+
];
|
|
465
|
+
}
|
|
466
|
+
function diffStats(diff) {
|
|
467
|
+
const addedLines = diff.filter((line) => line.type === "insert").length;
|
|
468
|
+
const deletedLines = diff.filter((line) => line.type === "delete").length;
|
|
469
|
+
return {
|
|
470
|
+
addedLines,
|
|
471
|
+
deletedLines,
|
|
472
|
+
changed: addedLines > 0 || deletedLines > 0,
|
|
473
|
+
};
|
|
474
|
+
}
|
|
217
475
|
export async function listExtensions(options = {}) {
|
|
218
476
|
await ensureExtensionsTables();
|
|
219
477
|
const db = getDb();
|
|
@@ -233,6 +491,95 @@ export async function getExtension(id) {
|
|
|
233
491
|
const access = await resolveAccess("extension", id);
|
|
234
492
|
return access?.resource ?? null;
|
|
235
493
|
}
|
|
494
|
+
export async function listExtensionHistory(id, options = {}) {
|
|
495
|
+
await ensureExtensionsTables();
|
|
496
|
+
const extension = await getExtension(id);
|
|
497
|
+
if (!extension)
|
|
498
|
+
return [];
|
|
499
|
+
const includeContent = options.includeContent === true;
|
|
500
|
+
const limit = clampHistoryLimit(options.limit);
|
|
501
|
+
const result = await getDbExec().execute({
|
|
502
|
+
sql: `SELECT id, tool_id, version, operation, summary, name, description,
|
|
503
|
+
content, icon, actor_email, owner_email, org_id, visibility, created_at
|
|
504
|
+
FROM tool_history
|
|
505
|
+
WHERE tool_id = ?
|
|
506
|
+
ORDER BY version DESC
|
|
507
|
+
LIMIT ?`,
|
|
508
|
+
args: [id, limit],
|
|
509
|
+
});
|
|
510
|
+
const entries = (result.rows ?? []).map((row) => extensionHistoryEntryFromRaw(row, includeContent));
|
|
511
|
+
if (entries.length > 0)
|
|
512
|
+
return entries;
|
|
513
|
+
return [extensionHistoryEntryFromExtension(extension, includeContent)];
|
|
514
|
+
}
|
|
515
|
+
export async function getExtensionHistoryVersion(id, versionValue) {
|
|
516
|
+
await ensureExtensionsTables();
|
|
517
|
+
const extension = await getExtension(id);
|
|
518
|
+
if (!extension)
|
|
519
|
+
return null;
|
|
520
|
+
const version = parseHistoryVersion(versionValue);
|
|
521
|
+
if (!version)
|
|
522
|
+
return null;
|
|
523
|
+
const persisted = await getPersistedHistoryEntry(id, version, true);
|
|
524
|
+
const entry = persisted ??
|
|
525
|
+
(!(await hasExtensionHistory(id)) && version === 1
|
|
526
|
+
? extensionHistoryEntryFromExtension(extension, true)
|
|
527
|
+
: null);
|
|
528
|
+
if (!entry)
|
|
529
|
+
return null;
|
|
530
|
+
const previous = version > 1 ? await getPersistedHistoryEntry(id, version - 1, true) : null;
|
|
531
|
+
const previousContent = previous?.content ?? "";
|
|
532
|
+
const currentContent = entry.content ?? "";
|
|
533
|
+
const diff = createLineDiff(previousContent, currentContent);
|
|
534
|
+
return {
|
|
535
|
+
entry,
|
|
536
|
+
previous,
|
|
537
|
+
diff,
|
|
538
|
+
stats: diffStats(diff),
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
export async function restoreExtensionHistoryVersion(id, versionValue) {
|
|
542
|
+
await ensureExtensionsTables();
|
|
543
|
+
await assertAccess("extension", id, "editor");
|
|
544
|
+
const version = parseHistoryVersion(versionValue);
|
|
545
|
+
if (!version)
|
|
546
|
+
return null;
|
|
547
|
+
const existingRows = await getDb()
|
|
548
|
+
.select()
|
|
549
|
+
.from(extensions)
|
|
550
|
+
.where(eq(extensions.id, id));
|
|
551
|
+
const existing = existingRows[0];
|
|
552
|
+
if (!existing)
|
|
553
|
+
return null;
|
|
554
|
+
await ensureExtensionHistoryBaseline(existing);
|
|
555
|
+
const target = await getPersistedHistoryEntry(id, version, true);
|
|
556
|
+
if (!target)
|
|
557
|
+
return null;
|
|
558
|
+
const beforeTargets = await extensionChangeTargetsForId(id);
|
|
559
|
+
await getDb()
|
|
560
|
+
.update(extensions)
|
|
561
|
+
.set({
|
|
562
|
+
name: target.name,
|
|
563
|
+
description: target.description,
|
|
564
|
+
content: target.content ?? "",
|
|
565
|
+
icon: target.icon,
|
|
566
|
+
updatedAt: new Date().toISOString(),
|
|
567
|
+
})
|
|
568
|
+
.where(eq(extensions.id, id));
|
|
569
|
+
const rows = await getDb()
|
|
570
|
+
.select()
|
|
571
|
+
.from(extensions)
|
|
572
|
+
.where(eq(extensions.id, id));
|
|
573
|
+
const row = rows[0] ?? null;
|
|
574
|
+
if (row) {
|
|
575
|
+
await recordExtensionHistorySnapshot(row, "restore", `Restored version ${version}`);
|
|
576
|
+
await notifyExtensionChanged([
|
|
577
|
+
...beforeTargets,
|
|
578
|
+
...(await extensionChangeTargetsForRow(row)),
|
|
579
|
+
]);
|
|
580
|
+
}
|
|
581
|
+
return row;
|
|
582
|
+
}
|
|
236
583
|
export async function createExtension(data) {
|
|
237
584
|
await ensureExtensionsTables();
|
|
238
585
|
const db = getDb();
|
|
@@ -255,6 +602,7 @@ export async function createExtension(data) {
|
|
|
255
602
|
visibility: "private",
|
|
256
603
|
};
|
|
257
604
|
await db.insert(extensions).values(row);
|
|
605
|
+
await recordExtensionHistorySnapshot(row, "create", "Created extension");
|
|
258
606
|
await notifyExtensionChanged([{ owner: row.ownerEmail }]);
|
|
259
607
|
return row;
|
|
260
608
|
}
|
|
@@ -281,10 +629,19 @@ export async function updateExtension(id, data) {
|
|
|
281
629
|
updates.icon = data.icon;
|
|
282
630
|
if (data.visibility !== undefined)
|
|
283
631
|
updates.visibility = data.visibility;
|
|
632
|
+
const existingRows = await db
|
|
633
|
+
.select()
|
|
634
|
+
.from(extensions)
|
|
635
|
+
.where(eq(extensions.id, id));
|
|
636
|
+
const existing = existingRows[0];
|
|
637
|
+
if (!existing)
|
|
638
|
+
return null;
|
|
639
|
+
await ensureExtensionHistoryBaseline(existing);
|
|
284
640
|
await db.update(extensions).set(updates).where(eq(extensions.id, id));
|
|
285
641
|
const rows = await db.select().from(extensions).where(eq(extensions.id, id));
|
|
286
642
|
const row = rows[0] ?? null;
|
|
287
643
|
if (row) {
|
|
644
|
+
await recordExtensionHistorySnapshot(row, "metadata-update", summarizeMetadataChange(existing, row));
|
|
288
645
|
await notifyExtensionChanged([
|
|
289
646
|
...beforeTargets,
|
|
290
647
|
...(await extensionChangeTargetsForRow(row)),
|
|
@@ -308,7 +665,9 @@ export async function updateExtensionContent(id, opts) {
|
|
|
308
665
|
.where(eq(extensions.id, id));
|
|
309
666
|
if (!existingRows[0])
|
|
310
667
|
return null;
|
|
311
|
-
const
|
|
668
|
+
const existing = existingRows[0];
|
|
669
|
+
const existingContent = existing.content;
|
|
670
|
+
await ensureExtensionHistoryBaseline(existing);
|
|
312
671
|
const update = await applyExtensionContentUpdate(existingContent, opts);
|
|
313
672
|
const beforeTargets = await extensionChangeTargetsForId(id);
|
|
314
673
|
await db
|
|
@@ -318,6 +677,7 @@ export async function updateExtensionContent(id, opts) {
|
|
|
318
677
|
const rows = await db.select().from(extensions).where(eq(extensions.id, id));
|
|
319
678
|
const row = rows[0] ?? null;
|
|
320
679
|
if (row) {
|
|
680
|
+
await recordExtensionHistorySnapshot(row, "content-update", summarizeContentChange(existingContent, row.content));
|
|
321
681
|
await notifyExtensionChanged([
|
|
322
682
|
...beforeTargets,
|
|
323
683
|
...(await extensionChangeTargetsForRow(row)),
|
|
@@ -340,6 +700,10 @@ export async function deleteExtension(id) {
|
|
|
340
700
|
sql: `DELETE FROM tool_data WHERE tool_id = ?`,
|
|
341
701
|
args: [id],
|
|
342
702
|
});
|
|
703
|
+
await getDbExec().execute({
|
|
704
|
+
sql: `DELETE FROM tool_history WHERE tool_id = ?`,
|
|
705
|
+
args: [id],
|
|
706
|
+
});
|
|
343
707
|
const { cascadeDeleteExtensionSlots } = await import("./slots/store.js");
|
|
344
708
|
await cascadeDeleteExtensionSlots(id);
|
|
345
709
|
await db.delete(extensions).where(eq(extensions.id, id));
|