framer-dalton 0.0.28 → 0.0.30
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 +3 -3
- package/dist/cli.js +293 -25
- package/dist/start-relay-server.js +110 -40
- package/docs/skills/framer-project.md +20 -20
- package/docs/skills/framer.md +11 -11
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ CLI and agent skills for interacting with Framer projects via the Framer Server
|
|
|
7
7
|
Install or refresh the globally available skills:
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
npx framer
|
|
10
|
+
npx @framer/agent@latest setup
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
That installs skills into `~/.agents/skills` and `~/.claude/skills`.
|
|
@@ -20,9 +20,9 @@ The CLI and the installed skills are meant to work together. There are three ski
|
|
|
20
20
|
- `framer-code-components` - explains specific prompts for how to write code components and provides examples.
|
|
21
21
|
- `framer-project-<project id>` - a dynamically-created skill for interacting with a specific project.
|
|
22
22
|
|
|
23
|
-
1. Running `npx framer
|
|
23
|
+
1. Running `npx @framer/agent@latest setup` will install the base `framer` and `framer-code-components` skills.
|
|
24
24
|
2. The frontmatter of `framer-code-components` tells agents to always load `framer` and the generated project skill first.
|
|
25
|
-
3. The frontmatter of `framer` tells agents to run `npx framer
|
|
25
|
+
3. The frontmatter of `framer` tells agents to run `npx @framer/agent@latest setup` BEFORE loading the skill. This command will auto-update the cli and update the skill files.
|
|
26
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.agent.getSystemPrompt` and the latest project context from `framer.agent.getContext`. Agents must load this project-specific skill immediately after creating a session before doing any connected-project work.
|
|
27
27
|
|
|
28
28
|
## Local Development
|
package/dist/cli.js
CHANGED
|
@@ -12,12 +12,12 @@ 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.30 */
|
|
16
16
|
var __defProp = Object.defineProperty;
|
|
17
17
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
18
18
|
|
|
19
19
|
// package.json
|
|
20
|
-
var name = "framer
|
|
20
|
+
var name = "@framer/agent";
|
|
21
21
|
|
|
22
22
|
// src/check-node-version.ts
|
|
23
23
|
var MINIMUM_NODE_VERSION = 22;
|
|
@@ -375,7 +375,7 @@ __name(markTelemetryNoticeShown, "markTelemetryNoticeShown");
|
|
|
375
375
|
// src/version.ts
|
|
376
376
|
var VERSION = (
|
|
377
377
|
// typeof is used to ensure this can be used just via tsx or node etc. without build
|
|
378
|
-
"0.0.
|
|
378
|
+
"0.0.30"
|
|
379
379
|
);
|
|
380
380
|
var trackingEndpoint = "https://events.framer.com/track";
|
|
381
381
|
var inProgressTrackings = /* @__PURE__ */ new Set();
|
|
@@ -592,7 +592,7 @@ function formatErrorForUser(error, opts) {
|
|
|
592
592
|
const rehydrated = rehydrateFramerAPIError(error);
|
|
593
593
|
if (rehydrated) return rehydrated.toString();
|
|
594
594
|
if (!opts?.fromExecResult && isRelayConnectionLost(error))
|
|
595
|
-
return "Connection to the Framer server was lost. Run `framer session new` to reconnect.";
|
|
595
|
+
return "Connection to the Framer server was lost. Run `npx @framer/agent@latest session new` to reconnect.";
|
|
596
596
|
const ref = getErrorRef(error);
|
|
597
597
|
return ref ? `${error.message} [ref: ${ref}]` : error.message;
|
|
598
598
|
}
|
|
@@ -855,7 +855,7 @@ function runBrowserAuthFlow(options) {
|
|
|
855
855
|
printError(` 1. Open Site Settings \u2192 General: ${settingsUrl}`);
|
|
856
856
|
printError(" 2. Scroll to API Keys and create a new key");
|
|
857
857
|
printError(
|
|
858
|
-
` 3. In another terminal, run: framer project auth ${pollProjectId} <your-api-key>`
|
|
858
|
+
` 3. In another terminal, run: npx @framer/agent@latest project auth ${pollProjectId} <your-api-key>`
|
|
859
859
|
);
|
|
860
860
|
print("");
|
|
861
861
|
print("Waiting for authorization in browser...");
|
|
@@ -869,7 +869,7 @@ function runBrowserAuthFlow(options) {
|
|
|
869
869
|
});
|
|
870
870
|
reject(
|
|
871
871
|
new Error(
|
|
872
|
-
pollProjectId ? "Browser authorization timed out. Use `framer project auth <projectUrlOrId> <apiKey>` instead." : "Browser authorization timed out."
|
|
872
|
+
pollProjectId ? "Browser authorization timed out. Use `npx @framer/agent@latest project auth <projectUrlOrId> <apiKey>` instead." : "Browser authorization timed out."
|
|
873
873
|
)
|
|
874
874
|
);
|
|
875
875
|
});
|
|
@@ -1070,6 +1070,114 @@ var types = {
|
|
|
1070
1070
|
}
|
|
1071
1071
|
]
|
|
1072
1072
|
},
|
|
1073
|
+
agentbranch: {
|
|
1074
|
+
name: "AgentBranch",
|
|
1075
|
+
description: "",
|
|
1076
|
+
kind: "interface",
|
|
1077
|
+
references: [],
|
|
1078
|
+
members: [
|
|
1079
|
+
{
|
|
1080
|
+
name: "id",
|
|
1081
|
+
type: "string",
|
|
1082
|
+
description: "",
|
|
1083
|
+
optional: false
|
|
1084
|
+
},
|
|
1085
|
+
{
|
|
1086
|
+
name: "title",
|
|
1087
|
+
type: "string",
|
|
1088
|
+
description: "",
|
|
1089
|
+
optional: false
|
|
1090
|
+
},
|
|
1091
|
+
{
|
|
1092
|
+
name: "url",
|
|
1093
|
+
type: "string",
|
|
1094
|
+
description: "",
|
|
1095
|
+
optional: false
|
|
1096
|
+
},
|
|
1097
|
+
{
|
|
1098
|
+
name: "baseId",
|
|
1099
|
+
type: "string",
|
|
1100
|
+
description: "",
|
|
1101
|
+
optional: true
|
|
1102
|
+
},
|
|
1103
|
+
{
|
|
1104
|
+
name: "joined",
|
|
1105
|
+
type: "boolean",
|
|
1106
|
+
description: "",
|
|
1107
|
+
optional: false
|
|
1108
|
+
}
|
|
1109
|
+
]
|
|
1110
|
+
},
|
|
1111
|
+
agentbranchchange: {
|
|
1112
|
+
name: "AgentBranchChange",
|
|
1113
|
+
description: "",
|
|
1114
|
+
kind: "interface",
|
|
1115
|
+
references: ["AgentBranchCollectionItemChange"],
|
|
1116
|
+
members: [
|
|
1117
|
+
{
|
|
1118
|
+
name: "type",
|
|
1119
|
+
type: '"page" | "collection" | "designPage" | "siteSettings" | "component" | "layoutTemplate"',
|
|
1120
|
+
description: "",
|
|
1121
|
+
optional: false
|
|
1122
|
+
},
|
|
1123
|
+
{
|
|
1124
|
+
name: "nodeId",
|
|
1125
|
+
type: "string",
|
|
1126
|
+
description: "",
|
|
1127
|
+
optional: false
|
|
1128
|
+
},
|
|
1129
|
+
{
|
|
1130
|
+
name: "name",
|
|
1131
|
+
type: "string",
|
|
1132
|
+
description: "",
|
|
1133
|
+
optional: false
|
|
1134
|
+
},
|
|
1135
|
+
{
|
|
1136
|
+
name: "isRemoved",
|
|
1137
|
+
type: "boolean",
|
|
1138
|
+
description: "",
|
|
1139
|
+
optional: false
|
|
1140
|
+
},
|
|
1141
|
+
{
|
|
1142
|
+
name: "items",
|
|
1143
|
+
type: "AgentBranchCollectionItemChange[]",
|
|
1144
|
+
description: "",
|
|
1145
|
+
optional: true
|
|
1146
|
+
},
|
|
1147
|
+
{
|
|
1148
|
+
name: "settingsTab",
|
|
1149
|
+
type: '"general" | "redirects"',
|
|
1150
|
+
description: "",
|
|
1151
|
+
optional: true
|
|
1152
|
+
}
|
|
1153
|
+
]
|
|
1154
|
+
},
|
|
1155
|
+
agentbranchcollectionitemchange: {
|
|
1156
|
+
name: "AgentBranchCollectionItemChange",
|
|
1157
|
+
description: "",
|
|
1158
|
+
kind: "interface",
|
|
1159
|
+
references: [],
|
|
1160
|
+
members: [
|
|
1161
|
+
{
|
|
1162
|
+
name: "nodeId",
|
|
1163
|
+
type: "string",
|
|
1164
|
+
description: "",
|
|
1165
|
+
optional: false
|
|
1166
|
+
},
|
|
1167
|
+
{
|
|
1168
|
+
name: "name",
|
|
1169
|
+
type: "string",
|
|
1170
|
+
description: "",
|
|
1171
|
+
optional: false
|
|
1172
|
+
},
|
|
1173
|
+
{
|
|
1174
|
+
name: "isRemoved",
|
|
1175
|
+
type: "boolean",
|
|
1176
|
+
description: "",
|
|
1177
|
+
optional: false
|
|
1178
|
+
}
|
|
1179
|
+
]
|
|
1180
|
+
},
|
|
1073
1181
|
agentclarificationanswer: {
|
|
1074
1182
|
name: "AgentClarificationAnswer",
|
|
1075
1183
|
description: "",
|
|
@@ -4374,6 +4482,20 @@ var types = {
|
|
|
4374
4482
|
alias: "PixelNumber | `${PixelNumber} ${PixelNumber} ${PixelNumber} ${PixelNumber}`",
|
|
4375
4483
|
references: ["PixelNumber"]
|
|
4376
4484
|
},
|
|
4485
|
+
framerreconnectoptions: {
|
|
4486
|
+
name: "FramerReconnectOptions",
|
|
4487
|
+
description: "",
|
|
4488
|
+
kind: "interface",
|
|
4489
|
+
references: [],
|
|
4490
|
+
members: [
|
|
4491
|
+
{
|
|
4492
|
+
name: "expectedInitialBranchId",
|
|
4493
|
+
type: "string",
|
|
4494
|
+
description: "",
|
|
4495
|
+
optional: true
|
|
4496
|
+
}
|
|
4497
|
+
]
|
|
4498
|
+
},
|
|
4377
4499
|
fusednumber: {
|
|
4378
4500
|
name: "FusedNumber",
|
|
4379
4501
|
description: "",
|
|
@@ -7338,6 +7460,66 @@ var types = {
|
|
|
7338
7460
|
description: "@alpha",
|
|
7339
7461
|
optional: false
|
|
7340
7462
|
},
|
|
7463
|
+
{
|
|
7464
|
+
name: "getActiveBranchForAgent",
|
|
7465
|
+
type: "() => Promise<AgentBranch>",
|
|
7466
|
+
description: "@alpha",
|
|
7467
|
+
optional: false
|
|
7468
|
+
},
|
|
7469
|
+
{
|
|
7470
|
+
name: "getBranchesForAgent",
|
|
7471
|
+
type: "() => Promise<AgentBranch[]>",
|
|
7472
|
+
description: "@alpha",
|
|
7473
|
+
optional: false
|
|
7474
|
+
},
|
|
7475
|
+
{
|
|
7476
|
+
name: "getBranchChangesForAgent",
|
|
7477
|
+
type: "(branchId: string) => Promise<AgentBranchChange[]>",
|
|
7478
|
+
description: "@alpha",
|
|
7479
|
+
optional: false
|
|
7480
|
+
},
|
|
7481
|
+
{
|
|
7482
|
+
name: "switchBranchForAgent",
|
|
7483
|
+
type: "(branchId: string) => Promise<void>",
|
|
7484
|
+
description: "@alpha",
|
|
7485
|
+
optional: false
|
|
7486
|
+
},
|
|
7487
|
+
{
|
|
7488
|
+
name: "createBranchForAgent",
|
|
7489
|
+
type: "(title?: string) => Promise<AgentBranch>",
|
|
7490
|
+
description: "@alpha",
|
|
7491
|
+
optional: false
|
|
7492
|
+
},
|
|
7493
|
+
{
|
|
7494
|
+
name: "mergeBranchForAgent",
|
|
7495
|
+
type: "(targetBranchId?: string) => Promise<void>",
|
|
7496
|
+
description: "@alpha",
|
|
7497
|
+
optional: false
|
|
7498
|
+
},
|
|
7499
|
+
{
|
|
7500
|
+
name: "joinBranchForAgent",
|
|
7501
|
+
type: "(branchId: string) => Promise<void>",
|
|
7502
|
+
description: "@alpha",
|
|
7503
|
+
optional: false
|
|
7504
|
+
},
|
|
7505
|
+
{
|
|
7506
|
+
name: "leaveBranchForAgent",
|
|
7507
|
+
type: "(branchId: string) => Promise<void>",
|
|
7508
|
+
description: "@alpha",
|
|
7509
|
+
optional: false
|
|
7510
|
+
},
|
|
7511
|
+
{
|
|
7512
|
+
name: "renameBranchForAgent",
|
|
7513
|
+
type: "(branchId: string, title: string) => Promise<void>",
|
|
7514
|
+
description: "@alpha",
|
|
7515
|
+
optional: false
|
|
7516
|
+
},
|
|
7517
|
+
{
|
|
7518
|
+
name: "deleteBranchForAgent",
|
|
7519
|
+
type: "(branchId: string) => Promise<void>",
|
|
7520
|
+
description: "@alpha",
|
|
7521
|
+
optional: false
|
|
7522
|
+
},
|
|
7341
7523
|
{
|
|
7342
7524
|
name: "readProjectForAgent",
|
|
7343
7525
|
type: "(queries: Record<string, unknown>[], options?: {\n pagePath?: string;\n }) => Promise<{\n results: unknown[];\n }>",
|
|
@@ -7941,9 +8123,9 @@ var types = {
|
|
|
7941
8123
|
optional: false
|
|
7942
8124
|
},
|
|
7943
8125
|
{
|
|
7944
|
-
name: "
|
|
8126
|
+
name: "framerAgentModel",
|
|
7945
8127
|
type: "string",
|
|
7946
|
-
description: "Inner Framer Agent model override. Defaults to `model` when omitted
|
|
8128
|
+
description: "Inner Framer Agent model override. Defaults to `model` when omitted.\n\nSupports the same `model=effort` syntax. Omitting effort is equivalent to `none`.",
|
|
7947
8129
|
optional: true
|
|
7948
8130
|
},
|
|
7949
8131
|
{
|
|
@@ -7996,6 +8178,18 @@ var types = {
|
|
|
7996
8178
|
description: "JSONL string with one training row per inner-agent step, present when `captureTrainingData` was set.",
|
|
7997
8179
|
optional: true
|
|
7998
8180
|
},
|
|
8181
|
+
{
|
|
8182
|
+
name: "traceFilename",
|
|
8183
|
+
type: "string",
|
|
8184
|
+
description: "Replay trace filename, present when the inner agent produced at least one model step.",
|
|
8185
|
+
optional: true
|
|
8186
|
+
},
|
|
8187
|
+
{
|
|
8188
|
+
name: "traceJson",
|
|
8189
|
+
type: "string",
|
|
8190
|
+
description: "Replay trace JSON with system, tools, and final ModelMessage conversation.",
|
|
8191
|
+
optional: true
|
|
8192
|
+
},
|
|
7999
8193
|
{
|
|
8000
8194
|
name: "fixtureFilename",
|
|
8001
8195
|
type: "string",
|
|
@@ -13933,6 +14127,20 @@ var methodsByCategory = {
|
|
|
13933
14127
|
description: 'Applies commands to the canvas to create, update, remove, move, or duplicate nodes.\n\nThe command syntax is documented in the string returned by {@link getSystemPrompt}.\nEach call is scoped to a single page.\n\n@param dsl - A string of commands separated by `;`. See {@link getSystemPrompt} for syntax.\n@param options.pagePath - Target page path (e.g. `"/about"`). Defaults to the active page.',
|
|
13934
14128
|
references: []
|
|
13935
14129
|
},
|
|
14130
|
+
{
|
|
14131
|
+
name: "createBranch",
|
|
14132
|
+
category: "FramerAgentAPI",
|
|
14133
|
+
signature: "createBranch(title?: string): Promise<AgentBranch>",
|
|
14134
|
+
description: "Creates a branch and switches to it.\n\n@param title - Optional branch title. The new branch is created from the active branch.\n@returns The created branch.",
|
|
14135
|
+
references: ["AgentBranch"]
|
|
14136
|
+
},
|
|
14137
|
+
{
|
|
14138
|
+
name: "deleteBranch",
|
|
14139
|
+
category: "FramerAgentAPI",
|
|
14140
|
+
signature: "deleteBranch(branchId: string): Promise<void>",
|
|
14141
|
+
description: "Deletes a branch.\n\n@param branchId - Branch id to delete.",
|
|
14142
|
+
references: []
|
|
14143
|
+
},
|
|
13936
14144
|
{
|
|
13937
14145
|
name: "flattenComponentInstance",
|
|
13938
14146
|
category: "FramerAgentAPI",
|
|
@@ -13940,6 +14148,13 @@ var methodsByCategory = {
|
|
|
13940
14148
|
description: 'Flattens a local component instance into raw editable layers.\n\n@param input - `{ id }`: the id of the component instance to flatten.\n@param options.pagePath - Target page path (e.g. `"/about"`). Defaults to the active page.\n@returns The flatten result \u2014 `success` with a `replacementId`, or `blocked` with a reason.',
|
|
13941
14149
|
references: []
|
|
13942
14150
|
},
|
|
14151
|
+
{
|
|
14152
|
+
name: "getActiveBranch",
|
|
14153
|
+
category: "FramerAgentAPI",
|
|
14154
|
+
signature: "getActiveBranch(): Promise<AgentBranch>",
|
|
14155
|
+
description: "Returns the active project branch.\n\n@returns Branch metadata for the active branch.",
|
|
14156
|
+
references: ["AgentBranch"]
|
|
14157
|
+
},
|
|
13943
14158
|
{
|
|
13944
14159
|
name: "getAncestors",
|
|
13945
14160
|
category: "FramerAgentAPI",
|
|
@@ -13947,11 +14162,25 @@ var methodsByCategory = {
|
|
|
13947
14162
|
description: 'Get every ancestor of a node, from the direct parent up to the page root.\n\n@param input - `{ id }`: the id of the node to start from.\n@param options.pagePath - Target page path (e.g. `"/about"`). Defaults to the active page.\n@returns The ancestors ordered from closest parent to the page root.',
|
|
13948
14163
|
references: []
|
|
13949
14164
|
},
|
|
14165
|
+
{
|
|
14166
|
+
name: "getBranchChanges",
|
|
14167
|
+
category: "FramerAgentAPI",
|
|
14168
|
+
signature: "getBranchChanges(branchId: string): Promise<AgentBranchChange[]>",
|
|
14169
|
+
description: "Returns a summarized changelog for a branch.\n\n@param branchId - Branch id.",
|
|
14170
|
+
references: ["AgentBranchChange"]
|
|
14171
|
+
},
|
|
14172
|
+
{
|
|
14173
|
+
name: "getBranches",
|
|
14174
|
+
category: "FramerAgentAPI",
|
|
14175
|
+
signature: "getBranches(): Promise<AgentBranch[]>",
|
|
14176
|
+
description: "Returns the project branches.\n\n@returns Branch metadata for each available branch.",
|
|
14177
|
+
references: ["AgentBranch"]
|
|
14178
|
+
},
|
|
13950
14179
|
{
|
|
13951
14180
|
name: "getContext",
|
|
13952
14181
|
category: "FramerAgentAPI",
|
|
13953
|
-
signature: "getContext(
|
|
13954
|
-
description:
|
|
14182
|
+
signature: "getContext(): Promise<string>",
|
|
14183
|
+
description: "Returns the dynamic project context as a string.\n\nThe context includes project-specific data:\n- **Available fonts** \u2014 font families loaded in the project.\n- **Components** \u2014 component names and their controls.\n- **Design tokens** \u2014 color tokens defined in the project.\n- **Style presets** \u2014 text style presets defined in the project.\n- **Icon sets** \u2014 available icon sets and their definitions.\n\nThis data changes per project. Pair with the static prompt\nfrom {@link getSystemPrompt} for complete agent context.\n\n@returns A string containing the project context.",
|
|
13955
14184
|
references: []
|
|
13956
14185
|
},
|
|
13957
14186
|
{
|
|
@@ -14003,6 +14232,20 @@ var methodsByCategory = {
|
|
|
14003
14232
|
description: "Returns the static agent system prompt as a string.\n\nThe prompt includes:\n- **Command reference** \u2014 syntax for adding, updating, removing, moving, and duplicating nodes.\n- **Design rules** \u2014 spacing, layout, typography, and responsive design guidance.\n- **Examples** \u2014 common UI patterns expressed as commands.\n- **`readProject` query reference** \u2014 available query types and their parameters.\n\nThis is the sole documentation for the command syntax used by {@link applyChanges}\nand the query types used by {@link readProject}.\n\nThe prompt is static and does not depend on any specific project.\nCall {@link getContext} to get the project-specific context.\n\n@returns A string containing the agent system prompt.",
|
|
14004
14233
|
references: []
|
|
14005
14234
|
},
|
|
14235
|
+
{
|
|
14236
|
+
name: "joinBranch",
|
|
14237
|
+
category: "FramerAgentAPI",
|
|
14238
|
+
signature: "joinBranch(branchId: string): Promise<void>",
|
|
14239
|
+
description: "Joins a branch as a contributor so the current user can edit it when permissions allow.\n\n@param branchId - Branch id to join.",
|
|
14240
|
+
references: []
|
|
14241
|
+
},
|
|
14242
|
+
{
|
|
14243
|
+
name: "leaveBranch",
|
|
14244
|
+
category: "FramerAgentAPI",
|
|
14245
|
+
signature: "leaveBranch(branchId: string): Promise<void>",
|
|
14246
|
+
description: "Leaves a branch as a contributor.\n\n@param branchId - Branch id to leave.",
|
|
14247
|
+
references: []
|
|
14248
|
+
},
|
|
14006
14249
|
{
|
|
14007
14250
|
name: "makeExternalComponentLocal",
|
|
14008
14251
|
category: "FramerAgentAPI",
|
|
@@ -14010,6 +14253,13 @@ var methodsByCategory = {
|
|
|
14010
14253
|
description: 'Converts an external component into a local project component.\n\n@param input - `{ id, replaceAll? }`: the id of the external instance, and whether to replace all instances.\n@param options.pagePath - Target page path (e.g. `"/about"`). Defaults to the active page.\n@returns `success` (with the local component name), `needs_confirmation` (retry with `replaceAll`), or `blocked` with a reason.',
|
|
14011
14254
|
references: []
|
|
14012
14255
|
},
|
|
14256
|
+
{
|
|
14257
|
+
name: "mergeBranch",
|
|
14258
|
+
category: "FramerAgentAPI",
|
|
14259
|
+
signature: "mergeBranch(targetBranchId?: string): Promise<void>",
|
|
14260
|
+
description: 'Merges the active branch into its base or a target branch and switches to the target.\n\n@param targetBranchId - Optional target branch id. Use `"main"` for the main branch.',
|
|
14261
|
+
references: []
|
|
14262
|
+
},
|
|
14013
14263
|
{
|
|
14014
14264
|
name: "paginate",
|
|
14015
14265
|
category: "FramerAgentAPI",
|
|
@@ -14034,29 +14284,29 @@ var methodsByCategory = {
|
|
|
14034
14284
|
{
|
|
14035
14285
|
name: "readComponentControls",
|
|
14036
14286
|
category: "FramerAgentAPI",
|
|
14037
|
-
signature: "readComponentControls(input: { componentIds: readonly string[]; }
|
|
14038
|
-
description:
|
|
14287
|
+
signature: "readComponentControls(input: { componentIds: readonly string[]; }): Promise<unknown>",
|
|
14288
|
+
description: "Reads control definitions for project components.\n\n@param input - `{ componentIds }`: component ids from project context or previous reads.\n@returns The matching component control definitions.",
|
|
14039
14289
|
references: []
|
|
14040
14290
|
},
|
|
14041
14291
|
{
|
|
14042
14292
|
name: "readIcons",
|
|
14043
14293
|
category: "FramerAgentAPI",
|
|
14044
|
-
signature: "readIcons(input: { iconSetName: string; }
|
|
14045
|
-
description:
|
|
14294
|
+
signature: "readIcons(input: { iconSetName: string; }): Promise<string[]>",
|
|
14295
|
+
description: "Reads exact icon names for an icon set.\n\n@param input - `{ iconSetName }`: icon set name from project context or previous reads.\n@returns The available icon names in that set.",
|
|
14046
14296
|
references: []
|
|
14047
14297
|
},
|
|
14048
14298
|
{
|
|
14049
14299
|
name: "readIconSetControls",
|
|
14050
14300
|
category: "FramerAgentAPI",
|
|
14051
|
-
signature: "readIconSetControls(input: { iconSetNames: readonly string[]; }
|
|
14052
|
-
description:
|
|
14301
|
+
signature: "readIconSetControls(input: { iconSetNames: readonly string[]; }): Promise<unknown>",
|
|
14302
|
+
description: "Reads icon-set control definitions.\n\n@param input - `{ iconSetNames }`: icon set names from project context or previous reads.\n@returns The matching icon-set control definitions.",
|
|
14053
14303
|
references: []
|
|
14054
14304
|
},
|
|
14055
14305
|
{
|
|
14056
14306
|
name: "readLayoutTemplateControls",
|
|
14057
14307
|
category: "FramerAgentAPI",
|
|
14058
|
-
signature: "readLayoutTemplateControls(input: { layoutTemplateIds: readonly string[]; }
|
|
14059
|
-
description:
|
|
14308
|
+
signature: "readLayoutTemplateControls(input: { layoutTemplateIds: readonly string[]; }): Promise<unknown>",
|
|
14309
|
+
description: "Reads layout-template control definitions.\n\n@param input - `{ layoutTemplateIds }`: layout template ids from project context or previous reads.\n@returns The matching layout-template control definitions.",
|
|
14060
14310
|
references: []
|
|
14061
14311
|
},
|
|
14062
14312
|
{
|
|
@@ -14069,15 +14319,22 @@ var methodsByCategory = {
|
|
|
14069
14319
|
{
|
|
14070
14320
|
name: "readShaderControls",
|
|
14071
14321
|
category: "FramerAgentAPI",
|
|
14072
|
-
signature: "readShaderControls(input: { shaderNames: readonly string[]; }
|
|
14073
|
-
description:
|
|
14322
|
+
signature: "readShaderControls(input: { shaderNames: readonly string[]; }): Promise<unknown>",
|
|
14323
|
+
description: "Reads shader control definitions.\n\n@param input - `{ shaderNames }`: shader names from project context or previous reads.\n@returns The matching shader control definitions.",
|
|
14324
|
+
references: []
|
|
14325
|
+
},
|
|
14326
|
+
{
|
|
14327
|
+
name: "renameBranch",
|
|
14328
|
+
category: "FramerAgentAPI",
|
|
14329
|
+
signature: "renameBranch(branchId: string, title: string): Promise<void>",
|
|
14330
|
+
description: "Renames a branch.\n\n@param branchId - Branch id to rename.\n@param title - New branch title.",
|
|
14074
14331
|
references: []
|
|
14075
14332
|
},
|
|
14076
14333
|
{
|
|
14077
14334
|
name: "reviewChanges",
|
|
14078
14335
|
category: "FramerAgentAPI",
|
|
14079
|
-
signature: "reviewChanges(
|
|
14080
|
-
description: "Reviews changes made by prior {@link applyChanges} calls in this session.\n\nConsumes accumulated diagnostics from the session's agent context.\n\n@
|
|
14336
|
+
signature: "reviewChanges(): Promise<unknown>",
|
|
14337
|
+
description: "Reviews changes made by prior {@link applyChanges} calls in this session.\n\nConsumes accumulated diagnostics from the session's agent context.\n\n@returns The session's accumulated changes, errors, warnings, and deferred trait reports.",
|
|
14081
14338
|
references: []
|
|
14082
14339
|
},
|
|
14083
14340
|
{
|
|
@@ -14093,6 +14350,13 @@ var methodsByCategory = {
|
|
|
14093
14350
|
signature: "serializeNodes(input: { ids: readonly string[]; depth?: number; attributeFilter?: readonly string[]; ancestorPath?: boolean; }, options?: { pagePath?: string; }): Promise<unknown>",
|
|
14094
14351
|
description: 'Serialize multiple nodes on the page. Ids that don\'t resolve to a node are skipped.\n\n@param input - `{ ids }` plus optional serialization options:\n - `depth` \u2014 limit how many descendant levels to include.\n - `attributeFilter` \u2014 only return the listed attributes per node.\n - `ancestorPath` \u2014 also return the path of ancestors up to the page root.\n@param options.pagePath - Target page path (e.g. `"/about"`). Defaults to the active page.\n@returns The serialized nodes that were found, in input order.',
|
|
14095
14352
|
references: []
|
|
14353
|
+
},
|
|
14354
|
+
{
|
|
14355
|
+
name: "switchBranch",
|
|
14356
|
+
category: "FramerAgentAPI",
|
|
14357
|
+
signature: "switchBranch(branchId: string): Promise<void>",
|
|
14358
|
+
description: 'Switches the active project branch.\n\n@param branchId - Branch id. Use `"main"` for the main branch.',
|
|
14359
|
+
references: []
|
|
14096
14360
|
}
|
|
14097
14361
|
],
|
|
14098
14362
|
framerapierror: [
|
|
@@ -16197,7 +16461,7 @@ function renderDocs(queries) {
|
|
|
16197
16461
|
const suggestion = findClosestMatch(query, methodNames);
|
|
16198
16462
|
const hint = suggestion ? ` Did you mean '${suggestion}'?` : "";
|
|
16199
16463
|
errors.push(
|
|
16200
|
-
`Method '${query}' not found.${hint} Use 'framer docs' to list all.`
|
|
16464
|
+
`Method '${query}' not found.${hint} Use 'npx @framer/agent@latest docs' to list all.`
|
|
16201
16465
|
);
|
|
16202
16466
|
return { lines, errors };
|
|
16203
16467
|
}
|
|
@@ -16246,7 +16510,7 @@ ${typeDef}`);
|
|
|
16246
16510
|
const suggestion = findClosestMatch(query, bareNames);
|
|
16247
16511
|
const hint = suggestion ? ` Did you mean '${suggestion}'?` : "";
|
|
16248
16512
|
errors.push(
|
|
16249
|
-
`'${query}' not found.${hint} Use 'framer docs' to list all.`
|
|
16513
|
+
`'${query}' not found.${hint} Use 'npx @framer/agent@latest docs' to list all.`
|
|
16250
16514
|
);
|
|
16251
16515
|
return { lines, errors };
|
|
16252
16516
|
}
|
|
@@ -16639,7 +16903,7 @@ function installSkills(options = { type: "base" }) {
|
|
|
16639
16903
|
__name(installSkills, "installSkills");
|
|
16640
16904
|
|
|
16641
16905
|
// src/cli.ts
|
|
16642
|
-
var PROGRAM_NAME = "framer
|
|
16906
|
+
var PROGRAM_NAME = "@framer/agent";
|
|
16643
16907
|
var program = new Command();
|
|
16644
16908
|
program.name(PROGRAM_NAME).version(VERSION).description("Framer Server API CLI").option("--debug", "Enable debug logging").hook("preAction", (thisCommand) => {
|
|
16645
16909
|
const opts = thisCommand.opts();
|
|
@@ -16814,6 +17078,10 @@ async function execAndPrint(sessionId, code) {
|
|
|
16814
17078
|
for (const line of result.output) {
|
|
16815
17079
|
print(line);
|
|
16816
17080
|
}
|
|
17081
|
+
if (result.branchChange) {
|
|
17082
|
+
const { title, id, url } = result.branchChange.activeBranch;
|
|
17083
|
+
print(`[FRAMER_BRANCH_CHANGE] title="${title}" id="${id}" url="${url}"`);
|
|
17084
|
+
}
|
|
16817
17085
|
if (result.error) {
|
|
16818
17086
|
printError(
|
|
16819
17087
|
`Error: ${formatErrorForUser(errorWithRef(result.error, result.errorRef), { fromExecResult: true })}`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs4 from 'fs';
|
|
2
2
|
import os5 from 'os';
|
|
3
|
-
import
|
|
3
|
+
import path5 from 'path';
|
|
4
4
|
import { execFileSync } from 'child_process';
|
|
5
5
|
import 'net';
|
|
6
6
|
import { fileURLToPath } from 'url';
|
|
@@ -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.30 */
|
|
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) => {
|
|
@@ -60,7 +60,7 @@ var __callDispose = (stack, error, hasError) => {
|
|
|
60
60
|
};
|
|
61
61
|
|
|
62
62
|
// package.json
|
|
63
|
-
var name = "framer
|
|
63
|
+
var name = "@framer/agent";
|
|
64
64
|
|
|
65
65
|
// src/check-node-version.ts
|
|
66
66
|
var MINIMUM_NODE_VERSION = 22;
|
|
@@ -79,16 +79,16 @@ var maxLogFileBytes = DEFAULT_MAX_LOG_FILE_BYTES;
|
|
|
79
79
|
var retainedLogFileBytes = DEFAULT_RETAINED_LOG_FILE_BYTES;
|
|
80
80
|
function getLogPath() {
|
|
81
81
|
if (process.env.XDG_STATE_HOME) {
|
|
82
|
-
return
|
|
82
|
+
return path5.join(process.env.XDG_STATE_HOME, "framer", "relay.log");
|
|
83
83
|
}
|
|
84
84
|
if (process.platform === "win32") {
|
|
85
|
-
return
|
|
85
|
+
return path5.join(
|
|
86
86
|
process.env.APPDATA || os5.homedir(),
|
|
87
87
|
"framer",
|
|
88
88
|
"relay.log"
|
|
89
89
|
);
|
|
90
90
|
}
|
|
91
|
-
return
|
|
91
|
+
return path5.join(os5.homedir(), ".local", "state", "framer", "relay.log");
|
|
92
92
|
}
|
|
93
93
|
__name(getLogPath, "getLogPath");
|
|
94
94
|
var logPath = getLogPath();
|
|
@@ -96,7 +96,7 @@ var initialized = false;
|
|
|
96
96
|
var logEntriesSinceTrimCheck = LOG_TRIM_CHECK_INTERVAL - 1;
|
|
97
97
|
function ensureLogDir() {
|
|
98
98
|
if (initialized) return;
|
|
99
|
-
const dir =
|
|
99
|
+
const dir = path5.dirname(logPath);
|
|
100
100
|
fs4.mkdirSync(dir, { recursive: true });
|
|
101
101
|
initialized = true;
|
|
102
102
|
}
|
|
@@ -143,12 +143,12 @@ function log(message) {
|
|
|
143
143
|
__name(log, "log");
|
|
144
144
|
function getConfigDir() {
|
|
145
145
|
if (process.env.XDG_CONFIG_HOME) {
|
|
146
|
-
return
|
|
146
|
+
return path5.join(process.env.XDG_CONFIG_HOME, "framer");
|
|
147
147
|
}
|
|
148
148
|
if (process.platform === "win32") {
|
|
149
|
-
return
|
|
149
|
+
return path5.join(process.env.APPDATA || os5.homedir(), "framer");
|
|
150
150
|
}
|
|
151
|
-
return
|
|
151
|
+
return path5.join(os5.homedir(), ".config", "framer");
|
|
152
152
|
}
|
|
153
153
|
__name(getConfigDir, "getConfigDir");
|
|
154
154
|
function ensureConfigDir() {
|
|
@@ -159,7 +159,7 @@ __name(ensureConfigDir, "ensureConfigDir");
|
|
|
159
159
|
|
|
160
160
|
// src/config/relay-token.ts
|
|
161
161
|
function getRelayTokenPath() {
|
|
162
|
-
return
|
|
162
|
+
return path5.join(getConfigDir(), "relay-token");
|
|
163
163
|
}
|
|
164
164
|
__name(getRelayTokenPath, "getRelayTokenPath");
|
|
165
165
|
function generateRelayToken() {
|
|
@@ -199,12 +199,12 @@ __name(debug, "debug");
|
|
|
199
199
|
// src/version.ts
|
|
200
200
|
var VERSION = (
|
|
201
201
|
// typeof is used to ensure this can be used just via tsx or node etc. without build
|
|
202
|
-
"0.0.
|
|
202
|
+
"0.0.30"
|
|
203
203
|
);
|
|
204
204
|
|
|
205
205
|
// src/relay-client.ts
|
|
206
206
|
var __filename$1 = fileURLToPath(import.meta.url);
|
|
207
|
-
|
|
207
|
+
path5.dirname(__filename$1);
|
|
208
208
|
var RELAY_PORT = Number(process.env.FRAMER_CLI_PORT) || 19988;
|
|
209
209
|
createTRPCClient({
|
|
210
210
|
links: [
|
|
@@ -303,30 +303,74 @@ var ScopedFS = class {
|
|
|
303
303
|
static {
|
|
304
304
|
__name(this, "ScopedFS");
|
|
305
305
|
}
|
|
306
|
-
|
|
306
|
+
allowedRoots;
|
|
307
307
|
constructor(allowedDirs) {
|
|
308
308
|
const defaultDirs = [process.cwd(), "/tmp", os5.tmpdir()];
|
|
309
309
|
const dirs = allowedDirs ?? defaultDirs;
|
|
310
|
-
this.
|
|
310
|
+
this.allowedRoots = [
|
|
311
|
+
...new Set(dirs.map((dir) => this.canonicalizePath(dir)))
|
|
312
|
+
];
|
|
311
313
|
}
|
|
312
|
-
isPathAllowed(
|
|
313
|
-
return this.
|
|
314
|
-
return
|
|
314
|
+
isPathAllowed(candidatePath) {
|
|
315
|
+
return this.allowedRoots.some((allowedRoot) => {
|
|
316
|
+
return candidatePath === allowedRoot || candidatePath.startsWith(allowedRoot + path5.sep);
|
|
315
317
|
});
|
|
316
318
|
}
|
|
317
319
|
resolvePath(filePath) {
|
|
318
|
-
const
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
320
|
+
const resolvedPath = path5.resolve(filePath);
|
|
321
|
+
const canonicalPath = this.canonicalizePath(resolvedPath);
|
|
322
|
+
if (!this.isPathAllowed(canonicalPath)) {
|
|
323
|
+
this.throwOutsideAllowedRoots(filePath);
|
|
324
|
+
}
|
|
325
|
+
return resolvedPath;
|
|
326
|
+
}
|
|
327
|
+
canonicalizePath(inputPath) {
|
|
328
|
+
const resolvedPath = path5.resolve(inputPath);
|
|
329
|
+
const missingSegments = [];
|
|
330
|
+
let ancestorPath = resolvedPath;
|
|
331
|
+
while (true) {
|
|
332
|
+
const realPath = this.resolveRealPath(ancestorPath);
|
|
333
|
+
if (realPath !== null) {
|
|
334
|
+
return path5.resolve(realPath, ...missingSegments);
|
|
335
|
+
}
|
|
336
|
+
if (this.isSymbolicLink(ancestorPath)) {
|
|
337
|
+
this.throwOutsideAllowedRoots(inputPath);
|
|
338
|
+
}
|
|
339
|
+
const parentPath = path5.dirname(ancestorPath);
|
|
340
|
+
if (parentPath === ancestorPath) {
|
|
341
|
+
return resolvedPath;
|
|
342
|
+
}
|
|
343
|
+
missingSegments.unshift(path5.basename(ancestorPath));
|
|
344
|
+
ancestorPath = parentPath;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
resolveRealPath(targetPath) {
|
|
348
|
+
try {
|
|
349
|
+
return path5.resolve(fs4.realpathSync.native(targetPath));
|
|
350
|
+
} catch (error) {
|
|
351
|
+
const errnoError = error;
|
|
352
|
+
if (errnoError.code === "ENOENT") {
|
|
353
|
+
return null;
|
|
354
|
+
}
|
|
327
355
|
throw error;
|
|
328
356
|
}
|
|
329
|
-
|
|
357
|
+
}
|
|
358
|
+
isSymbolicLink(targetPath) {
|
|
359
|
+
try {
|
|
360
|
+
return fs4.lstatSync(targetPath).isSymbolicLink();
|
|
361
|
+
} catch {
|
|
362
|
+
return false;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
throwOutsideAllowedRoots(filePath) {
|
|
366
|
+
const error = new Error(
|
|
367
|
+
`EPERM: operation not permitted, access outside allowed directories: ${filePath}`
|
|
368
|
+
);
|
|
369
|
+
error.code = "EPERM";
|
|
370
|
+
error.errno = -1;
|
|
371
|
+
error.syscall = "access";
|
|
372
|
+
error.path = filePath;
|
|
373
|
+
throw error;
|
|
330
374
|
}
|
|
331
375
|
// Sync methods
|
|
332
376
|
readFileSync = /* @__PURE__ */ __name((filePath, options) => {
|
|
@@ -558,6 +602,7 @@ __name(sandboxedImport, "sandboxedImport");
|
|
|
558
602
|
async function execute(session, code, options = {}) {
|
|
559
603
|
const { cwd } = options;
|
|
560
604
|
const output = [];
|
|
605
|
+
const previousBranch = await readActiveBranch(session);
|
|
561
606
|
const customConsole = {
|
|
562
607
|
log: /* @__PURE__ */ __name((...args) => {
|
|
563
608
|
output.push(args.map((arg) => formatValue(arg)).join(" "));
|
|
@@ -625,7 +670,8 @@ async function execute(session, code, options = {}) {
|
|
|
625
670
|
if (result !== void 0) {
|
|
626
671
|
output.push(formatValue(result));
|
|
627
672
|
}
|
|
628
|
-
|
|
673
|
+
const branchChange = await getBranchChange(session, previousBranch);
|
|
674
|
+
return { output, branchChange };
|
|
629
675
|
} catch (err) {
|
|
630
676
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
631
677
|
const retryable = isRetryableError(err);
|
|
@@ -633,17 +679,33 @@ async function execute(session, code, options = {}) {
|
|
|
633
679
|
session.connection.markDisconnected();
|
|
634
680
|
}
|
|
635
681
|
const errorRef = getErrorRef(err);
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
error: errorMessage,
|
|
639
|
-
...errorRef ? { errorRef } : {},
|
|
640
|
-
...retryable ? { retryable: true } : {}
|
|
641
|
-
};
|
|
682
|
+
const branchChange = await getBranchChange(session, previousBranch);
|
|
683
|
+
return { output, error: errorMessage, errorRef, retryable, branchChange };
|
|
642
684
|
} finally {
|
|
643
685
|
if (timeoutId) clearTimeout(timeoutId);
|
|
644
686
|
}
|
|
645
687
|
}
|
|
646
688
|
__name(execute, "execute");
|
|
689
|
+
async function readActiveBranch(session) {
|
|
690
|
+
try {
|
|
691
|
+
const branch = await session.connection.framer.agent.getActiveBranch();
|
|
692
|
+
session.connection.setActiveBranchId(branch.id);
|
|
693
|
+
return branch;
|
|
694
|
+
} catch {
|
|
695
|
+
return void 0;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
__name(readActiveBranch, "readActiveBranch");
|
|
699
|
+
async function getBranchChange(session, previousBranch) {
|
|
700
|
+
if (!previousBranch) return void 0;
|
|
701
|
+
const activeBranch = await readActiveBranch(session);
|
|
702
|
+
if (!activeBranch || previousBranch.id === activeBranch.id) return void 0;
|
|
703
|
+
return {
|
|
704
|
+
previousBranch,
|
|
705
|
+
activeBranch
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
__name(getBranchChange, "getBranchChange");
|
|
647
709
|
function isReconnectable(err) {
|
|
648
710
|
return isRetryableError(err) || isConnectionError(err instanceof Error ? err.message : String(err));
|
|
649
711
|
}
|
|
@@ -699,15 +761,19 @@ async function executeWithReconnect(session, code, options, execId) {
|
|
|
699
761
|
log(
|
|
700
762
|
`reconnect.failed exec=${execId} session=${session.id} req=${session.connection.framer.requestId} error="${err instanceof Error ? err.message : String(err)}"`
|
|
701
763
|
);
|
|
702
|
-
|
|
764
|
+
const failureResult = connectionFailureResult(
|
|
703
765
|
"Connection lost and failed to reconnect",
|
|
704
766
|
err
|
|
705
767
|
);
|
|
768
|
+
failureResult.branchChange = result.branchChange;
|
|
769
|
+
return failureResult;
|
|
706
770
|
}
|
|
707
771
|
log(
|
|
708
772
|
`reconnect.success exec=${execId} session=${session.id} req=${session.connection.framer.requestId}`
|
|
709
773
|
);
|
|
710
|
-
|
|
774
|
+
const retryResult = await execute(session, code, options);
|
|
775
|
+
retryResult.branchChange ??= result.branchChange;
|
|
776
|
+
return retryResult;
|
|
711
777
|
}
|
|
712
778
|
__name(executeWithReconnect, "executeWithReconnect");
|
|
713
779
|
function formatValue(value) {
|
|
@@ -776,7 +842,7 @@ var SettingsFileSchema = z.object({
|
|
|
776
842
|
telemetryNoticeShown: z.boolean().optional()
|
|
777
843
|
});
|
|
778
844
|
function getSettingsPath() {
|
|
779
|
-
return
|
|
845
|
+
return path5.join(getConfigDir(), "settings.json");
|
|
780
846
|
}
|
|
781
847
|
__name(getSettingsPath, "getSettingsPath");
|
|
782
848
|
var settings;
|
|
@@ -1049,6 +1115,7 @@ var Connection = class _Connection {
|
|
|
1049
1115
|
sessions = [];
|
|
1050
1116
|
status = "connected";
|
|
1051
1117
|
pendingReconnect;
|
|
1118
|
+
activeBranchId;
|
|
1052
1119
|
static async open(projectId, userId, apiKey, headlessServerUrl) {
|
|
1053
1120
|
const framer = await connect(projectId, apiKey, {
|
|
1054
1121
|
clientId: `dalton/${VERSION}`,
|
|
@@ -1062,6 +1129,9 @@ var Connection = class _Connection {
|
|
|
1062
1129
|
markDisconnected() {
|
|
1063
1130
|
this.status = "disconnected";
|
|
1064
1131
|
}
|
|
1132
|
+
setActiveBranchId(branchId) {
|
|
1133
|
+
this.activeBranchId = branchId;
|
|
1134
|
+
}
|
|
1065
1135
|
getServerSessionId() {
|
|
1066
1136
|
return this.framer.sessionId ?? MISSING_SERVER_SESSION_ID;
|
|
1067
1137
|
}
|
|
@@ -1105,7 +1175,7 @@ var Connection = class _Connection {
|
|
|
1105
1175
|
(session) => session.inflight === 0 && now - session.lastActivityAt >= timeoutMs
|
|
1106
1176
|
);
|
|
1107
1177
|
}
|
|
1108
|
-
async reconnect() {
|
|
1178
|
+
async reconnect(expectedInitialBranchId = this.activeBranchId) {
|
|
1109
1179
|
if (this.status === "connected") {
|
|
1110
1180
|
return;
|
|
1111
1181
|
}
|
|
@@ -1113,7 +1183,7 @@ var Connection = class _Connection {
|
|
|
1113
1183
|
return this.pendingReconnect;
|
|
1114
1184
|
}
|
|
1115
1185
|
this.status = "reconnecting";
|
|
1116
|
-
this.pendingReconnect = this.framer.reconnect().then(() => {
|
|
1186
|
+
this.pendingReconnect = this.framer.reconnect({ expectedInitialBranchId }).then(() => {
|
|
1117
1187
|
this.status = "connected";
|
|
1118
1188
|
trackConnectionReconnect({
|
|
1119
1189
|
projectId: this.projectId,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: {{SKILL_NAME}}
|
|
3
3
|
description: "Project-scoped Framer skill for project {{PROJECT_ID}}. Covers project reads, edits, change review, publishing, image sourcing, component operations, and canvas editing. Very important: never load this skill without having already read the `framer` skill and without having already run `session new`, which will dynamically update this skill."
|
|
4
|
-
allowed-tools: ["Bash(npx framer
|
|
4
|
+
allowed-tools: ["Bash(npx @framer/agent:*)", "Bash(npx @framer/agent@latest:*)", "Read({{FRAMER_TEMPORARY_DIR}}/*)", "Write({{FRAMER_TEMPORARY_DIR}}/*)"]
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
## Project Scope
|
|
@@ -15,11 +15,11 @@ Every connected-project task follows these steps:
|
|
|
15
15
|
|
|
16
16
|
### 1. Look up the API (before EVERY new use of an API method)
|
|
17
17
|
|
|
18
|
-
**You MUST run `npx framer
|
|
18
|
+
**You MUST run `npx @framer/agent@latest docs` before writing any code.** Do not guess method names or signatures.
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
|
-
npx framer
|
|
22
|
-
npx framer
|
|
21
|
+
npx @framer/agent@latest docs Collection # What methods exist?
|
|
22
|
+
npx @framer/agent@latest docs Collection.getItems # What are the parameters and return type?
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
### 2. Execute code
|
|
@@ -27,7 +27,7 @@ npx framer-dalton docs Collection.getItems # What are the parameters and return
|
|
|
27
27
|
Only after checking docs, use your write tool to write your code to a unique file under `{{FRAMER_TEMPORARY_DIR}}/`, then execute it with `-f`. Do not create code files with shell heredocs or `cat`. Name each file `<sessionId>-<short-summary>.js` where `<short-summary>` is a brief kebab-case description (e.g., `1-read-collections.js`, `1-add-team-member.js`).
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
|
-
npx framer
|
|
30
|
+
npx @framer/agent@latest exec -s 1 -f {{FRAMER_TEMPORARY_DIR}}/1-read-collections.js
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
### 3. Store results in `state`
|
|
@@ -36,13 +36,12 @@ Always save results you'll need again. Don't repeat API calls.
|
|
|
36
36
|
|
|
37
37
|
## Method Selection
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
Prefer the agent-specific methods `framer.agent.*` over regular plugin API methods (`framer.*`).
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
2. Agent-specific methods, such as `framer.agent.readProject`, `framer.agent.applyChanges`, and `framer.agent.publish`
|
|
43
|
-
3. Generic plugin API methods, such as top-level `framer.*` methods
|
|
41
|
+
For `framer.agent.readProject` and `framer.agent.applyChanges` calls, use the `npx @framer/agent@latest read-project` and `npx @framer/agent@latest apply-changes` subcommands when possible. It's still ok to call those methods in exec scripts if you require something more complex than a plain method call.
|
|
44
42
|
|
|
45
|
-
- Use `framer.agent.
|
|
43
|
+
- Use `framer.agent.getNode`, `framer.agent.getNodes`, `framer.agent.getNodesOfTypes`, `framer.agent.getScopeNode`, `framer.agent.getGroundNode`, `framer.agent.getParentNode`, `framer.agent.getAncestors`, `framer.agent.serialize`, `framer.agent.serializeNodes`, and `framer.agent.paginate` for project tree reads.
|
|
44
|
+
- Do not use `npx @framer/agent@latest read-project` or `framer.agent.readProject` for node tree reads unless you have just checked current docs and confirmed the exact query type. Query shapes such as `{ type: "node-by-id" }` are not valid for the current local API.
|
|
46
45
|
- Use `framer.agent.readComponentControls`, `framer.agent.readIconSetControls`, `framer.agent.readIcons`, `framer.agent.readLayoutTemplateControls`, and `framer.agent.readShaderControls` for reading the controls of components, icon sets, icons, layout templates, and shaders.
|
|
47
46
|
- Use `framer.agent.applyChanges` for page and layout edits. Do not use low-level node APIs like `createNode`, `setAttributes`, or `setRect` for design/layout work.
|
|
48
47
|
- Use `framer.agent.publish` for publishing. Do not use `publish`, `getDeployments`, or `deploy` for normal agent publishing flows.
|
|
@@ -57,6 +56,7 @@ During normal task execution, do not call `framer.agent.getSystemPrompt()` or `f
|
|
|
57
56
|
|
|
58
57
|
- Be concise. Don't narrate implementation details like field IDs, escaping, or internal steps. Just do the work and report what was accomplished in user-facing terms.
|
|
59
58
|
- Use `framer.*` for plugin API calls. Top-level methods are not globals.
|
|
59
|
+
- If command output includes `[FRAMER_BRANCH_CHANGE]`, tell the user the active branch changed and include the `url` value.
|
|
60
60
|
- Before making changes that add/update/delete content that the user has not clearly and explicitly requested in this conversation, inform the user of what you plan to do and why, and ask them to confirm.
|
|
61
61
|
- You do not need to ask for confirmation when carrying out a specific add/update/delete change that the user has already clearly requested (this counts as explicit consent).
|
|
62
62
|
- You do not need to explain or ask for confirmation for non-destructive calls like reading project state.
|
|
@@ -87,7 +87,7 @@ state.collections = await framer.getCollections();
|
|
|
87
87
|
```
|
|
88
88
|
|
|
89
89
|
```bash
|
|
90
|
-
npx framer
|
|
90
|
+
npx @framer/agent@latest exec -s 1 -f {{FRAMER_TEMPORARY_DIR}}/1-get-collections.js
|
|
91
91
|
```
|
|
92
92
|
|
|
93
93
|
```js
|
|
@@ -97,7 +97,7 @@ console.log(state.teamItems.length);
|
|
|
97
97
|
```
|
|
98
98
|
|
|
99
99
|
```bash
|
|
100
|
-
npx framer
|
|
100
|
+
npx @framer/agent@latest exec -s 1 -f {{FRAMER_TEMPORARY_DIR}}/1-get-team-items.js
|
|
101
101
|
```
|
|
102
102
|
|
|
103
103
|
Store anything you'll reference again.
|
|
@@ -141,7 +141,7 @@ Prompting may take a while to complete, so set the command timeout to 10 minutes
|
|
|
141
141
|
Prefer using your write tool to write code to a unique file under `{{FRAMER_TEMPORARY_DIR}}/` and executing it with `-f`. Do not create code files with shell heredocs or `cat`:
|
|
142
142
|
|
|
143
143
|
```bash
|
|
144
|
-
npx framer
|
|
144
|
+
npx @framer/agent@latest exec -s <sessionId> -f {{FRAMER_TEMPORARY_DIR}}/<sessionId>-<short-summary>.js
|
|
145
145
|
```
|
|
146
146
|
|
|
147
147
|
For short snippets, `exec` also accepts `-e <code>` or code piped on stdin.
|
|
@@ -154,24 +154,24 @@ In Windows PowerShell, if an argument contains nested quotes, use a single-quote
|
|
|
154
154
|
$value = @'
|
|
155
155
|
[{"key":"value","filter":["text","$rect"]}]
|
|
156
156
|
'@
|
|
157
|
-
npx framer
|
|
157
|
+
npx @framer/agent@latest <command> --option $value
|
|
158
158
|
```
|
|
159
159
|
|
|
160
160
|
## API Documentation
|
|
161
161
|
|
|
162
162
|
```bash
|
|
163
|
-
npx framer
|
|
164
|
-
npx framer
|
|
165
|
-
npx framer
|
|
166
|
-
npx framer
|
|
167
|
-
npx framer
|
|
163
|
+
npx @framer/agent@latest docs # List all available methods
|
|
164
|
+
npx @framer/agent@latest docs framer.getCollections # Show top level method
|
|
165
|
+
npx @framer/agent@latest docs Collection # Show class with all method signatures
|
|
166
|
+
npx @framer/agent@latest docs Collection.addItems # Show method + recursively expand all referenced types
|
|
167
|
+
npx @framer/agent@latest docs ScreenshotOptions # Show type + recursively expand all referenced types
|
|
168
168
|
```
|
|
169
169
|
|
|
170
170
|
`docs` with no arguments lists available methods. Looking up a class shows its full definition without expanding referenced types. Looking up a specific method or type automatically expands all referenced types recursively.
|
|
171
171
|
|
|
172
172
|
## API Examples
|
|
173
173
|
|
|
174
|
-
**STOP: These are patterns only. Before using any method below, run `npx framer
|
|
174
|
+
**STOP: These are patterns only. Before using any method below, run `npx @framer/agent@latest docs <ClassName>` to verify the current signature.**
|
|
175
175
|
|
|
176
176
|
### Working with Collections (CMS)
|
|
177
177
|
|
package/docs/skills/framer.md
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
name: framer
|
|
3
3
|
description: >
|
|
4
4
|
Use when the user wants to design, edit, or publish a website or web page — creating layouts, editing sections, updating text or images, managing CMS collections and content, syncing external data, creating or modifying code components, managing color and text styles, handling localization, or publishing deployments. Trigger when the user mentions Framer, references their website or web pages, asks to edit designs, update site content, or work with any Framer project — even if they don't explicitly say 'Framer'.
|
|
5
|
-
**Mandatory precondition**: run `npx framer
|
|
6
|
-
allowed-tools: ["Bash(npx framer
|
|
5
|
+
**Mandatory precondition**: run `npx @framer/agent@latest setup` and let it complete **BEFORE** loading this skill.
|
|
6
|
+
allowed-tools: ["Bash(npx @framer/agent:*)", "Bash(npx @framer/agent@latest:*)", "Read({{FRAMER_TEMPORARY_DIR}}/*)", "Write({{FRAMER_TEMPORARY_DIR}}/*)"]
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
If you didn't run this command before loading the skill, run it now:
|
|
10
10
|
```bash
|
|
11
|
-
npx framer
|
|
11
|
+
npx @framer/agent@latest setup
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
## Capabilities
|
|
@@ -29,14 +29,14 @@ What you can do with the Framer CLI:
|
|
|
29
29
|
|
|
30
30
|
### Permissions
|
|
31
31
|
|
|
32
|
-
The Framer CLI requires network access (to download packages and connect to Framer servers) and file-system access outside the project directory (to install skills into `~/.agents/skills/` and `~/.claude/skills/`). In sandboxed environments, you must execute `npx framer
|
|
32
|
+
The Framer CLI requires network access (to download packages and connect to Framer servers) and file-system access outside the project directory (to install skills into `~/.agents/skills/` and `~/.claude/skills/`). In sandboxed environments, you must execute `npx @framer/agent@latest` commands with elevated permissions from the get-go. Do not try to execute them with the default sandboxed permissions; they will hang or fail.
|
|
33
33
|
|
|
34
34
|
### Session Management
|
|
35
35
|
|
|
36
36
|
If the user refers to a project by name or description rather than giving a URL, first inspect the recent projects:
|
|
37
37
|
|
|
38
38
|
```bash
|
|
39
|
-
npx framer
|
|
39
|
+
npx @framer/agent@latest project list
|
|
40
40
|
```
|
|
41
41
|
|
|
42
42
|
Use that list to infer the likely project from the names and recency. If the right project is already known, use its project ID with `session new`. If there are multiple possible matches, clarify with the user. Only ask the user for a Project URL if there is no clear match. Avoid talking about the technical contents of returned data like IDs.
|
|
@@ -46,7 +46,7 @@ Each session maintains a persistent connection to a Framer project. Use sessions
|
|
|
46
46
|
Create a session against an existing project:
|
|
47
47
|
|
|
48
48
|
```bash
|
|
49
|
-
npx framer
|
|
49
|
+
npx @framer/agent@latest session new "<url or id>"
|
|
50
50
|
```
|
|
51
51
|
|
|
52
52
|
This prints the session ID. You must always use that session ID with `-s <id>` for all subsequent commands. Using the same session preserves your `state` between calls.
|
|
@@ -54,15 +54,15 @@ This prints the session ID. You must always use that session ID with `-s <id>` f
|
|
|
54
54
|
To create a brand new empty project and connect to it:
|
|
55
55
|
|
|
56
56
|
```bash
|
|
57
|
-
npx framer
|
|
58
|
-
npx framer
|
|
57
|
+
npx @framer/agent@latest project new
|
|
58
|
+
npx @framer/agent@latest session new "<returned project id>"
|
|
59
59
|
```
|
|
60
60
|
|
|
61
61
|
To remix (duplicate) an existing project and connect to the copy:
|
|
62
62
|
|
|
63
63
|
```bash
|
|
64
|
-
npx framer
|
|
65
|
-
npx framer
|
|
64
|
+
npx @framer/agent@latest project remix "<url, project id, or remix link>"
|
|
65
|
+
npx @framer/agent@latest session new "<returned project id>"
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
Note that during beta, you cannot connect to non-beta projects. If creating a session errors with a message about this, you need to move your project to beta.
|
|
@@ -70,7 +70,7 @@ Note that during beta, you cannot connect to non-beta projects. If creating a se
|
|
|
70
70
|
List active sessions:
|
|
71
71
|
|
|
72
72
|
```bash
|
|
73
|
-
npx framer
|
|
73
|
+
npx @framer/agent@latest session list
|
|
74
74
|
```
|
|
75
75
|
|
|
76
76
|
## Project-scoped skill
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "framer-dalton",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.30",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": "./dist/cli.js",
|
|
6
6
|
"main": "./dist/cli.js",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"@trpc/client": "^11.17.0",
|
|
25
25
|
"@trpc/server": "^11.17.0",
|
|
26
26
|
"commander": "^12.1.0",
|
|
27
|
-
"framer-api": "0.1.
|
|
27
|
+
"framer-api": "^0.1.16",
|
|
28
28
|
"zod": "^4.4.3"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|