@agent-native/core 0.7.83 → 0.8.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/action.js +1 -1
- package/dist/action.js.map +1 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +8 -8
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts +2 -0
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +44 -18
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/types.d.ts +1 -1
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +54 -11
- package/dist/cli/create.js.map +1 -1
- package/dist/cli/workspacify.d.ts.map +1 -1
- package/dist/cli/workspacify.js +12 -9
- package/dist/cli/workspacify.js.map +1 -1
- package/dist/client/AgentPanel.d.ts +1 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +22 -1
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +254 -29
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/agent-chat.d.ts +2 -0
- package/dist/client/agent-chat.d.ts.map +1 -1
- package/dist/client/agent-chat.js +11 -2
- package/dist/client/agent-chat.js.map +1 -1
- package/dist/client/composer/ComposerPlusMenu.js +1 -1
- package/dist/client/composer/ComposerPlusMenu.js.map +1 -1
- package/dist/client/composer/PromptComposer.js +1 -1
- package/dist/client/composer/PromptComposer.js.map +1 -1
- package/dist/client/composer/TiptapComposer.js +8 -8
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/composer/types.d.ts +1 -1
- package/dist/client/composer/types.d.ts.map +1 -1
- package/dist/client/composer/types.js.map +1 -1
- package/dist/client/extensions/EmbeddedExtension.d.ts +20 -0
- package/dist/client/extensions/EmbeddedExtension.d.ts.map +1 -0
- package/dist/client/{tools/EmbeddedTool.js → extensions/EmbeddedExtension.js} +41 -41
- package/dist/client/extensions/EmbeddedExtension.js.map +1 -0
- package/dist/client/extensions/ExtensionEditor.d.ts +5 -0
- package/dist/client/extensions/ExtensionEditor.d.ts.map +1 -0
- package/dist/client/extensions/ExtensionEditor.js +129 -0
- package/dist/client/extensions/ExtensionEditor.js.map +1 -0
- package/dist/client/{tools → extensions}/ExtensionSlot.d.ts +3 -3
- package/dist/client/extensions/ExtensionSlot.d.ts.map +1 -0
- package/dist/client/{tools → extensions}/ExtensionSlot.js +14 -14
- package/dist/client/extensions/ExtensionSlot.js.map +1 -0
- package/dist/client/extensions/ExtensionViewer.d.ts +5 -0
- package/dist/client/extensions/ExtensionViewer.d.ts.map +1 -0
- package/dist/client/{tools/ToolViewer.js → extensions/ExtensionViewer.js} +67 -65
- package/dist/client/extensions/ExtensionViewer.js.map +1 -0
- package/dist/client/extensions/ExtensionViewerPage.d.ts +2 -0
- package/dist/client/extensions/ExtensionViewerPage.d.ts.map +1 -0
- package/dist/client/{tools/ToolViewerPage.js → extensions/ExtensionViewerPage.js} +8 -8
- package/dist/client/extensions/ExtensionViewerPage.js.map +1 -0
- package/dist/client/extensions/ExtensionsListPage.d.ts +2 -0
- package/dist/client/extensions/ExtensionsListPage.d.ts.map +1 -0
- package/dist/client/extensions/ExtensionsListPage.js +67 -0
- package/dist/client/extensions/ExtensionsListPage.js.map +1 -0
- package/dist/client/extensions/ExtensionsSidebarSection.d.ts +2 -0
- package/dist/client/extensions/ExtensionsSidebarSection.d.ts.map +1 -0
- package/dist/client/{tools/ToolsSidebarSection.js → extensions/ExtensionsSidebarSection.js} +58 -58
- package/dist/client/extensions/ExtensionsSidebarSection.js.map +1 -0
- package/dist/client/{tools/tool-order.d.ts → extensions/extension-order.d.ts} +2 -2
- package/dist/client/extensions/extension-order.d.ts.map +1 -0
- package/dist/client/{tools/tool-order.js → extensions/extension-order.js} +3 -3
- package/dist/client/extensions/extension-order.js.map +1 -0
- package/dist/client/{tools → extensions}/iframe-bridge.d.ts +11 -11
- package/dist/client/extensions/iframe-bridge.d.ts.map +1 -0
- package/dist/client/{tools → extensions}/iframe-bridge.js +24 -24
- package/dist/client/extensions/iframe-bridge.js.map +1 -0
- package/dist/client/extensions/index.d.ts +14 -0
- package/dist/client/extensions/index.d.ts.map +1 -0
- package/dist/client/extensions/index.js +19 -0
- package/dist/client/extensions/index.js.map +1 -0
- package/dist/client/sse-event-processor.d.ts +2 -1
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +87 -6
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/extensions/actions.d.ts +3 -0
- package/dist/extensions/actions.d.ts.map +1 -0
- package/dist/{tools → extensions}/actions.js +54 -51
- package/dist/extensions/actions.js.map +1 -0
- package/dist/{tools → extensions}/fetch-tool.d.ts +4 -0
- package/dist/extensions/fetch-tool.d.ts.map +1 -0
- package/dist/{tools → extensions}/fetch-tool.js +12 -7
- package/dist/extensions/fetch-tool.js.map +1 -0
- package/dist/extensions/html-shell.d.ts +56 -0
- package/dist/extensions/html-shell.d.ts.map +1 -0
- package/dist/{tools → extensions}/html-shell.js +101 -83
- package/dist/extensions/html-shell.js.map +1 -0
- package/dist/{tools → extensions}/proxy-security.d.ts +2 -2
- package/dist/extensions/proxy-security.d.ts.map +1 -0
- package/dist/{tools → extensions}/proxy-security.js +3 -3
- package/dist/extensions/proxy-security.js.map +1 -0
- package/dist/extensions/routes.d.ts +2 -0
- package/dist/extensions/routes.d.ts.map +1 -0
- package/dist/{tools → extensions}/routes.js +73 -69
- package/dist/extensions/routes.js.map +1 -0
- package/dist/{tools → extensions}/schema.d.ts +44 -38
- package/dist/extensions/schema.d.ts.map +1 -0
- package/dist/{tools → extensions}/schema.js +41 -34
- package/dist/extensions/schema.js.map +1 -0
- package/dist/extensions/slots/routes.d.ts +15 -0
- package/dist/extensions/slots/routes.d.ts.map +1 -0
- package/dist/{tools → extensions}/slots/routes.js +26 -26
- package/dist/extensions/slots/routes.js.map +1 -0
- package/dist/{tools → extensions}/slots/schema.d.ts +24 -21
- package/dist/extensions/slots/schema.d.ts.map +1 -0
- package/dist/extensions/slots/schema.js +79 -0
- package/dist/extensions/slots/schema.js.map +1 -0
- package/dist/extensions/slots/store.d.ts +66 -0
- package/dist/extensions/slots/store.d.ts.map +1 -0
- package/dist/extensions/slots/store.js +238 -0
- package/dist/extensions/slots/store.js.map +1 -0
- package/dist/extensions/store.d.ts +40 -0
- package/dist/extensions/store.d.ts.map +1 -0
- package/dist/{tools → extensions}/store.js +59 -54
- package/dist/extensions/store.js.map +1 -0
- package/dist/extensions/theme.d.ts.map +1 -0
- package/dist/extensions/theme.js.map +1 -0
- package/dist/{tools → extensions}/url-safety.d.ts +5 -3
- package/dist/extensions/url-safety.d.ts.map +1 -0
- package/dist/{tools → extensions}/url-safety.js +11 -4
- package/dist/extensions/url-safety.js.map +1 -0
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +12 -10
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts +15 -0
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +64 -10
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/csrf.d.ts +3 -2
- package/dist/server/csrf.d.ts.map +1 -1
- package/dist/server/csrf.js +3 -2
- package/dist/server/csrf.js.map +1 -1
- package/dist/shared/workspace-app-id.d.ts +1 -1
- package/dist/shared/workspace-app-id.d.ts.map +1 -1
- package/dist/shared/workspace-app-id.js +5 -1
- package/dist/shared/workspace-app-id.js.map +1 -1
- package/dist/templates/workspace-root/README.md +5 -4
- package/dist/usage/store.d.ts +1 -1
- package/dist/usage/store.d.ts.map +1 -1
- package/dist/usage/store.js +1 -1
- package/dist/usage/store.js.map +1 -1
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +10 -1
- package/dist/vite/client.js.map +1 -1
- package/docs/content/actions.md +10 -10
- package/docs/content/extensions.md +230 -0
- package/docs/content/key-concepts.md +2 -2
- package/docs/content/server.md +13 -13
- package/docs/content/sharing.md +2 -2
- package/docs/content/template-dispatch.md +5 -0
- package/docs/content/what-is-agent-native.md +1 -1
- package/package.json +22 -17
- package/src/templates/workspace-root/README.md +5 -4
- package/dist/client/tools/EmbeddedTool.d.ts +0 -20
- package/dist/client/tools/EmbeddedTool.d.ts.map +0 -1
- package/dist/client/tools/EmbeddedTool.js.map +0 -1
- package/dist/client/tools/ExtensionSlot.d.ts.map +0 -1
- package/dist/client/tools/ExtensionSlot.js.map +0 -1
- package/dist/client/tools/ToolEditor.d.ts +0 -5
- package/dist/client/tools/ToolEditor.d.ts.map +0 -1
- package/dist/client/tools/ToolEditor.js +0 -129
- package/dist/client/tools/ToolEditor.js.map +0 -1
- package/dist/client/tools/ToolViewer.d.ts +0 -5
- package/dist/client/tools/ToolViewer.d.ts.map +0 -1
- package/dist/client/tools/ToolViewer.js.map +0 -1
- package/dist/client/tools/ToolViewerPage.d.ts +0 -2
- package/dist/client/tools/ToolViewerPage.d.ts.map +0 -1
- package/dist/client/tools/ToolViewerPage.js.map +0 -1
- package/dist/client/tools/ToolsListPage.d.ts +0 -2
- package/dist/client/tools/ToolsListPage.d.ts.map +0 -1
- package/dist/client/tools/ToolsListPage.js +0 -67
- package/dist/client/tools/ToolsListPage.js.map +0 -1
- package/dist/client/tools/ToolsSidebarSection.d.ts +0 -2
- package/dist/client/tools/ToolsSidebarSection.d.ts.map +0 -1
- package/dist/client/tools/ToolsSidebarSection.js.map +0 -1
- package/dist/client/tools/iframe-bridge.d.ts.map +0 -1
- package/dist/client/tools/iframe-bridge.js.map +0 -1
- package/dist/client/tools/index.d.ts +0 -8
- package/dist/client/tools/index.d.ts.map +0 -1
- package/dist/client/tools/index.js +0 -8
- package/dist/client/tools/index.js.map +0 -1
- package/dist/client/tools/tool-order.d.ts.map +0 -1
- package/dist/client/tools/tool-order.js.map +0 -1
- package/dist/tools/actions.d.ts +0 -3
- package/dist/tools/actions.d.ts.map +0 -1
- package/dist/tools/actions.js.map +0 -1
- package/dist/tools/fetch-tool.d.ts.map +0 -1
- package/dist/tools/fetch-tool.js.map +0 -1
- package/dist/tools/html-shell.d.ts +0 -45
- package/dist/tools/html-shell.d.ts.map +0 -1
- package/dist/tools/html-shell.js.map +0 -1
- package/dist/tools/proxy-security.d.ts.map +0 -1
- package/dist/tools/proxy-security.js.map +0 -1
- package/dist/tools/routes.d.ts +0 -2
- package/dist/tools/routes.d.ts.map +0 -1
- package/dist/tools/routes.js.map +0 -1
- package/dist/tools/schema.d.ts.map +0 -1
- package/dist/tools/schema.js.map +0 -1
- package/dist/tools/slots/routes.d.ts +0 -15
- package/dist/tools/slots/routes.d.ts.map +0 -1
- package/dist/tools/slots/routes.js.map +0 -1
- package/dist/tools/slots/schema.d.ts.map +0 -1
- package/dist/tools/slots/schema.js +0 -76
- package/dist/tools/slots/schema.js.map +0 -1
- package/dist/tools/slots/store.d.ts +0 -66
- package/dist/tools/slots/store.d.ts.map +0 -1
- package/dist/tools/slots/store.js +0 -227
- package/dist/tools/slots/store.js.map +0 -1
- package/dist/tools/store.d.ts +0 -40
- package/dist/tools/store.d.ts.map +0 -1
- package/dist/tools/store.js.map +0 -1
- package/dist/tools/theme.d.ts.map +0 -1
- package/dist/tools/theme.js.map +0 -1
- package/dist/tools/url-safety.d.ts.map +0 -1
- package/dist/tools/url-safety.js.map +0 -1
- package/docs/content/tools.md +0 -205
- /package/dist/{tools → extensions}/theme.d.ts +0 -0
- /package/dist/{tools → extensions}/theme.js +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export const
|
|
2
|
-
export const
|
|
3
|
-
export function
|
|
4
|
-
const
|
|
5
|
-
const
|
|
1
|
+
export const EXTENSION_IFRAME_CSP = "default-src 'none'; script-src 'self' https://cdn.jsdelivr.net 'unsafe-eval' 'unsafe-inline'; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://fonts.googleapis.com; font-src https://fonts.gstatic.com; connect-src 'self'; img-src 'self' data: blob:; media-src 'self' data: blob:; frame-src 'none'; object-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'self';";
|
|
2
|
+
export const EXTENSION_IFRAME_META_CSP = EXTENSION_IFRAME_CSP.replace(/\s*frame-ancestors 'self';?$/, "");
|
|
3
|
+
export function buildExtensionHtml(content, themeVars, isDark, extensionId, binding) {
|
|
4
|
+
const extensionIdJson = JSON.stringify(extensionId ?? "");
|
|
5
|
+
const extensionIdAttr = escapeHtmlAttribute(extensionId ?? "");
|
|
6
6
|
const bindingJson = JSON.stringify(binding ?? {
|
|
7
7
|
authorEmail: "",
|
|
8
8
|
viewerEmail: "",
|
|
@@ -14,14 +14,14 @@ export function buildToolHtml(content, themeVars, isDark, toolId, binding) {
|
|
|
14
14
|
<head>
|
|
15
15
|
<meta charset="utf-8" />
|
|
16
16
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
17
|
-
<meta http-equiv="Content-Security-Policy" content="${
|
|
18
|
-
${binding && !binding.isAuthor ? `<meta name="agent-native-
|
|
17
|
+
<meta http-equiv="Content-Security-Policy" content="${EXTENSION_IFRAME_META_CSP}" />
|
|
18
|
+
${binding && !binding.isAuthor ? `<meta name="agent-native-extension-author" content="${escapeHtmlAttribute(binding.authorEmail)}" />` : ""}
|
|
19
19
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
20
20
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
21
21
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300..700&display=swap" rel="stylesheet" />
|
|
22
22
|
<script>
|
|
23
|
-
var
|
|
24
|
-
var
|
|
23
|
+
var _extensionErrors = [];
|
|
24
|
+
var _extensionErrorDetails = [];
|
|
25
25
|
var _consoleLogs = [];
|
|
26
26
|
var _networkLogs = [];
|
|
27
27
|
|
|
@@ -46,16 +46,16 @@ export function buildToolHtml(content, themeVars, isDark, toolId, binding) {
|
|
|
46
46
|
function _collectError(message, stack) {
|
|
47
47
|
if (!message) return;
|
|
48
48
|
if (message === 'Script error.' || message === 'Script error') message = 'Runtime error';
|
|
49
|
-
if (
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
var toast = document.getElementById('
|
|
49
|
+
if (_extensionErrors.indexOf(message) !== -1) return;
|
|
50
|
+
_extensionErrors.push(message);
|
|
51
|
+
_extensionErrorDetails.push({ message: message, stack: stack || '' });
|
|
52
|
+
var toast = document.getElementById('__extension-error-toast');
|
|
53
53
|
if (!toast) return;
|
|
54
|
-
var msg = document.getElementById('
|
|
55
|
-
if (
|
|
56
|
-
msg.textContent =
|
|
54
|
+
var msg = document.getElementById('__extension-error-msg');
|
|
55
|
+
if (_extensionErrors.length === 1) {
|
|
56
|
+
msg.textContent = _extensionErrors[0];
|
|
57
57
|
} else {
|
|
58
|
-
msg.textContent =
|
|
58
|
+
msg.textContent = _extensionErrors.length + ' errors — ' + _extensionErrors[_extensionErrors.length - 1];
|
|
59
59
|
}
|
|
60
60
|
toast.style.display = 'block';
|
|
61
61
|
}
|
|
@@ -76,9 +76,9 @@ export function buildToolHtml(content, themeVars, isDark, toolId, binding) {
|
|
|
76
76
|
<!--
|
|
77
77
|
SECURITY: pinned to exact patch versions + SRI integrity hashes. A
|
|
78
78
|
malicious republish of @tailwindcss/browser@4.x or alpinejs@3.x would
|
|
79
|
-
otherwise inject code into every
|
|
79
|
+
otherwise inject code into every extension. To bump these versions:
|
|
80
80
|
1. npm view @tailwindcss/browser version (or alpinejs)
|
|
81
|
-
2. curl -sL https://cdn.jsdelivr.net/npm/@tailwindcss/browser@<v>
|
|
81
|
+
2. curl -sL https://cdn.jsdelivr.net/npm/@tailwindcss/browser@<v> \\
|
|
82
82
|
| openssl dgst -sha384 -binary | openssl base64 -A
|
|
83
83
|
3. Update the URL + integrity hash below in lockstep.
|
|
84
84
|
-->
|
|
@@ -132,13 +132,19 @@ export function buildToolHtml(content, themeVars, isDark, toolId, binding) {
|
|
|
132
132
|
<style>
|
|
133
133
|
*, *::before, *::after { border-color: hsl(var(--border)); }
|
|
134
134
|
body {
|
|
135
|
-
--agent-native-
|
|
135
|
+
--agent-native-extension-padding: clamp(16px, 2vw, 24px);
|
|
136
|
+
/* Legacy alias for pre-rename extension content (do not remove). */
|
|
137
|
+
--agent-native-tool-padding: var(--agent-native-extension-padding);
|
|
136
138
|
box-sizing: border-box;
|
|
137
139
|
font-family: 'Inter', sans-serif;
|
|
138
140
|
margin: 0;
|
|
139
141
|
min-height: 100vh;
|
|
140
|
-
padding: var(--agent-native-
|
|
142
|
+
padding: var(--agent-native-extension-padding);
|
|
141
143
|
}
|
|
144
|
+
body:has(> [data-extension-layout="full-bleed"]),
|
|
145
|
+
body:has(> [data-extension-padding="none"]),
|
|
146
|
+
body:has(> .agent-native-extension-bleed),
|
|
147
|
+
/* Legacy aliases (do not remove). */
|
|
142
148
|
body:has(> [data-tool-layout="full-bleed"]),
|
|
143
149
|
body:has(> [data-tool-padding="none"]),
|
|
144
150
|
body:has(> .agent-native-tool-bleed) {
|
|
@@ -146,16 +152,19 @@ export function buildToolHtml(content, themeVars, isDark, toolId, binding) {
|
|
|
146
152
|
}
|
|
147
153
|
</style>
|
|
148
154
|
<script>
|
|
149
|
-
var
|
|
150
|
-
var
|
|
155
|
+
var _extensionRequestSeq = 0;
|
|
156
|
+
var _extensionPendingRequests = {};
|
|
151
157
|
|
|
152
158
|
window.addEventListener('message', function(event) {
|
|
153
159
|
if (event.source !== window.parent) return;
|
|
154
160
|
var message = event.data || {};
|
|
155
|
-
if (
|
|
156
|
-
|
|
161
|
+
if (
|
|
162
|
+
message.type !== 'agent-native-extension-response' &&
|
|
163
|
+
message.type !== 'agent-native-tool-response'
|
|
164
|
+
) return;
|
|
165
|
+
var pending = _extensionPendingRequests[message.requestId];
|
|
157
166
|
if (!pending) return;
|
|
158
|
-
delete
|
|
167
|
+
delete _extensionPendingRequests[message.requestId];
|
|
159
168
|
if (message.error) {
|
|
160
169
|
pending.reject(new Error(message.error));
|
|
161
170
|
} else {
|
|
@@ -166,10 +175,10 @@ export function buildToolHtml(content, themeVars, isDark, toolId, binding) {
|
|
|
166
175
|
function hostRequest(path, options) {
|
|
167
176
|
options = options || {};
|
|
168
177
|
return new Promise(function(resolve, reject) {
|
|
169
|
-
var requestId = '
|
|
170
|
-
|
|
178
|
+
var requestId = 'extension-req-' + (++_extensionRequestSeq);
|
|
179
|
+
_extensionPendingRequests[requestId] = { resolve: resolve, reject: reject };
|
|
171
180
|
window.parent.postMessage({
|
|
172
|
-
type: 'agent-native-
|
|
181
|
+
type: 'agent-native-extension-request',
|
|
173
182
|
requestId: requestId,
|
|
174
183
|
path: path,
|
|
175
184
|
options: {
|
|
@@ -179,10 +188,10 @@ export function buildToolHtml(content, themeVars, isDark, toolId, binding) {
|
|
|
179
188
|
},
|
|
180
189
|
}, '*');
|
|
181
190
|
setTimeout(function() {
|
|
182
|
-
var pending =
|
|
191
|
+
var pending = _extensionPendingRequests[requestId];
|
|
183
192
|
if (!pending) return;
|
|
184
|
-
delete
|
|
185
|
-
pending.reject(new Error('
|
|
193
|
+
delete _extensionPendingRequests[requestId];
|
|
194
|
+
pending.reject(new Error('Extension host request timed out'));
|
|
186
195
|
}, 30000);
|
|
187
196
|
});
|
|
188
197
|
}
|
|
@@ -208,9 +217,9 @@ export function buildToolHtml(content, themeVars, isDark, toolId, binding) {
|
|
|
208
217
|
});
|
|
209
218
|
};
|
|
210
219
|
|
|
211
|
-
function
|
|
220
|
+
function extensionFetch(url, options) {
|
|
212
221
|
var opts = options || {};
|
|
213
|
-
return hostRequest('/_agent-native/
|
|
222
|
+
return hostRequest('/_agent-native/extensions/proxy', {
|
|
214
223
|
method: 'POST',
|
|
215
224
|
headers: { 'Content-Type': 'application/json' },
|
|
216
225
|
body: JSON.stringify({
|
|
@@ -266,7 +275,7 @@ export function buildToolHtml(content, themeVars, isDark, toolId, binding) {
|
|
|
266
275
|
async function dbQuery(sql, args) {
|
|
267
276
|
var body = { sql: sql };
|
|
268
277
|
if (args) body.args = args;
|
|
269
|
-
return appFetch('/_agent-native/
|
|
278
|
+
return appFetch('/_agent-native/extensions/sql/query', {
|
|
270
279
|
method: 'POST',
|
|
271
280
|
body: JSON.stringify(body),
|
|
272
281
|
});
|
|
@@ -275,54 +284,56 @@ export function buildToolHtml(content, themeVars, isDark, toolId, binding) {
|
|
|
275
284
|
async function dbExec(sql, args) {
|
|
276
285
|
var body = { sql: sql };
|
|
277
286
|
if (args) body.args = args;
|
|
278
|
-
return appFetch('/_agent-native/
|
|
287
|
+
return appFetch('/_agent-native/extensions/sql/exec', {
|
|
279
288
|
method: 'POST',
|
|
280
289
|
body: JSON.stringify(body),
|
|
281
290
|
});
|
|
282
291
|
}
|
|
283
292
|
|
|
284
|
-
var
|
|
285
|
-
var
|
|
286
|
-
window.
|
|
293
|
+
var _extensionId = ${extensionIdJson};
|
|
294
|
+
var _extensionBinding = ${bindingJson};
|
|
295
|
+
window.extensionBinding = _extensionBinding;
|
|
296
|
+
// Legacy alias for extension bodies authored before the rename.
|
|
297
|
+
window.toolBinding = _extensionBinding;
|
|
287
298
|
// SECURITY (audit H4): announce the resolved binding to the parent so the
|
|
288
299
|
// host bridge can gate dangerous helpers based on viewer role. Sent
|
|
289
300
|
// BEFORE the user-authored content has a chance to run, so a malicious
|
|
290
|
-
//
|
|
301
|
+
// extension body cannot suppress or rewrite the announcement. The parent
|
|
291
302
|
// ignores subsequent announcements for the same iframe; see
|
|
292
|
-
//
|
|
303
|
+
// ExtensionViewer.tsx / EmbeddedExtension.tsx.
|
|
293
304
|
try {
|
|
294
305
|
window.parent.postMessage(
|
|
295
306
|
{
|
|
296
|
-
type: 'agent-native-
|
|
297
|
-
|
|
298
|
-
binding:
|
|
307
|
+
type: 'agent-native-extension-binding',
|
|
308
|
+
extensionId: _extensionId,
|
|
309
|
+
binding: _extensionBinding,
|
|
299
310
|
},
|
|
300
311
|
'*',
|
|
301
312
|
);
|
|
302
313
|
} catch (_) {}
|
|
303
|
-
// SECURITY: when the viewer is not the author of this
|
|
304
|
-
// console warning. The bridge currently runs every helper with the
|
|
305
|
-
// viewer's session — a malicious shared
|
|
306
|
-
// any owned table row in scope, and resolve any user-scope secret.
|
|
307
|
-
// full consent step is tracked as TODO C1 in audit 05-tools-sandbox.md.
|
|
308
|
-
if (
|
|
314
|
+
// SECURITY: when the viewer is not the author of this extension, emit a
|
|
315
|
+
// clear console warning. The bridge currently runs every helper with the
|
|
316
|
+
// viewer's session — a malicious shared extension can call any action,
|
|
317
|
+
// read any owned table row in scope, and resolve any user-scope secret.
|
|
318
|
+
// A full consent step is tracked as TODO C1 in audit 05-tools-sandbox.md.
|
|
319
|
+
if (_extensionBinding && !_extensionBinding.isAuthor) {
|
|
309
320
|
try {
|
|
310
321
|
console.warn(
|
|
311
|
-
'[agent-native] Shared
|
|
312
|
-
'Author: ' + (
|
|
313
|
-
'Bridge calls (appAction, dbExec,
|
|
322
|
+
'[agent-native] Shared extension — running with viewer\\'s session. ' +
|
|
323
|
+
'Author: ' + (_extensionBinding.authorEmail || '<unknown>') + '. ' +
|
|
324
|
+
'Bridge calls (appAction, dbExec, extensionFetch) execute under ' +
|
|
314
325
|
'your account; they are gated by your permissions, not the ' +
|
|
315
|
-
'author\\'s. Do not run untrusted shared
|
|
326
|
+
'author\\'s. Do not run untrusted shared extensions.',
|
|
316
327
|
);
|
|
317
328
|
} catch (_) {}
|
|
318
329
|
}
|
|
319
330
|
|
|
320
|
-
var
|
|
331
|
+
var extensionData = {
|
|
321
332
|
async list(collection, opts) {
|
|
322
333
|
var limit = (opts && opts.limit) || 100;
|
|
323
334
|
var scope = (opts && opts.scope) || 'user';
|
|
324
|
-
var res = await hostRequest('/_agent-native/
|
|
325
|
-
if (!res.ok) throw new Error('Failed to list
|
|
335
|
+
var res = await hostRequest('/_agent-native/extensions/data/' + _extensionId + '/' + encodeURIComponent(collection) + '?limit=' + limit + '&scope=' + scope);
|
|
336
|
+
if (!res.ok) throw new Error('Failed to list extension data');
|
|
326
337
|
return res.body;
|
|
327
338
|
},
|
|
328
339
|
async get(collection, id, opts) {
|
|
@@ -332,26 +343,32 @@ export function buildToolHtml(content, themeVars, isDark, toolId, binding) {
|
|
|
332
343
|
},
|
|
333
344
|
async set(collection, id, data, opts) {
|
|
334
345
|
var scope = (opts && opts.scope) || 'user';
|
|
335
|
-
var res = await hostRequest('/_agent-native/
|
|
346
|
+
var res = await hostRequest('/_agent-native/extensions/data/' + _extensionId + '/' + encodeURIComponent(collection), {
|
|
336
347
|
method: 'POST',
|
|
337
348
|
headers: { 'Content-Type': 'application/json' },
|
|
338
349
|
body: JSON.stringify({ id: id, data: data, scope: scope }),
|
|
339
350
|
});
|
|
340
|
-
if (!res.ok) throw new Error('Failed to save
|
|
351
|
+
if (!res.ok) throw new Error('Failed to save extension data');
|
|
341
352
|
return res.body;
|
|
342
353
|
},
|
|
343
354
|
async remove(collection, id, opts) {
|
|
344
355
|
var scope = (opts && opts.scope) || 'user';
|
|
345
|
-
var res = await hostRequest('/_agent-native/
|
|
356
|
+
var res = await hostRequest('/_agent-native/extensions/data/' + _extensionId + '/' + encodeURIComponent(collection) + '/' + encodeURIComponent(id) + '?scope=' + scope, {
|
|
346
357
|
method: 'DELETE',
|
|
347
358
|
});
|
|
348
|
-
if (!res.ok) throw new Error('Failed to delete
|
|
359
|
+
if (!res.ok) throw new Error('Failed to delete extension data');
|
|
349
360
|
return res.body;
|
|
350
361
|
},
|
|
351
362
|
};
|
|
363
|
+
|
|
364
|
+
// Legacy aliases — extension bodies authored before the rename use
|
|
365
|
+
// toolFetch, toolData, toolId. Keep these working forever.
|
|
366
|
+
var toolFetch = extensionFetch;
|
|
367
|
+
var toolData = extensionData;
|
|
368
|
+
var _toolId = _extensionId;
|
|
352
369
|
</script>
|
|
353
370
|
<style>
|
|
354
|
-
#
|
|
371
|
+
#__extension-error-toast {
|
|
355
372
|
display: none;
|
|
356
373
|
position: fixed;
|
|
357
374
|
bottom: 16px;
|
|
@@ -375,11 +392,12 @@ export function buildToolHtml(content, themeVars, isDark, toolId, binding) {
|
|
|
375
392
|
}
|
|
376
393
|
</style>
|
|
377
394
|
<script>
|
|
378
|
-
// Extension-point slot context: when
|
|
379
|
-
// ExtensionSlot, the host pushes a context object via
|
|
380
|
-
// read it synchronously via window.slotContext
|
|
381
|
-
// via window.onSlotContext(fn). When rendered
|
|
382
|
-
// slotContext stays null and
|
|
395
|
+
// Extension-point slot context: when an extension is rendered embedded
|
|
396
|
+
// inside an ExtensionSlot, the host pushes a context object via
|
|
397
|
+
// postMessage. Extensions read it synchronously via window.slotContext
|
|
398
|
+
// or subscribe to changes via window.onSlotContext(fn). When rendered
|
|
399
|
+
// full-page (no ?slot= param), slotContext stays null and extensions
|
|
400
|
+
// branch on that.
|
|
383
401
|
window.slotContext = null;
|
|
384
402
|
var _slotContextSubscribers = [];
|
|
385
403
|
window.onSlotContext = function(fn) {
|
|
@@ -402,7 +420,7 @@ export function buildToolHtml(content, themeVars, isDark, toolId, binding) {
|
|
|
402
420
|
});
|
|
403
421
|
|
|
404
422
|
// Auto-resize the iframe to its content when running in slot mode. The
|
|
405
|
-
// host listens for agent-native-
|
|
423
|
+
// host listens for agent-native-extension-resize and adjusts the iframe height.
|
|
406
424
|
if (new URLSearchParams(location.search).get('slot')) {
|
|
407
425
|
var _lastH = 0;
|
|
408
426
|
var _reportHeight = function() {
|
|
@@ -412,7 +430,7 @@ export function buildToolHtml(content, themeVars, isDark, toolId, binding) {
|
|
|
412
430
|
);
|
|
413
431
|
if (h !== _lastH) {
|
|
414
432
|
_lastH = h;
|
|
415
|
-
window.parent.postMessage({ type: 'agent-native-
|
|
433
|
+
window.parent.postMessage({ type: 'agent-native-extension-resize', height: h }, '*');
|
|
416
434
|
}
|
|
417
435
|
};
|
|
418
436
|
if (typeof ResizeObserver !== 'undefined') {
|
|
@@ -451,7 +469,7 @@ export function buildToolHtml(content, themeVars, isDark, toolId, binding) {
|
|
|
451
469
|
e.preventDefault();
|
|
452
470
|
e.stopPropagation();
|
|
453
471
|
window.parent.postMessage({
|
|
454
|
-
type: 'agent-native-
|
|
472
|
+
type: 'agent-native-extension-keydown',
|
|
455
473
|
key: e.key, code: e.code,
|
|
456
474
|
metaKey: e.metaKey, ctrlKey: e.ctrlKey,
|
|
457
475
|
shiftKey: e.shiftKey, altKey: e.altKey,
|
|
@@ -460,7 +478,7 @@ export function buildToolHtml(content, themeVars, isDark, toolId, binding) {
|
|
|
460
478
|
}
|
|
461
479
|
if (e.key === 'Escape') {
|
|
462
480
|
window.parent.postMessage({
|
|
463
|
-
type: 'agent-native-
|
|
481
|
+
type: 'agent-native-extension-keydown',
|
|
464
482
|
key: e.key, code: e.code,
|
|
465
483
|
metaKey: false, ctrlKey: false,
|
|
466
484
|
shiftKey: false, altKey: false,
|
|
@@ -469,36 +487,36 @@ export function buildToolHtml(content, themeVars, isDark, toolId, binding) {
|
|
|
469
487
|
});
|
|
470
488
|
|
|
471
489
|
document.addEventListener('DOMContentLoaded', function() {
|
|
472
|
-
var fixBtn = document.getElementById('
|
|
490
|
+
var fixBtn = document.getElementById('__extension-error-fix');
|
|
473
491
|
if (fixBtn) {
|
|
474
492
|
fixBtn.addEventListener('click', function() {
|
|
475
493
|
window.parent.postMessage({
|
|
476
|
-
type: 'agent-native-
|
|
477
|
-
errors:
|
|
478
|
-
errorDetails:
|
|
494
|
+
type: 'agent-native-extension-error-fix',
|
|
495
|
+
errors: _extensionErrors,
|
|
496
|
+
errorDetails: _extensionErrorDetails,
|
|
479
497
|
consoleLogs: _consoleLogs.slice(-30),
|
|
480
498
|
networkLogs: _networkLogs.slice(-15)
|
|
481
499
|
}, '*');
|
|
482
|
-
document.getElementById('
|
|
500
|
+
document.getElementById('__extension-error-toast').style.display = 'none';
|
|
483
501
|
});
|
|
484
502
|
}
|
|
485
|
-
var dismissBtn = document.getElementById('
|
|
503
|
+
var dismissBtn = document.getElementById('__extension-error-dismiss');
|
|
486
504
|
if (dismissBtn) {
|
|
487
505
|
dismissBtn.addEventListener('click', function() {
|
|
488
|
-
document.getElementById('
|
|
506
|
+
document.getElementById('__extension-error-toast').style.display = 'none';
|
|
489
507
|
});
|
|
490
508
|
}
|
|
491
509
|
});
|
|
492
510
|
</script>
|
|
493
511
|
</head>
|
|
494
|
-
<body${
|
|
512
|
+
<body${extensionId ? ` data-extension-id="${extensionIdAttr}" data-tool-id="${extensionIdAttr}"` : ""} class="bg-background text-foreground">
|
|
495
513
|
${content}
|
|
496
|
-
<div id="
|
|
514
|
+
<div id="__extension-error-toast">
|
|
497
515
|
<div style="display:flex;align-items:flex-start;gap:8px;">
|
|
498
516
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="flex-shrink:0;margin-top:1px;"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
|
|
499
|
-
<span id="
|
|
500
|
-
<button id="
|
|
501
|
-
<button id="
|
|
517
|
+
<span id="__extension-error-msg" style="flex:1;overflow:hidden;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;"></span>
|
|
518
|
+
<button id="__extension-error-fix" style="cursor:pointer;border:none;background:rgba(255,255,255,.9);color:hsl(0 84.2% 40%);font-size:12px;font-weight:500;padding:4px 12px;border-radius:4px;flex-shrink:0;">Fix</button>
|
|
519
|
+
<button id="__extension-error-dismiss" style="cursor:pointer;border:none;background:transparent;color:inherit;font-size:16px;padding:2px 6px;opacity:0.7;flex-shrink:0;">×</button>
|
|
502
520
|
</div>
|
|
503
521
|
</div>
|
|
504
522
|
</body>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html-shell.js","sourceRoot":"","sources":["../../src/extensions/html-shell.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,oBAAoB,GAC/B,2YAA2Y,CAAC;AAE9Y,MAAM,CAAC,MAAM,yBAAyB,GAAG,oBAAoB,CAAC,OAAO,CACnE,8BAA8B,EAC9B,EAAE,CACH,CAAC;AAwDF,MAAM,UAAU,kBAAkB,CAChC,OAAe,EACf,SAAiB,EACjB,MAAe,EACf,WAAoB,EACpB,OAAgC;IAEhC,MAAM,eAAe,GAAG,IAAI,CAAC,SAAS,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;IAC1D,MAAM,eAAe,GAAG,mBAAmB,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;IAC/D,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAChC,OAAO,IAAI;QACT,WAAW,EAAE,EAAE;QACf,WAAW,EAAE,EAAE;QACf,QAAQ,EAAE,IAAI;QACd,IAAI,EAAE,OAAO;KACd,CACF,CAAC;IAEF,OAAO;iBACQ,MAAM,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE;;;;wDAIU,yBAAyB;IAC7E,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,uDAAuD,mBAAmB,CAAC,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WA8ElI,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yBAqMK,eAAe;8BACV,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA0NjC,WAAW,CAAC,CAAC,CAAC,uBAAuB,eAAe,mBAAmB,eAAe,GAAG,CAAC,CAAC,CAAC,EAAE;GACnG,OAAO;;;;;;;;;;SAUD,CAAC;AACV,CAAC;AAED,SAAS,mBAAmB,CAAC,KAAa;IACxC,OAAO,KAAK;SACT,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AAC3B,CAAC","sourcesContent":["export const EXTENSION_IFRAME_CSP =\n \"default-src 'none'; script-src 'self' https://cdn.jsdelivr.net 'unsafe-eval' 'unsafe-inline'; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://fonts.googleapis.com; font-src https://fonts.gstatic.com; connect-src 'self'; img-src 'self' data: blob:; media-src 'self' data: blob:; frame-src 'none'; object-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'self';\";\n\nexport const EXTENSION_IFRAME_META_CSP = EXTENSION_IFRAME_CSP.replace(\n /\\s*frame-ancestors 'self';?$/,\n \"\",\n);\n\n/**\n * SECURITY — EXTENSION CONTENT IS UNTRUSTED.\n *\n * `${content}` (line ~Body) interpolates raw HTML/JS authored by a user. This\n * file is the boundary between framework-controlled HTML and user-controlled\n * HTML. Two non-negotiable invariants for every change here:\n *\n * 1. The iframe MUST be rendered with a `sandbox` attribute that does NOT\n * include `allow-same-origin`. The viewer (`ExtensionViewer.tsx`,\n * `EmbeddedExtension.tsx`) sets `sandbox=\"allow-scripts allow-forms\"` —\n * and that is the only acceptable shape. Adding `allow-same-origin`\n * would give the extension full DOM access to the parent window via\n * cross-frame script.\n *\n * 2. Every reachable parent action must treat the postMessage payload as\n * hostile. The bridge in `iframe-bridge.ts` enforces a path allowlist,\n * header sanitization, and method allowlist; do not relax those gates\n * for \"convenience\" in this file or any caller.\n *\n * For the trust model rationale, see audit 05-tools-sandbox.md (C1) and the\n * `extensions` skill. When in doubt, fail closed.\n *\n * BACKWARDS COMPAT — the iframe injects helpers under both their canonical\n * `extension*` names (`extensionFetch`, `extensionData`, `extensionId`,\n * `extensionBinding`) AND legacy `tool*` aliases (`toolFetch`, `toolData`,\n * `toolId`, `toolBinding`) so existing user-authored extension bodies that\n * pre-date the rename keep working. Same for layout opt-ins:\n * `data-extension-layout=\"full-bleed\"` / `data-extension-padding=\"none\"` /\n * class `agent-native-extension-bleed` / CSS var\n * `--agent-native-extension-padding` are canonical; the `data-tool-*`,\n * `agent-native-tool-bleed`, and `--agent-native-tool-padding` variants are\n * accepted as aliases.\n */\n\nexport interface ExtensionRenderBinding {\n /** Email of the user who authored / owns the extension. */\n authorEmail: string;\n /** Email of the user currently viewing/running the extension. */\n viewerEmail: string;\n /** True when viewer === author. */\n isAuthor: boolean;\n /**\n * Resolved role for the viewer (\"owner\" | \"admin\" | \"editor\" | \"viewer\").\n *\n * TODO(security, audit H4): the host-side bridge does not yet gate any\n * helper based on this value — every viewer gets the same powers as the\n * author. The role is plumbed through so a follow-up PR can constrain\n * `appAction` / `dbExec` / `extensionFetch` for non-author viewers (and\n * eventually require an explicit consent step before running a shared\n * extension, audit C1). For now this is metadata only.\n */\n role: \"owner\" | \"admin\" | \"editor\" | \"viewer\";\n}\n\nexport function buildExtensionHtml(\n content: string,\n themeVars: string,\n isDark: boolean,\n extensionId?: string,\n binding?: ExtensionRenderBinding,\n): string {\n const extensionIdJson = JSON.stringify(extensionId ?? \"\");\n const extensionIdAttr = escapeHtmlAttribute(extensionId ?? \"\");\n const bindingJson = JSON.stringify(\n binding ?? {\n authorEmail: \"\",\n viewerEmail: \"\",\n isAuthor: true,\n role: \"owner\",\n },\n );\n\n return `<!DOCTYPE html>\n<html lang=\"en\"${isDark ? ' class=\"dark\"' : \"\"}>\n<head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <meta http-equiv=\"Content-Security-Policy\" content=\"${EXTENSION_IFRAME_META_CSP}\" />\n ${binding && !binding.isAuthor ? `<meta name=\"agent-native-extension-author\" content=\"${escapeHtmlAttribute(binding.authorEmail)}\" />` : \"\"}\n <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\" />\n <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin />\n <link href=\"https://fonts.googleapis.com/css2?family=Inter:wght@300..700&display=swap\" rel=\"stylesheet\" />\n <script>\n var _extensionErrors = [];\n var _extensionErrorDetails = [];\n var _consoleLogs = [];\n var _networkLogs = [];\n\n var _origConsole = { log: console.log, warn: console.warn, error: console.error, info: console.info };\n function _wrapConsole(level, orig) {\n return function() {\n var args = Array.prototype.slice.call(arguments);\n var msg = args.map(function(a) {\n try { return typeof a === 'object' ? JSON.stringify(a) : String(a); }\n catch(e) { return String(a); }\n }).join(' ');\n if (_consoleLogs.length >= 50) _consoleLogs.shift();\n _consoleLogs.push({ level: level, message: msg });\n orig.apply(console, arguments);\n };\n }\n console.log = _wrapConsole('log', _origConsole.log);\n console.warn = _wrapConsole('warn', _origConsole.warn);\n console.error = _wrapConsole('error', _origConsole.error);\n console.info = _wrapConsole('info', _origConsole.info);\n\n function _collectError(message, stack) {\n if (!message) return;\n if (message === 'Script error.' || message === 'Script error') message = 'Runtime error';\n if (_extensionErrors.indexOf(message) !== -1) return;\n _extensionErrors.push(message);\n _extensionErrorDetails.push({ message: message, stack: stack || '' });\n var toast = document.getElementById('__extension-error-toast');\n if (!toast) return;\n var msg = document.getElementById('__extension-error-msg');\n if (_extensionErrors.length === 1) {\n msg.textContent = _extensionErrors[0];\n } else {\n msg.textContent = _extensionErrors.length + ' errors — ' + _extensionErrors[_extensionErrors.length - 1];\n }\n toast.style.display = 'block';\n }\n\n window.addEventListener('error', function(event) {\n var msg = event.message || '';\n if (msg.indexOf('Alpine Expression Error') === 0) return;\n var stack = event.error && event.error.stack ? event.error.stack : '';\n _collectError(msg, stack);\n });\n\n window.addEventListener('unhandledrejection', function(event) {\n var msg = event.reason && event.reason.message ? event.reason.message : String(event.reason);\n var stack = event.reason && event.reason.stack ? event.reason.stack : '';\n _collectError(msg, stack);\n });\n </script>\n <!--\n SECURITY: pinned to exact patch versions + SRI integrity hashes. A\n malicious republish of @tailwindcss/browser@4.x or alpinejs@3.x would\n otherwise inject code into every extension. To bump these versions:\n 1. npm view @tailwindcss/browser version (or alpinejs)\n 2. curl -sL https://cdn.jsdelivr.net/npm/@tailwindcss/browser@<v> \\\\\n | openssl dgst -sha384 -binary | openssl base64 -A\n 3. Update the URL + integrity hash below in lockstep.\n -->\n <script\n src=\"https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4.2.4\"\n integrity=\"sha384-yNSZBFvuOWcmww494a9+1zNuvgUGEXoWkein7cxP8wHUTi3iXCU4vJ7hr3tzBCml\"\n crossorigin=\"anonymous\"\n ></script>\n <script\n defer\n src=\"https://cdn.jsdelivr.net/npm/alpinejs@3.15.11/dist/cdn.min.js\"\n integrity=\"sha384-WPtu0YHhJ3arcykfnv1JgUffWDSKRnqnDeTpJUbOc2os2moEmLkIdaeR0trPN4be\"\n crossorigin=\"anonymous\"\n ></script>\n <style>${themeVars}</style>\n <style type=\"text/tailwindcss\">\n @custom-variant dark (&:where(.dark, .dark *));\n @theme {\n --color-border: hsl(var(--border));\n --color-input: hsl(var(--input));\n --color-ring: hsl(var(--ring));\n --color-background: hsl(var(--background));\n --color-foreground: hsl(var(--foreground));\n --color-primary: hsl(var(--primary));\n --color-primary-foreground: hsl(var(--primary-foreground));\n --color-secondary: hsl(var(--secondary));\n --color-secondary-foreground: hsl(var(--secondary-foreground));\n --color-destructive: hsl(var(--destructive));\n --color-destructive-foreground: hsl(var(--destructive-foreground));\n --color-muted: hsl(var(--muted));\n --color-muted-foreground: hsl(var(--muted-foreground));\n --color-accent: hsl(var(--accent));\n --color-accent-foreground: hsl(var(--accent-foreground));\n --color-popover: hsl(var(--popover));\n --color-popover-foreground: hsl(var(--popover-foreground));\n --color-card: hsl(var(--card));\n --color-card-foreground: hsl(var(--card-foreground));\n --color-sidebar: hsl(var(--sidebar-background));\n --color-sidebar-foreground: hsl(var(--sidebar-foreground));\n --color-sidebar-primary: hsl(var(--sidebar-primary));\n --color-sidebar-primary-foreground: hsl(var(--sidebar-primary-foreground));\n --color-sidebar-accent: hsl(var(--sidebar-accent));\n --color-sidebar-accent-foreground: hsl(var(--sidebar-accent-foreground));\n --color-sidebar-border: hsl(var(--sidebar-border));\n --color-sidebar-ring: hsl(var(--sidebar-ring));\n --radius-lg: var(--radius);\n --radius-md: calc(var(--radius) - 2px);\n --radius-sm: calc(var(--radius) - 4px);\n }\n </style>\n\t <style>\n\t *, *::before, *::after { border-color: hsl(var(--border)); }\n\t body {\n\t --agent-native-extension-padding: clamp(16px, 2vw, 24px);\n\t /* Legacy alias for pre-rename extension content (do not remove). */\n\t --agent-native-tool-padding: var(--agent-native-extension-padding);\n\t box-sizing: border-box;\n\t font-family: 'Inter', sans-serif;\n\t margin: 0;\n\t min-height: 100vh;\n\t padding: var(--agent-native-extension-padding);\n\t }\n\t body:has(> [data-extension-layout=\"full-bleed\"]),\n\t body:has(> [data-extension-padding=\"none\"]),\n\t body:has(> .agent-native-extension-bleed),\n\t /* Legacy aliases (do not remove). */\n\t body:has(> [data-tool-layout=\"full-bleed\"]),\n\t body:has(> [data-tool-padding=\"none\"]),\n\t body:has(> .agent-native-tool-bleed) {\n\t padding: 0;\n\t }\n\t </style>\n\t <script>\n\t var _extensionRequestSeq = 0;\n\t var _extensionPendingRequests = {};\n\n\t window.addEventListener('message', function(event) {\n\t if (event.source !== window.parent) return;\n\t var message = event.data || {};\n\t if (\n\t message.type !== 'agent-native-extension-response' &&\n\t message.type !== 'agent-native-tool-response'\n\t ) return;\n\t var pending = _extensionPendingRequests[message.requestId];\n\t if (!pending) return;\n\t delete _extensionPendingRequests[message.requestId];\n\t if (message.error) {\n\t pending.reject(new Error(message.error));\n\t } else {\n\t pending.resolve(message.response);\n\t }\n\t });\n\n\t function hostRequest(path, options) {\n\t options = options || {};\n\t return new Promise(function(resolve, reject) {\n\t var requestId = 'extension-req-' + (++_extensionRequestSeq);\n\t _extensionPendingRequests[requestId] = { resolve: resolve, reject: reject };\n\t window.parent.postMessage({\n\t type: 'agent-native-extension-request',\n\t requestId: requestId,\n\t path: path,\n\t options: {\n\t method: options.method || 'GET',\n\t headers: options.headers || {},\n\t body: options.body,\n\t },\n\t }, '*');\n\t setTimeout(function() {\n\t var pending = _extensionPendingRequests[requestId];\n\t if (!pending) return;\n\t delete _extensionPendingRequests[requestId];\n\t pending.reject(new Error('Extension host request timed out'));\n\t }, 30000);\n\t });\n\t }\n\n\t var _origHostRequest = hostRequest;\n\t hostRequest = function(path, options) {\n\t var entry = { path: path, method: (options && options.method) || 'GET' };\n\t return _origHostRequest(path, options).then(function(res) {\n\t entry.ok = res.ok;\n\t entry.status = res.status;\n\t if (!res.ok && res.body) {\n\t try { entry.error = typeof res.body === 'string' ? res.body.slice(0, 200) : JSON.stringify(res.body).slice(0, 200); } catch(e) {}\n\t }\n\t if (_networkLogs.length >= 20) _networkLogs.shift();\n\t _networkLogs.push(entry);\n\t return res;\n\t }, function(err) {\n\t entry.ok = false;\n\t entry.error = err.message;\n\t if (_networkLogs.length >= 20) _networkLogs.shift();\n\t _networkLogs.push(entry);\n\t throw err;\n\t });\n\t };\n\n\t function extensionFetch(url, options) {\n\t var opts = options || {};\n\t return hostRequest('/_agent-native/extensions/proxy', {\n\t method: 'POST',\n\t headers: { 'Content-Type': 'application/json' },\n\t body: JSON.stringify({\n\t url: url,\n method: opts.method || 'GET',\n headers: opts.headers,\n body: opts.body,\n }),\n\t }).then(function(res) {\n\t var data = res.body;\n\t if (data.error && data.status === undefined) {\n\t throw new Error(data.error);\n\t }\n return {\n ok: data.status >= 200 && data.status < 300,\n status: data.status,\n\t json: function() { return Promise.resolve(data.body); },\n\t text: function() { return Promise.resolve(typeof data.body === 'string' ? data.body : JSON.stringify(data.body)); },\n\t };\n\t });\n\t }\n\n\t async function appAction(name, params) {\n\t params = params || {};\n\t var res = await hostRequest('/_agent-native/actions/' + encodeURIComponent(name), {\n\t method: 'POST',\n\t headers: { 'Content-Type': 'application/json' },\n\t body: JSON.stringify(params),\n\t });\n\t if (!res.ok) {\n\t var err = res.body || { error: res.statusText };\n\t throw new Error(err.error || 'Action failed: ' + res.status);\n\t }\n\t return res.body;\n\t }\n\n\t async function appFetch(path, options) {\n\t options = options || {};\n\t var res = await hostRequest(path, {\n\t ...options,\n\t headers: {\n\t 'Content-Type': 'application/json',\n\t ...(options.headers || {}),\n\t },\n\t });\n\t if (!res.ok) {\n\t var err = typeof res.body === 'object' && res.body ? res.body : { error: res.statusText };\n\t throw new Error(err.error || 'Request failed: ' + res.status);\n\t }\n\t return res.body;\n\t }\n\n async function dbQuery(sql, args) {\n var body = { sql: sql };\n if (args) body.args = args;\n return appFetch('/_agent-native/extensions/sql/query', {\n method: 'POST',\n body: JSON.stringify(body),\n });\n }\n\n async function dbExec(sql, args) {\n var body = { sql: sql };\n if (args) body.args = args;\n return appFetch('/_agent-native/extensions/sql/exec', {\n method: 'POST',\n body: JSON.stringify(body),\n });\n }\n\n var _extensionId = ${extensionIdJson};\n var _extensionBinding = ${bindingJson};\n window.extensionBinding = _extensionBinding;\n // Legacy alias for extension bodies authored before the rename.\n window.toolBinding = _extensionBinding;\n // SECURITY (audit H4): announce the resolved binding to the parent so the\n // host bridge can gate dangerous helpers based on viewer role. Sent\n // BEFORE the user-authored content has a chance to run, so a malicious\n // extension body cannot suppress or rewrite the announcement. The parent\n // ignores subsequent announcements for the same iframe; see\n // ExtensionViewer.tsx / EmbeddedExtension.tsx.\n try {\n window.parent.postMessage(\n {\n type: 'agent-native-extension-binding',\n extensionId: _extensionId,\n binding: _extensionBinding,\n },\n '*',\n );\n } catch (_) {}\n // SECURITY: when the viewer is not the author of this extension, emit a\n // clear console warning. The bridge currently runs every helper with the\n // viewer's session — a malicious shared extension can call any action,\n // read any owned table row in scope, and resolve any user-scope secret.\n // A full consent step is tracked as TODO C1 in audit 05-tools-sandbox.md.\n if (_extensionBinding && !_extensionBinding.isAuthor) {\n try {\n console.warn(\n '[agent-native] Shared extension — running with viewer\\\\'s session. ' +\n 'Author: ' + (_extensionBinding.authorEmail || '<unknown>') + '. ' +\n 'Bridge calls (appAction, dbExec, extensionFetch) execute under ' +\n 'your account; they are gated by your permissions, not the ' +\n 'author\\\\'s. Do not run untrusted shared extensions.',\n );\n } catch (_) {}\n }\n\n var extensionData = {\n\t async list(collection, opts) {\n\t var limit = (opts && opts.limit) || 100;\n\t var scope = (opts && opts.scope) || 'user';\n\t var res = await hostRequest('/_agent-native/extensions/data/' + _extensionId + '/' + encodeURIComponent(collection) + '?limit=' + limit + '&scope=' + scope);\n\t if (!res.ok) throw new Error('Failed to list extension data');\n\t return res.body;\n\t },\n async get(collection, id, opts) {\n var scope = (opts && opts.scope) || 'user';\n var items = await this.list(collection, { scope: scope });\n return (items || []).find(function(item) { return item.id === id; }) || null;\n },\n async set(collection, id, data, opts) {\n\t var scope = (opts && opts.scope) || 'user';\n\t var res = await hostRequest('/_agent-native/extensions/data/' + _extensionId + '/' + encodeURIComponent(collection), {\n\t method: 'POST',\n\t headers: { 'Content-Type': 'application/json' },\n\t body: JSON.stringify({ id: id, data: data, scope: scope }),\n\t });\n\t if (!res.ok) throw new Error('Failed to save extension data');\n\t return res.body;\n\t },\n\t async remove(collection, id, opts) {\n\t var scope = (opts && opts.scope) || 'user';\n\t var res = await hostRequest('/_agent-native/extensions/data/' + _extensionId + '/' + encodeURIComponent(collection) + '/' + encodeURIComponent(id) + '?scope=' + scope, {\n\t method: 'DELETE',\n\t });\n\t if (!res.ok) throw new Error('Failed to delete extension data');\n\t return res.body;\n\t },\n\t };\n\n\t // Legacy aliases — extension bodies authored before the rename use\n\t // toolFetch, toolData, toolId. Keep these working forever.\n\t var toolFetch = extensionFetch;\n\t var toolData = extensionData;\n\t var _toolId = _extensionId;\n\t </script>\n\t <style>\n\t #__extension-error-toast {\n\t display: none;\n\t position: fixed;\n\t bottom: 16px;\n\t right: 16px;\n\t max-width: 420px;\n\t background: hsl(var(--destructive));\n\t color: hsl(var(--destructive-foreground));\n\t border: 1px solid hsl(var(--destructive) / .6);\n\t border-radius: calc(var(--radius, .5rem) + 2px);\n\t padding: 12px 16px;\n\t font-size: 13px;\n\t line-height: 1.4;\n\t font-family: 'Inter', sans-serif;\n\t z-index: 9999;\n\t box-shadow: 0 4px 12px rgba(0,0,0,.15), 0 1px 3px rgba(0,0,0,.1);\n\t animation: __toast-in 0.2s ease-out;\n\t }\n\t @keyframes __toast-in {\n\t from { opacity: 0; transform: translateY(8px); }\n\t to { opacity: 1; transform: translateY(0); }\n\t }\n\t </style>\n\t <script>\n\t // Extension-point slot context: when an extension is rendered embedded\n\t // inside an ExtensionSlot, the host pushes a context object via\n\t // postMessage. Extensions read it synchronously via window.slotContext\n\t // or subscribe to changes via window.onSlotContext(fn). When rendered\n\t // full-page (no ?slot= param), slotContext stays null and extensions\n\t // branch on that.\n\t window.slotContext = null;\n\t var _slotContextSubscribers = [];\n\t window.onSlotContext = function(fn) {\n\t _slotContextSubscribers.push(fn);\n\t if (window.slotContext !== null) {\n\t try { fn(window.slotContext); } catch(_) {}\n\t }\n\t return function() {\n\t _slotContextSubscribers = _slotContextSubscribers.filter(function(f) { return f !== fn; });\n\t };\n\t };\n\t window.addEventListener('message', function(event) {\n\t if (event.source !== window.parent) return;\n\t var msg = event.data;\n\t if (!msg || msg.type !== 'agent-native-slot-context') return;\n\t window.slotContext = msg.context || {};\n\t _slotContextSubscribers.forEach(function(fn) {\n\t try { fn(window.slotContext); } catch(_) {}\n\t });\n\t });\n\n\t // Auto-resize the iframe to its content when running in slot mode. The\n\t // host listens for agent-native-extension-resize and adjusts the iframe height.\n\t if (new URLSearchParams(location.search).get('slot')) {\n\t var _lastH = 0;\n\t var _reportHeight = function() {\n\t var h = Math.max(\n\t document.documentElement.scrollHeight,\n\t document.body ? document.body.scrollHeight : 0,\n\t );\n\t if (h !== _lastH) {\n\t _lastH = h;\n\t window.parent.postMessage({ type: 'agent-native-extension-resize', height: h }, '*');\n\t }\n\t };\n\t if (typeof ResizeObserver !== 'undefined') {\n\t var _ro = new ResizeObserver(_reportHeight);\n\t document.addEventListener('DOMContentLoaded', function() {\n\t _ro.observe(document.documentElement);\n\t if (document.body) _ro.observe(document.body);\n\t });\n\t }\n\t // Initial reports — Alpine takes a tick to render after DOMContentLoaded.\n\t setTimeout(_reportHeight, 50);\n\t setTimeout(_reportHeight, 250);\n\t }\n\n\t window.addEventListener('message', function(event) {\n\t if (event.source !== window.parent) return;\n\t var msg = event.data;\n\t if (!msg || msg.type !== 'agent-native-theme-update') return;\n\t var root = document.documentElement;\n\t if (msg.isDark !== undefined) {\n\t if (msg.isDark) root.classList.add('dark');\n\t else root.classList.remove('dark');\n\t }\n\t var vars = msg.vars || {};\n\t for (var key in vars) {\n\t if (vars.hasOwnProperty(key)) {\n\t root.style.setProperty(key, vars[key]);\n\t }\n\t }\n\t });\n\n\t document.addEventListener('keydown', function(e) {\n\t if ((e.metaKey || e.ctrlKey) && !e.altKey) {\n\t var key = e.key.toLowerCase();\n\t if (key === 'c' || key === 'v' || key === 'x' || key === 'a' || key === 'z' || key === 'y') return;\n\t e.preventDefault();\n\t e.stopPropagation();\n\t window.parent.postMessage({\n\t type: 'agent-native-extension-keydown',\n\t key: e.key, code: e.code,\n\t metaKey: e.metaKey, ctrlKey: e.ctrlKey,\n\t shiftKey: e.shiftKey, altKey: e.altKey,\n\t }, '*');\n\t return;\n\t }\n\t if (e.key === 'Escape') {\n\t window.parent.postMessage({\n\t type: 'agent-native-extension-keydown',\n\t key: e.key, code: e.code,\n\t metaKey: false, ctrlKey: false,\n\t shiftKey: false, altKey: false,\n\t }, '*');\n\t }\n\t });\n\n\t document.addEventListener('DOMContentLoaded', function() {\n\t var fixBtn = document.getElementById('__extension-error-fix');\n\t if (fixBtn) {\n\t fixBtn.addEventListener('click', function() {\n\t window.parent.postMessage({\n\t type: 'agent-native-extension-error-fix',\n\t errors: _extensionErrors,\n\t errorDetails: _extensionErrorDetails,\n\t consoleLogs: _consoleLogs.slice(-30),\n\t networkLogs: _networkLogs.slice(-15)\n\t }, '*');\n\t document.getElementById('__extension-error-toast').style.display = 'none';\n\t });\n\t }\n\t var dismissBtn = document.getElementById('__extension-error-dismiss');\n\t if (dismissBtn) {\n\t dismissBtn.addEventListener('click', function() {\n\t document.getElementById('__extension-error-toast').style.display = 'none';\n\t });\n\t }\n\t });\n\t </script>\n\t</head>\n\t<body${extensionId ? ` data-extension-id=\"${extensionIdAttr}\" data-tool-id=\"${extensionIdAttr}\"` : \"\"} class=\"bg-background text-foreground\">\n\t${content}\n\t<div id=\"__extension-error-toast\">\n\t <div style=\"display:flex;align-items:flex-start;gap:8px;\">\n\t <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"flex-shrink:0;margin-top:1px;\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"12\"/><line x1=\"12\" y1=\"16\" x2=\"12.01\" y2=\"16\"/></svg>\n\t <span id=\"__extension-error-msg\" style=\"flex:1;overflow:hidden;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;\"></span>\n\t <button id=\"__extension-error-fix\" style=\"cursor:pointer;border:none;background:rgba(255,255,255,.9);color:hsl(0 84.2% 40%);font-size:12px;font-weight:500;padding:4px 12px;border-radius:4px;flex-shrink:0;\">Fix</button>\n\t <button id=\"__extension-error-dismiss\" style=\"cursor:pointer;border:none;background:transparent;color:inherit;font-size:16px;padding:2px 6px;opacity:0.7;flex-shrink:0;\">×</button>\n\t </div>\n\t</div>\n\t</body>\n\t</html>`;\n}\n\nfunction escapeHtmlAttribute(value: string): string {\n return value\n .replace(/&/g, \"&\")\n .replace(/\"/g, \""\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\");\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export declare const
|
|
2
|
-
export declare function
|
|
1
|
+
export declare const MAX_EXTENSION_PROXY_RESPONSE_SIZE: number;
|
|
2
|
+
export declare function normalizeExtensionProxyMethod(value: unknown): string | null;
|
|
3
3
|
export declare function sanitizeOutboundHeaders(value: unknown): Record<string, string>;
|
|
4
4
|
export declare function collectSecretValues(...groups: Array<Array<string> | undefined>): string[];
|
|
5
5
|
export declare function redactSecrets<T>(value: T, secretValues: string[]): T;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy-security.d.ts","sourceRoot":"","sources":["../../src/extensions/proxy-security.ts"],"names":[],"mappings":"AAuBA,eAAO,MAAM,iCAAiC,QAAc,CAAC;AAW7D,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAG3E;AAED,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,OAAO,GACb,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAexB;AAED,wBAAgB,mBAAmB,CACjC,GAAG,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC,GAC1C,MAAM,EAAE,CAQV;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,CAAC,CAiBpE;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,MAAM,CAQzE;AAaD,wBAAsB,yBAAyB,CAC7C,QAAQ,EAAE,QAAQ,EAClB,QAAQ,SAAoC,GAC3C,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAyD7D"}
|
|
@@ -19,7 +19,7 @@ const BLOCKED_OUTBOUND_HEADERS = new Set([
|
|
|
19
19
|
"x-forwarded-host",
|
|
20
20
|
"x-forwarded-proto",
|
|
21
21
|
]);
|
|
22
|
-
export const
|
|
22
|
+
export const MAX_EXTENSION_PROXY_RESPONSE_SIZE = 1024 * 1024;
|
|
23
23
|
const ALLOWED_METHODS = new Set([
|
|
24
24
|
"GET",
|
|
25
25
|
"POST",
|
|
@@ -28,7 +28,7 @@ const ALLOWED_METHODS = new Set([
|
|
|
28
28
|
"DELETE",
|
|
29
29
|
"HEAD",
|
|
30
30
|
]);
|
|
31
|
-
export function
|
|
31
|
+
export function normalizeExtensionProxyMethod(value) {
|
|
32
32
|
const method = String(value || "GET").toUpperCase();
|
|
33
33
|
return ALLOWED_METHODS.has(method) ? method : null;
|
|
34
34
|
}
|
|
@@ -99,7 +99,7 @@ function redactionCandidates(secret) {
|
|
|
99
99
|
catch { }
|
|
100
100
|
return [...candidates].sort((a, b) => b.length - a.length);
|
|
101
101
|
}
|
|
102
|
-
export async function readResponseTextWithLimit(response, maxBytes =
|
|
102
|
+
export async function readResponseTextWithLimit(response, maxBytes = MAX_EXTENSION_PROXY_RESPONSE_SIZE) {
|
|
103
103
|
const contentLength = response.headers.get("content-length");
|
|
104
104
|
if (contentLength && Number(contentLength) > maxBytes) {
|
|
105
105
|
return {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy-security.js","sourceRoot":"","sources":["../../src/extensions/proxy-security.ts"],"names":[],"mappings":"AAAA,MAAM,cAAc,GAAG,+BAA+B,CAAC;AAEvD,MAAM,wBAAwB,GAAG,IAAI,GAAG,CAAC;IACvC,YAAY;IACZ,gBAAgB;IAChB,QAAQ;IACR,WAAW;IACX,MAAM;IACN,YAAY;IACZ,QAAQ;IACR,oBAAoB;IACpB,qBAAqB;IACrB,SAAS;IACT,YAAY;IACZ,IAAI;IACJ,SAAS;IACT,mBAAmB;IACnB,SAAS;IACT,iBAAiB;IACjB,kBAAkB;IAClB,mBAAmB;CACpB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,iCAAiC,GAAG,IAAI,GAAG,IAAI,CAAC;AAE7D,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC;IAC9B,KAAK;IACL,MAAM;IACN,KAAK;IACL,OAAO;IACP,QAAQ;IACR,MAAM;CACP,CAAC,CAAC;AAEH,MAAM,UAAU,6BAA6B,CAAC,KAAc;IAC1D,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,IAAI,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;IACpD,OAAO,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,uBAAuB,CACrC,KAAc;IAEd,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAE3E,MAAM,OAAO,GAA2B,EAAE,CAAC;IAC3C,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACrD,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QACjC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,wBAAwB,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YACtE,SAAS;QACX,CAAC;QACD,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,IAAI;YAAE,SAAS;QAC1D,MAAM,WAAW,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;QACrC,IAAI,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC;YAAE,SAAS;QACzC,OAAO,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC;IAC9B,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,GAAG,MAAwC;IAE3C,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;IACjC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,KAAK,MAAM,KAAK,IAAI,KAAK,IAAI,EAAE,EAAE,CAAC;YAChC,IAAI,KAAK;gBAAE,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;IACD,OAAO,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;AACzD,CAAC;AAED,MAAM,UAAU,aAAa,CAAI,KAAQ,EAAE,YAAsB;IAC/D,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC5C,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,YAAY,CAAC,KAAK,EAAE,YAAY,CAAM,CAAC;IAChD,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,YAAY,CAAC,CAAM,CAAC;IACrE,CAAC;IACD,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvC,OAAO,MAAM,CAAC,WAAW,CACvB,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC;YAC1C,GAAG;YACH,aAAa,CAAC,KAAK,EAAE,YAAY,CAAC;SACnC,CAAC,CACE,CAAC;IACT,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,YAAsB;IAC/D,IAAI,GAAG,GAAG,IAAI,CAAC;IACf,KAAK,MAAM,MAAM,IAAI,YAAY,EAAE,CAAC;QAClC,KAAK,MAAM,SAAS,IAAI,mBAAmB,CAAC,MAAM,CAAC,EAAE,CAAC;YACpD,IAAI,SAAS;gBAAE,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,mBAAmB,CAAC,MAAc;IACzC,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;IACrC,IAAI,CAAC;QACH,UAAU,CAAC,GAAG,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;IACV,IAAI,CAAC;QACH,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;IACV,OAAO,CAAC,GAAG,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,QAAkB,EAClB,QAAQ,GAAG,iCAAiC;IAE5C,MAAM,aAAa,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAC7D,IAAI,aAAa,IAAI,MAAM,CAAC,aAAa,CAAC,GAAG,QAAQ,EAAE,CAAC;QACtD,OAAO;YACL,IAAI,EAAE,yBAAyB,aAAa,eAAe,QAAQ,GAAG;YACtE,SAAS,EAAE,IAAI;YACf,IAAI,EAAE,MAAM,CAAC,aAAa,CAAC;SAC5B,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,CAAC;IAC5C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC;QAC5C,IAAI,MAAM,CAAC,UAAU,GAAG,QAAQ,EAAE,CAAC;YACjC,OAAO;gBACL,IAAI,EAAE,yBAAyB,MAAM,CAAC,UAAU,eAAe,QAAQ,GAAG;gBAC1E,SAAS,EAAE,IAAI;gBACf,IAAI,EAAE,MAAM,CAAC,UAAU;aACxB,CAAC;QACJ,CAAC;QACD,OAAO;YACL,IAAI,EAAE,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC;YACtC,SAAS,EAAE,KAAK;YAChB,IAAI,EAAE,MAAM,CAAC,UAAU;SACxB,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAiB,EAAE,CAAC;IAChC,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,OAAO,IAAI,EAAE,CAAC;QACZ,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;QAC5C,IAAI,IAAI;YAAE,MAAM;QAChB,IAAI,CAAC,KAAK;YAAE,SAAS;QACrB,KAAK,IAAI,KAAK,CAAC,UAAU,CAAC;QAC1B,IAAI,KAAK,GAAG,QAAQ,EAAE,CAAC;YACrB,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YACtC,OAAO;gBACL,IAAI,EAAE,yBAAyB,KAAK,eAAe,QAAQ,GAAG;gBAC9D,SAAS,EAAE,IAAI;gBACf,IAAI,EAAE,KAAK;aACZ,CAAC;QACJ,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACrB,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC;IACrC,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,UAAU,CAAC;IAC7B,CAAC;IAED,OAAO;QACL,IAAI,EAAE,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC;QACtC,SAAS,EAAE,KAAK;QAChB,IAAI,EAAE,KAAK;KACZ,CAAC;AACJ,CAAC","sourcesContent":["const HEADER_NAME_RE = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;\n\nconst BLOCKED_OUTBOUND_HEADERS = new Set([\n \"connection\",\n \"content-length\",\n \"cookie\",\n \"forwarded\",\n \"host\",\n \"keep-alive\",\n \"origin\",\n \"proxy-authenticate\",\n \"proxy-authorization\",\n \"referer\",\n \"set-cookie\",\n \"te\",\n \"trailer\",\n \"transfer-encoding\",\n \"upgrade\",\n \"x-forwarded-for\",\n \"x-forwarded-host\",\n \"x-forwarded-proto\",\n]);\n\nexport const MAX_EXTENSION_PROXY_RESPONSE_SIZE = 1024 * 1024;\n\nconst ALLOWED_METHODS = new Set([\n \"GET\",\n \"POST\",\n \"PUT\",\n \"PATCH\",\n \"DELETE\",\n \"HEAD\",\n]);\n\nexport function normalizeExtensionProxyMethod(value: unknown): string | null {\n const method = String(value || \"GET\").toUpperCase();\n return ALLOWED_METHODS.has(method) ? method : null;\n}\n\nexport function sanitizeOutboundHeaders(\n value: unknown,\n): Record<string, string> {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return {};\n\n const headers: Record<string, string> = {};\n for (const [name, rawValue] of Object.entries(value)) {\n const lower = name.toLowerCase();\n if (!HEADER_NAME_RE.test(name) || BLOCKED_OUTBOUND_HEADERS.has(lower)) {\n continue;\n }\n if (rawValue === undefined || rawValue === null) continue;\n const headerValue = String(rawValue);\n if (/[\\r\\n]/.test(headerValue)) continue;\n headers[name] = headerValue;\n }\n return headers;\n}\n\nexport function collectSecretValues(\n ...groups: Array<Array<string> | undefined>\n): string[] {\n const values = new Set<string>();\n for (const group of groups) {\n for (const value of group ?? []) {\n if (value) values.add(value);\n }\n }\n return [...values].sort((a, b) => b.length - a.length);\n}\n\nexport function redactSecrets<T>(value: T, secretValues: string[]): T {\n if (secretValues.length === 0) return value;\n if (typeof value === \"string\") {\n return redactString(value, secretValues) as T;\n }\n if (Array.isArray(value)) {\n return value.map((item) => redactSecrets(item, secretValues)) as T;\n }\n if (value && typeof value === \"object\") {\n return Object.fromEntries(\n Object.entries(value).map(([key, entry]) => [\n key,\n redactSecrets(entry, secretValues),\n ]),\n ) as T;\n }\n return value;\n}\n\nexport function redactString(text: string, secretValues: string[]): string {\n let out = text;\n for (const secret of secretValues) {\n for (const candidate of redactionCandidates(secret)) {\n if (candidate) out = out.split(candidate).join(\"[redacted]\");\n }\n }\n return out;\n}\n\nfunction redactionCandidates(secret: string): string[] {\n const candidates = new Set([secret]);\n try {\n candidates.add(encodeURIComponent(secret));\n } catch {}\n try {\n candidates.add(encodeURI(secret));\n } catch {}\n return [...candidates].sort((a, b) => b.length - a.length);\n}\n\nexport async function readResponseTextWithLimit(\n response: Response,\n maxBytes = MAX_EXTENSION_PROXY_RESPONSE_SIZE,\n): Promise<{ text: string; truncated: boolean; size: number }> {\n const contentLength = response.headers.get(\"content-length\");\n if (contentLength && Number(contentLength) > maxBytes) {\n return {\n text: `(response too large - ${contentLength} bytes, max ${maxBytes})`,\n truncated: true,\n size: Number(contentLength),\n };\n }\n\n const reader = response.body?.getReader?.();\n if (!reader) {\n const buffer = await response.arrayBuffer();\n if (buffer.byteLength > maxBytes) {\n return {\n text: `(response truncated - ${buffer.byteLength} bytes, max ${maxBytes})`,\n truncated: true,\n size: buffer.byteLength,\n };\n }\n return {\n text: new TextDecoder().decode(buffer),\n truncated: false,\n size: buffer.byteLength,\n };\n }\n\n const chunks: Uint8Array[] = [];\n let total = 0;\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n if (!value) continue;\n total += value.byteLength;\n if (total > maxBytes) {\n await reader.cancel().catch(() => {});\n return {\n text: `(response truncated - ${total} bytes, max ${maxBytes})`,\n truncated: true,\n size: total,\n };\n }\n chunks.push(value);\n }\n\n const buffer = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n buffer.set(chunk, offset);\n offset += chunk.byteLength;\n }\n\n return {\n text: new TextDecoder().decode(buffer),\n truncated: false,\n size: total,\n };\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/extensions/routes.ts"],"names":[],"mappings":"AA+CA,wBAAgB,uBAAuB,2FA8BtC"}
|