@agent-native/core 0.19.1 → 0.20.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/agent/engine/builder-engine.d.ts.map +1 -1
- package/dist/agent/engine/builder-engine.js +12 -1
- package/dist/agent/engine/builder-engine.js.map +1 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +5 -0
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/thread-data-builder.d.ts +17 -0
- package/dist/agent/thread-data-builder.d.ts.map +1 -1
- package/dist/agent/thread-data-builder.js +210 -0
- package/dist/agent/thread-data-builder.js.map +1 -1
- package/dist/cli/code-agent-executor.d.ts +2 -0
- package/dist/cli/code-agent-executor.d.ts.map +1 -1
- package/dist/cli/code-agent-executor.js +39 -1
- package/dist/cli/code-agent-executor.js.map +1 -1
- package/dist/cli/code-agent-runs.d.ts +3 -0
- package/dist/cli/code-agent-runs.d.ts.map +1 -1
- package/dist/cli/code-agent-runs.js +3 -0
- package/dist/cli/code-agent-runs.js.map +1 -1
- package/dist/cli/connect.d.ts +3 -2
- package/dist/cli/connect.d.ts.map +1 -1
- package/dist/cli/connect.js +12 -8
- package/dist/cli/connect.js.map +1 -1
- package/dist/cli/mcp-config-writers.d.ts +3 -3
- package/dist/cli/mcp-config-writers.d.ts.map +1 -1
- package/dist/cli/mcp-config-writers.js +19 -8
- package/dist/cli/mcp-config-writers.js.map +1 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +8 -3
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +58 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +122 -50
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +172 -13
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/agent-sidebar-state.d.ts +2 -0
- package/dist/client/agent-sidebar-state.d.ts.map +1 -1
- package/dist/client/agent-sidebar-state.js +32 -0
- package/dist/client/agent-sidebar-state.js.map +1 -1
- package/dist/client/code-agent-chat-adapter.d.ts +81 -0
- package/dist/client/code-agent-chat-adapter.d.ts.map +1 -0
- package/dist/client/code-agent-chat-adapter.js +297 -0
- package/dist/client/code-agent-chat-adapter.js.map +1 -0
- package/dist/client/composer/PromptComposer.d.ts +11 -0
- package/dist/client/composer/PromptComposer.d.ts.map +1 -1
- package/dist/client/composer/PromptComposer.js +7 -3
- package/dist/client/composer/PromptComposer.js.map +1 -1
- package/dist/client/composer/TiptapComposer.d.ts +12 -1
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +16 -7
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/composer/index.d.ts +1 -0
- package/dist/client/composer/index.d.ts.map +1 -1
- package/dist/client/composer/index.js +1 -0
- package/dist/client/composer/index.js.map +1 -1
- package/dist/client/composer/prompt-attachments.d.ts +11 -0
- package/dist/client/composer/prompt-attachments.d.ts.map +1 -0
- package/dist/client/composer/prompt-attachments.js +45 -0
- package/dist/client/composer/prompt-attachments.js.map +1 -0
- package/dist/client/conversation/AgentConversation.d.ts.map +1 -1
- package/dist/client/conversation/AgentConversation.js +124 -1
- package/dist/client/conversation/AgentConversation.js.map +1 -1
- package/dist/client/conversation/code-agent-transcript.d.ts +25 -0
- package/dist/client/conversation/code-agent-transcript.d.ts.map +1 -0
- package/dist/client/conversation/code-agent-transcript.js +200 -0
- package/dist/client/conversation/code-agent-transcript.js.map +1 -0
- package/dist/client/conversation/index.d.ts +2 -1
- package/dist/client/conversation/index.d.ts.map +1 -1
- package/dist/client/conversation/index.js +1 -0
- package/dist/client/conversation/index.js.map +1 -1
- package/dist/client/conversation/types.d.ts +8 -0
- package/dist/client/conversation/types.d.ts.map +1 -1
- package/dist/client/conversation/types.js.map +1 -1
- package/dist/client/conversation/use-near-bottom-autoscroll.d.ts.map +1 -1
- package/dist/client/conversation/use-near-bottom-autoscroll.js +26 -9
- package/dist/client/conversation/use-near-bottom-autoscroll.js.map +1 -1
- package/dist/client/index.d.ts +5 -2
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +5 -2
- package/dist/client/index.js.map +1 -1
- package/dist/client/settings/useBuilderStatus.d.ts +2 -0
- package/dist/client/settings/useBuilderStatus.d.ts.map +1 -1
- package/dist/client/settings/useBuilderStatus.js +21 -3
- package/dist/client/settings/useBuilderStatus.js.map +1 -1
- package/dist/client/settings/useBuilderStatus.spec.js +16 -2
- package/dist/client/settings/useBuilderStatus.spec.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +3 -0
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/client/use-chat-models.d.ts +6 -1
- package/dist/client/use-chat-models.d.ts.map +1 -1
- package/dist/client/use-chat-models.js +7 -3
- package/dist/client/use-chat-models.js.map +1 -1
- package/dist/client/use-chat-models.spec.d.ts +2 -0
- package/dist/client/use-chat-models.spec.d.ts.map +1 -0
- package/dist/client/use-chat-models.spec.js +39 -0
- package/dist/client/use-chat-models.spec.js.map +1 -0
- package/dist/code-agents/background-controller.d.ts.map +1 -1
- package/dist/code-agents/background-controller.js +16 -0
- package/dist/code-agents/background-controller.js.map +1 -1
- package/dist/code-agents/index.d.ts +2 -0
- package/dist/code-agents/index.d.ts.map +1 -1
- package/dist/code-agents/index.js +2 -0
- package/dist/code-agents/index.js.map +1 -1
- package/dist/code-agents/prompt-attachments.d.ts +11 -0
- package/dist/code-agents/prompt-attachments.d.ts.map +1 -0
- package/dist/code-agents/prompt-attachments.js +23 -0
- package/dist/code-agents/prompt-attachments.js.map +1 -0
- package/dist/code-agents/transcript-normalizer.js +14 -5
- package/dist/code-agents/transcript-normalizer.js.map +1 -1
- package/dist/code-agents/transcript-order.d.ts +16 -0
- package/dist/code-agents/transcript-order.d.ts.map +1 -0
- package/dist/code-agents/transcript-order.js +47 -0
- package/dist/code-agents/transcript-order.js.map +1 -0
- package/dist/extensions/routes.d.ts.map +1 -1
- package/dist/extensions/routes.js +18 -14
- package/dist/extensions/routes.js.map +1 -1
- package/dist/mcp/build-server.d.ts +33 -1
- package/dist/mcp/build-server.d.ts.map +1 -1
- package/dist/mcp/build-server.js +39 -12
- package/dist/mcp/build-server.js.map +1 -1
- package/dist/mcp/builtin-tools.d.ts +3 -1
- package/dist/mcp/builtin-tools.d.ts.map +1 -1
- package/dist/mcp/builtin-tools.js +40 -10
- package/dist/mcp/builtin-tools.js.map +1 -1
- package/dist/mcp/connect-route.d.ts.map +1 -1
- package/dist/mcp/connect-route.js +299 -97
- package/dist/mcp/connect-route.js.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +17 -3
- package/dist/mcp/server.js.map +1 -1
- package/dist/secrets/substitution.d.ts +18 -0
- package/dist/secrets/substitution.d.ts.map +1 -1
- package/dist/secrets/substitution.js +74 -0
- package/dist/secrets/substitution.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +33 -0
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/auth.d.ts +8 -0
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +8 -1
- package/dist/server/auth.js.map +1 -1
- package/dist/server/deep-link.d.ts +0 -18
- package/dist/server/deep-link.d.ts.map +1 -1
- package/dist/server/deep-link.js +2 -1
- package/dist/server/deep-link.js.map +1 -1
- package/dist/server/open-route.d.ts.map +1 -1
- package/dist/server/open-route.js +23 -4
- package/dist/server/open-route.js.map +1 -1
- package/dist/shared/agent-sidebar-url.d.ts +6 -0
- package/dist/shared/agent-sidebar-url.d.ts.map +1 -0
- package/dist/shared/agent-sidebar-url.js +37 -0
- package/dist/shared/agent-sidebar-url.js.map +1 -0
- package/dist/shared/index.d.ts +1 -0
- package/dist/shared/index.d.ts.map +1 -1
- package/dist/shared/index.js +1 -0
- package/dist/shared/index.js.map +1 -1
- package/dist/styles/agent-conversation.css +403 -0
- package/dist/styles/agent-native.css +2 -0
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +1 -0
- package/dist/vite/client.js.map +1 -1
- package/docs/content/external-agents.md +27 -6
- package/docs/content/mcp-clients.md +2 -0
- package/docs/content/mcp-protocol.md +4 -2
- package/package.json +1 -1
|
@@ -28,11 +28,11 @@
|
|
|
28
28
|
*/
|
|
29
29
|
import { getMethod, getHeader } from "h3";
|
|
30
30
|
import { readBody } from "../server/h3-helpers.js";
|
|
31
|
-
import { getSession, getConfiguredLoginHtml } from "../server/auth.js";
|
|
31
|
+
import { getSession, getConfiguredLoginHtml, isLoopbackRequest, } from "../server/auth.js";
|
|
32
32
|
import { signA2AToken } from "../a2a/client.js";
|
|
33
33
|
import { getOrgDomain } from "../org/context.js";
|
|
34
34
|
import { randomUUID } from "node:crypto";
|
|
35
|
-
import { recordMintedToken, listTokens, revokeToken, createDeviceCode, getDeviceCode, approveDeviceCode, claimDeviceCodeForMint, finishDeviceCodeMint, releaseDeviceCodeMint, expireDeviceCode, MCP_CONNECT_SCOPE, DEFAULT_TOKEN_TTL_DAYS, MIN_TOKEN_TTL_DAYS, MAX_TOKEN_TTL_DAYS, DEVICE_CODE_TTL_MS, } from "./connect-store.js";
|
|
35
|
+
import { recordMintedToken, listTokens, revokeToken, createDeviceCode, getDeviceCode, approveDeviceCode, consumeDeviceCode, claimDeviceCodeForMint, finishDeviceCodeMint, releaseDeviceCodeMint, expireDeviceCode, MCP_CONNECT_SCOPE, DEFAULT_TOKEN_TTL_DAYS, MIN_TOKEN_TTL_DAYS, MAX_TOKEN_TTL_DAYS, DEVICE_CODE_TTL_MS, } from "./connect-store.js";
|
|
36
36
|
/** Device-flow poll interval hint (seconds). */
|
|
37
37
|
const DEVICE_POLL_INTERVAL_S = 3;
|
|
38
38
|
// Human-typable user code: 8 base32 chars, dashed XXXX-XXXX.
|
|
@@ -89,6 +89,17 @@ function appLabel(origin, options) {
|
|
|
89
89
|
function serverName(origin, options) {
|
|
90
90
|
return `agent-native-${appLabel(origin, options)}`;
|
|
91
91
|
}
|
|
92
|
+
function canUseDevOpenConnect(event) {
|
|
93
|
+
// Loopback determined from the real socket peer (isLoopbackRequest →
|
|
94
|
+
// getRequestIP without xForwardedFor), NOT a parsed `Host` header — the
|
|
95
|
+
// header is client-controlled, and it also handles IPv6 `::1`. A
|
|
96
|
+
// misconfigured public deploy with no secret thus can't unlock dev-open
|
|
97
|
+
// by spoofing `Host: localhost`.
|
|
98
|
+
return (isLoopbackRequest(event) &&
|
|
99
|
+
!process.env.A2A_SECRET?.trim() &&
|
|
100
|
+
!process.env.ACCESS_TOKEN?.trim() &&
|
|
101
|
+
!process.env.ACCESS_TOKENS?.trim());
|
|
102
|
+
}
|
|
92
103
|
function escapeHtml(s) {
|
|
93
104
|
return s
|
|
94
105
|
.replace(/&/g, "&")
|
|
@@ -142,17 +153,23 @@ async function mintConnectToken(params) {
|
|
|
142
153
|
});
|
|
143
154
|
return { token, jti };
|
|
144
155
|
}
|
|
145
|
-
function mcpResultPayload(appUrl,
|
|
156
|
+
function mcpResultPayload(appUrl, options, auth) {
|
|
146
157
|
const mcpUrl = `${appUrl}/_agent-native/mcp`;
|
|
147
158
|
const name = serverName(appUrl, options);
|
|
159
|
+
const headers = {};
|
|
160
|
+
if (auth.token)
|
|
161
|
+
headers.Authorization = `Bearer ${auth.token}`;
|
|
162
|
+
if (!auth.token && auth.ownerEmail) {
|
|
163
|
+
headers["X-Agent-Native-Owner-Email"] = auth.ownerEmail;
|
|
164
|
+
}
|
|
148
165
|
return {
|
|
149
|
-
token,
|
|
166
|
+
token: auth.token ?? "",
|
|
150
167
|
mcpUrl,
|
|
151
168
|
serverName: name,
|
|
152
169
|
mcpServerEntry: {
|
|
153
170
|
type: "http",
|
|
154
171
|
url: mcpUrl,
|
|
155
|
-
headers
|
|
172
|
+
...(Object.keys(headers).length ? { headers } : {}),
|
|
156
173
|
},
|
|
157
174
|
cli: `agent-native connect ${appUrl}`,
|
|
158
175
|
};
|
|
@@ -160,11 +177,25 @@ function mcpResultPayload(appUrl, token, options) {
|
|
|
160
177
|
// ---------------------------------------------------------------------------
|
|
161
178
|
// Connect page (server-rendered HTML string)
|
|
162
179
|
// ---------------------------------------------------------------------------
|
|
180
|
+
function agentNativeMarkSvg(className, gradientId) {
|
|
181
|
+
return `<svg class="${className}" width="114" height="66" viewBox="0 0 114 66" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false">
|
|
182
|
+
<path d="M24.5537 65.7695H0L15.0859 39.4619L37.708 0L60.4912 39.4619H39.6396L24.5537 65.7695Z" fill="white"/>
|
|
183
|
+
<path d="M89.446 0H114L76.2921 65.7704H51.7383L89.446 0Z" fill="url(#${gradientId})"/>
|
|
184
|
+
<defs>
|
|
185
|
+
<linearGradient id="${gradientId}" x1="101.702" y1="67.4791" x2="113.672" y2="-37.4275" gradientUnits="userSpaceOnUse">
|
|
186
|
+
<stop stop-color="#00B5FF"/>
|
|
187
|
+
<stop offset="1" stop-color="#48FFE4"/>
|
|
188
|
+
</linearGradient>
|
|
189
|
+
</defs>
|
|
190
|
+
</svg>`;
|
|
191
|
+
}
|
|
163
192
|
function renderConnectPage(params) {
|
|
164
193
|
const { origin, connectBasePath, email, appName, userCode } = params;
|
|
165
194
|
const safeOrigin = escapeHtml(origin);
|
|
166
195
|
const safeEmail = escapeHtml(email);
|
|
167
196
|
const safeApp = escapeHtml(appName);
|
|
197
|
+
const brandMarkSvg = agentNativeMarkSvg("brand-mark", "agent-native-connect-brand-gradient");
|
|
198
|
+
const flowMarkSvg = agentNativeMarkSvg("flow-mark", "agent-native-connect-flow-gradient");
|
|
168
199
|
const safeUserCode = userCode && USER_CODE_RE.test(userCode) ? escapeHtml(userCode) : "";
|
|
169
200
|
return `<!DOCTYPE html>
|
|
170
201
|
<html lang="en">
|
|
@@ -176,80 +207,103 @@ function renderConnectPage(params) {
|
|
|
176
207
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
177
208
|
:root {
|
|
178
209
|
color-scheme: dark;
|
|
179
|
-
--bg: #
|
|
180
|
-
--
|
|
181
|
-
--
|
|
182
|
-
--
|
|
210
|
+
--bg: #09090b; --panel: #121214; --panel-2: #0c0c0e;
|
|
211
|
+
--panel-soft: rgba(255,255,255,0.025);
|
|
212
|
+
--border: rgba(255,255,255,0.075); --border-strong: rgba(255,255,255,0.14);
|
|
213
|
+
--text: #f7f7f8; --muted: #a1a1aa; --subtle: #74747d;
|
|
214
|
+
--accent: #f4f4f5; --accent-fg: #09090b;
|
|
183
215
|
--ring: rgba(250,250,250,0.55);
|
|
184
216
|
--error: #fca5a5; --error-bg: rgba(127,29,29,0.18);
|
|
185
|
-
--ok: #86efac; --ok-bg: rgba(20,83,45,0.
|
|
217
|
+
--ok: #86efac; --ok-bg: rgba(20,83,45,0.12); --ok-border: rgba(134,239,172,0.18);
|
|
186
218
|
}
|
|
187
219
|
html, body { -webkit-font-smoothing: antialiased; }
|
|
188
220
|
body {
|
|
189
221
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
190
|
-
background:
|
|
191
|
-
radial-gradient(900px 540px at 50% -14%, rgba(255,255,255,0.05), transparent 60%),
|
|
192
|
-
linear-gradient(180deg, #121215 0%, var(--bg) 56%);
|
|
222
|
+
background: linear-gradient(180deg, #101013 0%, var(--bg) 58%);
|
|
193
223
|
color: var(--text); display: flex; align-items: center;
|
|
194
224
|
justify-content: center; min-height: 100vh; padding: 1.5rem 1rem;
|
|
195
225
|
}
|
|
196
226
|
.card {
|
|
197
|
-
width: 100%; max-width:
|
|
227
|
+
width: 100%; max-width: 440px;
|
|
198
228
|
background: var(--panel); border: 1px solid var(--border);
|
|
199
|
-
border-radius:
|
|
229
|
+
border-radius: 8px; box-shadow: 0 1px 0 rgba(255,255,255,0.04) inset,
|
|
200
230
|
0 30px 90px rgba(0,0,0,0.5);
|
|
201
|
-
padding:
|
|
231
|
+
padding: 1.25rem;
|
|
232
|
+
}
|
|
233
|
+
.topbar {
|
|
234
|
+
display: flex; align-items: center; justify-content: space-between;
|
|
235
|
+
gap: 0.75rem; margin-bottom: 1.75rem;
|
|
236
|
+
}
|
|
237
|
+
.brand-lockup {
|
|
238
|
+
display: flex; align-items: center; gap: 0.55rem;
|
|
239
|
+
color: var(--muted); font-size: 0.78rem; font-weight: 600;
|
|
240
|
+
}
|
|
241
|
+
.brand-mark { width: 18px; height: auto; display: block; }
|
|
242
|
+
.app-pill {
|
|
243
|
+
max-width: 50%; border: 1px solid var(--border);
|
|
244
|
+
border-radius: 999px; padding: 0.28rem 0.55rem;
|
|
245
|
+
color: var(--subtle); font-size: 0.72rem; line-height: 1;
|
|
246
|
+
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
202
247
|
}
|
|
203
|
-
|
|
204
|
-
.
|
|
248
|
+
.hero { padding: 0 0.75rem; text-align: center; }
|
|
249
|
+
.flow {
|
|
205
250
|
display: flex; align-items: center; justify-content: center;
|
|
206
|
-
gap: 0; margin: 0 auto 1.
|
|
251
|
+
gap: 0; margin: 0 auto 1.1rem; width: fit-content;
|
|
207
252
|
}
|
|
208
|
-
.
|
|
209
|
-
width:
|
|
253
|
+
.flow .tile {
|
|
254
|
+
width: 42px; height: 42px; border-radius: 8px;
|
|
210
255
|
display: flex; align-items: center; justify-content: center;
|
|
211
256
|
background: var(--panel-2); border: 1px solid var(--border-strong);
|
|
212
257
|
color: var(--text); flex-shrink: 0;
|
|
213
258
|
}
|
|
214
|
-
.
|
|
215
|
-
.
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
259
|
+
.flow-mark { width: 26px; height: auto; display: block; }
|
|
260
|
+
.flow .agent-symbol {
|
|
261
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
262
|
+
font-size: 0.95rem; font-weight: 700; letter-spacing: -0.04em;
|
|
263
|
+
}
|
|
264
|
+
.flow .conn {
|
|
265
|
+
width: 30px; height: 1px; flex-shrink: 0;
|
|
266
|
+
background: linear-gradient(90deg, transparent, var(--border-strong), transparent);
|
|
219
267
|
background-position: center;
|
|
220
268
|
}
|
|
221
269
|
.eyebrow {
|
|
222
270
|
text-align: center; font-size: 0.72rem; font-weight: 600;
|
|
223
271
|
letter-spacing: 0.08em; text-transform: uppercase;
|
|
224
|
-
color: var(--subtle); margin-bottom: 0.
|
|
272
|
+
color: var(--subtle); margin-bottom: 0.55rem;
|
|
225
273
|
}
|
|
226
274
|
h1 {
|
|
227
|
-
text-align: center; font-size: 1.
|
|
275
|
+
text-align: center; font-size: 1.45rem; font-weight: 680;
|
|
228
276
|
line-height: 1.25; margin-bottom: 0.55rem;
|
|
229
277
|
letter-spacing: -0.01em;
|
|
230
278
|
}
|
|
231
279
|
.sub {
|
|
232
280
|
text-align: center; color: var(--muted); font-size: 0.9rem;
|
|
233
|
-
line-height: 1.5; margin: 0 auto 0.
|
|
281
|
+
line-height: 1.5; margin: 0 auto 0.9rem; max-width: 36ch;
|
|
234
282
|
}
|
|
235
283
|
.identity {
|
|
236
|
-
|
|
237
|
-
|
|
284
|
+
display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
|
|
285
|
+
gap: 0.25rem 0.45rem; color: var(--subtle); font-size: 0.78rem;
|
|
286
|
+
line-height: 1.35; margin: 0 auto 1.4rem; max-width: 34ch;
|
|
238
287
|
}
|
|
239
288
|
.identity strong { color: var(--muted); font-weight: 600; }
|
|
240
|
-
.
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
.
|
|
289
|
+
.identity .origin { overflow-wrap: anywhere; }
|
|
290
|
+
.device-strip {
|
|
291
|
+
display: flex; align-items: center; justify-content: space-between;
|
|
292
|
+
gap: 0.75rem; border: 1px solid var(--border);
|
|
293
|
+
border-radius: 8px; padding: 0.55rem 0.65rem; margin: 0 0 0.9rem;
|
|
294
|
+
background: var(--panel-soft); color: var(--muted);
|
|
295
|
+
}
|
|
296
|
+
.device-strip .label {
|
|
297
|
+
font-size: 0.76rem; font-weight: 560; color: var(--subtle);
|
|
298
|
+
}
|
|
299
|
+
.device-strip .value {
|
|
300
|
+
font-size: 0.9rem; font-weight: 700;
|
|
248
301
|
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
249
|
-
letter-spacing: 0.
|
|
302
|
+
letter-spacing: 0.08em; color: var(--text);
|
|
303
|
+
}
|
|
250
304
|
button {
|
|
251
305
|
cursor: pointer; font: inherit; font-weight: 600; border: none;
|
|
252
|
-
border-radius:
|
|
306
|
+
border-radius: 8px; padding: 0.78rem 1rem;
|
|
253
307
|
}
|
|
254
308
|
button:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
|
|
255
309
|
.primary {
|
|
@@ -265,7 +319,7 @@ function renderConnectPage(params) {
|
|
|
265
319
|
}
|
|
266
320
|
.ghost:hover:not(:disabled) { color: var(--text); border-color: var(--subtle); }
|
|
267
321
|
pre {
|
|
268
|
-
background: var(--panel-2); border: 1px solid var(--border); border-radius:
|
|
322
|
+
background: var(--panel-2); border: 1px solid var(--border); border-radius: 8px;
|
|
269
323
|
padding: 0.9rem; font-size: 0.78rem; line-height: 1.5; overflow-x: auto;
|
|
270
324
|
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
271
325
|
color: #d4d4d8; margin: 0.5rem 0 1rem;
|
|
@@ -283,9 +337,11 @@ function renderConnectPage(params) {
|
|
|
283
337
|
.advanced > summary:focus-visible { outline: 2px solid var(--ring);
|
|
284
338
|
outline-offset: 2px; border-radius: 6px; }
|
|
285
339
|
.advanced > summary .chev {
|
|
286
|
-
width:
|
|
340
|
+
width: 7px; height: 7px; border-right: 1.5px solid currentColor;
|
|
341
|
+
border-bottom: 1.5px solid currentColor; transform: rotate(45deg);
|
|
342
|
+
transition: transform 0.15s ease; margin-top: -3px;
|
|
287
343
|
}
|
|
288
|
-
.advanced[open] > summary .chev { transform: rotate(
|
|
344
|
+
.advanced[open] > summary .chev { transform: rotate(225deg); margin-top: 2px; }
|
|
289
345
|
.advanced-body {
|
|
290
346
|
padding: 0.85rem 0.1rem 0.25rem;
|
|
291
347
|
}
|
|
@@ -302,56 +358,108 @@ function renderConnectPage(params) {
|
|
|
302
358
|
outline: none; border-color: var(--ring);
|
|
303
359
|
box-shadow: 0 0 0 3px rgba(250,250,250,0.12);
|
|
304
360
|
}
|
|
305
|
-
.
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
.
|
|
310
|
-
|
|
361
|
+
.connections {
|
|
362
|
+
margin-top: 1.1rem; border-top: 1px solid var(--border);
|
|
363
|
+
padding-top: 0.35rem;
|
|
364
|
+
}
|
|
365
|
+
.connections > summary {
|
|
366
|
+
list-style: none; cursor: pointer; user-select: none;
|
|
367
|
+
display: flex; align-items: center; gap: 0.55rem;
|
|
368
|
+
min-height: 2.2rem; color: var(--muted); font-size: 0.82rem;
|
|
369
|
+
}
|
|
370
|
+
.connections > summary::-webkit-details-marker { display: none; }
|
|
371
|
+
.connections > summary:focus-visible {
|
|
372
|
+
outline: 2px solid var(--ring); outline-offset: 2px; border-radius: 6px;
|
|
373
|
+
}
|
|
374
|
+
.connections-title { font-weight: 600; color: var(--muted); }
|
|
375
|
+
.connections-state {
|
|
376
|
+
margin-left: auto; color: var(--subtle); font-size: 0.73rem;
|
|
377
|
+
border: 1px solid var(--border); border-radius: 999px;
|
|
378
|
+
padding: 0.18rem 0.45rem; line-height: 1;
|
|
379
|
+
}
|
|
380
|
+
.connections .chev {
|
|
381
|
+
width: 7px; height: 7px; border-right: 1.5px solid currentColor;
|
|
382
|
+
border-bottom: 1.5px solid currentColor; transform: rotate(45deg);
|
|
383
|
+
transition: transform 0.15s ease; margin: -3px 0 0 0.15rem;
|
|
384
|
+
}
|
|
385
|
+
.connections[open] .chev { transform: rotate(225deg); margin-top: 2px; }
|
|
386
|
+
.token-list { padding-top: 0.4rem; }
|
|
311
387
|
.tok { display: flex; align-items: center; justify-content: space-between;
|
|
312
388
|
gap: 0.75rem; padding: 0.6rem 0; border-bottom: 1px solid var(--border);
|
|
313
389
|
font-size: 0.83rem; }
|
|
314
390
|
.tok:last-child { border-bottom: none; }
|
|
315
391
|
.tok .meta { color: var(--subtle); font-size: 0.74rem; margin-top: 0.1rem; }
|
|
316
392
|
.tok.revoked { opacity: 0.45; }
|
|
317
|
-
.
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
393
|
+
.empty-state {
|
|
394
|
+
color: var(--subtle); font-size: 0.78rem; line-height: 1.45;
|
|
395
|
+
padding: 0.3rem 0 0.45rem;
|
|
396
|
+
}
|
|
397
|
+
.msg { font-size: 0.83rem; padding: 0.7rem 0.8rem; border-radius: 8px;
|
|
398
|
+
margin-bottom: 0.9rem; display: none; line-height: 1.4; }
|
|
399
|
+
.msg.err { display: block; color: var(--error); background: var(--error-bg);
|
|
400
|
+
border: 1px solid rgba(252,165,165,0.16); }
|
|
401
|
+
.msg.ok { display: block; color: var(--ok); background: var(--ok-bg);
|
|
402
|
+
border: 1px solid var(--ok-border); }
|
|
403
|
+
.result-panel { padding-top: 0.15rem; }
|
|
404
|
+
.result-title {
|
|
405
|
+
color: var(--text); font-size: 0.95rem; font-weight: 650;
|
|
406
|
+
text-align: center; margin-bottom: 0.35rem;
|
|
407
|
+
}
|
|
408
|
+
.result-copy {
|
|
409
|
+
color: var(--muted); font-size: 0.83rem; line-height: 1.45;
|
|
410
|
+
text-align: center; margin: 0 auto 0.85rem; max-width: 34ch;
|
|
411
|
+
}
|
|
412
|
+
.section-label {
|
|
413
|
+
color: var(--subtle); font-size: 0.7rem; font-weight: 650;
|
|
414
|
+
letter-spacing: 0.08em; text-transform: uppercase; margin-top: 0.85rem;
|
|
415
|
+
}
|
|
416
|
+
@media (max-width: 480px) {
|
|
417
|
+
body { align-items: flex-start; padding: 0.75rem; }
|
|
418
|
+
.card { padding: 1rem; }
|
|
419
|
+
.hero { padding: 0; }
|
|
420
|
+
.topbar { margin-bottom: 1.35rem; }
|
|
421
|
+
h1 { font-size: 1.3rem; }
|
|
422
|
+
.app-pill { max-width: 46%; }
|
|
423
|
+
pre { font-size: 0.72rem; }
|
|
424
|
+
}
|
|
321
425
|
.hidden { display: none !important; }
|
|
322
426
|
</style>
|
|
323
427
|
</head>
|
|
324
428
|
<body>
|
|
325
429
|
<div class="card">
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
<
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
<rect x="3" y="13" width="8" height="8" rx="2.2" fill="currentColor" opacity="0.55"/>
|
|
333
|
-
<rect x="13" y="13" width="8" height="8" rx="2.2" fill="currentColor"/>
|
|
334
|
-
</svg>
|
|
335
|
-
</span>
|
|
336
|
-
<span class="conn" aria-hidden="true"></span>
|
|
337
|
-
<span class="tile" aria-hidden="true">
|
|
338
|
-
<svg width="22" height="22" viewBox="0 0 24 24" fill="none"
|
|
339
|
-
stroke="currentColor" stroke-width="2" stroke-linecap="round"
|
|
340
|
-
stroke-linejoin="round">
|
|
341
|
-
<path d="M9 8 L5 12 L9 16"/>
|
|
342
|
-
<path d="M15 8 L19 12 L15 16"/>
|
|
343
|
-
</svg>
|
|
344
|
-
</span>
|
|
430
|
+
<div class="topbar">
|
|
431
|
+
<div class="brand-lockup">
|
|
432
|
+
${brandMarkSvg}
|
|
433
|
+
<span>Agent Native</span>
|
|
434
|
+
</div>
|
|
435
|
+
<div class="app-pill" title="${safeApp}">${safeApp}</div>
|
|
345
436
|
</div>
|
|
346
437
|
|
|
347
|
-
<div class="
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
438
|
+
<div class="hero">
|
|
439
|
+
<!-- "Connect an external agent" is kept as the accessible consent label. -->
|
|
440
|
+
<div class="flow" role="img" aria-label="Connect an external agent to ${safeApp}">
|
|
441
|
+
<span class="tile" aria-hidden="true">
|
|
442
|
+
${flowMarkSvg}
|
|
443
|
+
</span>
|
|
444
|
+
<span class="conn" aria-hidden="true"></span>
|
|
445
|
+
<span class="tile" aria-hidden="true">
|
|
446
|
+
<span class="agent-symbol"></></span>
|
|
447
|
+
</span>
|
|
448
|
+
</div>
|
|
449
|
+
|
|
450
|
+
<div class="eyebrow">Connect an external agent</div>
|
|
451
|
+
<h1>${safeUserCode ? `Authorize ${safeApp} from your terminal?` : `Connect ${safeApp} to an agent`}</h1>
|
|
452
|
+
<p class="sub">Allow Claude Code, Codex, or Cowork to use ${safeApp} with your account. You can revoke access anytime.</p>
|
|
453
|
+
<p class="identity">
|
|
454
|
+
<span>Signed in as <strong>${safeEmail}</strong></span>
|
|
455
|
+
<span aria-hidden="true">·</span>
|
|
456
|
+
<span class="origin">${safeOrigin}</span>
|
|
457
|
+
</p>
|
|
458
|
+
</div>
|
|
351
459
|
|
|
352
|
-
<div id="codeCallout" class="
|
|
353
|
-
<
|
|
354
|
-
<
|
|
460
|
+
<div id="codeCallout" class="device-strip ${safeUserCode ? "" : "hidden"}">
|
|
461
|
+
<span class="label">Device code</span>
|
|
462
|
+
<span class="value" id="userCodeValue">${safeUserCode}</span>
|
|
355
463
|
</div>
|
|
356
464
|
|
|
357
465
|
<div id="msg" class="msg"></div>
|
|
@@ -361,9 +469,7 @@ function renderConnectPage(params) {
|
|
|
361
469
|
<details class="advanced">
|
|
362
470
|
<summary>
|
|
363
471
|
Advanced options
|
|
364
|
-
<
|
|
365
|
-
stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
|
|
366
|
-
aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg>
|
|
472
|
+
<span class="chev" aria-hidden="true"></span>
|
|
367
473
|
</summary>
|
|
368
474
|
<div class="advanced-body">
|
|
369
475
|
<div class="field">
|
|
@@ -378,23 +484,38 @@ function renderConnectPage(params) {
|
|
|
378
484
|
</details>
|
|
379
485
|
</div>
|
|
380
486
|
|
|
381
|
-
<div id="result" class="hidden">
|
|
382
|
-
<
|
|
487
|
+
<div id="result" class="result-panel hidden">
|
|
488
|
+
<div class="result-title">Connection token created</div>
|
|
489
|
+
<p class="result-copy" id="resultMsg">Paste this into your agent's MCP config. The token is shown only once.</p>
|
|
490
|
+
<div class="section-label">MCP config</div>
|
|
383
491
|
<pre id="mcpJson"></pre>
|
|
384
|
-
<
|
|
385
|
-
|
|
492
|
+
<details class="advanced">
|
|
493
|
+
<summary>
|
|
494
|
+
Terminal alternative
|
|
495
|
+
<span class="chev" aria-hidden="true"></span>
|
|
496
|
+
</summary>
|
|
497
|
+
<div class="advanced-body">
|
|
498
|
+
<pre id="cliLine"></pre>
|
|
499
|
+
</div>
|
|
500
|
+
</details>
|
|
386
501
|
</div>
|
|
387
502
|
|
|
388
|
-
<
|
|
389
|
-
<
|
|
390
|
-
|
|
391
|
-
|
|
503
|
+
<details id="connections" class="connections">
|
|
504
|
+
<summary>
|
|
505
|
+
<span class="connections-title">Existing connections</span>
|
|
506
|
+
<span id="connectionsState" class="connections-state">Checking</span>
|
|
507
|
+
<span class="chev" aria-hidden="true"></span>
|
|
508
|
+
</summary>
|
|
509
|
+
<div id="tokenList" class="token-list"><div class="empty-state">Checking connections...</div></div>
|
|
510
|
+
</details>
|
|
392
511
|
</div>
|
|
393
512
|
<script>
|
|
394
513
|
(function () {
|
|
395
514
|
var BASE = ${JSON.stringify(joinAppPath(connectBasePath, "/_agent-native/mcp/connect"))};
|
|
396
515
|
var USER_CODE = ${JSON.stringify(safeUserCode || null)};
|
|
397
516
|
var msgEl = document.getElementById("msg");
|
|
517
|
+
var connectionsEl = document.getElementById("connections");
|
|
518
|
+
var connectionsStateEl = document.getElementById("connectionsState");
|
|
398
519
|
function showMsg(text, kind) {
|
|
399
520
|
msgEl.textContent = text;
|
|
400
521
|
msgEl.className = "msg " + (kind || "err");
|
|
@@ -427,10 +548,23 @@ function renderConnectPage(params) {
|
|
|
427
548
|
var listEl = document.getElementById("tokenList");
|
|
428
549
|
try {
|
|
429
550
|
var res = await fetch(BASE + "/tokens", { credentials: "same-origin" });
|
|
430
|
-
if (!res.ok) {
|
|
551
|
+
if (!res.ok) {
|
|
552
|
+
connectionsStateEl.textContent = "Unavailable";
|
|
553
|
+
connectionsEl.open = true;
|
|
554
|
+
listEl.innerHTML = '<div class="empty-state">Could not load connections.</div>';
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
431
557
|
var data = await res.json();
|
|
432
558
|
var tokens = (data && data.tokens) || [];
|
|
433
|
-
if (!tokens.length) {
|
|
559
|
+
if (!tokens.length) {
|
|
560
|
+
connectionsStateEl.textContent = "None";
|
|
561
|
+
connectionsEl.open = false;
|
|
562
|
+
listEl.innerHTML = '<div class="empty-state">Created connections will appear here for revoking later.</div>';
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
var activeCount = tokens.filter(function (t) { return !t.revokedAt; }).length;
|
|
566
|
+
connectionsStateEl.textContent = activeCount === 1 ? "1 active" : activeCount + " active";
|
|
567
|
+
connectionsEl.open = true;
|
|
434
568
|
listEl.innerHTML = "";
|
|
435
569
|
tokens.forEach(function (t) {
|
|
436
570
|
var div = document.createElement("div");
|
|
@@ -460,7 +594,9 @@ function renderConnectPage(params) {
|
|
|
460
594
|
listEl.appendChild(div);
|
|
461
595
|
});
|
|
462
596
|
} catch (e) {
|
|
463
|
-
|
|
597
|
+
connectionsStateEl.textContent = "Unavailable";
|
|
598
|
+
connectionsEl.open = true;
|
|
599
|
+
listEl.innerHTML = '<div class="empty-state">Could not load connections.</div>';
|
|
464
600
|
}
|
|
465
601
|
}
|
|
466
602
|
|
|
@@ -478,9 +614,57 @@ function renderConnectPage(params) {
|
|
|
478
614
|
showMsg((a.data && a.data.error) || "Could not authorize this device code.");
|
|
479
615
|
return;
|
|
480
616
|
}
|
|
481
|
-
showMsg("Device authorized
|
|
617
|
+
showMsg("Device authorized — finishing connection… you can return to your terminal.", "ok");
|
|
482
618
|
btn.classList.add("hidden");
|
|
483
619
|
document.getElementById("mintForm").classList.add("hidden");
|
|
620
|
+
var cc = document.getElementById("codeCallout");
|
|
621
|
+
if (cc) cc.classList.add("hidden");
|
|
622
|
+
// The token is minted a few seconds later, when the CLI next polls
|
|
623
|
+
// /device/poll — so a single loadTokens() here runs BEFORE the row
|
|
624
|
+
// exists and the list would wrongly read "No connections yet" until
|
|
625
|
+
// a manual reload. Snapshot the EXISTING non-revoked token ids first
|
|
626
|
+
// so we announce "Connected" only when THIS device's freshly-minted
|
|
627
|
+
// token appears — a user who already has tokens must not get a false
|
|
628
|
+
// success the instant they authorize.
|
|
629
|
+
var priorIds = {};
|
|
630
|
+
try {
|
|
631
|
+
var pr = await fetch(BASE + "/tokens", { credentials: "same-origin" });
|
|
632
|
+
if (pr.ok) {
|
|
633
|
+
var pd = await pr.json();
|
|
634
|
+
((pd && pd.tokens) || []).forEach(function (t) {
|
|
635
|
+
if (!t.revokedAt) priorIds[t.id] = true;
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
} catch (e) {}
|
|
639
|
+
loadTokens();
|
|
640
|
+
var tries = 0;
|
|
641
|
+
var iv = setInterval(async function () {
|
|
642
|
+
tries++;
|
|
643
|
+
try {
|
|
644
|
+
var res = await fetch(BASE + "/tokens", { credentials: "same-origin" });
|
|
645
|
+
if (res.ok) {
|
|
646
|
+
var data = await res.json();
|
|
647
|
+
var fresh = ((data && data.tokens) || []).filter(function (t) {
|
|
648
|
+
return !t.revokedAt && !priorIds[t.id];
|
|
649
|
+
});
|
|
650
|
+
if (fresh.length > 0) {
|
|
651
|
+
clearInterval(iv);
|
|
652
|
+
showMsg("Connected. This device can now act as you — manage or revoke it below.", "ok");
|
|
653
|
+
loadTokens();
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
} catch (e) {}
|
|
658
|
+
if (tries >= 30) {
|
|
659
|
+
// No new token appeared in the window — e.g. the loopback
|
|
660
|
+
// dev-open path writes a header-only config and never mints.
|
|
661
|
+
// Don't claim "Connected" (we couldn't confirm a device token);
|
|
662
|
+
// keep the "authorized" message and just refresh the list.
|
|
663
|
+
clearInterval(iv);
|
|
664
|
+
loadTokens();
|
|
665
|
+
}
|
|
666
|
+
}, 2000);
|
|
667
|
+
return;
|
|
484
668
|
} else {
|
|
485
669
|
var m = await postJson("/token", { label: label, ttlDays: ttlDays });
|
|
486
670
|
if (!m.ok) {
|
|
@@ -565,6 +749,9 @@ export async function handleMcpConnect(event, subpath, options = {}) {
|
|
|
565
749
|
if (!session?.email)
|
|
566
750
|
return json({ error: "Unauthorized" }, 401);
|
|
567
751
|
if (!process.env.A2A_SECRET) {
|
|
752
|
+
if (canUseDevOpenConnect(event)) {
|
|
753
|
+
return json(mcpResultPayload(appUrl, options, { ownerEmail: session.email }));
|
|
754
|
+
}
|
|
568
755
|
return json({
|
|
569
756
|
error: "This deployment has no A2A_SECRET configured, so connect tokens cannot be minted.",
|
|
570
757
|
}, 503);
|
|
@@ -581,7 +768,7 @@ export async function handleMcpConnect(event, subpath, options = {}) {
|
|
|
581
768
|
label,
|
|
582
769
|
ttlDays,
|
|
583
770
|
});
|
|
584
|
-
return json(mcpResultPayload(appUrl,
|
|
771
|
+
return json(mcpResultPayload(appUrl, options, { token }));
|
|
585
772
|
}
|
|
586
773
|
catch {
|
|
587
774
|
return json({ error: "Failed to mint token." }, 500);
|
|
@@ -663,6 +850,21 @@ export async function handleMcpConnect(event, subpath, options = {}) {
|
|
|
663
850
|
}
|
|
664
851
|
// status === "approved" && ownerEmail bound → mint exactly once.
|
|
665
852
|
if (!process.env.A2A_SECRET) {
|
|
853
|
+
if (canUseDevOpenConnect(event)) {
|
|
854
|
+
const consumed = await consumeDeviceCode(deviceCode, `dev-open-${randomUUID()}`);
|
|
855
|
+
if (!consumed) {
|
|
856
|
+
const fresh = await getDeviceCode(deviceCode);
|
|
857
|
+
if (fresh?.status === "consumed")
|
|
858
|
+
return json({ status: "consumed" });
|
|
859
|
+
return json({ status: "pending" });
|
|
860
|
+
}
|
|
861
|
+
return json({
|
|
862
|
+
status: "approved",
|
|
863
|
+
...mcpResultPayload(appUrl, options, {
|
|
864
|
+
ownerEmail: row.ownerEmail,
|
|
865
|
+
}),
|
|
866
|
+
});
|
|
867
|
+
}
|
|
666
868
|
return json({ status: "error", error: "A2A_SECRET not configured" }, 503);
|
|
667
869
|
}
|
|
668
870
|
try {
|
|
@@ -700,7 +902,7 @@ export async function handleMcpConnect(event, subpath, options = {}) {
|
|
|
700
902
|
}
|
|
701
903
|
return json({
|
|
702
904
|
status: "approved",
|
|
703
|
-
...mcpResultPayload(appUrl,
|
|
905
|
+
...mcpResultPayload(appUrl, options, { token }),
|
|
704
906
|
});
|
|
705
907
|
}
|
|
706
908
|
catch {
|