figmanage 1.0.1 → 1.2.0
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 +74 -59
- package/dist/cli/analytics.d.ts +3 -0
- package/dist/cli/analytics.js +48 -0
- package/dist/cli/branching.d.ts +3 -0
- package/dist/cli/branching.js +56 -0
- package/dist/cli/comments.d.ts +3 -0
- package/dist/cli/comments.js +86 -0
- package/dist/cli/completion.d.ts +7 -0
- package/dist/cli/completion.js +160 -0
- package/dist/cli/components.d.ts +3 -0
- package/dist/cli/components.js +82 -0
- package/dist/cli/compound-commands.d.ts +14 -0
- package/dist/cli/compound-commands.js +291 -0
- package/dist/cli/export.d.ts +3 -0
- package/dist/cli/export.js +51 -0
- package/dist/cli/files.d.ts +3 -0
- package/dist/cli/files.js +156 -0
- package/dist/cli/format.js +147 -2
- package/dist/cli/helpers.d.ts +7 -0
- package/dist/cli/helpers.js +43 -0
- package/dist/cli/index.js +68 -89
- package/dist/cli/libraries.d.ts +3 -0
- package/dist/cli/libraries.js +26 -0
- package/dist/cli/navigate.d.ts +3 -0
- package/dist/cli/navigate.js +192 -0
- package/dist/cli/org.d.ts +3 -0
- package/dist/cli/org.js +227 -0
- package/dist/cli/permissions.d.ts +3 -0
- package/dist/cli/permissions.js +133 -0
- package/dist/cli/projects.d.ts +3 -0
- package/dist/cli/projects.js +110 -0
- package/dist/cli/reading.d.ts +3 -0
- package/dist/cli/reading.js +51 -0
- package/dist/cli/teams.d.ts +3 -0
- package/dist/cli/teams.js +56 -0
- package/dist/cli/variables.d.ts +3 -0
- package/dist/cli/variables.js +80 -0
- package/dist/cli/versions.d.ts +3 -0
- package/dist/cli/versions.js +46 -0
- package/dist/cli/webhooks.d.ts +3 -0
- package/dist/cli/webhooks.js +100 -0
- package/dist/operations/analytics.d.ts +10 -0
- package/dist/operations/analytics.js +15 -0
- package/dist/operations/branching.d.ts +24 -0
- package/dist/operations/branching.js +41 -0
- package/dist/operations/comments.d.ts +43 -0
- package/dist/operations/comments.js +65 -0
- package/dist/operations/components.d.ts +24 -0
- package/dist/operations/components.js +30 -0
- package/dist/operations/compound-manager.d.ts +101 -0
- package/dist/operations/compound-manager.js +629 -0
- package/dist/operations/compound.d.ts +102 -0
- package/dist/operations/compound.js +595 -0
- package/dist/operations/export.d.ts +19 -0
- package/dist/operations/export.js +27 -0
- package/dist/operations/files.d.ts +55 -0
- package/dist/operations/files.js +89 -0
- package/dist/operations/libraries.d.ts +5 -0
- package/dist/operations/libraries.js +10 -0
- package/dist/operations/navigate.d.ts +99 -0
- package/dist/operations/navigate.js +266 -0
- package/dist/operations/org.d.ts +95 -0
- package/dist/operations/org.js +205 -0
- package/dist/operations/permissions.d.ts +59 -0
- package/dist/operations/permissions.js +112 -0
- package/dist/operations/projects.d.ts +29 -0
- package/dist/operations/projects.js +40 -0
- package/dist/operations/reading.d.ts +12 -0
- package/dist/operations/reading.js +20 -0
- package/dist/operations/teams.d.ts +17 -0
- package/dist/operations/teams.js +17 -0
- package/dist/operations/variables.d.ts +17 -0
- package/dist/operations/variables.js +39 -0
- package/dist/operations/versions.d.ts +23 -0
- package/dist/operations/versions.js +27 -0
- package/dist/operations/webhooks.d.ts +25 -0
- package/dist/operations/webhooks.js +38 -0
- package/dist/tools/analytics.js +6 -16
- package/dist/tools/branching.js +7 -36
- package/dist/tools/comments.js +9 -56
- package/dist/tools/components.js +7 -19
- package/dist/tools/compound-manager.js +21 -644
- package/dist/tools/compound.js +32 -566
- package/dist/tools/export.js +4 -23
- package/dist/tools/files.js +21 -68
- package/dist/tools/libraries.js +4 -11
- package/dist/tools/navigate.js +23 -246
- package/dist/tools/org.js +29 -245
- package/dist/tools/permissions.js +18 -97
- package/dist/tools/projects.js +8 -27
- package/dist/tools/reading.js +5 -15
- package/dist/tools/teams.js +8 -16
- package/dist/tools/variables.js +13 -30
- package/dist/tools/versions.js +6 -24
- package/dist/tools/webhooks.js +7 -24
- package/package.json +1 -1
- package/dist/cli/commands.d.ts +0 -47
- package/dist/cli/commands.js +0 -1204
package/dist/tools/export.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { publicClient } from '../clients/public-api.js';
|
|
3
2
|
import { defineTool, toolResult, toolError, figmaId } from './register.js';
|
|
3
|
+
import { exportNodes, getImageFills } from '../operations/export.js';
|
|
4
4
|
// -- export_nodes --
|
|
5
5
|
defineTool({
|
|
6
6
|
toolset: 'export',
|
|
@@ -16,22 +16,8 @@ defineTool({
|
|
|
16
16
|
},
|
|
17
17
|
}, async ({ file_key, node_ids, format, scale }) => {
|
|
18
18
|
try {
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
format: format || 'png',
|
|
22
|
-
};
|
|
23
|
-
if (scale)
|
|
24
|
-
params.scale = String(Math.min(Math.max(scale, 0.01), 4));
|
|
25
|
-
const res = await publicClient(config).get(`/v1/images/${file_key}`, { params });
|
|
26
|
-
const images = res.data?.images || {};
|
|
27
|
-
const err = res.data?.err;
|
|
28
|
-
if (err)
|
|
29
|
-
return toolError(`Export error: ${err}`);
|
|
30
|
-
const results = Object.entries(images).map(([nodeId, url]) => ({
|
|
31
|
-
node_id: nodeId,
|
|
32
|
-
url,
|
|
33
|
-
}));
|
|
34
|
-
return toolResult(JSON.stringify(results, null, 2));
|
|
19
|
+
const result = await exportNodes(config, { file_key, node_ids, format, scale });
|
|
20
|
+
return toolResult(JSON.stringify(result, null, 2));
|
|
35
21
|
}
|
|
36
22
|
catch (e) {
|
|
37
23
|
return toolError(`Failed to export nodes: ${e.response?.status || e.message}`);
|
|
@@ -51,12 +37,7 @@ defineTool({
|
|
|
51
37
|
},
|
|
52
38
|
}, async ({ file_key }) => {
|
|
53
39
|
try {
|
|
54
|
-
const
|
|
55
|
-
const images = res.data?.meta?.images || res.data?.images || {};
|
|
56
|
-
const results = Object.entries(images).map(([ref, url]) => ({
|
|
57
|
-
image_ref: ref,
|
|
58
|
-
url,
|
|
59
|
-
}));
|
|
40
|
+
const results = await getImageFills(config, { file_key });
|
|
60
41
|
if (results.length === 0)
|
|
61
42
|
return toolResult('No image fills in this file.');
|
|
62
43
|
return toolResult(JSON.stringify(results, null, 2));
|
package/dist/tools/files.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { defineTool, toolResult, toolError, figmaId } from './register.js';
|
|
3
|
+
import { createFile, renameFile, moveFiles, duplicateFile, trashFiles, restoreFiles, favoriteFile, setLinkAccess, } from '../operations/files.js';
|
|
4
4
|
// -- create_file --
|
|
5
5
|
defineTool({
|
|
6
6
|
toolset: 'files',
|
|
@@ -16,18 +16,8 @@ defineTool({
|
|
|
16
16
|
},
|
|
17
17
|
}, async ({ project_id, editor_type, org_id }) => {
|
|
18
18
|
try {
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
org_id: resolveOrgId(config, org_id),
|
|
22
|
-
editor_type: editor_type || 'design',
|
|
23
|
-
});
|
|
24
|
-
const f = res.data?.meta?.fig_file || res.data?.meta || res.data;
|
|
25
|
-
return toolResult(JSON.stringify({
|
|
26
|
-
key: f.key,
|
|
27
|
-
name: f.name,
|
|
28
|
-
editor_type: f.editor_type,
|
|
29
|
-
url: `https://www.figma.com/design/${f.key}`,
|
|
30
|
-
}, null, 2));
|
|
19
|
+
const result = await createFile(config, { project_id, editor_type, org_id });
|
|
20
|
+
return toolResult(JSON.stringify(result, null, 2));
|
|
31
21
|
}
|
|
32
22
|
catch (e) {
|
|
33
23
|
return toolError(`Failed to create file: ${e.response?.status || e.message}`);
|
|
@@ -49,10 +39,7 @@ defineTool({
|
|
|
49
39
|
},
|
|
50
40
|
}, async ({ file_key, name }) => {
|
|
51
41
|
try {
|
|
52
|
-
await
|
|
53
|
-
key: file_key,
|
|
54
|
-
name,
|
|
55
|
-
});
|
|
42
|
+
await renameFile(config, { file_key, name });
|
|
56
43
|
return toolResult(`Renamed to "${name}"`);
|
|
57
44
|
}
|
|
58
45
|
catch (e) {
|
|
@@ -75,19 +62,10 @@ defineTool({
|
|
|
75
62
|
},
|
|
76
63
|
}, async ({ file_keys, destination_project_id }) => {
|
|
77
64
|
try {
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
restore_files: false,
|
|
83
|
-
}));
|
|
84
|
-
const res = await internalClient(config).put('/api/files_batch', { files });
|
|
85
|
-
const data = res.data?.meta || res.data;
|
|
86
|
-
const succeeded = Object.keys(data.success || {}).length;
|
|
87
|
-
const failed = Object.keys(data.errors || {}).length;
|
|
88
|
-
let msg = `Moved ${succeeded} file(s) to project ${destination_project_id}`;
|
|
89
|
-
if (failed > 0)
|
|
90
|
-
msg += `. ${failed} failed: ${JSON.stringify(data.errors)}`;
|
|
65
|
+
const result = await moveFiles(config, { file_keys, destination_project_id });
|
|
66
|
+
let msg = `Moved ${result.succeeded} file(s) to project ${destination_project_id}`;
|
|
67
|
+
if (result.failed > 0)
|
|
68
|
+
msg += `. ${result.failed} failed: ${JSON.stringify(result.errors)}`;
|
|
91
69
|
return toolResult(msg);
|
|
92
70
|
}
|
|
93
71
|
catch (e) {
|
|
@@ -110,16 +88,8 @@ defineTool({
|
|
|
110
88
|
},
|
|
111
89
|
}, async ({ file_key, project_id }) => {
|
|
112
90
|
try {
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
params: project_id ? { folder_id: project_id } : undefined,
|
|
116
|
-
});
|
|
117
|
-
const f = res.data?.meta?.fig_file || res.data?.meta || res.data;
|
|
118
|
-
return toolResult(JSON.stringify({
|
|
119
|
-
key: f.key,
|
|
120
|
-
name: f.name,
|
|
121
|
-
url: `https://www.figma.com/design/${f.key}`,
|
|
122
|
-
}, null, 2));
|
|
91
|
+
const result = await duplicateFile(config, { file_key, project_id });
|
|
92
|
+
return toolResult(JSON.stringify(result, null, 2));
|
|
123
93
|
}
|
|
124
94
|
catch (e) {
|
|
125
95
|
return toolError(`Failed to duplicate file: ${e.response?.status || e.message}`);
|
|
@@ -141,13 +111,8 @@ defineTool({
|
|
|
141
111
|
},
|
|
142
112
|
}, async ({ file_keys }) => {
|
|
143
113
|
try {
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
data: { files, trashed: true },
|
|
147
|
-
});
|
|
148
|
-
const data = res.data?.meta || res.data;
|
|
149
|
-
const succeeded = Object.keys(data.success || {}).length;
|
|
150
|
-
return toolResult(`Trashed ${succeeded} file(s)`);
|
|
114
|
+
const result = await trashFiles(config, { file_keys });
|
|
115
|
+
return toolResult(`Trashed ${result.succeeded} file(s)`);
|
|
151
116
|
}
|
|
152
117
|
catch (e) {
|
|
153
118
|
return toolError(`Failed to trash files: ${e.response?.status || e.message}`);
|
|
@@ -168,14 +133,10 @@ defineTool({
|
|
|
168
133
|
},
|
|
169
134
|
}, async ({ file_keys }) => {
|
|
170
135
|
try {
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
const failed = Object.keys(data?.errors || {}).length;
|
|
176
|
-
let msg = `Restored ${succeeded} file(s)`;
|
|
177
|
-
if (failed > 0)
|
|
178
|
-
msg += `. ${failed} failed: ${JSON.stringify(data.errors)}`;
|
|
136
|
+
const result = await restoreFiles(config, { file_keys });
|
|
137
|
+
let msg = `Restored ${result.succeeded} file(s)`;
|
|
138
|
+
if (result.failed > 0)
|
|
139
|
+
msg += `. ${result.failed} failed: ${JSON.stringify(result.errors)}`;
|
|
179
140
|
return toolResult(msg);
|
|
180
141
|
}
|
|
181
142
|
catch (e) {
|
|
@@ -198,13 +159,8 @@ defineTool({
|
|
|
198
159
|
},
|
|
199
160
|
}, async ({ file_key, favorited }) => {
|
|
200
161
|
try {
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
resource_type: 'file',
|
|
204
|
-
resource_id_or_key: file_key,
|
|
205
|
-
is_favorited: isFavorited,
|
|
206
|
-
});
|
|
207
|
-
return toolResult(`${isFavorited ? 'Favorited' : 'Unfavorited'} file ${file_key}`);
|
|
162
|
+
const result = await favoriteFile(config, { file_key, favorited });
|
|
163
|
+
return toolResult(`${result.favorited ? 'Favorited' : 'Unfavorited'} file ${file_key}`);
|
|
208
164
|
}
|
|
209
165
|
catch (e) {
|
|
210
166
|
return toolError(`Failed to toggle favorite: ${e.response?.status || e.message}`);
|
|
@@ -226,11 +182,8 @@ defineTool({
|
|
|
226
182
|
},
|
|
227
183
|
}, async ({ file_key, link_access }) => {
|
|
228
184
|
try {
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
});
|
|
232
|
-
const newAccess = res.data?.meta?.link_access || link_access || 'inherit';
|
|
233
|
-
return toolResult(`Set link access to "${newAccess}" on file ${file_key}`);
|
|
185
|
+
const result = await setLinkAccess(config, { file_key, link_access });
|
|
186
|
+
return toolResult(`Set link access to "${result.link_access}" on file ${file_key}`);
|
|
234
187
|
}
|
|
235
188
|
catch (e) {
|
|
236
189
|
return toolError(`Failed to set link access: ${e.response?.status || e.message}`);
|
package/dist/tools/libraries.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { defineTool, toolResult, toolError, figmaId } from './register.js';
|
|
2
|
+
import { listOrgLibraries } from '../operations/libraries.js';
|
|
3
3
|
// -- list_org_libraries --
|
|
4
4
|
defineTool({
|
|
5
5
|
toolset: 'libraries',
|
|
@@ -12,15 +12,8 @@ defineTool({
|
|
|
12
12
|
},
|
|
13
13
|
}, async ({ org_id }) => {
|
|
14
14
|
try {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
orgId = requireOrgId(config, org_id);
|
|
18
|
-
}
|
|
19
|
-
catch (e) {
|
|
20
|
-
return toolError(e.message);
|
|
21
|
-
}
|
|
22
|
-
const res = await internalClient(config).get('/api/design_systems/libraries', { params: { org_id: orgId, include_sharing_group_info: true } });
|
|
23
|
-
return toolResult(JSON.stringify(res.data, null, 2));
|
|
15
|
+
const result = await listOrgLibraries(config, { org_id });
|
|
16
|
+
return toolResult(JSON.stringify(result, null, 2));
|
|
24
17
|
}
|
|
25
18
|
catch (e) {
|
|
26
19
|
return toolError(`Failed to list org libraries: ${e.response?.status || e.message}`);
|
package/dist/tools/navigate.js
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { hasCookie, hasPat } from '../auth/client.js';
|
|
5
|
-
import { defineTool, toolResult, toolError, figmaId, requireOrgId } from './register.js';
|
|
6
|
-
import { checkAuth, formatAuthStatus } from '../auth/health.js';
|
|
2
|
+
import { defineTool, toolResult, toolError, figmaId } from './register.js';
|
|
3
|
+
import { checkAuthStatus, listOrgs, switchOrg, listTeams, listProjects, listFiles, listRecentFiles, search, getFileInfo, listFavorites, } from '../operations/navigate.js';
|
|
7
4
|
// -- check_auth --
|
|
8
5
|
defineTool({
|
|
9
6
|
toolset: 'navigate',
|
|
@@ -11,8 +8,8 @@ defineTool({
|
|
|
11
8
|
register(server, config) {
|
|
12
9
|
server.registerTool('check_auth', { description: 'Check authentication status for both PAT and session cookie' }, async () => {
|
|
13
10
|
try {
|
|
14
|
-
const
|
|
15
|
-
return toolResult(
|
|
11
|
+
const result = await checkAuthStatus(config);
|
|
12
|
+
return toolResult(result.formatted);
|
|
16
13
|
}
|
|
17
14
|
catch (e) {
|
|
18
15
|
return toolError(`Auth check failed: ${e.message}`);
|
|
@@ -29,63 +26,15 @@ defineTool({
|
|
|
29
26
|
description: 'List all Figma workspaces (orgs) you belong to. Shows which is currently active.',
|
|
30
27
|
}, async () => {
|
|
31
28
|
try {
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
.
|
|
36
|
-
.map((o) => ({ id: String(o.id), name: o.name || String(o.id) }));
|
|
37
|
-
// Fallback: meta.orgs is empty for non-admin users.
|
|
38
|
-
// Extract unique org_ids from meta.roles instead.
|
|
39
|
-
if (orgs.length === 0) {
|
|
40
|
-
const roles = res.data?.meta?.roles || [];
|
|
41
|
-
const orgIds = [...new Set(roles
|
|
42
|
-
.map((r) => r.org_id)
|
|
43
|
-
.filter((id) => id != null)
|
|
44
|
-
.map(String))];
|
|
45
|
-
// Also include config.orgId if not already covered
|
|
46
|
-
if (config.orgId && !orgIds.includes(config.orgId)) {
|
|
47
|
-
orgIds.push(config.orgId);
|
|
48
|
-
}
|
|
49
|
-
// Resolve names via domain lookup
|
|
50
|
-
orgs = await Promise.all(orgIds.map(async (id) => {
|
|
51
|
-
let name = id;
|
|
52
|
-
try {
|
|
53
|
-
const domRes = await api.get(`/api/orgs/${id}/domains`);
|
|
54
|
-
const domains = domRes.data?.meta || [];
|
|
55
|
-
if (Array.isArray(domains) && domains.length > 0) {
|
|
56
|
-
name = domains[0].domain || id;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
catch { /* domain lookup optional */ }
|
|
60
|
-
return { id, name };
|
|
61
|
-
}));
|
|
62
|
-
}
|
|
63
|
-
// Merge with any existing registry entries (preserves names from setup)
|
|
64
|
-
if (config.orgs && config.orgs.length > 0) {
|
|
65
|
-
for (const existing of config.orgs) {
|
|
66
|
-
if (!orgs.find(o => o.id === existing.id)) {
|
|
67
|
-
orgs.push(existing);
|
|
68
|
-
}
|
|
69
|
-
else {
|
|
70
|
-
// Prefer stored name over ID-only fallback
|
|
71
|
-
const entry = orgs.find(o => o.id === existing.id);
|
|
72
|
-
if (entry.name === entry.id && existing.name !== existing.id) {
|
|
73
|
-
entry.name = existing.name;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
29
|
+
const orgs = await listOrgs(config);
|
|
30
|
+
// Side effect: update config org registry
|
|
31
|
+
if (orgs.length > 0) {
|
|
32
|
+
config.orgs = orgs.map(o => ({ id: o.id, name: o.name }));
|
|
77
33
|
}
|
|
78
|
-
if (orgs.length > 0)
|
|
79
|
-
config.orgs = orgs;
|
|
80
34
|
if (orgs.length === 0) {
|
|
81
35
|
return toolResult('No workspaces found. You may be on a free/starter plan.');
|
|
82
36
|
}
|
|
83
|
-
|
|
84
|
-
id: o.id,
|
|
85
|
-
name: o.name,
|
|
86
|
-
active: o.id === config.orgId,
|
|
87
|
-
}));
|
|
88
|
-
return toolResult(JSON.stringify(result, null, 2));
|
|
37
|
+
return toolResult(JSON.stringify(orgs, null, 2));
|
|
89
38
|
}
|
|
90
39
|
catch (e) {
|
|
91
40
|
return toolError(`Failed to list orgs: ${e.response?.status || e.message}`);
|
|
@@ -105,42 +54,10 @@ defineTool({
|
|
|
105
54
|
},
|
|
106
55
|
}, async ({ org }) => {
|
|
107
56
|
try {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
id: String(o.id),
|
|
113
|
-
name: o.name,
|
|
114
|
-
}));
|
|
115
|
-
if (orgs.length > 0)
|
|
116
|
-
config.orgs = orgs;
|
|
117
|
-
}
|
|
118
|
-
if (!config.orgs || config.orgs.length === 0) {
|
|
119
|
-
return toolError('No workspaces found. You may be on a free/starter plan.');
|
|
120
|
-
}
|
|
121
|
-
// Try exact ID match first
|
|
122
|
-
let match = config.orgs.find(o => o.id === org);
|
|
123
|
-
// Then case-insensitive substring match on name
|
|
124
|
-
if (!match) {
|
|
125
|
-
const lower = org.toLowerCase();
|
|
126
|
-
const matches = config.orgs.filter(o => o.name.toLowerCase().includes(lower));
|
|
127
|
-
if (matches.length === 1) {
|
|
128
|
-
match = matches[0];
|
|
129
|
-
}
|
|
130
|
-
else if (matches.length > 1) {
|
|
131
|
-
const names = matches.map(o => `${o.name} (${o.id})`).join(', ');
|
|
132
|
-
return toolError(`Ambiguous: "${org}" matches multiple workspaces: ${names}. Be more specific or use the org ID.`);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
if (!match) {
|
|
136
|
-
const available = config.orgs.map(o => `${o.name} (${o.id})`).join(', ');
|
|
137
|
-
return toolError(`No workspace matching "${org}". Available: ${available}`);
|
|
138
|
-
}
|
|
139
|
-
const previousId = config.orgId;
|
|
140
|
-
const previousOrg = config.orgs.find(o => o.id === previousId);
|
|
141
|
-
const previousLabel = previousOrg ? `${previousOrg.name} (${previousId})` : (previousId || 'none');
|
|
142
|
-
config.orgId = match.id;
|
|
143
|
-
return toolResult(`Switched workspace: ${previousLabel} -> ${match.name} (${match.id})`);
|
|
57
|
+
const result = await switchOrg(config, { org });
|
|
58
|
+
// Side effect: update config org ID
|
|
59
|
+
config.orgId = result.current.id;
|
|
60
|
+
return toolResult(`Switched workspace: ${result.previous} -> ${result.current.name} (${result.current.id})`);
|
|
144
61
|
}
|
|
145
62
|
catch (e) {
|
|
146
63
|
return toolError(`Failed to switch org: ${e.response?.status || e.message}`);
|
|
@@ -157,21 +74,7 @@ defineTool({
|
|
|
157
74
|
description: 'List all teams you belong to. Returns team IDs and names.',
|
|
158
75
|
}, async () => {
|
|
159
76
|
try {
|
|
160
|
-
|
|
161
|
-
const res = await internalClient(config).get(`/api/orgs/${config.orgId}/teams`);
|
|
162
|
-
const data = res.data?.meta || res.data;
|
|
163
|
-
const teams = (data.teams || (Array.isArray(data) ? data : [])).map((t) => ({
|
|
164
|
-
id: String(t.id),
|
|
165
|
-
name: t.name,
|
|
166
|
-
}));
|
|
167
|
-
return toolResult(JSON.stringify(teams, null, 2));
|
|
168
|
-
}
|
|
169
|
-
// Fallback: get teams from user/state
|
|
170
|
-
const res = await internalClient(config).get('/api/user/state');
|
|
171
|
-
const teams = (res.data?.meta?.teams || []).map((t) => ({
|
|
172
|
-
id: String(t.id),
|
|
173
|
-
name: t.name,
|
|
174
|
-
}));
|
|
77
|
+
const teams = await listTeams(config);
|
|
175
78
|
return toolResult(JSON.stringify(teams, null, 2));
|
|
176
79
|
}
|
|
177
80
|
catch (e) {
|
|
@@ -192,23 +95,8 @@ defineTool({
|
|
|
192
95
|
},
|
|
193
96
|
}, async ({ team_id }) => {
|
|
194
97
|
try {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
const rows = res.data?.meta?.folder_rows || res.data || [];
|
|
198
|
-
const projects = (Array.isArray(rows) ? rows : []).map((p) => ({
|
|
199
|
-
id: String(p.id),
|
|
200
|
-
name: p.name || p.path,
|
|
201
|
-
}));
|
|
202
|
-
return toolResult(JSON.stringify(projects, null, 2));
|
|
203
|
-
}
|
|
204
|
-
else {
|
|
205
|
-
const res = await publicClient(config).get(`/v1/teams/${team_id}/projects`);
|
|
206
|
-
const projects = (res.data.projects || []).map((p) => ({
|
|
207
|
-
id: String(p.id),
|
|
208
|
-
name: p.name,
|
|
209
|
-
}));
|
|
210
|
-
return toolResult(JSON.stringify(projects, null, 2));
|
|
211
|
-
}
|
|
98
|
+
const projects = await listProjects(config, { team_id });
|
|
99
|
+
return toolResult(JSON.stringify(projects, null, 2));
|
|
212
100
|
}
|
|
213
101
|
catch (e) {
|
|
214
102
|
return toolError(`Failed to list projects: ${e.response?.status || e.message}`);
|
|
@@ -230,47 +118,8 @@ defineTool({
|
|
|
230
118
|
},
|
|
231
119
|
}, async ({ project_id, page_size, page_token }) => {
|
|
232
120
|
try {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
if (hasPat(config)) {
|
|
236
|
-
const res = await publicClient(config).get(`/v1/projects/${project_id}/files`);
|
|
237
|
-
const files = (res.data.files || []).map((f) => ({
|
|
238
|
-
key: f.key,
|
|
239
|
-
name: f.name,
|
|
240
|
-
last_modified: f.last_modified,
|
|
241
|
-
thumbnail_url: f.thumbnail_url,
|
|
242
|
-
}));
|
|
243
|
-
return toolResult(JSON.stringify({ files }, null, 2));
|
|
244
|
-
}
|
|
245
|
-
else {
|
|
246
|
-
const pageSize = Math.min(page_size || 25, 100);
|
|
247
|
-
const params = new URLSearchParams({
|
|
248
|
-
folderId: project_id,
|
|
249
|
-
sort_column: 'touched_at',
|
|
250
|
-
sort_order: 'desc',
|
|
251
|
-
page_size: String(pageSize),
|
|
252
|
-
file_type: '',
|
|
253
|
-
});
|
|
254
|
-
if (page_token)
|
|
255
|
-
params.set('before', page_token);
|
|
256
|
-
const res = await internalClient(config).get(`/api/folders/${project_id}/paginated_files?${params}`);
|
|
257
|
-
const meta = res.data?.meta || res.data;
|
|
258
|
-
const files = (meta.files || meta.results || []).map((f) => ({
|
|
259
|
-
key: f.key,
|
|
260
|
-
name: f.name,
|
|
261
|
-
last_modified: f.touched_at || f.updated_at || f.last_modified,
|
|
262
|
-
editor_type: f.editor_type,
|
|
263
|
-
}));
|
|
264
|
-
const pagination = res.data?.pagination;
|
|
265
|
-
const result = { files };
|
|
266
|
-
if (pagination?.next_page || files.length === pageSize) {
|
|
267
|
-
result.pagination = {
|
|
268
|
-
has_more: true,
|
|
269
|
-
hint: `To get the next page, call list_files again with page_token="${pagination?.next_page || files[files.length - 1]?.last_modified || ''}"`,
|
|
270
|
-
};
|
|
271
|
-
}
|
|
272
|
-
return toolResult(JSON.stringify(result, null, 2));
|
|
273
|
-
}
|
|
121
|
+
const result = await listFiles(config, { project_id, page_size, page_token });
|
|
122
|
+
return toolResult(JSON.stringify(result, null, 2));
|
|
274
123
|
}
|
|
275
124
|
catch (e) {
|
|
276
125
|
return toolError(`Failed to list files: ${e.response?.status || e.message}`);
|
|
@@ -287,16 +136,7 @@ defineTool({
|
|
|
287
136
|
description: 'List your recently viewed/edited files across all teams.',
|
|
288
137
|
}, async () => {
|
|
289
138
|
try {
|
|
290
|
-
const
|
|
291
|
-
const files = (res.data?.meta?.recent_files || []).map((f) => ({
|
|
292
|
-
key: f.key,
|
|
293
|
-
name: f.name,
|
|
294
|
-
team_id: f.team_id,
|
|
295
|
-
folder_id: f.folder_id,
|
|
296
|
-
editor_type: f.editor_type,
|
|
297
|
-
last_touched: f.touched_at,
|
|
298
|
-
url: f.url || `https://www.figma.com/design/${f.key}`,
|
|
299
|
-
}));
|
|
139
|
+
const files = await listRecentFiles(config);
|
|
300
140
|
return toolResult(JSON.stringify(files, null, 2));
|
|
301
141
|
}
|
|
302
142
|
catch (e) {
|
|
@@ -319,40 +159,7 @@ defineTool({
|
|
|
319
159
|
},
|
|
320
160
|
}, async ({ query, sort, org_id }) => {
|
|
321
161
|
try {
|
|
322
|
-
|
|
323
|
-
try {
|
|
324
|
-
orgId = requireOrgId(config, org_id);
|
|
325
|
-
}
|
|
326
|
-
catch (e) {
|
|
327
|
-
return toolError(e.message);
|
|
328
|
-
}
|
|
329
|
-
const params = {
|
|
330
|
-
query,
|
|
331
|
-
sort: sort || 'relevancy',
|
|
332
|
-
desc: 'true',
|
|
333
|
-
is_global: 'true',
|
|
334
|
-
org_id: orgId,
|
|
335
|
-
current_org_id: orgId,
|
|
336
|
-
plan_type: 'org',
|
|
337
|
-
};
|
|
338
|
-
const res = await internalClient(config).get('/api/search/file_browser_preview', { params });
|
|
339
|
-
const meta = res.data?.meta;
|
|
340
|
-
// Response is an array of category objects; files are in the first entry
|
|
341
|
-
const categories = Array.isArray(meta) ? meta : [];
|
|
342
|
-
const fileCategory = categories.find((c) => c.search_model_type === 'files') || categories[0];
|
|
343
|
-
const rawResults = fileCategory?.results || [];
|
|
344
|
-
const results = rawResults.map((r) => {
|
|
345
|
-
const m = r.model || r;
|
|
346
|
-
return {
|
|
347
|
-
key: m.key,
|
|
348
|
-
name: m.name,
|
|
349
|
-
editor_type: m.editor_type,
|
|
350
|
-
team_id: m.team_id ? String(m.team_id) : undefined,
|
|
351
|
-
folder_id: m.folder_id ? String(m.folder_id) : undefined,
|
|
352
|
-
last_modified: m.touched_at,
|
|
353
|
-
url: m.url || `https://www.figma.com/design/${m.key}`,
|
|
354
|
-
};
|
|
355
|
-
});
|
|
162
|
+
const results = await search(config, { query, sort, org_id });
|
|
356
163
|
if (results.length === 0) {
|
|
357
164
|
return toolResult('No results. Try list_recent_files or browse via list_projects + list_files.');
|
|
358
165
|
}
|
|
@@ -376,32 +183,8 @@ defineTool({
|
|
|
376
183
|
},
|
|
377
184
|
}, async ({ file_key }) => {
|
|
378
185
|
try {
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
const res = await publicClient(config).get(`/v1/files/${file_key}/meta`);
|
|
382
|
-
const f = res.data.file || res.data;
|
|
383
|
-
return toolResult(JSON.stringify({
|
|
384
|
-
key: f.key || file_key,
|
|
385
|
-
name: f.name,
|
|
386
|
-
last_modified: f.lastModified || f.last_modified,
|
|
387
|
-
editor_type: f.editorType || f.editor_type,
|
|
388
|
-
url: `https://www.figma.com/design/${file_key}`,
|
|
389
|
-
}, null, 2));
|
|
390
|
-
}
|
|
391
|
-
else {
|
|
392
|
-
const res = await internalClient(config).get(`/api/files/${file_key}`);
|
|
393
|
-
const f = res.data?.meta || res.data;
|
|
394
|
-
return toolResult(JSON.stringify({
|
|
395
|
-
key: f.key || file_key,
|
|
396
|
-
name: f.name,
|
|
397
|
-
updated_at: f.updated_at,
|
|
398
|
-
editor_type: f.editor_type,
|
|
399
|
-
folder_id: f.folder_id ? String(f.folder_id) : undefined,
|
|
400
|
-
team_id: f.team_id ? String(f.team_id) : undefined,
|
|
401
|
-
link_access: f.link_access,
|
|
402
|
-
url: f.url || `https://www.figma.com/design/${file_key}`,
|
|
403
|
-
}, null, 2));
|
|
404
|
-
}
|
|
186
|
+
const info = await getFileInfo(config, { file_key });
|
|
187
|
+
return toolResult(JSON.stringify(info, null, 2));
|
|
405
188
|
}
|
|
406
189
|
catch (e) {
|
|
407
190
|
return toolError(`Failed to get file info: ${e.response?.status || e.message}`);
|
|
@@ -418,13 +201,7 @@ defineTool({
|
|
|
418
201
|
description: 'List your starred/favorited files. Note: may not work on all account types.',
|
|
419
202
|
}, async () => {
|
|
420
203
|
try {
|
|
421
|
-
const
|
|
422
|
-
const data = res.data?.meta || res.data;
|
|
423
|
-
const favorites = (Array.isArray(data) ? data : data.favorites || data.resources || []).map((f) => ({
|
|
424
|
-
key: f.key || f.file_key || f.id,
|
|
425
|
-
name: f.name,
|
|
426
|
-
type: f.resource_type || f.type,
|
|
427
|
-
}));
|
|
204
|
+
const favorites = await listFavorites(config);
|
|
428
205
|
return toolResult(JSON.stringify(favorites, null, 2));
|
|
429
206
|
}
|
|
430
207
|
catch (e) {
|