@agent-native/core 0.84.56 → 0.84.58
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/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +14 -0
- package/corpus/core/docs/content/locales/ar-SA/template-analytics.mdx +2 -0
- package/corpus/core/docs/content/locales/ar-SA/template-clips.mdx +2 -0
- package/corpus/core/docs/content/locales/de-DE/template-analytics.mdx +2 -0
- package/corpus/core/docs/content/locales/de-DE/template-clips.mdx +2 -0
- package/corpus/core/docs/content/locales/es-ES/template-analytics.mdx +2 -0
- package/corpus/core/docs/content/locales/es-ES/template-clips.mdx +2 -0
- package/corpus/core/docs/content/locales/fr-FR/template-analytics.mdx +2 -0
- package/corpus/core/docs/content/locales/fr-FR/template-clips.mdx +2 -0
- package/corpus/core/docs/content/locales/hi-IN/template-analytics.mdx +2 -0
- package/corpus/core/docs/content/locales/hi-IN/template-clips.mdx +2 -0
- package/corpus/core/docs/content/locales/ja-JP/template-analytics.mdx +2 -0
- package/corpus/core/docs/content/locales/ja-JP/template-clips.mdx +2 -0
- package/corpus/core/docs/content/locales/ko-KR/template-analytics.mdx +2 -0
- package/corpus/core/docs/content/locales/ko-KR/template-clips.mdx +2 -0
- package/corpus/core/docs/content/locales/pt-BR/template-analytics.mdx +2 -0
- package/corpus/core/docs/content/locales/pt-BR/template-clips.mdx +2 -0
- package/corpus/core/docs/content/locales/zh-CN/template-analytics.mdx +2 -0
- package/corpus/core/docs/content/locales/zh-CN/template-clips.mdx +2 -0
- package/corpus/core/docs/content/locales/zh-TW/template-analytics.mdx +2 -0
- package/corpus/core/docs/content/locales/zh-TW/template-clips.mdx +2 -0
- package/corpus/core/docs/content/template-analytics.mdx +2 -0
- package/corpus/core/docs/content/template-clips.mdx +22 -10
- package/corpus/core/docs/content/tracking.mdx +2 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/action.ts +11 -0
- package/corpus/core/src/agent/production-agent.ts +24 -46
- package/corpus/core/src/client/AssistantChat.tsx +69 -12
- package/corpus/core/src/client/MultiTabAssistantChat.tsx +1 -1
- package/corpus/core/src/client/conversation/use-near-bottom-autoscroll.ts +45 -2
- package/corpus/core/src/coding-tools/run-code.ts +1 -0
- package/corpus/core/src/integrations/webhook-handler.ts +10 -9
- package/corpus/core/src/server/action-discovery.ts +3 -0
- package/corpus/core/src/server/credential-provider.ts +11 -6
- package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +8 -0
- package/corpus/templates/analytics/AGENTS.md +5 -0
- package/corpus/templates/analytics/actions/create-session-replay-agent-link.ts +31 -0
- package/corpus/templates/analytics/app/i18n/zh-TW.ts +2 -0
- package/corpus/templates/analytics/app/i18n-data.ts +20 -0
- package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +65 -3
- package/corpus/templates/analytics/changelog/2026-07-02-session-replays-can-be-copied-as-temporary-private-links-for-agents.md +6 -0
- package/corpus/templates/analytics/server/handlers/session-replay.ts +78 -0
- package/corpus/templates/analytics/server/lib/agent-chat-plan-mode.ts +29 -0
- package/corpus/templates/analytics/server/lib/session-replay-agent-context.ts +313 -0
- package/corpus/templates/analytics/server/lib/session-replay.ts +198 -3
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +9 -1
- package/corpus/templates/analytics/server/routes/[...page].get.ts +104 -1
- package/corpus/templates/analytics/server/routes/api/session-replay/agent-context.json.get.ts +48 -0
- package/corpus/templates/analytics/server/routes/api/session-replay/agent-events.json.get.ts +63 -0
- package/corpus/templates/analytics/shared/session-replay-agent-access.ts +10 -0
- package/corpus/templates/clips/.agents/skills/video-sharing/SKILL.md +14 -8
- package/corpus/templates/clips/AGENTS.md +3 -2
- package/corpus/templates/clips/actions/create-recording-agent-link.ts +90 -0
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +43 -46
- package/corpus/templates/clips/app/components/sharing/share-ui.tsx +5 -3
- package/corpus/templates/clips/app/i18n/ar-SA.ts +1 -1
- package/corpus/templates/clips/app/i18n/de-DE.ts +1 -1
- package/corpus/templates/clips/app/i18n/en-US.ts +1 -1
- package/corpus/templates/clips/app/i18n/es-ES.ts +1 -1
- package/corpus/templates/clips/app/i18n/fr-FR.ts +1 -1
- package/corpus/templates/clips/app/i18n/hi-IN.ts +1 -1
- package/corpus/templates/clips/app/i18n/ja-JP.ts +1 -1
- package/corpus/templates/clips/app/i18n/ko-KR.ts +1 -1
- package/corpus/templates/clips/app/i18n/pt-BR.ts +1 -1
- package/corpus/templates/clips/app/i18n/zh-CN.ts +1 -1
- package/corpus/templates/clips/app/i18n/zh-TW.ts +1 -1
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +100 -46
- package/corpus/templates/clips/changelog/2026-07-02-private-recordings-can-be-shared-with-agents-through-temporary-links-without-making-them-public.md +6 -0
- package/corpus/templates/clips/server/lib/public-agent-context.ts +26 -13
- package/corpus/templates/clips/server/routes/api/agent-context.json.get.ts +2 -1
- package/corpus/templates/clips/server/routes/api/agent-frame.jpg.get.ts +2 -1
- package/corpus/templates/clips/server/routes/api/agent-transcript.json.get.ts +2 -1
- package/corpus/templates/clips/server/routes/api/public-recording.get.ts +7 -2
- package/corpus/templates/clips/shared/agent-context.ts +5 -0
- package/corpus/templates/design/actions/list-design-extensions.ts +2 -2
- package/corpus/templates/design/app/components/design/CodeWorkbenchHost.tsx +11 -89
- package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +314 -181
- package/corpus/templates/design/app/components/design/DesignImportPanel.tsx +92 -134
- package/corpus/templates/design/app/components/design/EditPanel.tsx +93 -19
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +18 -26
- package/corpus/templates/design/app/components/design/TokensPanel.tsx +220 -192
- package/corpus/templates/design/app/components/design/code-workbench-theme.ts +150 -0
- package/corpus/templates/design/app/i18n/zh-TW.ts +5 -7
- package/corpus/templates/design/app/i18n-data.ts +61 -77
- package/corpus/templates/design/app/lib/design-import.ts +4 -1
- package/corpus/templates/design/app/pages/DesignEditor.tsx +97 -48
- package/corpus/templates/design/changelog/2026-07-02-design-inspector-and-canvas-controls-stay-consistent-when-se.md +6 -0
- package/corpus/templates/design/changelog/2026-07-02-fixed-a-crash-when-opening-the-code-editor.md +6 -0
- package/corpus/templates/design/changelog/2026-07-02-fixed-motion-drawer-open-transition.md +6 -0
- package/corpus/templates/design/changelog/2026-07-02-improved-design-assets-panel.md +6 -0
- package/dist/action.d.ts +8 -0
- package/dist/action.d.ts.map +1 -1
- package/dist/action.js +3 -0
- package/dist/action.js.map +1 -1
- package/dist/agent/production-agent.d.ts +10 -11
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +21 -44
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +1 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +61 -11
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +1 -1
- package/dist/client/MultiTabAssistantChat.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 +43 -2
- package/dist/client/conversation/use-near-bottom-autoscroll.js.map +1 -1
- package/dist/coding-tools/run-code.d.ts.map +1 -1
- package/dist/coding-tools/run-code.js +1 -0
- package/dist/coding-tools/run-code.js.map +1 -1
- package/dist/collab/routes.d.ts +2 -2
- package/dist/integrations/webhook-handler.d.ts +1 -0
- package/dist/integrations/webhook-handler.d.ts.map +1 -1
- package/dist/integrations/webhook-handler.js +9 -9
- package/dist/integrations/webhook-handler.js.map +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +3 -3
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/server/action-discovery.d.ts.map +1 -1
- package/dist/server/action-discovery.js +3 -0
- package/dist/server/action-discovery.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/credential-provider.d.ts +4 -0
- package/dist/server/credential-provider.d.ts.map +1 -1
- package/dist/server/credential-provider.js +13 -7
- package/dist/server/credential-provider.js.map +1 -1
- package/docs/content/locales/ar-SA/template-analytics.mdx +2 -0
- package/docs/content/locales/ar-SA/template-clips.mdx +2 -0
- package/docs/content/locales/de-DE/template-analytics.mdx +2 -0
- package/docs/content/locales/de-DE/template-clips.mdx +2 -0
- package/docs/content/locales/es-ES/template-analytics.mdx +2 -0
- package/docs/content/locales/es-ES/template-clips.mdx +2 -0
- package/docs/content/locales/fr-FR/template-analytics.mdx +2 -0
- package/docs/content/locales/fr-FR/template-clips.mdx +2 -0
- package/docs/content/locales/hi-IN/template-analytics.mdx +2 -0
- package/docs/content/locales/hi-IN/template-clips.mdx +2 -0
- package/docs/content/locales/ja-JP/template-analytics.mdx +2 -0
- package/docs/content/locales/ja-JP/template-clips.mdx +2 -0
- package/docs/content/locales/ko-KR/template-analytics.mdx +2 -0
- package/docs/content/locales/ko-KR/template-clips.mdx +2 -0
- package/docs/content/locales/pt-BR/template-analytics.mdx +2 -0
- package/docs/content/locales/pt-BR/template-clips.mdx +2 -0
- package/docs/content/locales/zh-CN/template-analytics.mdx +2 -0
- package/docs/content/locales/zh-CN/template-clips.mdx +2 -0
- package/docs/content/locales/zh-TW/template-analytics.mdx +2 -0
- package/docs/content/locales/zh-TW/template-clips.mdx +2 -0
- package/docs/content/template-analytics.mdx +2 -0
- package/docs/content/template-clips.mdx +22 -10
- package/docs/content/tracking.mdx +2 -0
- package/package.json +1 -1
|
@@ -403,7 +403,7 @@ const messages = {
|
|
|
403
403
|
agentPrompt:
|
|
404
404
|
"获取这个 Clips 代理上下文 URL:{{agentContextUrl}}。使用 transcript.segments 读取语音上下文,获取 recommendedFrames 或帧 API URL 来查看屏幕,并在 browserDiagnostics 存在时检查经过脱敏的控制台日志和 fetch/XHR 请求元数据。",
|
|
405
405
|
agentTokenDescription:
|
|
406
|
-
"
|
|
406
|
+
"这个临时代理 URL 可让代理读取剪辑,而无需将其设为公开。它会在两小时后过期。",
|
|
407
407
|
gifPreview: "GIF 预览",
|
|
408
408
|
openPlayer: "开放玩家",
|
|
409
409
|
downloadMp4: "下载MP4",
|
|
@@ -403,7 +403,7 @@ const messages = {
|
|
|
403
403
|
agentPrompt:
|
|
404
404
|
"取得這個 Clips Agent 脈絡 URL:{{agentContextUrl}}。使用 transcript.segments 讀取語音脈絡,取得 recommendedFrames 或影格 API URL 來查看螢幕,並在 browserDiagnostics 存在時檢查已遮蔽的主控台記錄和 fetch/XHR 請求中繼資料。",
|
|
405
405
|
agentTokenDescription:
|
|
406
|
-
"
|
|
406
|
+
"這個臨時 Agent URL 可讓 Agent 讀取剪輯,而不必將其設為公開。它會在兩小時後過期。",
|
|
407
407
|
gifPreview: "GIF 預覽",
|
|
408
408
|
openPlayer: "開啟播放器",
|
|
409
409
|
downloadMp4: "下載 MP4",
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
import {
|
|
12
12
|
getRequestUserEmail,
|
|
13
13
|
signShortLivedToken,
|
|
14
|
+
verifyShortLivedToken,
|
|
14
15
|
} from "@agent-native/core/server";
|
|
15
16
|
import { resolveAccess } from "@agent-native/core/sharing";
|
|
16
17
|
import {
|
|
@@ -32,7 +33,12 @@ import {
|
|
|
32
33
|
useState,
|
|
33
34
|
type ReactNode,
|
|
34
35
|
} from "react";
|
|
35
|
-
import
|
|
36
|
+
import {
|
|
37
|
+
data,
|
|
38
|
+
type HeadersArgs,
|
|
39
|
+
type LoaderFunctionArgs,
|
|
40
|
+
type MetaFunction,
|
|
41
|
+
} from "react-router";
|
|
36
42
|
import { useLoaderData, useNavigate, useParams } from "react-router";
|
|
37
43
|
|
|
38
44
|
import { CaptureInstallButton } from "@/components/capture-install-options";
|
|
@@ -65,7 +71,12 @@ import { parsePlaybackSpeed } from "@/lib/playback-speed";
|
|
|
65
71
|
import { isStorageSetupFailureReason } from "@/lib/storage-failures";
|
|
66
72
|
|
|
67
73
|
import { getDb, schema } from "../../server/db";
|
|
68
|
-
import {
|
|
74
|
+
import { CLIPS_AGENT_ACCESS_PARAM } from "../../server/lib/public-agent-context";
|
|
75
|
+
import {
|
|
76
|
+
agentAccessTokenResourceId,
|
|
77
|
+
buildAgentApiUrls,
|
|
78
|
+
safeJsonForHtml,
|
|
79
|
+
} from "../../shared/agent-context";
|
|
69
80
|
import {
|
|
70
81
|
isLoomEmbedBackedRecording,
|
|
71
82
|
isLoomRecordingSource,
|
|
@@ -99,6 +110,34 @@ type SharePageLoaderData = {
|
|
|
99
110
|
shareUrl: string | null;
|
|
100
111
|
};
|
|
101
112
|
|
|
113
|
+
const PRIVATE_AGENT_SHARE_HEADERS = {
|
|
114
|
+
"Cache-Control": "private, max-age=0, no-store",
|
|
115
|
+
"Referrer-Policy": "no-referrer",
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
function emptyLoaderData(url: URL): SharePageLoaderData {
|
|
119
|
+
return {
|
|
120
|
+
recording: null,
|
|
121
|
+
agentContextUrl: null,
|
|
122
|
+
origin: url.origin,
|
|
123
|
+
shareUrl: null,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function shareLoaderData(
|
|
128
|
+
payload: SharePageLoaderData,
|
|
129
|
+
privateAgentAccess = false,
|
|
130
|
+
) {
|
|
131
|
+
if (!privateAgentAccess) return payload;
|
|
132
|
+
return data(payload, {
|
|
133
|
+
headers: PRIVATE_AGENT_SHARE_HEADERS,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function headers({ loaderHeaders }: HeadersArgs) {
|
|
138
|
+
return loaderHeaders;
|
|
139
|
+
}
|
|
140
|
+
|
|
102
141
|
function failureDetail(reason: string | null | undefined): string | null {
|
|
103
142
|
const trimmed = reason?.trim();
|
|
104
143
|
if (!trimmed) return null;
|
|
@@ -132,13 +171,7 @@ function shouldShowGeneratedTitleSkeleton(
|
|
|
132
171
|
|
|
133
172
|
export async function loader({ params, url }: LoaderFunctionArgs) {
|
|
134
173
|
const id = params.shareId;
|
|
135
|
-
if (!id)
|
|
136
|
-
return {
|
|
137
|
-
recording: null,
|
|
138
|
-
agentContextUrl: null,
|
|
139
|
-
origin: url.origin,
|
|
140
|
-
shareUrl: null,
|
|
141
|
-
};
|
|
174
|
+
if (!id) return emptyLoaderData(url);
|
|
142
175
|
|
|
143
176
|
const [rec] = await getDb()
|
|
144
177
|
.select({
|
|
@@ -151,6 +184,7 @@ export async function loader({ params, url }: LoaderFunctionArgs) {
|
|
|
151
184
|
status: schema.recordings.status,
|
|
152
185
|
ownerEmail: schema.recordings.ownerEmail,
|
|
153
186
|
password: schema.recordings.password,
|
|
187
|
+
expiresAt: schema.recordings.expiresAt,
|
|
154
188
|
archivedAt: schema.recordings.archivedAt,
|
|
155
189
|
trashedAt: schema.recordings.trashedAt,
|
|
156
190
|
})
|
|
@@ -158,24 +192,25 @@ export async function loader({ params, url }: LoaderFunctionArgs) {
|
|
|
158
192
|
.where(eq(schema.recordings.id, id))
|
|
159
193
|
.limit(1);
|
|
160
194
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
195
|
+
const agentAccessToken = url.searchParams.get(CLIPS_AGENT_ACCESS_PARAM) ?? "";
|
|
196
|
+
const hasAgentAccessToken = Boolean(agentAccessToken);
|
|
197
|
+
const tokenGrantsAgentAccess = agentAccessToken
|
|
198
|
+
? verifyShortLivedToken(agentAccessToken, agentAccessTokenResourceId(id)).ok
|
|
199
|
+
: false;
|
|
200
|
+
|
|
201
|
+
if (!rec) return shareLoaderData(emptyLoaderData(url), hasAgentAccessToken);
|
|
202
|
+
|
|
203
|
+
if (rec.expiresAt) {
|
|
204
|
+
const expires = new Date(rec.expiresAt).getTime();
|
|
205
|
+
if (Number.isFinite(expires) && expires < Date.now()) {
|
|
206
|
+
return shareLoaderData(emptyLoaderData(url), hasAgentAccessToken);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
168
209
|
|
|
169
|
-
if (rec.visibility !== "public") {
|
|
210
|
+
if (rec.visibility !== "public" && !tokenGrantsAgentAccess) {
|
|
170
211
|
const userEmail = getRequestUserEmail();
|
|
171
212
|
const access = userEmail ? await resolveAccess("recording", id) : null;
|
|
172
|
-
if (!access)
|
|
173
|
-
return {
|
|
174
|
-
recording: null,
|
|
175
|
-
agentContextUrl: null,
|
|
176
|
-
origin: url.origin,
|
|
177
|
-
shareUrl: null,
|
|
178
|
-
};
|
|
213
|
+
if (!access) return emptyLoaderData(url);
|
|
179
214
|
}
|
|
180
215
|
|
|
181
216
|
const recording: SharePageMetaRecording = {
|
|
@@ -190,29 +225,37 @@ export async function loader({ params, url }: LoaderFunctionArgs) {
|
|
|
190
225
|
trashedAt: rec.trashedAt,
|
|
191
226
|
};
|
|
192
227
|
const canExposeAgentContext =
|
|
193
|
-
rec.visibility === "public"
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
228
|
+
(rec.visibility === "public" || tokenGrantsAgentAccess) &&
|
|
229
|
+
!rec.archivedAt &&
|
|
230
|
+
!rec.trashedAt;
|
|
231
|
+
const token = tokenGrantsAgentAccess
|
|
232
|
+
? agentAccessToken
|
|
233
|
+
: canExposeAgentContext &&
|
|
234
|
+
rec.password &&
|
|
235
|
+
getRequestUserEmail() === rec.ownerEmail
|
|
236
|
+
? signShortLivedToken({ resourceId: agentAccessTokenResourceId(id) })
|
|
199
237
|
: undefined;
|
|
200
238
|
const canExposeAnonymousAgentContext = canExposeAgentContext && !rec.password;
|
|
201
239
|
const canExposeOwnerAgentContext = canExposeAgentContext && Boolean(token);
|
|
202
|
-
return
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
240
|
+
return shareLoaderData(
|
|
241
|
+
{
|
|
242
|
+
recording,
|
|
243
|
+
origin: url.origin,
|
|
244
|
+
shareUrl: `${url.origin}${url.pathname}`,
|
|
245
|
+
agentContextUrl:
|
|
246
|
+
canExposeAnonymousAgentContext || canExposeOwnerAgentContext
|
|
247
|
+
? buildAgentApiUrls(id, {
|
|
248
|
+
origin: url.origin,
|
|
249
|
+
basePath:
|
|
250
|
+
process.env.VITE_APP_BASE_PATH ||
|
|
251
|
+
process.env.APP_BASE_PATH ||
|
|
252
|
+
"",
|
|
253
|
+
token,
|
|
254
|
+
}).contextUrl
|
|
255
|
+
: null,
|
|
256
|
+
},
|
|
257
|
+
hasAgentAccessToken || canExposeOwnerAgentContext,
|
|
258
|
+
);
|
|
216
259
|
}
|
|
217
260
|
|
|
218
261
|
export const meta: MetaFunction<typeof loader> = ({ loaderData }) => {
|
|
@@ -273,7 +316,7 @@ function AgentDiscovery({
|
|
|
273
316
|
{t("sharePage.agentReadableContext")}
|
|
274
317
|
</a>
|
|
275
318
|
<script
|
|
276
|
-
type="application/json"
|
|
319
|
+
type="application/agent-native+json"
|
|
277
320
|
id="clips-agent-context"
|
|
278
321
|
dangerouslySetInnerHTML={{ __html: safeJsonForHtml(payload) }}
|
|
279
322
|
/>
|
|
@@ -362,9 +405,17 @@ export default function ShareRoute() {
|
|
|
362
405
|
[],
|
|
363
406
|
);
|
|
364
407
|
const [downloading, setDownloading] = useState(false);
|
|
408
|
+
const agentAccessToken = useMemo(() => {
|
|
409
|
+
if (typeof window === "undefined") return "";
|
|
410
|
+
return (
|
|
411
|
+
new URLSearchParams(window.location.search).get(
|
|
412
|
+
CLIPS_AGENT_ACCESS_PARAM,
|
|
413
|
+
) ?? ""
|
|
414
|
+
);
|
|
415
|
+
}, []);
|
|
365
416
|
|
|
366
417
|
const dataQ = useQuery({
|
|
367
|
-
queryKey: ["public-recording", shareId, password],
|
|
418
|
+
queryKey: ["public-recording", shareId, password, agentAccessToken],
|
|
368
419
|
queryFn: async () => {
|
|
369
420
|
const url = new URL(
|
|
370
421
|
`${appBasePath()}/api/public-recording`,
|
|
@@ -372,6 +423,9 @@ export default function ShareRoute() {
|
|
|
372
423
|
);
|
|
373
424
|
url.searchParams.set("id", shareId ?? "");
|
|
374
425
|
if (password) url.searchParams.set("password", password);
|
|
426
|
+
if (agentAccessToken) {
|
|
427
|
+
url.searchParams.set(CLIPS_AGENT_ACCESS_PARAM, agentAccessToken);
|
|
428
|
+
}
|
|
375
429
|
const res = await fetch(url.toString());
|
|
376
430
|
const data = await res.json().catch(() => ({}));
|
|
377
431
|
return { ok: res.ok, status: res.status, data };
|
|
@@ -9,6 +9,7 @@ import { asc, eq } from "drizzle-orm";
|
|
|
9
9
|
import { getRequestURL, setResponseHeader, type H3Event } from "h3";
|
|
10
10
|
|
|
11
11
|
import {
|
|
12
|
+
agentAccessTokenResourceId,
|
|
12
13
|
buildAgentApiUrls,
|
|
13
14
|
buildRecommendedFrames,
|
|
14
15
|
CLIP_AGENT_CONTEXT_VERSION,
|
|
@@ -53,6 +54,8 @@ export type PublicAgentAccessResult =
|
|
|
53
54
|
| { ok: false; failure: PublicAgentFailure };
|
|
54
55
|
|
|
55
56
|
const DEFAULT_MAX_AGENT_FRAME_MEDIA_BYTES = 200 * 1024 * 1024;
|
|
57
|
+
export const CLIPS_AGENT_ACCESS_PARAM = "agent_access";
|
|
58
|
+
export const CLIPS_AGENT_ACCESS_TTL_SECONDS = 2 * 60 * 60;
|
|
56
59
|
|
|
57
60
|
function sameOwnerEmail(
|
|
58
61
|
a: string | null | undefined,
|
|
@@ -207,8 +210,20 @@ export async function loadPublicAgentAccess(
|
|
|
207
210
|
const viewerIsOwner = Boolean(
|
|
208
211
|
session?.email && sameOwnerEmail(session.email, recording.ownerEmail),
|
|
209
212
|
);
|
|
213
|
+
const suppliedToken = options.token ?? "";
|
|
214
|
+
const tokenAccess = suppliedToken
|
|
215
|
+
? verifyShortLivedToken(
|
|
216
|
+
suppliedToken,
|
|
217
|
+
agentAccessTokenResourceId(recording.id),
|
|
218
|
+
)
|
|
219
|
+
: null;
|
|
220
|
+
const tokenAllowsAgentAccess = Boolean(tokenAccess?.ok);
|
|
210
221
|
|
|
211
|
-
if (
|
|
222
|
+
if (
|
|
223
|
+
recording.visibility !== "public" &&
|
|
224
|
+
!viewerIsOwner &&
|
|
225
|
+
!tokenAllowsAgentAccess
|
|
226
|
+
) {
|
|
212
227
|
return {
|
|
213
228
|
ok: false,
|
|
214
229
|
failure: { status: 404, body: { error: "Not found" } },
|
|
@@ -229,26 +244,22 @@ export async function loadPublicAgentAccess(
|
|
|
229
244
|
}
|
|
230
245
|
|
|
231
246
|
let apiToken: string | null = null;
|
|
247
|
+
if (tokenAllowsAgentAccess) {
|
|
248
|
+
apiToken = suppliedToken;
|
|
249
|
+
}
|
|
232
250
|
if (
|
|
233
251
|
recording.password &&
|
|
234
252
|
recording.visibility === "public" &&
|
|
235
253
|
viewerIsOwner
|
|
236
254
|
) {
|
|
237
|
-
apiToken = signShortLivedToken({
|
|
255
|
+
apiToken = signShortLivedToken({
|
|
256
|
+
resourceId: agentAccessTokenResourceId(recording.id),
|
|
257
|
+
});
|
|
238
258
|
}
|
|
239
259
|
|
|
240
260
|
if (recording.password && !viewerIsOwner) {
|
|
241
|
-
const suppliedToken = options.token ?? "";
|
|
242
261
|
const suppliedPassword = options.password ?? "";
|
|
243
|
-
let allowed =
|
|
244
|
-
|
|
245
|
-
if (suppliedToken) {
|
|
246
|
-
const result = verifyShortLivedToken(suppliedToken, recording.id);
|
|
247
|
-
if (result.ok) {
|
|
248
|
-
allowed = true;
|
|
249
|
-
apiToken = suppliedToken;
|
|
250
|
-
}
|
|
251
|
-
}
|
|
262
|
+
let allowed = tokenAllowsAgentAccess;
|
|
252
263
|
|
|
253
264
|
if (
|
|
254
265
|
!allowed &&
|
|
@@ -256,7 +267,9 @@ export async function loadPublicAgentAccess(
|
|
|
256
267
|
verifySharePassword(suppliedPassword, recording.password)
|
|
257
268
|
) {
|
|
258
269
|
allowed = true;
|
|
259
|
-
apiToken = signShortLivedToken({
|
|
270
|
+
apiToken = signShortLivedToken({
|
|
271
|
+
resourceId: agentAccessTokenResourceId(recording.id),
|
|
272
|
+
});
|
|
260
273
|
}
|
|
261
274
|
|
|
262
275
|
if (!allowed) {
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
loadPublicAgentAccess,
|
|
24
24
|
parseAgentChapters,
|
|
25
25
|
queryString,
|
|
26
|
+
CLIPS_AGENT_ACCESS_PARAM,
|
|
26
27
|
} from "../../lib/public-agent-context.js";
|
|
27
28
|
|
|
28
29
|
export default defineEventHandler(async (event: H3Event) => {
|
|
@@ -32,7 +33,7 @@ export default defineEventHandler(async (event: H3Event) => {
|
|
|
32
33
|
const id = queryString(query.id);
|
|
33
34
|
const accessResult = await loadPublicAgentAccess(event, id, {
|
|
34
35
|
password: queryString(query.password),
|
|
35
|
-
token: queryString(query.t),
|
|
36
|
+
token: queryString(query[CLIPS_AGENT_ACCESS_PARAM]) || queryString(query.t),
|
|
36
37
|
});
|
|
37
38
|
|
|
38
39
|
if (!accessResult.ok) {
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
} from "h3";
|
|
14
14
|
|
|
15
15
|
import {
|
|
16
|
+
CLIPS_AGENT_ACCESS_PARAM,
|
|
16
17
|
loadPublicAgentAccess,
|
|
17
18
|
loadRecordingMediaBytes,
|
|
18
19
|
queryString,
|
|
@@ -95,7 +96,7 @@ export default defineEventHandler(async (event: H3Event) => {
|
|
|
95
96
|
const id = queryString(query.id);
|
|
96
97
|
const accessResult = await loadPublicAgentAccess(event, id, {
|
|
97
98
|
password: queryString(query.password),
|
|
98
|
-
token: queryString(query.t),
|
|
99
|
+
token: queryString(query[CLIPS_AGENT_ACCESS_PARAM]) || queryString(query.t),
|
|
99
100
|
});
|
|
100
101
|
|
|
101
102
|
if (!accessResult.ok) {
|
|
@@ -16,6 +16,7 @@ import { buildAgentApiUrls } from "../../../shared/agent-context.js";
|
|
|
16
16
|
import { isLoomEmbedBackedRecording } from "../../../shared/loom.js";
|
|
17
17
|
import {
|
|
18
18
|
applyAgentJsonHeaders,
|
|
19
|
+
CLIPS_AGENT_ACCESS_PARAM,
|
|
19
20
|
getServerAppBasePath,
|
|
20
21
|
loadAgentTranscript,
|
|
21
22
|
loadPublicAgentAccess,
|
|
@@ -30,7 +31,7 @@ export default defineEventHandler(async (event: H3Event) => {
|
|
|
30
31
|
const id = queryString(query.id);
|
|
31
32
|
const accessResult = await loadPublicAgentAccess(event, id, {
|
|
32
33
|
password: queryString(query.password),
|
|
33
|
-
token: queryString(query.t),
|
|
34
|
+
token: queryString(query[CLIPS_AGENT_ACCESS_PARAM]) || queryString(query.t),
|
|
34
35
|
});
|
|
35
36
|
|
|
36
37
|
if (!accessResult.ok) {
|
|
@@ -26,7 +26,10 @@ import {
|
|
|
26
26
|
type H3Event,
|
|
27
27
|
} from "h3";
|
|
28
28
|
|
|
29
|
-
import {
|
|
29
|
+
import {
|
|
30
|
+
agentAccessTokenResourceId,
|
|
31
|
+
buildAgentApiUrls,
|
|
32
|
+
} from "../../../shared/agent-context.js";
|
|
30
33
|
import {
|
|
31
34
|
normalizeTranscriptSegments,
|
|
32
35
|
parseTranscriptSegments,
|
|
@@ -248,7 +251,9 @@ export default defineEventHandler(async (event) => {
|
|
|
248
251
|
rec.visibility === "public" && !rec.archivedAt && !rec.trashedAt;
|
|
249
252
|
const agentToken =
|
|
250
253
|
canExposeAgentContext && rec.password
|
|
251
|
-
? signShortLivedToken({
|
|
254
|
+
? signShortLivedToken({
|
|
255
|
+
resourceId: agentAccessTokenResourceId(recordingId),
|
|
256
|
+
})
|
|
252
257
|
: undefined;
|
|
253
258
|
const agentContextUrl = canExposeAgentContext
|
|
254
259
|
? buildAgentApiUrls(recordingId, {
|
|
@@ -4,6 +4,11 @@ export const CLIP_AGENT_CONTEXT_VERSION = 1;
|
|
|
4
4
|
export const AGENT_CONTEXT_ENDPOINT = "/api/agent-context.json";
|
|
5
5
|
export const AGENT_TRANSCRIPT_ENDPOINT = "/api/agent-transcript.json";
|
|
6
6
|
export const AGENT_FRAME_ENDPOINT = "/api/agent-frame.jpg";
|
|
7
|
+
export const CLIP_AGENT_ACCESS_TOKEN_PREFIX = "clip-agent-context";
|
|
8
|
+
|
|
9
|
+
export function agentAccessTokenResourceId(recordingId: string): string {
|
|
10
|
+
return `${CLIP_AGENT_ACCESS_TOKEN_PREFIX}:${recordingId}`;
|
|
11
|
+
}
|
|
7
12
|
|
|
8
13
|
export interface AgentApiUrls {
|
|
9
14
|
contextUrl: string;
|
|
@@ -68,7 +68,7 @@ const FIRST_PARTY_EXTENSIONS: FirstPartyDesignExtension[] = [
|
|
|
68
68
|
id: "design.asset-library",
|
|
69
69
|
name: "Asset Library",
|
|
70
70
|
description:
|
|
71
|
-
"Browse and insert Design
|
|
71
|
+
"Browse and insert Design primitives, generated or uploaded media, and rendered Figma library components into the active design screen. " +
|
|
72
72
|
"Selection-aware: inserts near the selected element when one is active.",
|
|
73
73
|
icon: "Photo",
|
|
74
74
|
availability: "available",
|
|
@@ -77,7 +77,7 @@ const FIRST_PARTY_EXTENSIONS: FirstPartyDesignExtension[] = [
|
|
|
77
77
|
capabilities: [
|
|
78
78
|
{
|
|
79
79
|
id: "native",
|
|
80
|
-
label: "Browse Design
|
|
80
|
+
label: "Browse Design assets",
|
|
81
81
|
status: "available",
|
|
82
82
|
reason:
|
|
83
83
|
"Uses list-design-native-assets and insert-design-native-asset for editable HTML primitives/components.",
|
|
@@ -28,10 +28,11 @@ import { Button } from "@/components/ui/button";
|
|
|
28
28
|
import { Spinner } from "@/components/ui/spinner";
|
|
29
29
|
import { cn } from "@/lib/utils";
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
import {
|
|
32
|
+
normalizeMonacoThemeColor,
|
|
33
|
+
readCodeWorkbenchTheme,
|
|
34
|
+
type CodeWorkbenchTheme,
|
|
35
|
+
} from "./code-workbench-theme";
|
|
35
36
|
|
|
36
37
|
interface CodeWorkbenchDraft {
|
|
37
38
|
content: string;
|
|
@@ -56,87 +57,6 @@ interface CodeWorkbenchHostProps {
|
|
|
56
57
|
onActiveFileChange?: (file: CodeWorkbenchActiveFile | null) => void;
|
|
57
58
|
}
|
|
58
59
|
|
|
59
|
-
const WORKBENCH_THEME_VARS: Record<string, string[]> = {
|
|
60
|
-
"--workbench-bg": ["--design-editor-panel-bg", "--background"],
|
|
61
|
-
"--workbench-sidebar-bg": ["--design-editor-panel-bg", "--card"],
|
|
62
|
-
"--workbench-editor-bg": ["--design-editor-panel-bg", "--background"],
|
|
63
|
-
"--workbench-surface-bg": ["--design-editor-control-bg", "--muted"],
|
|
64
|
-
"--workbench-border": ["--design-editor-control-border", "--border"],
|
|
65
|
-
"--workbench-fg": ["--foreground"],
|
|
66
|
-
"--workbench-muted-fg": ["--muted-foreground"],
|
|
67
|
-
"--workbench-hover-bg": ["--design-editor-layer-hover-color", "--accent"],
|
|
68
|
-
"--workbench-active-bg": ["--design-editor-selection-color", "--accent"],
|
|
69
|
-
"--workbench-active-fg": [
|
|
70
|
-
"--design-editor-accent-color",
|
|
71
|
-
"--accent-foreground",
|
|
72
|
-
],
|
|
73
|
-
"--workbench-accent": ["--design-editor-accent-color", "--primary"],
|
|
74
|
-
"--workbench-button-bg": ["--design-editor-control-bg", "--background"],
|
|
75
|
-
"--workbench-button-fg": ["--foreground"],
|
|
76
|
-
"--workbench-selection-bg": ["--design-editor-selection-color", "--accent"],
|
|
77
|
-
"--workbench-dirty": ["--warning", "--destructive"],
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
function normalizeThemeColorValue(value: string): string {
|
|
81
|
-
const trimmed = value.trim();
|
|
82
|
-
if (!trimmed) return "";
|
|
83
|
-
if (/^-?\d+(\.\d+)?\s+-?\d+(\.\d+)?%/.test(trimmed)) {
|
|
84
|
-
return `hsl(${trimmed})`;
|
|
85
|
-
}
|
|
86
|
-
return trimmed;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function resolveCssColorValue(value: string): string {
|
|
90
|
-
if (typeof window === "undefined" || typeof document === "undefined") {
|
|
91
|
-
return value;
|
|
92
|
-
}
|
|
93
|
-
const probe = document.createElement("span");
|
|
94
|
-
probe.style.color = value;
|
|
95
|
-
probe.style.position = "fixed";
|
|
96
|
-
probe.style.pointerEvents = "none";
|
|
97
|
-
probe.style.visibility = "hidden";
|
|
98
|
-
document.body.appendChild(probe);
|
|
99
|
-
const resolved = window.getComputedStyle(probe).color;
|
|
100
|
-
probe.remove();
|
|
101
|
-
return resolved || value;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function readThemeVar(
|
|
105
|
-
elementStyles: CSSStyleDeclaration,
|
|
106
|
-
rootStyles: CSSStyleDeclaration,
|
|
107
|
-
names: string[],
|
|
108
|
-
): string | undefined {
|
|
109
|
-
for (const name of names) {
|
|
110
|
-
const value =
|
|
111
|
-
elementStyles.getPropertyValue(name) || rootStyles.getPropertyValue(name);
|
|
112
|
-
const normalized = normalizeThemeColorValue(value);
|
|
113
|
-
if (normalized) return resolveCssColorValue(normalized);
|
|
114
|
-
}
|
|
115
|
-
return undefined;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function readCodeWorkbenchTheme(
|
|
119
|
-
element: HTMLElement | null,
|
|
120
|
-
): CodeWorkbenchTheme {
|
|
121
|
-
if (typeof window === "undefined" || !element) {
|
|
122
|
-
return { colorScheme: "light", values: {} };
|
|
123
|
-
}
|
|
124
|
-
const elementStyles = window.getComputedStyle(element);
|
|
125
|
-
const rootStyles = window.getComputedStyle(document.documentElement);
|
|
126
|
-
const values: Record<string, string> = {};
|
|
127
|
-
for (const [targetVar, sourceVars] of Object.entries(WORKBENCH_THEME_VARS)) {
|
|
128
|
-
const value = readThemeVar(elementStyles, rootStyles, sourceVars);
|
|
129
|
-
if (value) values[targetVar] = value;
|
|
130
|
-
}
|
|
131
|
-
const colorScheme =
|
|
132
|
-
document.documentElement.classList.contains("dark") ||
|
|
133
|
-
elementStyles.colorScheme.includes("dark") ||
|
|
134
|
-
rootStyles.colorScheme.includes("dark")
|
|
135
|
-
? "dark"
|
|
136
|
-
: "light";
|
|
137
|
-
return { colorScheme, values };
|
|
138
|
-
}
|
|
139
|
-
|
|
140
60
|
let monacoEnvironmentInstalled = false;
|
|
141
61
|
|
|
142
62
|
function ensureMonacoEnvironment() {
|
|
@@ -210,10 +130,12 @@ function defineMonacoTheme(theme: CodeWorkbenchTheme): string {
|
|
|
210
130
|
"editorWidget.background": values["--workbench-surface-bg"],
|
|
211
131
|
"editorWidget.border": values["--workbench-border"],
|
|
212
132
|
focusBorder: values["--workbench-accent"],
|
|
213
|
-
})
|
|
214
|
-
(
|
|
215
|
-
|
|
216
|
-
|
|
133
|
+
})
|
|
134
|
+
.map(([key, value]) => [key, normalizeMonacoThemeColor(value)] as const)
|
|
135
|
+
.filter(
|
|
136
|
+
(entry): entry is [string, string] =>
|
|
137
|
+
typeof entry[1] === "string" && entry[1].length > 0,
|
|
138
|
+
),
|
|
217
139
|
);
|
|
218
140
|
monaco.editor.defineTheme(name, {
|
|
219
141
|
base: dark ? "vs-dark" : "vs",
|