@agent-native/creative-context 0.6.0 → 0.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/actions/list-context-connections.js +1 -1
  2. package/dist/actions/list-context-connections.js.map +1 -1
  3. package/dist/client/CreativeContextChip.d.ts.map +1 -1
  4. package/dist/client/CreativeContextChip.js +1 -1
  5. package/dist/client/CreativeContextChip.js.map +1 -1
  6. package/dist/client/CreativeContextPanel.js +1 -1
  7. package/dist/client/CreativeContextPanel.js.map +1 -1
  8. package/dist/client/CreativeContextSettingsLink.d.ts.map +1 -1
  9. package/dist/client/CreativeContextSettingsLink.js +1 -1
  10. package/dist/client/CreativeContextSettingsLink.js.map +1 -1
  11. package/dist/connectors/rendered-page.d.ts +7 -11
  12. package/dist/connectors/rendered-page.d.ts.map +1 -1
  13. package/dist/connectors/rendered-page.js +298 -37
  14. package/dist/connectors/rendered-page.js.map +1 -1
  15. package/dist/jobs/server-worker.d.ts.map +1 -1
  16. package/dist/jobs/server-worker.js +9 -1
  17. package/dist/jobs/server-worker.js.map +1 -1
  18. package/dist/server/resource-paths.d.ts +1 -1
  19. package/dist/server/resource-paths.d.ts.map +1 -1
  20. package/dist/server/resource-paths.js +1 -1
  21. package/dist/server/resource-paths.js.map +1 -1
  22. package/package.json +4 -3
  23. package/src/actions/list-context-connections.spec.ts +1 -1
  24. package/src/actions/list-context-connections.ts +1 -1
  25. package/src/client/CreativeContextChip.tsx +1 -1
  26. package/src/client/CreativeContextPanel.tsx +1 -1
  27. package/src/client/CreativeContextSettingsLink.tsx +1 -1
  28. package/src/connectors/rendered-page.spec.ts +287 -1
  29. package/src/connectors/rendered-page.ts +393 -46
  30. package/src/connectors/website.spec.ts +3 -3
  31. package/src/jobs/server-worker.ts +13 -6
  32. package/src/server/resource-paths.spec.ts +1 -1
  33. package/src/server/resource-paths.ts +1 -1
@@ -1,4 +1,4 @@
1
- export const CREATIVE_CONTEXT_LIBRARY_PATH = "/agent#library";
1
+ export const CREATIVE_CONTEXT_LIBRARY_PATH = "/settings/library";
2
2
  export function getCreativeContextResourcePath() {
3
3
  return CREATIVE_CONTEXT_LIBRARY_PATH;
4
4
  }
@@ -1 +1 @@
1
- {"version":3,"file":"resource-paths.js","sourceRoot":"","sources":["../../src/server/resource-paths.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,6BAA6B,GAAG,gBAAgB,CAAC;AAE9D,MAAM,UAAU,8BAA8B;IAC5C,OAAO,6BAA6B,CAAC;AACvC,CAAC"}
1
+ {"version":3,"file":"resource-paths.js","sourceRoot":"","sources":["../../src/server/resource-paths.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,6BAA6B,GAAG,mBAAmB,CAAC;AAEjE,MAAM,UAAU,8BAA8B;IAC5C,OAAO,6BAA6B,CAAC;AACvC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/creative-context",
3
- "version": "0.6.0",
3
+ "version": "0.6.3",
4
4
  "description": "Shared creative context ingestion, versioning, search, brand DNA, and context-pack primitives for agent-native apps.",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {
@@ -57,8 +57,8 @@
57
57
  "react-dom": "^19.2.7",
58
58
  "typescript-7": "npm:typescript@^7.0.2",
59
59
  "vitest": "^4.1.5",
60
- "@agent-native/core": "0.141.7",
61
- "@agent-native/toolkit": "0.13.2"
60
+ "@agent-native/core": "0.146.5",
61
+ "@agent-native/toolkit": "0.13.5"
62
62
  },
63
63
  "peerDependencies": {
64
64
  "@agent-native/core": ">=0.8.0",
@@ -84,6 +84,7 @@
84
84
  }
85
85
  },
86
86
  "optionalDependencies": {
87
+ "@sparticuz/chromium": "149.0.0",
87
88
  "officeparser": "^7.3.0",
88
89
  "playwright": "^1.61.1"
89
90
  },
@@ -17,7 +17,7 @@ describe("creative context connection paths", () => {
17
17
  `/_agent-native/connections/oauth/${provider}/start`,
18
18
  );
19
19
  expect(url.searchParams.get("appId")).toBe(appId);
20
- expect(url.searchParams.get("return")).toBe("/agent#library");
20
+ expect(url.searchParams.get("return")).toBe("/settings/library");
21
21
  },
22
22
  );
23
23
  });
@@ -4,7 +4,7 @@ import { z } from "zod";
4
4
 
5
5
  import { getCreativeContext } from "../server/context.js";
6
6
 
7
- const CREATIVE_CONTEXT_LIBRARY_PATH = "/agent#library";
7
+ const CREATIVE_CONTEXT_LIBRARY_PATH = "/settings/library";
8
8
 
9
9
  export function creativeContextConnectionPath(input: {
10
10
  provider: "google_drive" | "figma" | "notion";
@@ -99,7 +99,7 @@ export function CreativeContextChip({
99
99
  }
100
100
 
101
101
  export function CreativeContextComposerChip({
102
- href = "/agent#library",
102
+ href = "/settings/library",
103
103
  className,
104
104
  }: {
105
105
  href?: string;
@@ -3191,7 +3191,7 @@ export function CreativeContextPanel({
3191
3191
  : t("creativeContext.applyBrandContext")}
3192
3192
  </Button>
3193
3193
  <Button asChild type="button" variant="outline">
3194
- <a href="/agent">
3194
+ <a href="/settings/agent">
3195
3195
  {t("creativeContext.generateWithContext")}
3196
3196
  <IconArrowUpRight />
3197
3197
  </a>
@@ -2,7 +2,7 @@ import { useT } from "@agent-native/core/client/i18n";
2
2
  import { IconArrowUpRight, IconBooks } from "@tabler/icons-react";
3
3
 
4
4
  export function CreativeContextSettingsLink({
5
- href = "/agent#library",
5
+ href = "/settings/library",
6
6
  }: {
7
7
  href?: string;
8
8
  }) {
@@ -1,10 +1,11 @@
1
1
  import { afterEach, describe, expect, it, vi } from "vitest";
2
2
 
3
3
  const isBlockedExtensionUrlWithDns = vi.hoisted(() => vi.fn(async () => false));
4
+ const ssrfSafeFetch = vi.hoisted(() => vi.fn());
4
5
 
5
6
  vi.mock("@agent-native/core/extensions/url-safety", () => ({
6
7
  isBlockedExtensionUrlWithDns,
7
- ssrfSafeFetch: vi.fn(),
8
+ ssrfSafeFetch,
8
9
  }));
9
10
 
10
11
  const { renderWithPlaywright } = await import("./rendered-page.js");
@@ -12,6 +13,7 @@ const { renderWithPlaywright } = await import("./rendered-page.js");
12
13
  describe("renderWithPlaywright lifecycle", () => {
13
14
  afterEach(() => {
14
15
  vi.useRealTimers();
16
+ ssrfSafeFetch.mockReset();
15
17
  });
16
18
 
17
19
  it("closes isolated contexts and reports bounded stabilization failures", async () => {
@@ -91,4 +93,288 @@ describe("renderWithPlaywright lifecycle", () => {
91
93
  );
92
94
  expect(result.diagnostics).toEqual(expect.arrayContaining(result.warnings));
93
95
  });
96
+
97
+ it("hydrates through the SSRF-safe network proxy without forwarding cookies", async () => {
98
+ ssrfSafeFetch
99
+ .mockResolvedValueOnce(
100
+ new Response(
101
+ "<!doctype html><html><head><title>Proxy example</title></head><body>Hydrated CTA</body></html>",
102
+ { status: 200, headers: { "content-type": "text/html" } },
103
+ ),
104
+ )
105
+ .mockResolvedValueOnce(
106
+ new Response(".cta{color:rgb(13 20 27)}", {
107
+ status: 200,
108
+ headers: { "content-type": "text/css" },
109
+ }),
110
+ );
111
+
112
+ let routeHandler:
113
+ | ((route: {
114
+ request: () => {
115
+ url: () => string;
116
+ isNavigationRequest: () => boolean;
117
+ resourceType: () => string;
118
+ method: () => string;
119
+ headers: () => Record<string, string>;
120
+ };
121
+ continue: () => Promise<void>;
122
+ abort: () => Promise<void>;
123
+ fulfill: (options: {
124
+ status: number;
125
+ headers: Record<string, string>;
126
+ body: Uint8Array;
127
+ }) => Promise<void>;
128
+ }) => Promise<void>)
129
+ | undefined;
130
+ const fulfilled: Array<{
131
+ status: number;
132
+ headers: Record<string, string>;
133
+ body: Uint8Array;
134
+ }> = [];
135
+
136
+ const invokeRoute = async (
137
+ url: string,
138
+ navigation: boolean,
139
+ type: string,
140
+ ) => {
141
+ if (!routeHandler) throw new Error("route handler was not installed");
142
+ await routeHandler({
143
+ request: () => ({
144
+ url: () => url,
145
+ isNavigationRequest: () => navigation,
146
+ resourceType: () => type,
147
+ method: () => "GET",
148
+ headers: () => ({
149
+ accept: "text/html",
150
+ cookie: "session=must-not-forward",
151
+ "user-agent": "fixture-browser",
152
+ }),
153
+ }),
154
+ continue: async () => undefined,
155
+ abort: async () => undefined,
156
+ fulfill: async (options) => {
157
+ fulfilled.push(options);
158
+ },
159
+ });
160
+ };
161
+
162
+ let evaluateCalls = 0;
163
+ const page = {
164
+ async route(_pattern: string, handler: (route: never) => Promise<void>) {
165
+ routeHandler = handler as typeof routeHandler;
166
+ },
167
+ async goto() {
168
+ await invokeRoute("https://example.com/", true, "document");
169
+ await invokeRoute(
170
+ "https://example.com/styles.css",
171
+ false,
172
+ "stylesheet",
173
+ );
174
+ },
175
+ async waitForLoadState() {},
176
+ async title() {
177
+ return "Proxy example";
178
+ },
179
+ url() {
180
+ return "https://example.com/";
181
+ },
182
+ locator() {
183
+ return { innerText: async () => "Hydrated CTA" };
184
+ },
185
+ async setViewportSize() {},
186
+ async screenshot() {
187
+ return new Uint8Array([1]);
188
+ },
189
+ async evaluate<T>() {
190
+ evaluateCalls += 1;
191
+ if (evaluateCalls === 3) {
192
+ return {
193
+ title: "Proxy example",
194
+ text: "Hydrated CTA",
195
+ assets: [],
196
+ internalLinks: [],
197
+ designTokens: {
198
+ colors: ["rgb(13 20 27)"],
199
+ typography: [],
200
+ spacing: [],
201
+ radii: [],
202
+ cssVariables: {},
203
+ },
204
+ } as T;
205
+ }
206
+ return undefined as T;
207
+ },
208
+ };
209
+ const context = {
210
+ async newPage() {
211
+ return page;
212
+ },
213
+ async close() {},
214
+ };
215
+ const browser = {
216
+ contexts: () => [],
217
+ async newContext() {
218
+ return context;
219
+ },
220
+ async close() {},
221
+ };
222
+
223
+ const result = await renderWithPlaywright(
224
+ {
225
+ chromium: {
226
+ async launch() {
227
+ return browser;
228
+ },
229
+ async connectOverCDP() {
230
+ return browser;
231
+ },
232
+ },
233
+ } as never,
234
+ { url: "https://example.com/", timeoutMs: 5_000 },
235
+ [],
236
+ "local-playwright",
237
+ );
238
+
239
+ expect(result).toMatchObject({
240
+ rendered: true,
241
+ method: "local-playwright",
242
+ text: "Hydrated CTA",
243
+ });
244
+ expect(fulfilled).toHaveLength(2);
245
+ expect(new TextDecoder().decode(fulfilled[0].body)).toContain(
246
+ "Proxy example",
247
+ );
248
+ expect(ssrfSafeFetch).toHaveBeenCalledTimes(2);
249
+ expect(ssrfSafeFetch.mock.calls[0][1].headers).toEqual({
250
+ accept: "text/html",
251
+ "user-agent": "fixture-browser",
252
+ });
253
+ });
254
+
255
+ it("reserves browser resource slots before overlapping proxy fetches", async () => {
256
+ let releaseFetch!: () => void;
257
+ const fetchGate = new Promise<void>((resolve) => {
258
+ releaseFetch = resolve;
259
+ });
260
+ let fetchCount = 0;
261
+ ssrfSafeFetch.mockImplementation(async () => {
262
+ fetchCount += 1;
263
+ await fetchGate;
264
+ return new Response("resource", { status: 200 });
265
+ });
266
+
267
+ let routeHandler:
268
+ | ((route: {
269
+ request: () => {
270
+ url: () => string;
271
+ isNavigationRequest: () => boolean;
272
+ resourceType: () => string;
273
+ method: () => string;
274
+ headers: () => Record<string, string>;
275
+ };
276
+ continue: () => Promise<void>;
277
+ abort: (reason?: string) => Promise<void>;
278
+ fulfill: (options: {
279
+ status: number;
280
+ headers: Record<string, string>;
281
+ body: Uint8Array;
282
+ }) => Promise<void>;
283
+ }) => Promise<void>)
284
+ | undefined;
285
+ let abortCount = 0;
286
+
287
+ const page = {
288
+ async route(_pattern: string, handler: (route: never) => Promise<void>) {
289
+ routeHandler = handler as typeof routeHandler;
290
+ },
291
+ async goto() {
292
+ if (!routeHandler) throw new Error("route handler was not installed");
293
+ const requests = Array.from({ length: 401 }, (_, index) =>
294
+ routeHandler!({
295
+ request: () => ({
296
+ url: () => `https://example.com/resource-${index}`,
297
+ isNavigationRequest: () => index === 0,
298
+ resourceType: () => "script",
299
+ method: () => "GET",
300
+ headers: () => ({}),
301
+ }),
302
+ continue: async () => undefined,
303
+ abort: async () => {
304
+ abortCount += 1;
305
+ },
306
+ fulfill: async () => undefined,
307
+ }),
308
+ );
309
+ while (fetchCount < 400) await Promise.resolve();
310
+ releaseFetch();
311
+ await Promise.all(requests);
312
+ },
313
+ async waitForLoadState() {},
314
+ async title() {
315
+ return "Example";
316
+ },
317
+ url() {
318
+ return "https://example.com/";
319
+ },
320
+ locator() {
321
+ return { innerText: async () => "Example" };
322
+ },
323
+ async setViewportSize() {},
324
+ async screenshot() {
325
+ return new Uint8Array([1]);
326
+ },
327
+ async evaluate<T>() {
328
+ return {
329
+ title: "Example",
330
+ text: "Example",
331
+ assets: [],
332
+ internalLinks: [],
333
+ designTokens: {
334
+ colors: [],
335
+ typography: [],
336
+ spacing: [],
337
+ radii: [],
338
+ cssVariables: {},
339
+ },
340
+ } as T;
341
+ },
342
+ };
343
+ const context = {
344
+ pages: () => [],
345
+ async newPage() {
346
+ return page;
347
+ },
348
+ async close() {},
349
+ };
350
+ const browser = {
351
+ contexts: () => [],
352
+ async newContext() {
353
+ return context;
354
+ },
355
+ async close() {},
356
+ };
357
+
358
+ const result = await renderWithPlaywright(
359
+ {
360
+ chromium: {
361
+ async launch() {
362
+ return browser;
363
+ },
364
+ async connectOverCDP() {
365
+ return browser;
366
+ },
367
+ },
368
+ } as never,
369
+ { url: "https://example.com/", timeoutMs: 5_000 },
370
+ [],
371
+ "local-playwright",
372
+ );
373
+
374
+ expect(fetchCount).toBe(400);
375
+ expect(abortCount).toBe(1);
376
+ expect(result.warnings).toContain(
377
+ "Browser resource budget reached (400 requests).",
378
+ );
379
+ });
94
380
  });