framer-dalton 0.0.25 → 0.0.26
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/cli.js +242 -12
- package/dist/start-relay-server.js +111 -27
- package/docs/skills/framer-project.md +18 -5
- package/package.json +8 -7
package/dist/cli.js
CHANGED
|
@@ -12,7 +12,7 @@ import net from 'net';
|
|
|
12
12
|
import { fileURLToPath } from 'url';
|
|
13
13
|
import { createTRPCClient, httpLink } from '@trpc/client';
|
|
14
14
|
|
|
15
|
-
/* @framer/ai CLI v0.0.
|
|
15
|
+
/* @framer/ai CLI v0.0.26 */
|
|
16
16
|
var __defProp = Object.defineProperty;
|
|
17
17
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
18
18
|
|
|
@@ -29,6 +29,51 @@ Please upgrade: https://nodejs.org/`
|
|
|
29
29
|
);
|
|
30
30
|
process.exit(1);
|
|
31
31
|
}
|
|
32
|
+
|
|
33
|
+
// src/agent-thread-context.ts
|
|
34
|
+
var MAX_AGENT_THREAD_ID_LENGTH = 256;
|
|
35
|
+
var MAX_AGENT_PROVIDER_LENGTH = 64;
|
|
36
|
+
var SAFE_VALUE_PATTERN = /^[A-Za-z0-9._:-]+$/;
|
|
37
|
+
var providerEnvVars = [
|
|
38
|
+
{ provider: "codex", envVar: "CODEX_THREAD_ID" },
|
|
39
|
+
{ provider: "claude", envVar: "CLAUDE_CODE_SESSION_ID" },
|
|
40
|
+
{ provider: "claude", envVar: "CLAUDE_CODE_REMOTE_SESSION_ID" }
|
|
41
|
+
];
|
|
42
|
+
function normalizeValue(value, maxLength) {
|
|
43
|
+
const trimmed = value?.trim();
|
|
44
|
+
if (!trimmed) return void 0;
|
|
45
|
+
if (trimmed.length > maxLength) return void 0;
|
|
46
|
+
if (!SAFE_VALUE_PATTERN.test(trimmed)) return void 0;
|
|
47
|
+
return trimmed;
|
|
48
|
+
}
|
|
49
|
+
__name(normalizeValue, "normalizeValue");
|
|
50
|
+
function normalizeAgentThreadContext(input) {
|
|
51
|
+
const agentThreadId = normalizeValue(
|
|
52
|
+
input.agentThreadId,
|
|
53
|
+
MAX_AGENT_THREAD_ID_LENGTH
|
|
54
|
+
);
|
|
55
|
+
if (!agentThreadId) return {};
|
|
56
|
+
const agentProvider = normalizeValue(
|
|
57
|
+
input.agentProvider,
|
|
58
|
+
MAX_AGENT_PROVIDER_LENGTH
|
|
59
|
+
);
|
|
60
|
+
return {
|
|
61
|
+
agentThreadId,
|
|
62
|
+
...agentProvider ? { agentProvider } : {}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
__name(normalizeAgentThreadContext, "normalizeAgentThreadContext");
|
|
66
|
+
function resolveAgentThreadContext(env = process.env) {
|
|
67
|
+
for (const { provider, envVar } of providerEnvVars) {
|
|
68
|
+
const agentThreadContext = normalizeAgentThreadContext({
|
|
69
|
+
agentThreadId: env[envVar],
|
|
70
|
+
agentProvider: provider
|
|
71
|
+
});
|
|
72
|
+
if (agentThreadContext.agentThreadId) return agentThreadContext;
|
|
73
|
+
}
|
|
74
|
+
return {};
|
|
75
|
+
}
|
|
76
|
+
__name(resolveAgentThreadContext, "resolveAgentThreadContext");
|
|
32
77
|
function openUrl(url) {
|
|
33
78
|
const platform = process.platform;
|
|
34
79
|
let cmd;
|
|
@@ -330,7 +375,7 @@ __name(markTelemetryNoticeShown, "markTelemetryNoticeShown");
|
|
|
330
375
|
// src/version.ts
|
|
331
376
|
var VERSION = (
|
|
332
377
|
// typeof is used to ensure this can be used just via tsx or node etc. without build
|
|
333
|
-
"0.0.
|
|
378
|
+
"0.0.26"
|
|
334
379
|
);
|
|
335
380
|
var trackingEndpoint = "https://events.framer.com/track";
|
|
336
381
|
var inProgressTrackings = /* @__PURE__ */ new Set();
|
|
@@ -420,6 +465,45 @@ function trackError(payload) {
|
|
|
420
465
|
});
|
|
421
466
|
}
|
|
422
467
|
__name(trackError, "trackError");
|
|
468
|
+
|
|
469
|
+
// src/connection-errors.ts
|
|
470
|
+
var RELAY_CONNECTION_LOST_PATTERNS = [
|
|
471
|
+
"fetch failed",
|
|
472
|
+
// undici top-level: relay gone / socket closed mid-request
|
|
473
|
+
"other side closed",
|
|
474
|
+
// undici SocketError: relay exited mid-request
|
|
475
|
+
"UND_ERR_SOCKET",
|
|
476
|
+
// undici socket error code
|
|
477
|
+
"ECONNREFUSED",
|
|
478
|
+
// relay not listening
|
|
479
|
+
"ECONNRESET",
|
|
480
|
+
// connection reset mid-request
|
|
481
|
+
"socket hang up"
|
|
482
|
+
];
|
|
483
|
+
function isRelayConnectionLost(err) {
|
|
484
|
+
const seen = /* @__PURE__ */ new Set();
|
|
485
|
+
const stack = [err];
|
|
486
|
+
while (stack.length > 0) {
|
|
487
|
+
const node = stack.pop();
|
|
488
|
+
if (node == null || seen.has(node)) continue;
|
|
489
|
+
seen.add(node);
|
|
490
|
+
const e = node;
|
|
491
|
+
const text = `${typeof e.message === "string" ? e.message : ""} ${typeof e.code === "string" ? e.code : ""}`;
|
|
492
|
+
if (RELAY_CONNECTION_LOST_PATTERNS.some((pattern) => text.includes(pattern)))
|
|
493
|
+
return true;
|
|
494
|
+
if (e.cause != null) stack.push(e.cause);
|
|
495
|
+
if (Array.isArray(e.errors)) stack.push(...e.errors);
|
|
496
|
+
}
|
|
497
|
+
return false;
|
|
498
|
+
}
|
|
499
|
+
__name(isRelayConnectionLost, "isRelayConnectionLost");
|
|
500
|
+
var AUTH_ERROR_PATTERNS = ["does not have access", "UNAUTHORIZED"];
|
|
501
|
+
function isAuthError(errorMessage) {
|
|
502
|
+
return AUTH_ERROR_PATTERNS.some((p) => errorMessage.includes(p));
|
|
503
|
+
}
|
|
504
|
+
__name(isAuthError, "isAuthError");
|
|
505
|
+
|
|
506
|
+
// src/utils.ts
|
|
423
507
|
function rehydrateFramerAPIError(error) {
|
|
424
508
|
if (!(error instanceof Error)) return null;
|
|
425
509
|
const framerApi = error.data?.framerApi;
|
|
@@ -448,10 +532,12 @@ function formatError(error) {
|
|
|
448
532
|
return String(error);
|
|
449
533
|
}
|
|
450
534
|
__name(formatError, "formatError");
|
|
451
|
-
function formatErrorForUser(error) {
|
|
535
|
+
function formatErrorForUser(error, opts) {
|
|
452
536
|
if (!(error instanceof Error)) return String(error);
|
|
453
537
|
const rehydrated = rehydrateFramerAPIError(error);
|
|
454
538
|
if (rehydrated) return rehydrated.toString();
|
|
539
|
+
if (!opts?.fromExecResult && isRelayConnectionLost(error))
|
|
540
|
+
return "Connection to the Framer server was lost. Run `framer session new` to reconnect.";
|
|
455
541
|
const ref = getErrorRef(error);
|
|
456
542
|
return ref ? `${error.message} [ref: ${ref}]` : error.message;
|
|
457
543
|
}
|
|
@@ -819,13 +905,6 @@ async function acquireAuthWithRemixProject(sourceProjectUrlOrId) {
|
|
|
819
905
|
}
|
|
820
906
|
__name(acquireAuthWithRemixProject, "acquireAuthWithRemixProject");
|
|
821
907
|
|
|
822
|
-
// src/connection-errors.ts
|
|
823
|
-
var AUTH_ERROR_PATTERNS = ["does not have access", "UNAUTHORIZED"];
|
|
824
|
-
function isAuthError(errorMessage) {
|
|
825
|
-
return AUTH_ERROR_PATTERNS.some((p) => errorMessage.includes(p));
|
|
826
|
-
}
|
|
827
|
-
__name(isAuthError, "isAuthError");
|
|
828
|
-
|
|
829
908
|
// src/closest-match.ts
|
|
830
909
|
function levenshteinDistance(source, target) {
|
|
831
910
|
const sourceLength = source.length;
|
|
@@ -7210,12 +7289,48 @@ var types = {
|
|
|
7210
7289
|
description: "@alpha",
|
|
7211
7290
|
optional: false
|
|
7212
7291
|
},
|
|
7292
|
+
{
|
|
7293
|
+
name: "readComponentControlsForAgent",
|
|
7294
|
+
type: "(input: {\n componentIds: readonly string[];\n }, options?: {\n pagePath?: string;\n }) => Promise<unknown>",
|
|
7295
|
+
description: "@alpha",
|
|
7296
|
+
optional: false
|
|
7297
|
+
},
|
|
7298
|
+
{
|
|
7299
|
+
name: "readIconSetControlsForAgent",
|
|
7300
|
+
type: "(input: {\n iconSetNames: readonly string[];\n }, options?: {\n pagePath?: string;\n }) => Promise<unknown>",
|
|
7301
|
+
description: "@alpha",
|
|
7302
|
+
optional: false
|
|
7303
|
+
},
|
|
7304
|
+
{
|
|
7305
|
+
name: "readIconsForAgent",
|
|
7306
|
+
type: "(input: {\n iconSetName: string;\n }, options?: {\n pagePath?: string;\n }) => Promise<string[]>",
|
|
7307
|
+
description: "@alpha",
|
|
7308
|
+
optional: false
|
|
7309
|
+
},
|
|
7310
|
+
{
|
|
7311
|
+
name: "readLayoutTemplateControlsForAgent",
|
|
7312
|
+
type: "(input: {\n layoutTemplateIds: readonly string[];\n }, options?: {\n pagePath?: string;\n }) => Promise<unknown>",
|
|
7313
|
+
description: "@alpha",
|
|
7314
|
+
optional: false
|
|
7315
|
+
},
|
|
7316
|
+
{
|
|
7317
|
+
name: "readShaderControlsForAgent",
|
|
7318
|
+
type: "(input: {\n shaderNames: readonly string[];\n }, options?: {\n pagePath?: string;\n }) => Promise<unknown>",
|
|
7319
|
+
description: "@alpha",
|
|
7320
|
+
optional: false
|
|
7321
|
+
},
|
|
7213
7322
|
{
|
|
7214
7323
|
name: "applyAgentChanges",
|
|
7215
7324
|
type: "(dsl: string, options?: {\n pagePath?: string;\n }) => Promise<void>",
|
|
7216
7325
|
description: "@alpha",
|
|
7217
7326
|
optional: false
|
|
7218
7327
|
},
|
|
7328
|
+
{
|
|
7329
|
+
name: "ping",
|
|
7330
|
+
type: "() => Promise<void>",
|
|
7331
|
+
description: "@alpha Liveness round-trip; resolves when Vekter's plugin handler answers.",
|
|
7332
|
+
optional: false
|
|
7333
|
+
},
|
|
7219
7334
|
{
|
|
7220
7335
|
name: "publishForAgent",
|
|
7221
7336
|
type: "(input?: Record<string, unknown>) => Promise<unknown>",
|
|
@@ -7825,6 +7940,18 @@ var types = {
|
|
|
7825
7940
|
type: "string",
|
|
7826
7941
|
description: "JSONL string with one training row per inner-agent step, present when `captureTrainingData` was set.",
|
|
7827
7942
|
optional: true
|
|
7943
|
+
},
|
|
7944
|
+
{
|
|
7945
|
+
name: "fixtureFilename",
|
|
7946
|
+
type: "string",
|
|
7947
|
+
description: "Document fixture filename, always present after a completed run.",
|
|
7948
|
+
optional: false
|
|
7949
|
+
},
|
|
7950
|
+
{
|
|
7951
|
+
name: "fixtureContent",
|
|
7952
|
+
type: "string",
|
|
7953
|
+
description: "Document fixture JSON content (tree + code files).",
|
|
7954
|
+
optional: false
|
|
7828
7955
|
}
|
|
7829
7956
|
]
|
|
7830
7957
|
},
|
|
@@ -13224,6 +13351,13 @@ var methodsByCategory = {
|
|
|
13224
13351
|
description: 'Paginate a large array of values across multiple calls. The cursor is\nopaque and only valid within the same headless session and page.\n\n- First page: pass `{ items }`.\n- Continuation: pass `{ keyName, cursor }` using values returned by a previous call.\n\n@param input - `{ items }` for a fresh array, or `{ keyName, cursor }` for continuation.\n@param options.pagePath - Target page path (e.g. `"/about"`). Defaults to the active page.\n@returns The current page, including `keyName`, `cursor`, `results`, `totalResults`, and (if more pages remain) `nextCursor`.',
|
|
13225
13352
|
references: []
|
|
13226
13353
|
},
|
|
13354
|
+
{
|
|
13355
|
+
name: "[$framerApiOnly.ping]",
|
|
13356
|
+
category: "framer",
|
|
13357
|
+
signature: "[$framerApiOnly.ping](): Promise<void>",
|
|
13358
|
+
description: "Liveness round-trip: resolves once Vekter's plugin handler answers. Used by the headless\n backend to verify the transport actually routes, not just that the page is alive.",
|
|
13359
|
+
references: []
|
|
13360
|
+
},
|
|
13227
13361
|
{
|
|
13228
13362
|
name: "[$framerApiOnly.publishForAgent]",
|
|
13229
13363
|
category: "framer",
|
|
@@ -13238,6 +13372,34 @@ var methodsByCategory = {
|
|
|
13238
13372
|
description: 'Searches for stock images to use on the canvas (e.g. `"FrameNode"` `fill` values). Returns\ncandidate images with preview thumbnails and a `url` field for each. The returned URLs are\nregistered as trusted for this session so they can be applied directly via `fill="<url>"`\nin a subsequent {@link applyAgentChanges} DSL command. Without calling this method first,\nexternal image URLs are rejected at apply time.\n\nThe input schema is documented in the string returned by {@link getAgentSystemPrompt}.\n\n@param input - Search input object (source / query / count / orientation).\n@returns An object describing the candidates or an error.',
|
|
13239
13373
|
references: []
|
|
13240
13374
|
},
|
|
13375
|
+
{
|
|
13376
|
+
name: "[$framerApiOnly.readComponentControlsForAgent]",
|
|
13377
|
+
category: "framer",
|
|
13378
|
+
signature: "[$framerApiOnly.readComponentControlsForAgent](input: { componentIds: readonly string[]; }, options?: { pagePath?: string; }): Promise<unknown>",
|
|
13379
|
+
description: "@alpha",
|
|
13380
|
+
references: []
|
|
13381
|
+
},
|
|
13382
|
+
{
|
|
13383
|
+
name: "[$framerApiOnly.readIconSetControlsForAgent]",
|
|
13384
|
+
category: "framer",
|
|
13385
|
+
signature: "[$framerApiOnly.readIconSetControlsForAgent](input: { iconSetNames: readonly string[]; }, options?: { pagePath?: string; }): Promise<unknown>",
|
|
13386
|
+
description: "@alpha",
|
|
13387
|
+
references: []
|
|
13388
|
+
},
|
|
13389
|
+
{
|
|
13390
|
+
name: "[$framerApiOnly.readIconsForAgent]",
|
|
13391
|
+
category: "framer",
|
|
13392
|
+
signature: "[$framerApiOnly.readIconsForAgent](input: { iconSetName: string; }, options?: { pagePath?: string; }): Promise<string[]>",
|
|
13393
|
+
description: "@alpha",
|
|
13394
|
+
references: []
|
|
13395
|
+
},
|
|
13396
|
+
{
|
|
13397
|
+
name: "[$framerApiOnly.readLayoutTemplateControlsForAgent]",
|
|
13398
|
+
category: "framer",
|
|
13399
|
+
signature: "[$framerApiOnly.readLayoutTemplateControlsForAgent](input: { layoutTemplateIds: readonly string[]; }, options?: { pagePath?: string; }): Promise<unknown>",
|
|
13400
|
+
description: "@alpha",
|
|
13401
|
+
references: []
|
|
13402
|
+
},
|
|
13241
13403
|
{
|
|
13242
13404
|
name: "[$framerApiOnly.readProjectForAgent]",
|
|
13243
13405
|
category: "framer",
|
|
@@ -13245,6 +13407,13 @@ var methodsByCategory = {
|
|
|
13245
13407
|
description: 'Reads project state by executing an array of queries against the project.\n\nReturns one result per query. Available query types and their parameters\nare documented in the string returned by {@link getAgentSystemPrompt}.\n\n@param queries - Array of query objects. See {@link getAgentSystemPrompt} for available types.\n@param options.pagePath - Target page path (e.g. `"/about"`). Defaults to the active page.\n@returns An object with a `results` array, one entry per query.',
|
|
13246
13408
|
references: []
|
|
13247
13409
|
},
|
|
13410
|
+
{
|
|
13411
|
+
name: "[$framerApiOnly.readShaderControlsForAgent]",
|
|
13412
|
+
category: "framer",
|
|
13413
|
+
signature: "[$framerApiOnly.readShaderControlsForAgent](input: { shaderNames: readonly string[]; }, options?: { pagePath?: string; }): Promise<unknown>",
|
|
13414
|
+
description: "@alpha",
|
|
13415
|
+
references: []
|
|
13416
|
+
},
|
|
13248
13417
|
{
|
|
13249
13418
|
name: "[$framerApiOnly.reviewChangesForAgent]",
|
|
13250
13419
|
category: "framer",
|
|
@@ -13804,6 +13973,13 @@ var methodsByCategory = {
|
|
|
13804
13973
|
description: 'Paginate a large array of values across multiple calls. The cursor is\nopaque and only valid within the same headless session and page.\n\n- First page: pass `{ items }`.\n- Continuation: pass `{ keyName, cursor }` using values returned by a previous call.\n\n@param input - `{ items }` for a fresh array, or `{ keyName, cursor }` for continuation.\n@param options.pagePath - Target page path (e.g. `"/about"`). Defaults to the active page.\n@returns The current page, including `keyName`, `cursor`, `results`, `totalResults`, and (if more pages remain) `nextCursor`.',
|
|
13805
13974
|
references: []
|
|
13806
13975
|
},
|
|
13976
|
+
{
|
|
13977
|
+
name: "ping",
|
|
13978
|
+
category: "framer",
|
|
13979
|
+
signature: "ping(): Promise<void>",
|
|
13980
|
+
description: "Liveness round-trip: resolves once Vekter's plugin handler answers. Used by the headless\n backend to verify the transport actually routes, not just that the page is alive.",
|
|
13981
|
+
references: []
|
|
13982
|
+
},
|
|
13807
13983
|
{
|
|
13808
13984
|
name: "publish",
|
|
13809
13985
|
category: "framer",
|
|
@@ -13825,6 +14001,34 @@ var methodsByCategory = {
|
|
|
13825
14001
|
description: 'Searches for stock images to use on the canvas (e.g. `"FrameNode"` `fill` values). Returns\ncandidate images with preview thumbnails and a `url` field for each. The returned URLs are\nregistered as trusted for this session so they can be applied directly via `fill="<url>"`\nin a subsequent {@link applyAgentChanges} DSL command. Without calling this method first,\nexternal image URLs are rejected at apply time.\n\nThe input schema is documented in the string returned by {@link getAgentSystemPrompt}.\n\n@param input - Search input object (source / query / count / orientation).\n@returns An object describing the candidates or an error.',
|
|
13826
14002
|
references: []
|
|
13827
14003
|
},
|
|
14004
|
+
{
|
|
14005
|
+
name: "readComponentControlsForAgent",
|
|
14006
|
+
category: "framer",
|
|
14007
|
+
signature: "readComponentControlsForAgent(input: { componentIds: readonly string[]; }, options?: { pagePath?: string; }): Promise<unknown>",
|
|
14008
|
+
description: "@alpha",
|
|
14009
|
+
references: []
|
|
14010
|
+
},
|
|
14011
|
+
{
|
|
14012
|
+
name: "readIconSetControlsForAgent",
|
|
14013
|
+
category: "framer",
|
|
14014
|
+
signature: "readIconSetControlsForAgent(input: { iconSetNames: readonly string[]; }, options?: { pagePath?: string; }): Promise<unknown>",
|
|
14015
|
+
description: "@alpha",
|
|
14016
|
+
references: []
|
|
14017
|
+
},
|
|
14018
|
+
{
|
|
14019
|
+
name: "readIconsForAgent",
|
|
14020
|
+
category: "framer",
|
|
14021
|
+
signature: "readIconsForAgent(input: { iconSetName: string; }, options?: { pagePath?: string; }): Promise<string[]>",
|
|
14022
|
+
description: "@alpha",
|
|
14023
|
+
references: []
|
|
14024
|
+
},
|
|
14025
|
+
{
|
|
14026
|
+
name: "readLayoutTemplateControlsForAgent",
|
|
14027
|
+
category: "framer",
|
|
14028
|
+
signature: "readLayoutTemplateControlsForAgent(input: { layoutTemplateIds: readonly string[]; }, options?: { pagePath?: string; }): Promise<unknown>",
|
|
14029
|
+
description: "@alpha",
|
|
14030
|
+
references: []
|
|
14031
|
+
},
|
|
13828
14032
|
{
|
|
13829
14033
|
name: "readProjectForAgent",
|
|
13830
14034
|
category: "framer",
|
|
@@ -13832,6 +14036,13 @@ var methodsByCategory = {
|
|
|
13832
14036
|
description: 'Reads project state by executing an array of queries against the project.\n\nReturns one result per query. Available query types and their parameters\nare documented in the string returned by {@link getAgentSystemPrompt}.\n\n@param queries - Array of query objects. See {@link getAgentSystemPrompt} for available types.\n@param options.pagePath - Target page path (e.g. `"/about"`). Defaults to the active page.\n@returns An object with a `results` array, one entry per query.',
|
|
13833
14037
|
references: []
|
|
13834
14038
|
},
|
|
14039
|
+
{
|
|
14040
|
+
name: "readShaderControlsForAgent",
|
|
14041
|
+
category: "framer",
|
|
14042
|
+
signature: "readShaderControlsForAgent(input: { shaderNames: readonly string[]; }, options?: { pagePath?: string; }): Promise<unknown>",
|
|
14043
|
+
description: "@alpha",
|
|
14044
|
+
references: []
|
|
14045
|
+
},
|
|
13835
14046
|
{
|
|
13836
14047
|
name: "rejectAllPending",
|
|
13837
14048
|
category: "framer",
|
|
@@ -14024,6 +14235,22 @@ var methodsByCategory = {
|
|
|
14024
14235
|
references: ["NamedImageAssetInput", "ImageAsset"]
|
|
14025
14236
|
}
|
|
14026
14237
|
],
|
|
14238
|
+
framerapierror: [
|
|
14239
|
+
{
|
|
14240
|
+
name: "recentMethods",
|
|
14241
|
+
category: "FramerAPIError",
|
|
14242
|
+
signature: "recentMethods: string[]",
|
|
14243
|
+
description: "@see FramerAPIErrorOptions.recentMethods",
|
|
14244
|
+
references: []
|
|
14245
|
+
},
|
|
14246
|
+
{
|
|
14247
|
+
name: "retryable",
|
|
14248
|
+
category: "FramerAPIError",
|
|
14249
|
+
signature: "retryable: boolean",
|
|
14250
|
+
description: "@see FramerAPIErrorOptions.retryable",
|
|
14251
|
+
references: []
|
|
14252
|
+
}
|
|
14253
|
+
],
|
|
14027
14254
|
imageasset: [
|
|
14028
14255
|
{
|
|
14029
14256
|
name: "altText",
|
|
@@ -16729,7 +16956,7 @@ async function execAndPrint(sessionId, code) {
|
|
|
16729
16956
|
}
|
|
16730
16957
|
if (result.error) {
|
|
16731
16958
|
printError(
|
|
16732
|
-
`Error: ${formatErrorForUser(errorWithRef(result.error, result.errorRef))}`
|
|
16959
|
+
`Error: ${formatErrorForUser(errorWithRef(result.error, result.errorRef), { fromExecResult: true })}`
|
|
16733
16960
|
);
|
|
16734
16961
|
await waitForTrackingToFinish();
|
|
16735
16962
|
process.exit(1);
|
|
@@ -16807,6 +17034,7 @@ session.command("new <projectUrlOrId>").description("Create a new session and pr
|
|
|
16807
17034
|
).action(
|
|
16808
17035
|
async (projectUrlOrId, options) => {
|
|
16809
17036
|
const projectId = extractProjectId(projectUrlOrId);
|
|
17037
|
+
const agentThreadContext = resolveAgentThreadContext();
|
|
16810
17038
|
let sessionId;
|
|
16811
17039
|
let userId;
|
|
16812
17040
|
try {
|
|
@@ -16831,7 +17059,8 @@ session.command("new <projectUrlOrId>").description("Create a new session and pr
|
|
|
16831
17059
|
projectId,
|
|
16832
17060
|
apiKey: credentials.apiKey,
|
|
16833
17061
|
userId: credentials.userId,
|
|
16834
|
-
headlessServerUrl
|
|
17062
|
+
headlessServerUrl,
|
|
17063
|
+
...agentThreadContext
|
|
16835
17064
|
});
|
|
16836
17065
|
sessionId = result.id;
|
|
16837
17066
|
debug("session.new", `session created id=${sessionId}`);
|
|
@@ -16885,6 +17114,7 @@ session.command("new <projectUrlOrId>").description("Create a new session and pr
|
|
|
16885
17114
|
errorType: "session_new_error",
|
|
16886
17115
|
projectId,
|
|
16887
17116
|
userId,
|
|
17117
|
+
...agentThreadContext,
|
|
16888
17118
|
errorMessage: message
|
|
16889
17119
|
});
|
|
16890
17120
|
printError(`Failed to create session: ${formatErrorForUser(err)}`);
|
|
@@ -14,7 +14,7 @@ import { z } from 'zod';
|
|
|
14
14
|
import { createRequire } from 'module';
|
|
15
15
|
import * as vm from 'vm';
|
|
16
16
|
|
|
17
|
-
/* @framer/ai relay server v0.0.
|
|
17
|
+
/* @framer/ai relay server v0.0.26 */
|
|
18
18
|
var __defProp = Object.defineProperty;
|
|
19
19
|
var __knownSymbol = (name2, symbol) => (symbol = Symbol[name2]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name2);
|
|
20
20
|
var __typeError = (msg) => {
|
|
@@ -160,7 +160,7 @@ __name(debug, "debug");
|
|
|
160
160
|
// src/version.ts
|
|
161
161
|
var VERSION = (
|
|
162
162
|
// typeof is used to ensure this can be used just via tsx or node etc. without build
|
|
163
|
-
"0.0.
|
|
163
|
+
"0.0.26"
|
|
164
164
|
);
|
|
165
165
|
|
|
166
166
|
// src/relay-client.ts
|
|
@@ -182,6 +182,35 @@ createTRPCClient({
|
|
|
182
182
|
]
|
|
183
183
|
});
|
|
184
184
|
|
|
185
|
+
// src/agent-thread-context.ts
|
|
186
|
+
var MAX_AGENT_THREAD_ID_LENGTH = 256;
|
|
187
|
+
var MAX_AGENT_PROVIDER_LENGTH = 64;
|
|
188
|
+
var SAFE_VALUE_PATTERN = /^[A-Za-z0-9._:-]+$/;
|
|
189
|
+
function normalizeValue(value, maxLength) {
|
|
190
|
+
const trimmed = value?.trim();
|
|
191
|
+
if (!trimmed) return void 0;
|
|
192
|
+
if (trimmed.length > maxLength) return void 0;
|
|
193
|
+
if (!SAFE_VALUE_PATTERN.test(trimmed)) return void 0;
|
|
194
|
+
return trimmed;
|
|
195
|
+
}
|
|
196
|
+
__name(normalizeValue, "normalizeValue");
|
|
197
|
+
function normalizeAgentThreadContext(input) {
|
|
198
|
+
const agentThreadId = normalizeValue(
|
|
199
|
+
input.agentThreadId,
|
|
200
|
+
MAX_AGENT_THREAD_ID_LENGTH
|
|
201
|
+
);
|
|
202
|
+
if (!agentThreadId) return {};
|
|
203
|
+
const agentProvider = normalizeValue(
|
|
204
|
+
input.agentProvider,
|
|
205
|
+
MAX_AGENT_PROVIDER_LENGTH
|
|
206
|
+
);
|
|
207
|
+
return {
|
|
208
|
+
agentThreadId,
|
|
209
|
+
...agentProvider ? { agentProvider } : {}
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
__name(normalizeAgentThreadContext, "normalizeAgentThreadContext");
|
|
213
|
+
|
|
185
214
|
// src/connection-errors.ts
|
|
186
215
|
var CONNECTION_ERROR_PATTERNS = [
|
|
187
216
|
"Connection closed",
|
|
@@ -198,8 +227,15 @@ var CONNECTION_ERROR_PATTERNS = [
|
|
|
198
227
|
// Our own check in executor.ts
|
|
199
228
|
"Execution timed out after",
|
|
200
229
|
// Likely dead connection
|
|
201
|
-
"Session expired"
|
|
230
|
+
"Session expired",
|
|
202
231
|
// Server-side session expiry
|
|
232
|
+
// Typed page-death (PROJECT_CLOSED) for the string fallback; isRetryableError is the primary path.
|
|
233
|
+
"Project session crashed",
|
|
234
|
+
// PageCrashedError (renderer crash / OOM)
|
|
235
|
+
"Project was closed",
|
|
236
|
+
// StaleSessionError (page closed/reaped)
|
|
237
|
+
"Session was recycled"
|
|
238
|
+
// SessionRecycledError (destroy->respawn reconnect race)
|
|
203
239
|
];
|
|
204
240
|
function isConnectionError(errorMessage) {
|
|
205
241
|
return CONNECTION_ERROR_PATTERNS.some(
|
|
@@ -207,6 +243,18 @@ function isConnectionError(errorMessage) {
|
|
|
207
243
|
);
|
|
208
244
|
}
|
|
209
245
|
__name(isConnectionError, "isConnectionError");
|
|
246
|
+
function isRetryableError(err) {
|
|
247
|
+
if (err && typeof err === "object") {
|
|
248
|
+
const e = err;
|
|
249
|
+
if (e.retryable === true) return true;
|
|
250
|
+
if (e.code === "PROJECT_CLOSED") return true;
|
|
251
|
+
if (typeof e.message === "string" && isConnectionError(e.message))
|
|
252
|
+
return true;
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
return typeof err === "string" && isConnectionError(err);
|
|
256
|
+
}
|
|
257
|
+
__name(isRetryableError, "isRetryableError");
|
|
210
258
|
var AUTH_ERROR_PATTERNS = ["does not have access", "UNAUTHORIZED"];
|
|
211
259
|
function isAuthError(errorMessage) {
|
|
212
260
|
return AUTH_ERROR_PATTERNS.some((p) => errorMessage.includes(p));
|
|
@@ -541,20 +589,37 @@ async function execute(session, code, options = {}) {
|
|
|
541
589
|
return { output };
|
|
542
590
|
} catch (err) {
|
|
543
591
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
544
|
-
|
|
592
|
+
const retryable = isRetryableError(err);
|
|
593
|
+
if (retryable || isConnectionError(errorMessage)) {
|
|
545
594
|
session.connection.markDisconnected();
|
|
546
595
|
}
|
|
547
596
|
const errorRef = getErrorRef(err);
|
|
548
597
|
return {
|
|
549
598
|
output,
|
|
550
599
|
error: errorMessage,
|
|
551
|
-
...errorRef ? { errorRef } : {}
|
|
600
|
+
...errorRef ? { errorRef } : {},
|
|
601
|
+
...retryable ? { retryable: true } : {}
|
|
552
602
|
};
|
|
553
603
|
} finally {
|
|
554
604
|
if (timeoutId) clearTimeout(timeoutId);
|
|
555
605
|
}
|
|
556
606
|
}
|
|
557
607
|
__name(execute, "execute");
|
|
608
|
+
function isReconnectable(err) {
|
|
609
|
+
return isRetryableError(err) || isConnectionError(err instanceof Error ? err.message : String(err));
|
|
610
|
+
}
|
|
611
|
+
__name(isReconnectable, "isReconnectable");
|
|
612
|
+
function connectionFailureResult(prefix, err) {
|
|
613
|
+
const inner = err instanceof Error ? err.message : String(err);
|
|
614
|
+
const errorRef = getErrorRef(err);
|
|
615
|
+
return {
|
|
616
|
+
output: [],
|
|
617
|
+
error: `${prefix}: ${inner}`,
|
|
618
|
+
...errorRef ? { errorRef } : {},
|
|
619
|
+
...isRetryableError(err) ? { retryable: true } : {}
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
__name(connectionFailureResult, "connectionFailureResult");
|
|
558
623
|
async function executeWithReconnect(session, code, options, execId) {
|
|
559
624
|
if (!session.connection.isConnected()) {
|
|
560
625
|
log(
|
|
@@ -563,17 +628,27 @@ async function executeWithReconnect(session, code, options, execId) {
|
|
|
563
628
|
try {
|
|
564
629
|
await session.connection.reconnect();
|
|
565
630
|
} catch (err) {
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
631
|
+
if (!isReconnectable(err)) {
|
|
632
|
+
return connectionFailureResult(
|
|
633
|
+
"Failed to get connection for session",
|
|
634
|
+
err
|
|
635
|
+
);
|
|
636
|
+
}
|
|
637
|
+
log(
|
|
638
|
+
`exec.reconnect.retry exec=${execId} session=${session.id} reason="${err instanceof Error ? err.message : String(err)}"`
|
|
639
|
+
);
|
|
640
|
+
try {
|
|
641
|
+
await session.connection.reconnect();
|
|
642
|
+
} catch (err2) {
|
|
643
|
+
return connectionFailureResult(
|
|
644
|
+
"Failed to get connection for session",
|
|
645
|
+
err2
|
|
646
|
+
);
|
|
647
|
+
}
|
|
573
648
|
}
|
|
574
649
|
}
|
|
575
650
|
const result = await execute(session, code, options);
|
|
576
|
-
if (!result.error || !isConnectionError(result.error)) {
|
|
651
|
+
if (!result.error || !(result.retryable || isConnectionError(result.error))) {
|
|
577
652
|
return result;
|
|
578
653
|
}
|
|
579
654
|
log(
|
|
@@ -582,16 +657,13 @@ async function executeWithReconnect(session, code, options, execId) {
|
|
|
582
657
|
try {
|
|
583
658
|
await session.connection.reconnect();
|
|
584
659
|
} catch (err) {
|
|
585
|
-
const inner = err instanceof Error ? err.message : String(err);
|
|
586
|
-
const errorRef = getErrorRef(err);
|
|
587
660
|
log(
|
|
588
|
-
`reconnect.failed exec=${execId} session=${session.id} req=${session.connection.framer.requestId} error="${
|
|
661
|
+
`reconnect.failed exec=${execId} session=${session.id} req=${session.connection.framer.requestId} error="${err instanceof Error ? err.message : String(err)}"`
|
|
662
|
+
);
|
|
663
|
+
return connectionFailureResult(
|
|
664
|
+
"Connection lost and failed to reconnect",
|
|
665
|
+
err
|
|
589
666
|
);
|
|
590
|
-
return {
|
|
591
|
-
output: [],
|
|
592
|
-
error: `Connection lost and failed to reconnect: ${inner}`,
|
|
593
|
-
...errorRef ? { errorRef } : {}
|
|
594
|
-
};
|
|
595
667
|
}
|
|
596
668
|
log(
|
|
597
669
|
`reconnect.success exec=${execId} session=${session.id} req=${session.connection.framer.requestId}`
|
|
@@ -897,10 +969,11 @@ var Connection = class _Connection {
|
|
|
897
969
|
headlessServerUrl: this.headlessServerUrl
|
|
898
970
|
}));
|
|
899
971
|
}
|
|
900
|
-
createSession(id) {
|
|
972
|
+
createSession(id, agentThreadContext) {
|
|
901
973
|
const session = {
|
|
902
974
|
id,
|
|
903
975
|
state: {},
|
|
976
|
+
agentThreadContext,
|
|
904
977
|
lastActivityAt: Date.now(),
|
|
905
978
|
inflight: 0,
|
|
906
979
|
connection: this
|
|
@@ -971,14 +1044,17 @@ var SessionManager = class {
|
|
|
971
1044
|
}
|
|
972
1045
|
connections = [];
|
|
973
1046
|
idleCheck = null;
|
|
974
|
-
async create(projectId, userId, apiKey, headlessServerUrl) {
|
|
1047
|
+
async create(projectId, userId, apiKey, headlessServerUrl, agentThreadContext = {}) {
|
|
975
1048
|
const connection = await this.findOrCreateConnection(
|
|
976
1049
|
projectId,
|
|
977
1050
|
userId,
|
|
978
1051
|
apiKey,
|
|
979
1052
|
headlessServerUrl
|
|
980
1053
|
);
|
|
981
|
-
const session = connection.createSession(
|
|
1054
|
+
const session = connection.createSession(
|
|
1055
|
+
this.getNextSessionId(),
|
|
1056
|
+
agentThreadContext
|
|
1057
|
+
);
|
|
982
1058
|
this.startIdleCheck();
|
|
983
1059
|
return session;
|
|
984
1060
|
}
|
|
@@ -1008,6 +1084,7 @@ var SessionManager = class {
|
|
|
1008
1084
|
projectId: session.connection.projectId,
|
|
1009
1085
|
userId: session.connection.userId,
|
|
1010
1086
|
sessionId: session.connection.getServerSessionId(),
|
|
1087
|
+
...session.agentThreadContext,
|
|
1011
1088
|
durationMs,
|
|
1012
1089
|
hasError: result.error !== void 0,
|
|
1013
1090
|
...result.error ? { errorMessage: result.error } : {}
|
|
@@ -1048,7 +1125,8 @@ var SessionManager = class {
|
|
|
1048
1125
|
trackSessionDestroy({
|
|
1049
1126
|
projectId: session.connection.projectId,
|
|
1050
1127
|
userId: session.connection.userId,
|
|
1051
|
-
sessionId: session.connection.getServerSessionId()
|
|
1128
|
+
sessionId: session.connection.getServerSessionId(),
|
|
1129
|
+
...session.agentThreadContext
|
|
1052
1130
|
});
|
|
1053
1131
|
session.connection.removeSession(id);
|
|
1054
1132
|
if (!session.connection.hasSessions()) {
|
|
@@ -1202,16 +1280,20 @@ var appRouter = t.router({
|
|
|
1202
1280
|
projectId: z.string(),
|
|
1203
1281
|
apiKey: z.string(),
|
|
1204
1282
|
userId: z.string().optional(),
|
|
1205
|
-
headlessServerUrl: z.string()
|
|
1283
|
+
headlessServerUrl: z.string(),
|
|
1284
|
+
agentThreadId: z.string().optional(),
|
|
1285
|
+
agentProvider: z.string().optional()
|
|
1206
1286
|
})
|
|
1207
1287
|
).mutation(async ({ input }) => {
|
|
1208
1288
|
const start = performance.now();
|
|
1289
|
+
const agentThreadContext = normalizeAgentThreadContext(input);
|
|
1209
1290
|
try {
|
|
1210
1291
|
const session = await sessionManager.create(
|
|
1211
1292
|
input.projectId,
|
|
1212
1293
|
input.userId,
|
|
1213
1294
|
input.apiKey,
|
|
1214
|
-
input.headlessServerUrl
|
|
1295
|
+
input.headlessServerUrl,
|
|
1296
|
+
agentThreadContext
|
|
1215
1297
|
);
|
|
1216
1298
|
log(
|
|
1217
1299
|
`session.new id=${session.id} project=${input.projectId} headlessServerUrl=${input.headlessServerUrl}`
|
|
@@ -1220,6 +1302,7 @@ var appRouter = t.router({
|
|
|
1220
1302
|
projectId: input.projectId,
|
|
1221
1303
|
userId: session.connection.userId,
|
|
1222
1304
|
sessionId: session.connection.getServerSessionId(),
|
|
1305
|
+
...session.agentThreadContext,
|
|
1223
1306
|
durationMs: Math.round(performance.now() - start)
|
|
1224
1307
|
});
|
|
1225
1308
|
return { id: session.id };
|
|
@@ -1229,6 +1312,7 @@ var appRouter = t.router({
|
|
|
1229
1312
|
errorType: "session_create_error",
|
|
1230
1313
|
projectId: input.projectId,
|
|
1231
1314
|
userId: input.userId,
|
|
1315
|
+
...agentThreadContext,
|
|
1232
1316
|
errorMessage: message
|
|
1233
1317
|
});
|
|
1234
1318
|
throw new TRPCError({
|
|
@@ -39,6 +39,7 @@ Always save results you'll need again. Don't repeat API calls.
|
|
|
39
39
|
Use agent-specific methods whenever a plugin API method and an agent method overlap:
|
|
40
40
|
|
|
41
41
|
- Use `readProjectForAgent`, `getNodeForAgent`, `getNodesForAgent`, `getNodesOfTypesForAgent`, `getScopeNodeForAgent`, `getGroundNodeForAgent`, `getParentNodeForAgent`, `getAncestorsForAgent`, `serializeForAgent`, `serializeNodesForAgent`, and `paginateForAgent` for project tree reads.
|
|
42
|
+
- Use `readComponentControlsForAgent`, `readIconSetControlsForAgent`, `readIconsForAgent`, `readLayoutTemplateControlsForAgent`, and `readShaderControlsForAgent` for reading the controls of components, icon sets, icons, layout templates, and shaders.
|
|
42
43
|
- Use `applyAgentChanges` for page and layout edits. Do not use low-level node APIs like `createNode`, `setAttributes`, or `setRect` for design/layout work.
|
|
43
44
|
- Use `publishForAgent` for publishing. Do not use `publish`, `getDeployments`, or `deploy` for normal agent publishing flows.
|
|
44
45
|
|
|
@@ -170,7 +171,7 @@ npx framer-dalton docs ScreenshotOptions # Show type + recursively expa
|
|
|
170
171
|
|
|
171
172
|
Collections are Framer's CMS. Each collection has fields (columns) and items (rows). Use the plugin Collection API (the methods below) for collection schema and item CRUD.
|
|
172
173
|
|
|
173
|
-
**Exception — CMS Collection Lists.** The in-canvas construct that *renders* a collection as a repeating list of cards (the "Collection List" / repeater) is an agent/page concern. Build and edit those through `readProjectForAgent` and `applyAgentChanges`, not the plugin API. Use the plugin Collection API to read available collections and field schema first, then
|
|
174
|
+
**Exception — CMS Collection Lists.** The in-canvas construct that *renders* a collection as a repeating list of cards (the "Collection List" / repeater) is an agent/page concern. Build and edit those through `readProjectForAgent` and `applyAgentChanges`, not the plugin API. Use the plugin Collection API to read available collections and field schema first, then use `applyAgentChanges` to add the list.
|
|
174
175
|
|
|
175
176
|
#### Reading Collections
|
|
176
177
|
|
|
@@ -182,10 +183,11 @@ console.log(collections.map((c) => ({ name: c.name, id: c.id })));
|
|
|
182
183
|
// Get a specific collection by ID
|
|
183
184
|
const collection = await framer.getCollection("collection-id");
|
|
184
185
|
|
|
185
|
-
// Get fields (columns)
|
|
186
|
+
// Get fields (columns)
|
|
186
187
|
const fields = await collection.getFields();
|
|
187
|
-
console.log
|
|
188
|
-
|
|
188
|
+
// Note that a plain console.log will miss getter-backed properties
|
|
189
|
+
console.log(fields.map((f) => ({ id: f.id, name: f.name, type: f.type })));
|
|
190
|
+
// [{ id: "BnNuS2i3o", name: "Title", type: "string" }, ...]
|
|
189
191
|
|
|
190
192
|
// Get items (rows)
|
|
191
193
|
const items = await collection.getItems();
|
|
@@ -244,6 +246,17 @@ const ids = items.map((i) => i.id).reverse();
|
|
|
244
246
|
await collection.setItemOrder(ids);
|
|
245
247
|
```
|
|
246
248
|
|
|
249
|
+
#### Writing enum fields
|
|
250
|
+
|
|
251
|
+
Enum fields are **write-by-case-id, read-by-name**: a write must pass the case's `id`, but `getItems()` reads the value back as the case name. Writing `{ type: "enum", value: "New" }` (the name) is rejected — look up the id from the field definition first:
|
|
252
|
+
|
|
253
|
+
```js
|
|
254
|
+
const caseId = field.cases.find((c) => c.name === "New").id;
|
|
255
|
+
await collection.addItems([
|
|
256
|
+
{ slug: "hello-world", fieldData: { [field.id]: { type: "enum", value: caseId } } },
|
|
257
|
+
]);
|
|
258
|
+
```
|
|
259
|
+
|
|
247
260
|
#### Working with CollectionItem
|
|
248
261
|
|
|
249
262
|
```js
|
|
@@ -262,7 +275,7 @@ await item.remove();
|
|
|
262
275
|
|
|
263
276
|
### Working with Images
|
|
264
277
|
|
|
265
|
-
Canvas editing accepts image URLs directly when setting an image fill, so the typical task is to obtain a URL and
|
|
278
|
+
Canvas editing accepts image URLs directly when setting an image fill, so the typical task is to obtain a URL and use it directly with `applyAgentChanges`.
|
|
266
279
|
|
|
267
280
|
There are three sources you'll typically pull URLs from:
|
|
268
281
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "framer-dalton",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.26",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": "./dist/cli.js",
|
|
6
6
|
"main": "./dist/cli.js",
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"typecheck": "tsc --noEmit",
|
|
17
17
|
"clean": "rm -rf dist",
|
|
18
18
|
"check": "biome check .",
|
|
19
|
+
"check:fix": "biome check --write .",
|
|
19
20
|
"format": "biome format --write .",
|
|
20
21
|
"generate-types": "tsx scripts/generate-types.ts"
|
|
21
22
|
},
|
|
@@ -23,17 +24,17 @@
|
|
|
23
24
|
"@trpc/client": "^11.17.0",
|
|
24
25
|
"@trpc/server": "^11.17.0",
|
|
25
26
|
"commander": "^12.1.0",
|
|
26
|
-
"framer-api": "0.1.
|
|
27
|
-
"zod": "^4.3
|
|
27
|
+
"framer-api": "^0.1.14-beta",
|
|
28
|
+
"zod": "^4.4.3"
|
|
28
29
|
},
|
|
29
30
|
"devDependencies": {
|
|
30
31
|
"@biomejs/biome": "^2.4.13",
|
|
31
|
-
"@framerjs/framer-events": "0.0.
|
|
32
|
-
"@types/node": "24.
|
|
32
|
+
"@framerjs/framer-events": "0.0.183",
|
|
33
|
+
"@types/node": "24.12.4",
|
|
33
34
|
"tsup": "^8.0.2",
|
|
34
|
-
"tsx": "^4.
|
|
35
|
+
"tsx": "^4.22.3",
|
|
35
36
|
"typescript": "^5.9.2",
|
|
36
|
-
"vitest": "^4.
|
|
37
|
+
"vitest": "^4.1.7"
|
|
37
38
|
},
|
|
38
39
|
"packageManager": "yarn@4.13.0",
|
|
39
40
|
"engines": {
|