@agent-native/core 0.49.11 → 0.49.13

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.
Files changed (77) hide show
  1. package/dist/cli/pr-visual-recap-workflow.d.ts +1 -1
  2. package/dist/cli/pr-visual-recap-workflow.d.ts.map +1 -1
  3. package/dist/cli/pr-visual-recap-workflow.js +1 -1
  4. package/dist/cli/pr-visual-recap-workflow.js.map +1 -1
  5. package/dist/cli/recap.d.ts +37 -0
  6. package/dist/cli/recap.d.ts.map +1 -1
  7. package/dist/cli/recap.js +240 -0
  8. package/dist/cli/recap.js.map +1 -1
  9. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  10. package/dist/client/MultiTabAssistantChat.js +5 -10
  11. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  12. package/dist/client/blocks/library/question-form.js +1 -1
  13. package/dist/client/blocks/library/question-form.js.map +1 -1
  14. package/dist/client/extensions/EmbeddedExtension.d.ts.map +1 -1
  15. package/dist/client/extensions/EmbeddedExtension.js +4 -0
  16. package/dist/client/extensions/EmbeddedExtension.js.map +1 -1
  17. package/dist/client/extensions/ExtensionViewer.d.ts.map +1 -1
  18. package/dist/client/extensions/ExtensionViewer.js +12 -4
  19. package/dist/client/extensions/ExtensionViewer.js.map +1 -1
  20. package/dist/client/extensions/ExtensionsListPage.d.ts.map +1 -1
  21. package/dist/client/extensions/ExtensionsListPage.js +14 -9
  22. package/dist/client/extensions/ExtensionsListPage.js.map +1 -1
  23. package/dist/client/extensions/ExtensionsSidebarSection.d.ts.map +1 -1
  24. package/dist/client/extensions/ExtensionsSidebarSection.js +6 -4
  25. package/dist/client/extensions/ExtensionsSidebarSection.js.map +1 -1
  26. package/dist/client/extensions/iframe-bridge.d.ts +8 -0
  27. package/dist/client/extensions/iframe-bridge.d.ts.map +1 -1
  28. package/dist/client/extensions/iframe-bridge.js +54 -0
  29. package/dist/client/extensions/iframe-bridge.js.map +1 -1
  30. package/dist/client/progress/RunsTray.d.ts.map +1 -1
  31. package/dist/client/progress/RunsTray.js +12 -3
  32. package/dist/client/progress/RunsTray.js.map +1 -1
  33. package/dist/client/resources/ResourceEditor.d.ts +1 -3
  34. package/dist/client/resources/ResourceEditor.d.ts.map +1 -1
  35. package/dist/client/resources/ResourceEditor.js +8 -23
  36. package/dist/client/resources/ResourceEditor.js.map +1 -1
  37. package/dist/client/resources/ResourcesPanel.d.ts.map +1 -1
  38. package/dist/client/resources/ResourcesPanel.js +4 -9
  39. package/dist/client/resources/ResourcesPanel.js.map +1 -1
  40. package/dist/client/settings/VoiceTranscriptionSection.d.ts.map +1 -1
  41. package/dist/client/settings/VoiceTranscriptionSection.js +1 -1
  42. package/dist/client/settings/VoiceTranscriptionSection.js.map +1 -1
  43. package/dist/client/sharing/ShareButton.d.ts +5 -1
  44. package/dist/client/sharing/ShareButton.d.ts.map +1 -1
  45. package/dist/client/sharing/ShareButton.js +15 -7
  46. package/dist/client/sharing/ShareButton.js.map +1 -1
  47. package/dist/client/sharing/ShareDialog.d.ts.map +1 -1
  48. package/dist/client/sharing/ShareDialog.js +6 -2
  49. package/dist/client/sharing/ShareDialog.js.map +1 -1
  50. package/dist/extensions/actions.d.ts.map +1 -1
  51. package/dist/extensions/actions.js +70 -2
  52. package/dist/extensions/actions.js.map +1 -1
  53. package/dist/extensions/html-shell.d.ts +12 -0
  54. package/dist/extensions/html-shell.d.ts.map +1 -1
  55. package/dist/extensions/html-shell.js.map +1 -1
  56. package/dist/extensions/local.d.ts +35 -0
  57. package/dist/extensions/local.d.ts.map +1 -0
  58. package/dist/extensions/local.js +334 -0
  59. package/dist/extensions/local.js.map +1 -0
  60. package/dist/extensions/routes.d.ts.map +1 -1
  61. package/dist/extensions/routes.js +92 -12
  62. package/dist/extensions/routes.js.map +1 -1
  63. package/dist/extensions/slots/store.d.ts.map +1 -1
  64. package/dist/extensions/slots/store.js +72 -4
  65. package/dist/extensions/slots/store.js.map +1 -1
  66. package/dist/local-artifacts/index.d.ts +4 -0
  67. package/dist/local-artifacts/index.d.ts.map +1 -1
  68. package/dist/local-artifacts/index.js +60 -35
  69. package/dist/local-artifacts/index.js.map +1 -1
  70. package/dist/vite/client.d.ts +11 -1
  71. package/dist/vite/client.d.ts.map +1 -1
  72. package/dist/vite/client.js +26 -1
  73. package/dist/vite/client.js.map +1 -1
  74. package/docs/content/extensions.md +65 -0
  75. package/docs/content/local-file-mode.md +378 -0
  76. package/docs/content/template-content.md +53 -4
  77. package/package.json +1 -1
@@ -0,0 +1,334 @@
1
+ import fs from "node:fs/promises";
2
+ import fsSync from "node:fs";
3
+ import path from "node:path";
4
+ import { loadAgentNativeManifest, resolveAgentNativeDataMode, } from "../local-artifacts/index.js";
5
+ const LOCAL_EXTENSION_ID_RE = /^[a-z0-9][a-z0-9._-]{0,79}$/;
6
+ function isRecord(value) {
7
+ return !!value && typeof value === "object" && !Array.isArray(value);
8
+ }
9
+ function errorCode(error) {
10
+ return isRecord(error) && typeof error.code === "string"
11
+ ? error.code
12
+ : undefined;
13
+ }
14
+ function asStringArray(value) {
15
+ if (typeof value === "string" && value.trim())
16
+ return [value.trim()];
17
+ if (!Array.isArray(value))
18
+ return [];
19
+ return value.filter((item) => typeof item === "string");
20
+ }
21
+ function normalizeSlash(filePath) {
22
+ return filePath.replace(/\\/g, "/");
23
+ }
24
+ function normalizeRelativePath(filePath, label = "path") {
25
+ if (!filePath || typeof filePath !== "string") {
26
+ throw new Error(`${label} is required`);
27
+ }
28
+ if (filePath.includes("\0")) {
29
+ throw new Error(`${label} must not contain null bytes`);
30
+ }
31
+ if (path.isAbsolute(filePath)) {
32
+ throw new Error(`${label} must be relative`);
33
+ }
34
+ const normalized = normalizeSlash(path.posix.normalize(normalizeSlash(filePath)));
35
+ if (!normalized ||
36
+ normalized === "." ||
37
+ normalized === ".." ||
38
+ normalized.startsWith("../") ||
39
+ normalized.split("/").some((part) => !part || part === "." || part === "..")) {
40
+ throw new Error(`${label} must be a safe relative path`);
41
+ }
42
+ return normalized;
43
+ }
44
+ function resolveInside(basePath, relativePath, label) {
45
+ const safePath = normalizeRelativePath(relativePath, label);
46
+ const absolutePath = path.resolve(basePath, safePath);
47
+ const relative = path.relative(basePath, absolutePath);
48
+ if (relative === "" ||
49
+ relative.startsWith("..") ||
50
+ path.isAbsolute(relative)) {
51
+ throw new Error(`${label} "${relativePath}" is outside the workspace`);
52
+ }
53
+ return { safePath, absolutePath };
54
+ }
55
+ function noFollowOpenFlags() {
56
+ return fsSync.constants.O_RDONLY | (fsSync.constants.O_NOFOLLOW ?? 0);
57
+ }
58
+ function assertNoSymlinkPathSync(rootPath, absolutePath) {
59
+ const relative = path.relative(rootPath, absolutePath);
60
+ const segments = relative.split(path.sep).filter(Boolean);
61
+ let current = rootPath;
62
+ const pathsToCheck = [
63
+ current,
64
+ ...segments.map((segment) => {
65
+ current = path.join(current, segment);
66
+ return current;
67
+ }),
68
+ ];
69
+ for (const candidate of pathsToCheck) {
70
+ const stat = fsSync.lstatSync(candidate);
71
+ if (stat.isSymbolicLink()) {
72
+ throw new Error(`Path "${candidate}" must not traverse a symlink`);
73
+ }
74
+ }
75
+ }
76
+ function readTextFileWithoutSymlink(rootPath, filePath) {
77
+ assertNoSymlinkPathSync(rootPath, filePath);
78
+ const fd = fsSync.openSync(filePath, noFollowOpenFlags());
79
+ try {
80
+ return {
81
+ content: fsSync.readFileSync(fd, "utf8"),
82
+ stat: fsSync.fstatSync(fd),
83
+ };
84
+ }
85
+ finally {
86
+ fsSync.closeSync(fd);
87
+ }
88
+ }
89
+ function normalizeExtensionId(rawId, manifestPath) {
90
+ const id = rawId.trim();
91
+ if (!LOCAL_EXTENSION_ID_RE.test(id)) {
92
+ throw new Error(`Local extension id in ${manifestPath} must match ${LOCAL_EXTENSION_ID_RE}`);
93
+ }
94
+ return id;
95
+ }
96
+ function titleFromId(id) {
97
+ return (id
98
+ .replace(/[-_]+/g, " ")
99
+ .replace(/\s+/g, " ")
100
+ .trim()
101
+ .replace(/\b\w/g, (char) => char.toUpperCase()) || id);
102
+ }
103
+ function normalizeActionPermission(value) {
104
+ const trimmed = value.trim();
105
+ if (!trimmed)
106
+ return null;
107
+ if (trimmed === "*")
108
+ return "*";
109
+ if (/^[A-Za-z0-9_.:-]+$/.test(trimmed))
110
+ return trimmed;
111
+ return null;
112
+ }
113
+ function normalizePermissions(value) {
114
+ const permissions = {
115
+ appActions: [],
116
+ extensionData: true,
117
+ sql: false,
118
+ externalFetch: false,
119
+ };
120
+ const appActions = new Set();
121
+ const addAction = (action) => {
122
+ const normalized = normalizeActionPermission(action);
123
+ if (normalized)
124
+ appActions.add(normalized);
125
+ };
126
+ if (Array.isArray(value)) {
127
+ for (const item of value) {
128
+ if (typeof item !== "string")
129
+ continue;
130
+ if (item === "extensionData" || item === "storage") {
131
+ permissions.extensionData = true;
132
+ continue;
133
+ }
134
+ if (item === "sql" || item === "dbQuery" || item === "dbExec") {
135
+ permissions.sql = true;
136
+ continue;
137
+ }
138
+ if (item === "externalFetch" || item === "extensionFetch") {
139
+ permissions.externalFetch = true;
140
+ continue;
141
+ }
142
+ const actionMatch = item.match(/^(?:appAction|action):(.+)$/);
143
+ if (actionMatch?.[1])
144
+ addAction(actionMatch[1]);
145
+ }
146
+ }
147
+ else if (isRecord(value)) {
148
+ const rawActions = value.appActions === "*" || value.actions === "*"
149
+ ? ["*"]
150
+ : [...asStringArray(value.appActions), ...asStringArray(value.actions)];
151
+ for (const action of rawActions)
152
+ addAction(action);
153
+ if (typeof value.extensionData === "boolean") {
154
+ permissions.extensionData = value.extensionData;
155
+ }
156
+ if (typeof value.storage === "boolean") {
157
+ permissions.extensionData = value.storage;
158
+ }
159
+ if (typeof value.sql === "boolean")
160
+ permissions.sql = value.sql;
161
+ if (typeof value.externalFetch === "boolean") {
162
+ permissions.externalFetch = value.externalFetch;
163
+ }
164
+ if (typeof value.extensionFetch === "boolean") {
165
+ permissions.externalFetch = value.extensionFetch;
166
+ }
167
+ }
168
+ permissions.appActions = [...appActions].sort();
169
+ return permissions;
170
+ }
171
+ function manifestAppExtensions(app) {
172
+ return asStringArray(app.extensions);
173
+ }
174
+ function readJsonFile(rootPath, filePath) {
175
+ try {
176
+ const { content, stat } = readTextFileWithoutSymlink(rootPath, filePath);
177
+ return { value: JSON.parse(content), stat };
178
+ }
179
+ catch (error) {
180
+ if (errorCode(error) === "ENOENT")
181
+ return null;
182
+ throw error;
183
+ }
184
+ }
185
+ async function readLocalExtensionFolder(options) {
186
+ const extensionAbsolutePath = path.join(options.rootAbsolutePath, options.folderName);
187
+ const extensionRelativePath = normalizeSlash(path.relative(options.workspaceRoot, extensionAbsolutePath));
188
+ const manifestAbsolutePath = path.join(extensionAbsolutePath, "extension.json");
189
+ const manifestRead = readJsonFile(extensionAbsolutePath, manifestAbsolutePath);
190
+ if (!manifestRead)
191
+ return null;
192
+ const rawManifest = manifestRead.value;
193
+ if (!isRecord(rawManifest)) {
194
+ throw new Error(`Local extension manifest must be an object: ${manifestAbsolutePath}`);
195
+ }
196
+ const manifest = rawManifest;
197
+ const manifestRelativePath = normalizeSlash(path.relative(options.workspaceRoot, manifestAbsolutePath));
198
+ const id = normalizeExtensionId(typeof manifest.id === "string" ? manifest.id : options.folderName, manifestAbsolutePath);
199
+ const entry = String(typeof manifest.entry === "string"
200
+ ? manifest.entry
201
+ : typeof manifest.main === "string"
202
+ ? manifest.main
203
+ : "index.html");
204
+ const { safePath: entrySafePath, absolutePath: entryAbsolutePath } = resolveInside(extensionAbsolutePath, entry, "entry");
205
+ const { content, stat: entryStat } = readTextFileWithoutSymlink(extensionAbsolutePath, entryAbsolutePath);
206
+ const manifestStat = manifestRead.stat;
207
+ const updatedAt = new Date(Math.max(manifestStat.mtimeMs, entryStat.mtimeMs)).toISOString();
208
+ const createdAt = new Date(Math.min(manifestStat.birthtimeMs, entryStat.birthtimeMs)).toISOString();
209
+ const slots = [
210
+ ...new Set([
211
+ ...asStringArray(manifest.slots),
212
+ ...asStringArray(manifest.slot),
213
+ ]),
214
+ ].sort();
215
+ const permissions = normalizePermissions(manifest.permissions);
216
+ const name = typeof manifest.name === "string" && manifest.name.trim()
217
+ ? manifest.name.trim()
218
+ : titleFromId(id);
219
+ return {
220
+ id,
221
+ name,
222
+ description: typeof manifest.description === "string" ? manifest.description : "",
223
+ content,
224
+ icon: typeof manifest.icon === "string" ? manifest.icon : null,
225
+ createdAt,
226
+ updatedAt,
227
+ hiddenAt: null,
228
+ hiddenBy: null,
229
+ ownerEmail: "local-files",
230
+ orgId: null,
231
+ visibility: "private",
232
+ source: {
233
+ mode: "local-files",
234
+ appId: options.appId,
235
+ rootPath: options.rootPath,
236
+ extensionPath: extensionRelativePath,
237
+ manifestPath: manifestRelativePath,
238
+ entryPath: normalizeSlash(path.posix.join(extensionRelativePath, entrySafePath)),
239
+ permissions,
240
+ slots,
241
+ },
242
+ };
243
+ }
244
+ async function listLocalExtensionsForRoot(options) {
245
+ let rootStat;
246
+ try {
247
+ rootStat = await fs.lstat(options.rootAbsolutePath);
248
+ }
249
+ catch (error) {
250
+ if (errorCode(error) === "ENOENT")
251
+ return [];
252
+ throw error;
253
+ }
254
+ if (!rootStat.isDirectory() || rootStat.isSymbolicLink())
255
+ return [];
256
+ const entries = await fs.readdir(options.rootAbsolutePath, {
257
+ withFileTypes: true,
258
+ });
259
+ const rows = [];
260
+ for (const entry of entries) {
261
+ if (!entry.isDirectory() ||
262
+ entry.isSymbolicLink() ||
263
+ entry.name.startsWith(".")) {
264
+ continue;
265
+ }
266
+ const row = await readLocalExtensionFolder({
267
+ appId: options.appId,
268
+ workspaceRoot: options.workspaceRoot,
269
+ rootPath: options.rootPath,
270
+ rootAbsolutePath: options.rootAbsolutePath,
271
+ folderName: entry.name,
272
+ });
273
+ if (row)
274
+ rows.push(row);
275
+ }
276
+ return rows;
277
+ }
278
+ export async function listLocalExtensions(options = {}) {
279
+ const loaded = await loadAgentNativeManifest({ ...options, optional: true });
280
+ if (!loaded)
281
+ return [];
282
+ const rows = [];
283
+ const seenIds = new Map();
284
+ for (const [appId, app] of Object.entries(loaded.manifest.apps ?? {})) {
285
+ const extensionRoots = manifestAppExtensions(app);
286
+ if (extensionRoots.length === 0)
287
+ continue;
288
+ const mode = await resolveAgentNativeDataMode({
289
+ ...options,
290
+ manifestPath: loaded.path,
291
+ appId,
292
+ defaults: { mode: app.mode },
293
+ });
294
+ if (mode !== "local-files")
295
+ continue;
296
+ for (const root of extensionRoots) {
297
+ const { safePath, absolutePath } = resolveInside(loaded.rootDir, root, "extensions");
298
+ rows.push(...(await listLocalExtensionsForRoot({
299
+ appId,
300
+ workspaceRoot: loaded.rootDir,
301
+ rootPath: safePath,
302
+ rootAbsolutePath: absolutePath,
303
+ })));
304
+ }
305
+ }
306
+ for (const row of rows) {
307
+ const existingPath = seenIds.get(row.id);
308
+ if (existingPath) {
309
+ throw new Error(`Duplicate local extension id "${row.id}" in ${existingPath} and ${row.source.manifestPath}`);
310
+ }
311
+ seenIds.set(row.id, row.source.manifestPath);
312
+ }
313
+ return rows.sort((a, b) => a.name.localeCompare(b.name) || a.id.localeCompare(b.id));
314
+ }
315
+ export async function getLocalExtension(id, options = {}) {
316
+ const rows = await listLocalExtensions(options);
317
+ return rows.find((row) => row.id === id) ?? null;
318
+ }
319
+ export function isLocalExtensionRow(row) {
320
+ return (isRecord(row) && isRecord(row.source) && row.source.mode === "local-files");
321
+ }
322
+ export function localExtensionSourceSummary(row) {
323
+ return {
324
+ mode: row.source.mode,
325
+ appId: row.source.appId,
326
+ rootPath: row.source.rootPath,
327
+ extensionPath: row.source.extensionPath,
328
+ manifestPath: row.source.manifestPath,
329
+ entryPath: row.source.entryPath,
330
+ slots: row.source.slots,
331
+ permissions: row.source.permissions,
332
+ };
333
+ }
334
+ //# sourceMappingURL=local.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local.js","sourceRoot":"","sources":["../../src/extensions/local.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from \"node:fs/promises\";\nimport fsSync from \"node:fs\";\nimport path from \"node:path\";\nimport {\n loadAgentNativeManifest,\n resolveAgentNativeDataMode,\n type AgentNativeManifestApp,\n type LoadAgentNativeManifestOptions,\n} from \"../local-artifacts/index.js\";\nimport type { ExtensionRow } from \"./store.js\";\n\nexport interface LocalExtensionPermissions {\n appActions: string[];\n extensionData: boolean;\n sql: boolean;\n externalFetch: boolean;\n}\n\nexport interface LocalExtensionSource {\n mode: \"local-files\";\n appId: string;\n rootPath: string;\n extensionPath: string;\n manifestPath: string;\n entryPath: string;\n permissions: LocalExtensionPermissions;\n slots: string[];\n}\n\nexport interface LocalExtensionRow extends ExtensionRow {\n source: LocalExtensionSource;\n}\n\ninterface LocalExtensionManifest {\n id?: unknown;\n name?: unknown;\n description?: unknown;\n icon?: unknown;\n entry?: unknown;\n main?: unknown;\n slots?: unknown;\n slot?: unknown;\n permissions?: unknown;\n}\n\nconst LOCAL_EXTENSION_ID_RE = /^[a-z0-9][a-z0-9._-]{0,79}$/;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return !!value && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction errorCode(error: unknown): string | undefined {\n return isRecord(error) && typeof error.code === \"string\"\n ? error.code\n : undefined;\n}\n\nfunction asStringArray(value: unknown): string[] {\n if (typeof value === \"string\" && value.trim()) return [value.trim()];\n if (!Array.isArray(value)) return [];\n return value.filter((item): item is string => typeof item === \"string\");\n}\n\nfunction normalizeSlash(filePath: string): string {\n return filePath.replace(/\\\\/g, \"/\");\n}\n\nfunction normalizeRelativePath(filePath: string, label = \"path\"): string {\n if (!filePath || typeof filePath !== \"string\") {\n throw new Error(`${label} is required`);\n }\n if (filePath.includes(\"\\0\")) {\n throw new Error(`${label} must not contain null bytes`);\n }\n if (path.isAbsolute(filePath)) {\n throw new Error(`${label} must be relative`);\n }\n const normalized = normalizeSlash(\n path.posix.normalize(normalizeSlash(filePath)),\n );\n if (\n !normalized ||\n normalized === \".\" ||\n normalized === \"..\" ||\n normalized.startsWith(\"../\") ||\n normalized.split(\"/\").some((part) => !part || part === \".\" || part === \"..\")\n ) {\n throw new Error(`${label} must be a safe relative path`);\n }\n return normalized;\n}\n\nfunction resolveInside(basePath: string, relativePath: string, label: string) {\n const safePath = normalizeRelativePath(relativePath, label);\n const absolutePath = path.resolve(basePath, safePath);\n const relative = path.relative(basePath, absolutePath);\n if (\n relative === \"\" ||\n relative.startsWith(\"..\") ||\n path.isAbsolute(relative)\n ) {\n throw new Error(`${label} \"${relativePath}\" is outside the workspace`);\n }\n return { safePath, absolutePath };\n}\n\nfunction noFollowOpenFlags(): number {\n return fsSync.constants.O_RDONLY | (fsSync.constants.O_NOFOLLOW ?? 0);\n}\n\nfunction assertNoSymlinkPathSync(rootPath: string, absolutePath: string): void {\n const relative = path.relative(rootPath, absolutePath);\n const segments = relative.split(path.sep).filter(Boolean);\n let current = rootPath;\n const pathsToCheck = [\n current,\n ...segments.map((segment) => {\n current = path.join(current, segment);\n return current;\n }),\n ];\n\n for (const candidate of pathsToCheck) {\n const stat = fsSync.lstatSync(candidate);\n if (stat.isSymbolicLink()) {\n throw new Error(`Path \"${candidate}\" must not traverse a symlink`);\n }\n }\n}\n\nfunction readTextFileWithoutSymlink(rootPath: string, filePath: string) {\n assertNoSymlinkPathSync(rootPath, filePath);\n const fd = fsSync.openSync(filePath, noFollowOpenFlags());\n try {\n return {\n content: fsSync.readFileSync(fd, \"utf8\"),\n stat: fsSync.fstatSync(fd),\n };\n } finally {\n fsSync.closeSync(fd);\n }\n}\n\nfunction normalizeExtensionId(rawId: string, manifestPath: string): string {\n const id = rawId.trim();\n if (!LOCAL_EXTENSION_ID_RE.test(id)) {\n throw new Error(\n `Local extension id in ${manifestPath} must match ${LOCAL_EXTENSION_ID_RE}`,\n );\n }\n return id;\n}\n\nfunction titleFromId(id: string): string {\n return (\n id\n .replace(/[-_]+/g, \" \")\n .replace(/\\s+/g, \" \")\n .trim()\n .replace(/\\b\\w/g, (char) => char.toUpperCase()) || id\n );\n}\n\nfunction normalizeActionPermission(value: string): string | null {\n const trimmed = value.trim();\n if (!trimmed) return null;\n if (trimmed === \"*\") return \"*\";\n if (/^[A-Za-z0-9_.:-]+$/.test(trimmed)) return trimmed;\n return null;\n}\n\nfunction normalizePermissions(value: unknown): LocalExtensionPermissions {\n const permissions: LocalExtensionPermissions = {\n appActions: [],\n extensionData: true,\n sql: false,\n externalFetch: false,\n };\n const appActions = new Set<string>();\n\n const addAction = (action: string) => {\n const normalized = normalizeActionPermission(action);\n if (normalized) appActions.add(normalized);\n };\n\n if (Array.isArray(value)) {\n for (const item of value) {\n if (typeof item !== \"string\") continue;\n if (item === \"extensionData\" || item === \"storage\") {\n permissions.extensionData = true;\n continue;\n }\n if (item === \"sql\" || item === \"dbQuery\" || item === \"dbExec\") {\n permissions.sql = true;\n continue;\n }\n if (item === \"externalFetch\" || item === \"extensionFetch\") {\n permissions.externalFetch = true;\n continue;\n }\n const actionMatch = item.match(/^(?:appAction|action):(.+)$/);\n if (actionMatch?.[1]) addAction(actionMatch[1]);\n }\n } else if (isRecord(value)) {\n const rawActions =\n value.appActions === \"*\" || value.actions === \"*\"\n ? [\"*\"]\n : [...asStringArray(value.appActions), ...asStringArray(value.actions)];\n for (const action of rawActions) addAction(action);\n if (typeof value.extensionData === \"boolean\") {\n permissions.extensionData = value.extensionData;\n }\n if (typeof value.storage === \"boolean\") {\n permissions.extensionData = value.storage;\n }\n if (typeof value.sql === \"boolean\") permissions.sql = value.sql;\n if (typeof value.externalFetch === \"boolean\") {\n permissions.externalFetch = value.externalFetch;\n }\n if (typeof value.extensionFetch === \"boolean\") {\n permissions.externalFetch = value.extensionFetch;\n }\n }\n\n permissions.appActions = [...appActions].sort();\n return permissions;\n}\n\nfunction manifestAppExtensions(app: AgentNativeManifestApp): string[] {\n return asStringArray(app.extensions);\n}\n\nfunction readJsonFile(\n rootPath: string,\n filePath: string,\n): { value: unknown; stat: fsSync.Stats } | null {\n try {\n const { content, stat } = readTextFileWithoutSymlink(rootPath, filePath);\n return { value: JSON.parse(content) as unknown, stat };\n } catch (error) {\n if (errorCode(error) === \"ENOENT\") return null;\n throw error;\n }\n}\n\nasync function readLocalExtensionFolder(options: {\n appId: string;\n workspaceRoot: string;\n rootPath: string;\n rootAbsolutePath: string;\n folderName: string;\n}): Promise<LocalExtensionRow | null> {\n const extensionAbsolutePath = path.join(\n options.rootAbsolutePath,\n options.folderName,\n );\n const extensionRelativePath = normalizeSlash(\n path.relative(options.workspaceRoot, extensionAbsolutePath),\n );\n const manifestAbsolutePath = path.join(\n extensionAbsolutePath,\n \"extension.json\",\n );\n const manifestRead = readJsonFile(\n extensionAbsolutePath,\n manifestAbsolutePath,\n );\n if (!manifestRead) return null;\n const rawManifest = manifestRead.value;\n if (!isRecord(rawManifest)) {\n throw new Error(\n `Local extension manifest must be an object: ${manifestAbsolutePath}`,\n );\n }\n const manifest = rawManifest as LocalExtensionManifest;\n const manifestRelativePath = normalizeSlash(\n path.relative(options.workspaceRoot, manifestAbsolutePath),\n );\n const id = normalizeExtensionId(\n typeof manifest.id === \"string\" ? manifest.id : options.folderName,\n manifestAbsolutePath,\n );\n const entry = String(\n typeof manifest.entry === \"string\"\n ? manifest.entry\n : typeof manifest.main === \"string\"\n ? manifest.main\n : \"index.html\",\n );\n const { safePath: entrySafePath, absolutePath: entryAbsolutePath } =\n resolveInside(extensionAbsolutePath, entry, \"entry\");\n const { content, stat: entryStat } = readTextFileWithoutSymlink(\n extensionAbsolutePath,\n entryAbsolutePath,\n );\n const manifestStat = manifestRead.stat;\n const updatedAt = new Date(\n Math.max(manifestStat.mtimeMs, entryStat.mtimeMs),\n ).toISOString();\n const createdAt = new Date(\n Math.min(manifestStat.birthtimeMs, entryStat.birthtimeMs),\n ).toISOString();\n const slots = [\n ...new Set([\n ...asStringArray(manifest.slots),\n ...asStringArray(manifest.slot),\n ]),\n ].sort();\n const permissions = normalizePermissions(manifest.permissions);\n const name =\n typeof manifest.name === \"string\" && manifest.name.trim()\n ? manifest.name.trim()\n : titleFromId(id);\n\n return {\n id,\n name,\n description:\n typeof manifest.description === \"string\" ? manifest.description : \"\",\n content,\n icon: typeof manifest.icon === \"string\" ? manifest.icon : null,\n createdAt,\n updatedAt,\n hiddenAt: null,\n hiddenBy: null,\n ownerEmail: \"local-files\",\n orgId: null,\n visibility: \"private\",\n source: {\n mode: \"local-files\",\n appId: options.appId,\n rootPath: options.rootPath,\n extensionPath: extensionRelativePath,\n manifestPath: manifestRelativePath,\n entryPath: normalizeSlash(\n path.posix.join(extensionRelativePath, entrySafePath),\n ),\n permissions,\n slots,\n },\n };\n}\n\nasync function listLocalExtensionsForRoot(options: {\n appId: string;\n workspaceRoot: string;\n rootPath: string;\n rootAbsolutePath: string;\n}): Promise<LocalExtensionRow[]> {\n let rootStat;\n try {\n rootStat = await fs.lstat(options.rootAbsolutePath);\n } catch (error) {\n if (errorCode(error) === \"ENOENT\") return [];\n throw error;\n }\n if (!rootStat.isDirectory() || rootStat.isSymbolicLink()) return [];\n\n const entries = await fs.readdir(options.rootAbsolutePath, {\n withFileTypes: true,\n });\n const rows: LocalExtensionRow[] = [];\n for (const entry of entries) {\n if (\n !entry.isDirectory() ||\n entry.isSymbolicLink() ||\n entry.name.startsWith(\".\")\n ) {\n continue;\n }\n const row = await readLocalExtensionFolder({\n appId: options.appId,\n workspaceRoot: options.workspaceRoot,\n rootPath: options.rootPath,\n rootAbsolutePath: options.rootAbsolutePath,\n folderName: entry.name,\n });\n if (row) rows.push(row);\n }\n return rows;\n}\n\nexport async function listLocalExtensions(\n options: LoadAgentNativeManifestOptions = {},\n): Promise<LocalExtensionRow[]> {\n const loaded = await loadAgentNativeManifest({ ...options, optional: true });\n if (!loaded) return [];\n\n const rows: LocalExtensionRow[] = [];\n const seenIds = new Map<string, string>();\n for (const [appId, app] of Object.entries(loaded.manifest.apps ?? {})) {\n const extensionRoots = manifestAppExtensions(app);\n if (extensionRoots.length === 0) continue;\n const mode = await resolveAgentNativeDataMode({\n ...options,\n manifestPath: loaded.path,\n appId,\n defaults: { mode: app.mode },\n });\n if (mode !== \"local-files\") continue;\n\n for (const root of extensionRoots) {\n const { safePath, absolutePath } = resolveInside(\n loaded.rootDir,\n root,\n \"extensions\",\n );\n rows.push(\n ...(await listLocalExtensionsForRoot({\n appId,\n workspaceRoot: loaded.rootDir,\n rootPath: safePath,\n rootAbsolutePath: absolutePath,\n })),\n );\n }\n }\n\n for (const row of rows) {\n const existingPath = seenIds.get(row.id);\n if (existingPath) {\n throw new Error(\n `Duplicate local extension id \"${row.id}\" in ${existingPath} and ${row.source.manifestPath}`,\n );\n }\n seenIds.set(row.id, row.source.manifestPath);\n }\n\n return rows.sort(\n (a, b) => a.name.localeCompare(b.name) || a.id.localeCompare(b.id),\n );\n}\n\nexport async function getLocalExtension(\n id: string,\n options: LoadAgentNativeManifestOptions = {},\n): Promise<LocalExtensionRow | null> {\n const rows = await listLocalExtensions(options);\n return rows.find((row) => row.id === id) ?? null;\n}\n\nexport function isLocalExtensionRow(\n row: ExtensionRow | LocalExtensionRow | null | undefined,\n): row is LocalExtensionRow {\n return (\n isRecord(row) && isRecord(row.source) && row.source.mode === \"local-files\"\n );\n}\n\nexport function localExtensionSourceSummary(row: LocalExtensionRow) {\n return {\n mode: row.source.mode,\n appId: row.source.appId,\n rootPath: row.source.rootPath,\n extensionPath: row.source.extensionPath,\n manifestPath: row.source.manifestPath,\n entryPath: row.source.entryPath,\n slots: row.source.slots,\n permissions: row.source.permissions,\n };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/extensions/routes.ts"],"names":[],"mappings":"AAwDA,wBAAgB,uBAAuB,2FAuCtC;AA8tBD,eAAO,MAAM,kBAAkB,QACqa,CAAC;AAMrc,eAAO,MAAM,gBAAgB,QACwkB,CAAC;AAMtmB,eAAO,MAAM,oBAAoB,QACgB,CAAC;AAMlD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAQ/D"}
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/extensions/routes.ts"],"names":[],"mappings":"AA8DA,wBAAgB,uBAAuB,2FAuCtC;AAizBD,eAAO,MAAM,kBAAkB,QACqa,CAAC;AAMrc,eAAO,MAAM,gBAAgB,QACwkB,CAAC;AAMtmB,eAAO,MAAM,oBAAoB,QACgB,CAAC;AAMlD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAQ/D"}
@@ -6,6 +6,7 @@ import { runWithRequestContext, getRequestOrgId, } from "../server/request-conte
6
6
  import { getOrgContext } from "../org/context.js";
7
7
  import { getDbExec, isPostgres } from "../db/client.js";
8
8
  import { listExtensions, getExtension, getExtensionHistoryVersion, listExtensionHistory, createExtension, updateExtension, updateExtensionContent, restoreExtensionHistoryVersion, deleteExtension, hideExtension, unhideExtension, globalHideExtension, globalUnhideExtension, ensureExtensionsTables, } from "./store.js";
9
+ import { getLocalExtension, isLocalExtensionRow, listLocalExtensions, } from "./local.js";
9
10
  import { buildExtensionHtml, EXTENSION_IFRAME_CSP } from "./html-shell.js";
10
11
  import { getThemeVars } from "./theme.js";
11
12
  import { resolveKeyReferencesWithRequestScopes, validateUrlAllowlist, getResolvedKeyAllowlist, } from "../secrets/substitution.js";
@@ -88,7 +89,8 @@ async function dispatch(event, method, parts, userEmail) {
88
89
  const includeGloballyHidden = event.url?.searchParams?.get("includeGloballyHidden") === "true";
89
90
  const includeContent = event.url?.searchParams?.get("includeContent") === "true";
90
91
  const rows = await listExtensions({ includeGloballyHidden });
91
- return Promise.all(rows.map((row) => extensionResponse(row, undefined, { includeContent })));
92
+ const localRows = includeGloballyHidden ? [] : await listLocalExtensions();
93
+ return Promise.all([...rows, ...localRows].map((row) => extensionResponse(row, undefined, { includeContent })));
92
94
  }
93
95
  // POST / — create
94
96
  if (method === "POST" && parts.length === 0) {
@@ -103,6 +105,25 @@ async function dispatch(event, method, parts, userEmail) {
103
105
  }
104
106
  // GET /:id/render
105
107
  if (method === "GET" && parts.length === 2 && parts[1] === "render") {
108
+ const localExtension = await getLocalExtension(parts[0]);
109
+ if (localExtension) {
110
+ const search = event.url?.search || "";
111
+ const isDark = search.includes("dark=1") || search.includes("dark=true");
112
+ const themeVars = getThemeVars(isDark);
113
+ const html = buildExtensionHtml(localExtension.content, themeVars, isDark, parts[0], {
114
+ authorEmail: localExtension.ownerEmail,
115
+ viewerEmail: userEmail,
116
+ isAuthor: false,
117
+ role: "viewer",
118
+ source: "local-files",
119
+ permissions: localExtension.source.permissions,
120
+ });
121
+ setResponseHeader(event, "Content-Type", "text/html; charset=utf-8");
122
+ setResponseHeader(event, "Content-Security-Policy", EXTENSION_IFRAME_CSP);
123
+ setResponseHeader(event, "X-Content-Type-Options", "nosniff");
124
+ setResponseHeader(event, "Referrer-Policy", "no-referrer");
125
+ return html;
126
+ }
106
127
  const access = await resolveAccess("extension", parts[0]);
107
128
  const extension = access?.resource;
108
129
  if (!extension) {
@@ -132,6 +153,9 @@ async function dispatch(event, method, parts, userEmail) {
132
153
  }
133
154
  // GET /:id/history — list saved snapshots for an extension
134
155
  if (method === "GET" && parts.length === 2 && parts[1] === "history") {
156
+ const localResponse = await localExtensionSqlOnlyResponse(event, parts[0]);
157
+ if (localResponse)
158
+ return localResponse;
135
159
  const limitParam = event.url?.searchParams?.get("limit");
136
160
  const limit = limitParam === null || limitParam === undefined
137
161
  ? undefined
@@ -146,6 +170,9 @@ async function dispatch(event, method, parts, userEmail) {
146
170
  }
147
171
  // GET /:id/history/:version — fetch one snapshot plus its previous-version diff
148
172
  if (method === "GET" && parts.length === 3 && parts[1] === "history") {
173
+ const localResponse = await localExtensionSqlOnlyResponse(event, parts[0]);
174
+ if (localResponse)
175
+ return localResponse;
149
176
  const detail = await getExtensionHistoryVersion(parts[0], parts[2]);
150
177
  if (!detail) {
151
178
  setResponseStatus(event, 404);
@@ -158,6 +185,9 @@ async function dispatch(event, method, parts, userEmail) {
158
185
  parts.length === 4 &&
159
186
  parts[1] === "history" &&
160
187
  parts[3] === "restore") {
188
+ const localResponse = await localExtensionSqlOnlyResponse(event, parts[0]);
189
+ if (localResponse)
190
+ return localResponse;
161
191
  const restored = await restoreExtensionHistoryVersion(parts[0], parts[2]);
162
192
  if (!restored) {
163
193
  setResponseStatus(event, 404);
@@ -167,6 +197,10 @@ async function dispatch(event, method, parts, userEmail) {
167
197
  }
168
198
  // GET /:id
169
199
  if (method === "GET" && parts.length === 1) {
200
+ const localExtension = await getLocalExtension(parts[0]);
201
+ if (localExtension) {
202
+ return extensionResponse(localExtension, "viewer");
203
+ }
170
204
  const access = await resolveAccess("extension", parts[0]);
171
205
  if (!access) {
172
206
  setResponseStatus(event, 404);
@@ -177,6 +211,9 @@ async function dispatch(event, method, parts, userEmail) {
177
211
  // POST /:id/hide — remove from the current user's Extensions list/sidebar
178
212
  // without deleting the underlying extension for teammates or shared slots.
179
213
  if (method === "POST" && parts.length === 2 && parts[1] === "hide") {
214
+ const localResponse = await localExtensionSqlOnlyResponse(event, parts[0]);
215
+ if (localResponse)
216
+ return localResponse;
180
217
  const ok = await hideExtension(parts[0]);
181
218
  if (!ok) {
182
219
  setResponseStatus(event, 404);
@@ -186,6 +223,9 @@ async function dispatch(event, method, parts, userEmail) {
186
223
  }
187
224
  // POST /:id/unhide — restore an extension hidden by the current user.
188
225
  if (method === "POST" && parts.length === 2 && parts[1] === "unhide") {
226
+ const localResponse = await localExtensionSqlOnlyResponse(event, parts[0]);
227
+ if (localResponse)
228
+ return localResponse;
189
229
  const ok = await unhideExtension(parts[0]);
190
230
  if (!ok) {
191
231
  setResponseStatus(event, 404);
@@ -195,6 +235,9 @@ async function dispatch(event, method, parts, userEmail) {
195
235
  }
196
236
  // POST /:id/global-hide — admin/owner hides the extension from EVERYONE.
197
237
  if (method === "POST" && parts.length === 2 && parts[1] === "global-hide") {
238
+ const localResponse = await localExtensionSqlOnlyResponse(event, parts[0]);
239
+ if (localResponse)
240
+ return localResponse;
198
241
  const ok = await globalHideExtension(parts[0]);
199
242
  if (!ok) {
200
243
  setResponseStatus(event, 404);
@@ -204,6 +247,9 @@ async function dispatch(event, method, parts, userEmail) {
204
247
  }
205
248
  // POST /:id/global-unhide — admin/owner reverses a global hide.
206
249
  if (method === "POST" && parts.length === 2 && parts[1] === "global-unhide") {
250
+ const localResponse = await localExtensionSqlOnlyResponse(event, parts[0]);
251
+ if (localResponse)
252
+ return localResponse;
207
253
  const ok = await globalUnhideExtension(parts[0]);
208
254
  if (!ok) {
209
255
  setResponseStatus(event, 404);
@@ -213,6 +259,9 @@ async function dispatch(event, method, parts, userEmail) {
213
259
  }
214
260
  // PUT /:id
215
261
  if (method === "PUT" && parts.length === 1) {
262
+ const localResponse = await localExtensionSqlOnlyResponse(event, parts[0]);
263
+ if (localResponse)
264
+ return localResponse;
216
265
  const body = await readBody(event);
217
266
  const hasContentUpdate = body.content !== undefined ||
218
267
  body.patches !== undefined ||
@@ -245,6 +294,9 @@ async function dispatch(event, method, parts, userEmail) {
245
294
  }
246
295
  // DELETE /:id
247
296
  if (method === "DELETE" && parts.length === 1) {
297
+ const localResponse = await localExtensionSqlOnlyResponse(event, parts[0]);
298
+ if (localResponse)
299
+ return localResponse;
248
300
  const ok = await deleteExtension(parts[0]);
249
301
  if (!ok) {
250
302
  setResponseStatus(event, 404);
@@ -256,27 +308,49 @@ async function dispatch(event, method, parts, userEmail) {
256
308
  return { error: "Not found" };
257
309
  }
258
310
  async function extensionResponse(row, role, options = {}) {
259
- const resolvedRole = role ??
260
- (await resolveAccess("extension", row.id)
261
- .then((access) => access?.role ?? null)
262
- .catch(() => null));
311
+ const local = isLocalExtensionRow(row);
312
+ const resolvedRole = local
313
+ ? "viewer"
314
+ : (role ??
315
+ (await resolveAccess("extension", row.id)
316
+ .then((access) => access?.role ?? null)
317
+ .catch(() => null)));
263
318
  const responseRow = options.includeContent === false
264
319
  ? (({ content: _content, ...rest }) => rest)(row)
265
320
  : row;
266
321
  return {
267
322
  ...responseRow,
268
323
  role: resolvedRole,
269
- canEdit: resolvedRole
270
- ? ["owner", "admin", "editor"].includes(resolvedRole)
271
- : false,
272
- canDelete: resolvedRole ? ["owner", "admin"].includes(resolvedRole) : false,
324
+ canEdit: local
325
+ ? false
326
+ : resolvedRole
327
+ ? ["owner", "admin", "editor"].includes(resolvedRole)
328
+ : false,
329
+ canDelete: local
330
+ ? false
331
+ : resolvedRole
332
+ ? ["owner", "admin"].includes(resolvedRole)
333
+ : false,
273
334
  globallyHidden: row.hiddenAt != null,
274
335
  };
275
336
  }
337
+ async function localExtensionSqlOnlyResponse(event, extensionId) {
338
+ const localExtension = await getLocalExtension(extensionId);
339
+ if (!localExtension)
340
+ return null;
341
+ setResponseStatus(event, 400);
342
+ return {
343
+ error: "This extension is backed by local files. Edit its extension.json or entry file in the workspace; SQL-backed extension history, sharing, hide, update, and delete operations do not apply.",
344
+ source: localExtension.source,
345
+ };
346
+ }
276
347
  async function handleExtensionDataList(event, extensionId, collection, userEmail) {
277
348
  await ensureExtensionsTables();
278
349
  const extension = await getExtension(extensionId);
279
- if (!extension) {
350
+ const localExtension = extension
351
+ ? null
352
+ : await getLocalExtension(extensionId);
353
+ if (!extension && !localExtension) {
280
354
  setResponseStatus(event, 404);
281
355
  return { error: "Extension not found" };
282
356
  }
@@ -328,7 +402,10 @@ async function handleExtensionDataList(event, extensionId, collection, userEmail
328
402
  async function handleExtensionDataUpsert(event, extensionId, collection, userEmail) {
329
403
  await ensureExtensionsTables();
330
404
  const extension = await getExtension(extensionId);
331
- if (!extension) {
405
+ const localExtension = extension
406
+ ? null
407
+ : await getLocalExtension(extensionId);
408
+ if (!extension && !localExtension) {
332
409
  setResponseStatus(event, 404);
333
410
  return { error: "Extension not found" };
334
411
  }
@@ -387,7 +464,10 @@ async function handleExtensionDataUpsert(event, extensionId, collection, userEma
387
464
  async function handleExtensionDataDelete(event, extensionId, collection, itemId, userEmail) {
388
465
  await ensureExtensionsTables();
389
466
  const extension = await getExtension(extensionId);
390
- if (!extension) {
467
+ const localExtension = extension
468
+ ? null
469
+ : await getLocalExtension(extensionId);
470
+ if (!extension && !localExtension) {
391
471
  setResponseStatus(event, 404);
392
472
  return { error: "Extension not found" };
393
473
  }