framer-dalton 0.0.24 → 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/README.md +5 -5
- package/dist/cli.js +347 -36
- package/dist/start-relay-server.js +112 -27
- package/docs/skills/framer-code-components.md +1 -1
- package/docs/skills/framer-project.md +439 -0
- package/docs/skills/framer.md +6 -383
- package/package.json +8 -7
- package/docs/skills/framer-canvas-editing-project.md +0 -49
package/README.md
CHANGED
|
@@ -16,14 +16,14 @@ That installs skills into `~/.agents/skills` and `~/.claude/skills`.
|
|
|
16
16
|
|
|
17
17
|
The CLI and the installed skills are meant to work together. There are three skills:
|
|
18
18
|
|
|
19
|
-
- `framer` - the base skill that explains
|
|
19
|
+
- `framer` - the base meta skill that explains setup, project selection, and session creation.
|
|
20
20
|
- `framer-code-components` - explains specific prompts for how to write code components and provides examples.
|
|
21
|
-
- `framer-
|
|
21
|
+
- `framer-project-<project id>` - a dynamically-created skill for interacting with a specific project.
|
|
22
22
|
|
|
23
|
-
1. Running `npx framer-dalton@latest setup` will install the base `framer` and `framer-code-
|
|
24
|
-
2. The frontmatter of `framer-code-components` tells agents to always load `framer` first.
|
|
23
|
+
1. Running `npx framer-dalton@latest setup` will install the base `framer` and `framer-code-components` skills.
|
|
24
|
+
2. The frontmatter of `framer-code-components` tells agents to always load `framer` and the generated project skill first.
|
|
25
25
|
3. The frontmatter of `framer` tells agents to run `npx framer-dalton@latest setup` BEFORE loading the skill. This command will auto-update the cli and update the skill files.
|
|
26
|
-
4. Creating a new session for a project will create a `framer-
|
|
26
|
+
4. Creating a new session for a project will create a `framer-project-<project id>` file. This file contains the latest agent system prompt from `framer.getAgentSystemPrompt` and the latest project context from `framer.getAgentContext`. Agents must load this project-specific skill immediately after creating a session before doing any connected-project work.
|
|
27
27
|
|
|
28
28
|
## Local Development
|
|
29
29
|
|
package/dist/cli.js
CHANGED
|
@@ -8,10 +8,11 @@ import { spawn, execFile } from 'child_process';
|
|
|
8
8
|
import { z } from 'zod';
|
|
9
9
|
import os from 'os';
|
|
10
10
|
import { ErrorCode, FramerAPIError } from 'framer-api';
|
|
11
|
+
import net from 'net';
|
|
11
12
|
import { fileURLToPath } from 'url';
|
|
12
13
|
import { createTRPCClient, httpLink } from '@trpc/client';
|
|
13
14
|
|
|
14
|
-
/* @framer/ai CLI v0.0.
|
|
15
|
+
/* @framer/ai CLI v0.0.26 */
|
|
15
16
|
var __defProp = Object.defineProperty;
|
|
16
17
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
17
18
|
|
|
@@ -28,6 +29,51 @@ Please upgrade: https://nodejs.org/`
|
|
|
28
29
|
);
|
|
29
30
|
process.exit(1);
|
|
30
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");
|
|
31
77
|
function openUrl(url) {
|
|
32
78
|
const platform = process.platform;
|
|
33
79
|
let cmd;
|
|
@@ -329,7 +375,7 @@ __name(markTelemetryNoticeShown, "markTelemetryNoticeShown");
|
|
|
329
375
|
// src/version.ts
|
|
330
376
|
var VERSION = (
|
|
331
377
|
// typeof is used to ensure this can be used just via tsx or node etc. without build
|
|
332
|
-
"0.0.
|
|
378
|
+
"0.0.26"
|
|
333
379
|
);
|
|
334
380
|
var trackingEndpoint = "https://events.framer.com/track";
|
|
335
381
|
var inProgressTrackings = /* @__PURE__ */ new Set();
|
|
@@ -419,6 +465,45 @@ function trackError(payload) {
|
|
|
419
465
|
});
|
|
420
466
|
}
|
|
421
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
|
|
422
507
|
function rehydrateFramerAPIError(error) {
|
|
423
508
|
if (!(error instanceof Error)) return null;
|
|
424
509
|
const framerApi = error.data?.framerApi;
|
|
@@ -447,10 +532,12 @@ function formatError(error) {
|
|
|
447
532
|
return String(error);
|
|
448
533
|
}
|
|
449
534
|
__name(formatError, "formatError");
|
|
450
|
-
function formatErrorForUser(error) {
|
|
535
|
+
function formatErrorForUser(error, opts) {
|
|
451
536
|
if (!(error instanceof Error)) return String(error);
|
|
452
537
|
const rehydrated = rehydrateFramerAPIError(error);
|
|
453
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.";
|
|
454
541
|
const ref = getErrorRef(error);
|
|
455
542
|
return ref ? `${error.message} [ref: ${ref}]` : error.message;
|
|
456
543
|
}
|
|
@@ -818,13 +905,6 @@ async function acquireAuthWithRemixProject(sourceProjectUrlOrId) {
|
|
|
818
905
|
}
|
|
819
906
|
__name(acquireAuthWithRemixProject, "acquireAuthWithRemixProject");
|
|
820
907
|
|
|
821
|
-
// src/connection-errors.ts
|
|
822
|
-
var AUTH_ERROR_PATTERNS = ["does not have access", "UNAUTHORIZED"];
|
|
823
|
-
function isAuthError(errorMessage) {
|
|
824
|
-
return AUTH_ERROR_PATTERNS.some((p) => errorMessage.includes(p));
|
|
825
|
-
}
|
|
826
|
-
__name(isAuthError, "isAuthError");
|
|
827
|
-
|
|
828
908
|
// src/closest-match.ts
|
|
829
909
|
function levenshteinDistance(source, target) {
|
|
830
910
|
const sourceLength = source.length;
|
|
@@ -1803,6 +1883,12 @@ var types = {
|
|
|
1803
1883
|
kind: "interface",
|
|
1804
1884
|
references: ["CodeExportCommon"],
|
|
1805
1885
|
members: [
|
|
1886
|
+
{
|
|
1887
|
+
name: "componentId",
|
|
1888
|
+
type: "string",
|
|
1889
|
+
description: "Component ID for this code file export.",
|
|
1890
|
+
optional: false
|
|
1891
|
+
},
|
|
1806
1892
|
{
|
|
1807
1893
|
name: "insertURL",
|
|
1808
1894
|
type: "string",
|
|
@@ -4478,7 +4564,7 @@ var types = {
|
|
|
4478
4564
|
name: "HostnameType",
|
|
4479
4565
|
description: "",
|
|
4480
4566
|
kind: "alias",
|
|
4481
|
-
alias: '"default" | "custom" | "version"',
|
|
4567
|
+
alias: '"default" | "custom" | "version" | "branch"',
|
|
4482
4568
|
references: []
|
|
4483
4569
|
},
|
|
4484
4570
|
imageassetdata: {
|
|
@@ -6959,7 +7045,7 @@ var types = {
|
|
|
6959
7045
|
},
|
|
6960
7046
|
{
|
|
6961
7047
|
name: "unstable_getCodeFile",
|
|
6962
|
-
type: "(
|
|
7048
|
+
type: "(idOrPath: string) => Promise<CodeFileData | null>",
|
|
6963
7049
|
description: "@deprecated",
|
|
6964
7050
|
optional: false
|
|
6965
7051
|
},
|
|
@@ -7019,7 +7105,7 @@ var types = {
|
|
|
7019
7105
|
},
|
|
7020
7106
|
{
|
|
7021
7107
|
name: "getCodeFile",
|
|
7022
|
-
type: "(
|
|
7108
|
+
type: "(idOrPath: string) => Promise<CodeFileData | null>",
|
|
7023
7109
|
description: "",
|
|
7024
7110
|
optional: false
|
|
7025
7111
|
},
|
|
@@ -7203,12 +7289,48 @@ var types = {
|
|
|
7203
7289
|
description: "@alpha",
|
|
7204
7290
|
optional: false
|
|
7205
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
|
+
},
|
|
7206
7322
|
{
|
|
7207
7323
|
name: "applyAgentChanges",
|
|
7208
7324
|
type: "(dsl: string, options?: {\n pagePath?: string;\n }) => Promise<void>",
|
|
7209
7325
|
description: "@alpha",
|
|
7210
7326
|
optional: false
|
|
7211
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
|
+
},
|
|
7212
7334
|
{
|
|
7213
7335
|
name: "publishForAgent",
|
|
7214
7336
|
type: "(input?: Record<string, unknown>) => Promise<unknown>",
|
|
@@ -7760,9 +7882,15 @@ var types = {
|
|
|
7760
7882
|
{
|
|
7761
7883
|
name: "model",
|
|
7762
7884
|
type: "string",
|
|
7763
|
-
description: "",
|
|
7885
|
+
description: "Supervisor model id, optionally with eval-style reasoning effort suffix, e.g. `anthropic/claude-opus-4.6=low`.",
|
|
7764
7886
|
optional: false
|
|
7765
7887
|
},
|
|
7888
|
+
{
|
|
7889
|
+
name: "userAgentModel",
|
|
7890
|
+
type: "string",
|
|
7891
|
+
description: "Inner Framer Agent model override. Defaults to `model` when omitted. Supports the same `model=effort` syntax.",
|
|
7892
|
+
optional: true
|
|
7893
|
+
},
|
|
7766
7894
|
{
|
|
7767
7895
|
name: "pagePath",
|
|
7768
7896
|
type: "string",
|
|
@@ -7812,6 +7940,18 @@ var types = {
|
|
|
7812
7940
|
type: "string",
|
|
7813
7941
|
description: "JSONL string with one training row per inner-agent step, present when `captureTrainingData` was set.",
|
|
7814
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
|
|
7815
7955
|
}
|
|
7816
7956
|
]
|
|
7817
7957
|
},
|
|
@@ -13211,6 +13351,13 @@ var methodsByCategory = {
|
|
|
13211
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`.',
|
|
13212
13352
|
references: []
|
|
13213
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
|
+
},
|
|
13214
13361
|
{
|
|
13215
13362
|
name: "[$framerApiOnly.publishForAgent]",
|
|
13216
13363
|
category: "framer",
|
|
@@ -13225,6 +13372,34 @@ var methodsByCategory = {
|
|
|
13225
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.',
|
|
13226
13373
|
references: []
|
|
13227
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
|
+
},
|
|
13228
13403
|
{
|
|
13229
13404
|
name: "[$framerApiOnly.readProjectForAgent]",
|
|
13230
13405
|
category: "framer",
|
|
@@ -13232,6 +13407,13 @@ var methodsByCategory = {
|
|
|
13232
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.',
|
|
13233
13408
|
references: []
|
|
13234
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
|
+
},
|
|
13235
13417
|
{
|
|
13236
13418
|
name: "[$framerApiOnly.reviewChangesForAgent]",
|
|
13237
13419
|
category: "framer",
|
|
@@ -13514,8 +13696,8 @@ var methodsByCategory = {
|
|
|
13514
13696
|
{
|
|
13515
13697
|
name: "getCodeFile",
|
|
13516
13698
|
category: "framer",
|
|
13517
|
-
signature: "getCodeFile(
|
|
13518
|
-
description: 'Get a specific code file by its ID.\n\n@param
|
|
13699
|
+
signature: "getCodeFile(idOrPath: string): Promise<CodeFile | null>",
|
|
13700
|
+
description: 'Get a specific code file by its ID or file path.\n\n@param idOrPath - The unique identifier (e.g. `"codeFile/AbcD_23"`) or\nfile path (e.g. `"Button.tsx"` or `"overrides/withAnalytics.tsx"`) of the\ncode file.\n@returns The CodeFile instance or `null` if not found.\n\n@example\n```ts\n// Look up by ID\nconst codeFile = await framer.getCodeFile("codeFile/AbcD_23")\n\n// Look up by file path\nconst codeFile = await framer.getCodeFile("Button.tsx")\n```\n@category code-files',
|
|
13519
13701
|
references: ["CodeFile"]
|
|
13520
13702
|
},
|
|
13521
13703
|
{
|
|
@@ -13791,6 +13973,13 @@ var methodsByCategory = {
|
|
|
13791
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`.',
|
|
13792
13974
|
references: []
|
|
13793
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
|
+
},
|
|
13794
13983
|
{
|
|
13795
13984
|
name: "publish",
|
|
13796
13985
|
category: "framer",
|
|
@@ -13812,6 +14001,34 @@ var methodsByCategory = {
|
|
|
13812
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.',
|
|
13813
14002
|
references: []
|
|
13814
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
|
+
},
|
|
13815
14032
|
{
|
|
13816
14033
|
name: "readProjectForAgent",
|
|
13817
14034
|
category: "framer",
|
|
@@ -13819,6 +14036,13 @@ var methodsByCategory = {
|
|
|
13819
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.',
|
|
13820
14037
|
references: []
|
|
13821
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
|
+
},
|
|
13822
14046
|
{
|
|
13823
14047
|
name: "rejectAllPending",
|
|
13824
14048
|
category: "framer",
|
|
@@ -14011,6 +14235,22 @@ var methodsByCategory = {
|
|
|
14011
14235
|
references: ["NamedImageAssetInput", "ImageAsset"]
|
|
14012
14236
|
}
|
|
14013
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
|
+
],
|
|
14014
14254
|
imageasset: [
|
|
14015
14255
|
{
|
|
14016
14256
|
name: "altText",
|
|
@@ -16225,6 +16465,7 @@ __name(readRelayToken, "readRelayToken");
|
|
|
16225
16465
|
var __filename$1 = fileURLToPath(import.meta.url);
|
|
16226
16466
|
var __dirname$1 = path7.dirname(__filename$1);
|
|
16227
16467
|
var RELAY_PORT = Number(process.env.FRAMER_CLI_PORT) || 19988;
|
|
16468
|
+
var WAIT_TIMEOUT_SECONDS = 5;
|
|
16228
16469
|
var client = createTRPCClient({
|
|
16229
16470
|
links: [
|
|
16230
16471
|
httpLink({
|
|
@@ -16252,6 +16493,43 @@ function sleep(ms) {
|
|
|
16252
16493
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
16253
16494
|
}
|
|
16254
16495
|
__name(sleep, "sleep");
|
|
16496
|
+
var PORT_PROBE_TIMEOUT_MS = 1e3;
|
|
16497
|
+
function probePort(port) {
|
|
16498
|
+
return new Promise((resolve) => {
|
|
16499
|
+
const socket = net.createConnection({ host: "127.0.0.1", port });
|
|
16500
|
+
const done = /* @__PURE__ */ __name((result) => {
|
|
16501
|
+
socket.removeAllListeners();
|
|
16502
|
+
socket.destroy();
|
|
16503
|
+
resolve(result);
|
|
16504
|
+
}, "done");
|
|
16505
|
+
socket.setTimeout(PORT_PROBE_TIMEOUT_MS);
|
|
16506
|
+
socket.once("connect", () => done("in_use"));
|
|
16507
|
+
socket.once("timeout", () => {
|
|
16508
|
+
debug("relay", `port ${port} probe timed out`);
|
|
16509
|
+
done("unknown");
|
|
16510
|
+
});
|
|
16511
|
+
socket.once("error", (err) => {
|
|
16512
|
+
if (err.code === "ECONNREFUSED") {
|
|
16513
|
+
done("free");
|
|
16514
|
+
} else {
|
|
16515
|
+
debug("relay", `port ${port} probe error: ${err.code ?? err.message}`);
|
|
16516
|
+
done("unknown");
|
|
16517
|
+
}
|
|
16518
|
+
});
|
|
16519
|
+
});
|
|
16520
|
+
}
|
|
16521
|
+
__name(probePort, "probePort");
|
|
16522
|
+
async function waitForPortFree(port, timeoutMs) {
|
|
16523
|
+
const deadline = Date.now() + timeoutMs;
|
|
16524
|
+
while (Date.now() < deadline) {
|
|
16525
|
+
if (await probePort(port) === "free") {
|
|
16526
|
+
return true;
|
|
16527
|
+
}
|
|
16528
|
+
await sleep(100);
|
|
16529
|
+
}
|
|
16530
|
+
return false;
|
|
16531
|
+
}
|
|
16532
|
+
__name(waitForPortFree, "waitForPortFree");
|
|
16255
16533
|
function compareVersions(v1, v2) {
|
|
16256
16534
|
const parts1 = v1.split(".").map(Number);
|
|
16257
16535
|
const parts2 = v2.split(".").map(Number);
|
|
@@ -16287,8 +16565,20 @@ async function ensureRelayServerRunning(options = {}) {
|
|
|
16287
16565
|
debug("relay", "shutting down old server...");
|
|
16288
16566
|
try {
|
|
16289
16567
|
await client.shutdown.mutate();
|
|
16290
|
-
|
|
16291
|
-
|
|
16568
|
+
} catch (err) {
|
|
16569
|
+
debug(
|
|
16570
|
+
"relay",
|
|
16571
|
+
`shutdown RPC failed: ${err instanceof Error ? err.message : String(err)}`
|
|
16572
|
+
);
|
|
16573
|
+
}
|
|
16574
|
+
const freed = await waitForPortFree(
|
|
16575
|
+
RELAY_PORT,
|
|
16576
|
+
WAIT_TIMEOUT_SECONDS * 1e3
|
|
16577
|
+
);
|
|
16578
|
+
if (!freed) {
|
|
16579
|
+
throw new Error(
|
|
16580
|
+
`Old relay server (v${serverVersion}) is still listening on 127.0.0.1:${RELAY_PORT} after ${WAIT_TIMEOUT_SECONDS}s; please kill it manually`
|
|
16581
|
+
);
|
|
16292
16582
|
}
|
|
16293
16583
|
debug("relay", "old server shut down");
|
|
16294
16584
|
} else {
|
|
@@ -16317,7 +16607,7 @@ async function ensureRelayServerRunning(options = {}) {
|
|
|
16317
16607
|
"relay",
|
|
16318
16608
|
`readiness poll ${attempt + 1}/10: version=${newVersion ?? "not ready"}`
|
|
16319
16609
|
);
|
|
16320
|
-
if (newVersion) {
|
|
16610
|
+
if (newVersion === VERSION) {
|
|
16321
16611
|
logger?.log("Relay server started successfully");
|
|
16322
16612
|
return;
|
|
16323
16613
|
}
|
|
@@ -16380,26 +16670,27 @@ function renderTemplate(template, values) {
|
|
|
16380
16670
|
return rendered;
|
|
16381
16671
|
}
|
|
16382
16672
|
__name(renderTemplate, "renderTemplate");
|
|
16383
|
-
var
|
|
16673
|
+
var PROJECT_SKILL_PREFIX = "framer-project-";
|
|
16674
|
+
var LEGACY_CANVAS_EDITING_SKILL_PREFIX = "framer-canvas-editing-project-";
|
|
16384
16675
|
function toSafeProjectId(projectId) {
|
|
16385
16676
|
return projectId.replace(/[^a-zA-Z0-9_-]/g, "-");
|
|
16386
16677
|
}
|
|
16387
16678
|
__name(toSafeProjectId, "toSafeProjectId");
|
|
16388
|
-
function
|
|
16389
|
-
const skillName = `${
|
|
16390
|
-
const template = readSkillDoc("framer-
|
|
16679
|
+
function buildProjectSkill(projectId, agentContext, agentPrompt) {
|
|
16680
|
+
const skillName = `${PROJECT_SKILL_PREFIX}${toSafeProjectId(projectId)}`;
|
|
16681
|
+
const template = readSkillDoc("framer-project.md");
|
|
16391
16682
|
const content = `${renderTemplate(template, {
|
|
16392
16683
|
SKILL_NAME: skillName,
|
|
16393
16684
|
PROJECT_ID: projectId,
|
|
16394
16685
|
GENERATED_AT: (/* @__PURE__ */ new Date()).toISOString(),
|
|
16395
|
-
|
|
16686
|
+
AGENT_PROMPT: agentPrompt.trimEnd(),
|
|
16396
16687
|
AGENT_CONTEXT: agentContext.trimEnd(),
|
|
16397
16688
|
FRAMER_TEMPORARY_DIR
|
|
16398
16689
|
})}
|
|
16399
16690
|
`;
|
|
16400
16691
|
return { skillName, content };
|
|
16401
16692
|
}
|
|
16402
|
-
__name(
|
|
16693
|
+
__name(buildProjectSkill, "buildProjectSkill");
|
|
16403
16694
|
function writeSkill(root, skillName, content) {
|
|
16404
16695
|
fs7.mkdirSync(root, { recursive: true });
|
|
16405
16696
|
const rootStat = fs7.statSync(root);
|
|
@@ -16417,6 +16708,17 @@ function writeSkill(root, skillName, content) {
|
|
|
16417
16708
|
return filePath;
|
|
16418
16709
|
}
|
|
16419
16710
|
__name(writeSkill, "writeSkill");
|
|
16711
|
+
function cleanupLegacyCanvasEditingSkills(skillRoots = getDefaultSkillRoots()) {
|
|
16712
|
+
for (const root of skillRoots) {
|
|
16713
|
+
if (!fs7.existsSync(root)) continue;
|
|
16714
|
+
for (const entry of fs7.readdirSync(root)) {
|
|
16715
|
+
if (!entry.startsWith(LEGACY_CANVAS_EDITING_SKILL_PREFIX)) continue;
|
|
16716
|
+
const skillDir = path7.join(root, entry);
|
|
16717
|
+
fs7.rmSync(skillDir, { recursive: true, force: true, maxRetries: 2 });
|
|
16718
|
+
}
|
|
16719
|
+
}
|
|
16720
|
+
}
|
|
16721
|
+
__name(cleanupLegacyCanvasEditingSkills, "cleanupLegacyCanvasEditingSkills");
|
|
16420
16722
|
function getDefaultSkillRoots() {
|
|
16421
16723
|
const home = os.homedir();
|
|
16422
16724
|
return [
|
|
@@ -16425,16 +16727,15 @@ function getDefaultSkillRoots() {
|
|
|
16425
16727
|
];
|
|
16426
16728
|
}
|
|
16427
16729
|
__name(getDefaultSkillRoots, "getDefaultSkillRoots");
|
|
16428
|
-
function cleanupStaleSkills(activeProjectIds, skillRoots) {
|
|
16429
|
-
const roots = skillRoots ?? getDefaultSkillRoots();
|
|
16730
|
+
function cleanupStaleSkills(activeProjectIds, skillRoots = getDefaultSkillRoots()) {
|
|
16430
16731
|
const sanitizedActiveProjectIds = new Set(
|
|
16431
16732
|
[...activeProjectIds].map(toSafeProjectId)
|
|
16432
16733
|
);
|
|
16433
|
-
for (const root of
|
|
16734
|
+
for (const root of skillRoots) {
|
|
16434
16735
|
if (!fs7.existsSync(root)) continue;
|
|
16435
16736
|
for (const entry of fs7.readdirSync(root)) {
|
|
16436
|
-
if (!entry.startsWith(
|
|
16437
|
-
const sanitizedProjectId = entry.slice(
|
|
16737
|
+
if (!entry.startsWith(PROJECT_SKILL_PREFIX)) continue;
|
|
16738
|
+
const sanitizedProjectId = entry.slice(PROJECT_SKILL_PREFIX.length);
|
|
16438
16739
|
const isActive = sanitizedActiveProjectIds.has(sanitizedProjectId);
|
|
16439
16740
|
if (isActive) continue;
|
|
16440
16741
|
const skillDir = path7.join(root, entry);
|
|
@@ -16453,10 +16754,10 @@ function installSkills(options = { type: "base" }) {
|
|
|
16453
16754
|
}
|
|
16454
16755
|
];
|
|
16455
16756
|
if (options.type === "project") {
|
|
16456
|
-
const projectSkill =
|
|
16757
|
+
const projectSkill = buildProjectSkill(
|
|
16457
16758
|
options.projectId,
|
|
16458
16759
|
options.agentContext,
|
|
16459
|
-
options.
|
|
16760
|
+
options.agentPrompt
|
|
16460
16761
|
);
|
|
16461
16762
|
skills.push({
|
|
16462
16763
|
name: projectSkill.skillName,
|
|
@@ -16559,14 +16860,14 @@ __name(getAgentContext, "getAgentContext");
|
|
|
16559
16860
|
async function refreshSkillsFromSession(sessionId, projectId) {
|
|
16560
16861
|
try {
|
|
16561
16862
|
debug("skills", "fetching agent system prompt + context...");
|
|
16562
|
-
const [
|
|
16863
|
+
const [agentPrompt, agentContext] = await Promise.all([
|
|
16563
16864
|
getAgentSystemPrompt(sessionId),
|
|
16564
16865
|
getAgentContext(sessionId)
|
|
16565
16866
|
]);
|
|
16566
16867
|
debug("skills", "installing skills...");
|
|
16567
16868
|
installSkills({
|
|
16568
16869
|
type: "project",
|
|
16569
|
-
|
|
16870
|
+
agentPrompt,
|
|
16570
16871
|
projectId,
|
|
16571
16872
|
agentContext
|
|
16572
16873
|
});
|
|
@@ -16655,7 +16956,7 @@ async function execAndPrint(sessionId, code) {
|
|
|
16655
16956
|
}
|
|
16656
16957
|
if (result.error) {
|
|
16657
16958
|
printError(
|
|
16658
|
-
`Error: ${formatErrorForUser(errorWithRef(result.error, result.errorRef))}`
|
|
16959
|
+
`Error: ${formatErrorForUser(errorWithRef(result.error, result.errorRef), { fromExecResult: true })}`
|
|
16659
16960
|
);
|
|
16660
16961
|
await waitForTrackingToFinish();
|
|
16661
16962
|
process.exit(1);
|
|
@@ -16733,6 +17034,7 @@ session.command("new <projectUrlOrId>").description("Create a new session and pr
|
|
|
16733
17034
|
).action(
|
|
16734
17035
|
async (projectUrlOrId, options) => {
|
|
16735
17036
|
const projectId = extractProjectId(projectUrlOrId);
|
|
17037
|
+
const agentThreadContext = resolveAgentThreadContext();
|
|
16736
17038
|
let sessionId;
|
|
16737
17039
|
let userId;
|
|
16738
17040
|
try {
|
|
@@ -16757,7 +17059,8 @@ session.command("new <projectUrlOrId>").description("Create a new session and pr
|
|
|
16757
17059
|
projectId,
|
|
16758
17060
|
apiKey: credentials.apiKey,
|
|
16759
17061
|
userId: credentials.userId,
|
|
16760
|
-
headlessServerUrl
|
|
17062
|
+
headlessServerUrl,
|
|
17063
|
+
...agentThreadContext
|
|
16761
17064
|
});
|
|
16762
17065
|
sessionId = result.id;
|
|
16763
17066
|
debug("session.new", `session created id=${sessionId}`);
|
|
@@ -16811,6 +17114,7 @@ session.command("new <projectUrlOrId>").description("Create a new session and pr
|
|
|
16811
17114
|
errorType: "session_new_error",
|
|
16812
17115
|
projectId,
|
|
16813
17116
|
userId,
|
|
17117
|
+
...agentThreadContext,
|
|
16814
17118
|
errorMessage: message
|
|
16815
17119
|
});
|
|
16816
17120
|
printError(`Failed to create session: ${formatErrorForUser(err)}`);
|
|
@@ -16937,6 +17241,13 @@ program.command("setup").description(
|
|
|
16937
17241
|
"Install Framer skills into ~/.agents/skills and ~/.claude/skills"
|
|
16938
17242
|
).action(async () => {
|
|
16939
17243
|
try {
|
|
17244
|
+
try {
|
|
17245
|
+
cleanupLegacyCanvasEditingSkills();
|
|
17246
|
+
} catch (error) {
|
|
17247
|
+
printWarning(
|
|
17248
|
+
`Failed to clean up legacy canvas editing skills: ${formatErrorForUser(error)}`
|
|
17249
|
+
);
|
|
17250
|
+
}
|
|
16940
17251
|
const results = installSkills();
|
|
16941
17252
|
await waitForClaudeCodeSkillDiscovery();
|
|
16942
17253
|
printSetupSummary(results);
|