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.
Files changed (98) hide show
  1. package/README.md +74 -59
  2. package/dist/cli/analytics.d.ts +3 -0
  3. package/dist/cli/analytics.js +48 -0
  4. package/dist/cli/branching.d.ts +3 -0
  5. package/dist/cli/branching.js +56 -0
  6. package/dist/cli/comments.d.ts +3 -0
  7. package/dist/cli/comments.js +86 -0
  8. package/dist/cli/completion.d.ts +7 -0
  9. package/dist/cli/completion.js +160 -0
  10. package/dist/cli/components.d.ts +3 -0
  11. package/dist/cli/components.js +82 -0
  12. package/dist/cli/compound-commands.d.ts +14 -0
  13. package/dist/cli/compound-commands.js +291 -0
  14. package/dist/cli/export.d.ts +3 -0
  15. package/dist/cli/export.js +51 -0
  16. package/dist/cli/files.d.ts +3 -0
  17. package/dist/cli/files.js +156 -0
  18. package/dist/cli/format.js +147 -2
  19. package/dist/cli/helpers.d.ts +7 -0
  20. package/dist/cli/helpers.js +43 -0
  21. package/dist/cli/index.js +68 -89
  22. package/dist/cli/libraries.d.ts +3 -0
  23. package/dist/cli/libraries.js +26 -0
  24. package/dist/cli/navigate.d.ts +3 -0
  25. package/dist/cli/navigate.js +192 -0
  26. package/dist/cli/org.d.ts +3 -0
  27. package/dist/cli/org.js +227 -0
  28. package/dist/cli/permissions.d.ts +3 -0
  29. package/dist/cli/permissions.js +133 -0
  30. package/dist/cli/projects.d.ts +3 -0
  31. package/dist/cli/projects.js +110 -0
  32. package/dist/cli/reading.d.ts +3 -0
  33. package/dist/cli/reading.js +51 -0
  34. package/dist/cli/teams.d.ts +3 -0
  35. package/dist/cli/teams.js +56 -0
  36. package/dist/cli/variables.d.ts +3 -0
  37. package/dist/cli/variables.js +80 -0
  38. package/dist/cli/versions.d.ts +3 -0
  39. package/dist/cli/versions.js +46 -0
  40. package/dist/cli/webhooks.d.ts +3 -0
  41. package/dist/cli/webhooks.js +100 -0
  42. package/dist/operations/analytics.d.ts +10 -0
  43. package/dist/operations/analytics.js +15 -0
  44. package/dist/operations/branching.d.ts +24 -0
  45. package/dist/operations/branching.js +41 -0
  46. package/dist/operations/comments.d.ts +43 -0
  47. package/dist/operations/comments.js +65 -0
  48. package/dist/operations/components.d.ts +24 -0
  49. package/dist/operations/components.js +30 -0
  50. package/dist/operations/compound-manager.d.ts +101 -0
  51. package/dist/operations/compound-manager.js +629 -0
  52. package/dist/operations/compound.d.ts +102 -0
  53. package/dist/operations/compound.js +595 -0
  54. package/dist/operations/export.d.ts +19 -0
  55. package/dist/operations/export.js +27 -0
  56. package/dist/operations/files.d.ts +55 -0
  57. package/dist/operations/files.js +89 -0
  58. package/dist/operations/libraries.d.ts +5 -0
  59. package/dist/operations/libraries.js +10 -0
  60. package/dist/operations/navigate.d.ts +99 -0
  61. package/dist/operations/navigate.js +266 -0
  62. package/dist/operations/org.d.ts +95 -0
  63. package/dist/operations/org.js +205 -0
  64. package/dist/operations/permissions.d.ts +59 -0
  65. package/dist/operations/permissions.js +112 -0
  66. package/dist/operations/projects.d.ts +29 -0
  67. package/dist/operations/projects.js +40 -0
  68. package/dist/operations/reading.d.ts +12 -0
  69. package/dist/operations/reading.js +20 -0
  70. package/dist/operations/teams.d.ts +17 -0
  71. package/dist/operations/teams.js +17 -0
  72. package/dist/operations/variables.d.ts +17 -0
  73. package/dist/operations/variables.js +39 -0
  74. package/dist/operations/versions.d.ts +23 -0
  75. package/dist/operations/versions.js +27 -0
  76. package/dist/operations/webhooks.d.ts +25 -0
  77. package/dist/operations/webhooks.js +38 -0
  78. package/dist/tools/analytics.js +6 -16
  79. package/dist/tools/branching.js +7 -36
  80. package/dist/tools/comments.js +9 -56
  81. package/dist/tools/components.js +7 -19
  82. package/dist/tools/compound-manager.js +21 -644
  83. package/dist/tools/compound.js +32 -566
  84. package/dist/tools/export.js +4 -23
  85. package/dist/tools/files.js +21 -68
  86. package/dist/tools/libraries.js +4 -11
  87. package/dist/tools/navigate.js +23 -246
  88. package/dist/tools/org.js +29 -245
  89. package/dist/tools/permissions.js +18 -97
  90. package/dist/tools/projects.js +8 -27
  91. package/dist/tools/reading.js +5 -15
  92. package/dist/tools/teams.js +8 -16
  93. package/dist/tools/variables.js +13 -30
  94. package/dist/tools/versions.js +6 -24
  95. package/dist/tools/webhooks.js +7 -24
  96. package/package.json +1 -1
  97. package/dist/cli/commands.d.ts +0 -47
  98. package/dist/cli/commands.js +0 -1204
package/dist/tools/org.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod';
2
- import { internalClient } from '../clients/internal-api.js';
3
- import { defineTool, toolResult, toolError, figmaId, requireOrgId } from './register.js';
2
+ import { defineTool, toolResult, toolError, figmaId } from './register.js';
3
+ import { listAdmins, listOrgTeams, seatUsage, listTeamMembers, billingOverview, listInvoices, orgDomains, aiCreditUsage, exportMembers, listOrgMembers, contractRates, changeSeat, } from '../operations/org.js';
4
4
  // -- list_admins --
5
5
  defineTool({
6
6
  toolset: 'org',
@@ -14,24 +14,8 @@ defineTool({
14
14
  },
15
15
  }, async ({ org_id, include_license_admins }) => {
16
16
  try {
17
- let orgId;
18
- try {
19
- orgId = requireOrgId(config, org_id);
20
- }
21
- catch (e) {
22
- return toolError(e.message);
23
- }
24
- const res = await internalClient(config).get(`/api/orgs/${orgId}/admins`, { params: { include_license_admins: include_license_admins ?? false } });
25
- const admins = (res.data?.meta || []).map((a) => ({
26
- user_id: a.user_id,
27
- email: a.user?.email,
28
- name: a.user?.handle,
29
- permission: a.permission,
30
- seat_type: a.active_seat_type?.key || null,
31
- is_email_validated: a.is_email_validated,
32
- license_admin: a.license_admin,
33
- }));
34
- return toolResult(JSON.stringify(admins, null, 2));
17
+ const result = await listAdmins(config, { org_id, include_license_admins });
18
+ return toolResult(JSON.stringify(result, null, 2));
35
19
  }
36
20
  catch (e) {
37
21
  return toolError(`Failed to list admins: ${e.response?.status || e.message}`);
@@ -52,30 +36,8 @@ defineTool({
52
36
  },
53
37
  }, async ({ org_id, include_secret_teams }) => {
54
38
  try {
55
- let orgId;
56
- try {
57
- orgId = requireOrgId(config, org_id);
58
- }
59
- catch (e) {
60
- return toolError(e.message);
61
- }
62
- const res = await internalClient(config).get(`/api/orgs/${orgId}/teams`, {
63
- params: {
64
- include_member_count: true,
65
- include_project_count: true,
66
- include_top_members: true,
67
- include_secret_teams: include_secret_teams ?? false,
68
- },
69
- });
70
- const teams = res.data?.meta || res.data;
71
- const mapped = (Array.isArray(teams) ? teams : []).map((t) => ({
72
- id: String(t.id),
73
- name: t.name,
74
- member_count: t.member_count ?? null,
75
- project_count: t.project_count ?? null,
76
- access_level: t.access_level ?? null,
77
- }));
78
- return toolResult(JSON.stringify(mapped, null, 2));
39
+ const result = await listOrgTeams(config, { org_id, include_secret_teams });
40
+ return toolResult(JSON.stringify(result, null, 2));
79
41
  }
80
42
  catch (e) {
81
43
  return toolError(`Failed to list org teams: ${e.response?.status || e.message}`);
@@ -96,18 +58,8 @@ defineTool({
96
58
  },
97
59
  }, async ({ org_id, search_query }) => {
98
60
  try {
99
- let orgId;
100
- try {
101
- orgId = requireOrgId(config, org_id);
102
- }
103
- catch (e) {
104
- return toolError(e.message);
105
- }
106
- const params = {};
107
- if (search_query)
108
- params.search_query = search_query;
109
- const res = await internalClient(config).get(`/api/orgs/${orgId}/org_users/filter_counts`, { params });
110
- return toolResult(JSON.stringify(res.data?.meta || res.data, null, 2));
61
+ const result = await seatUsage(config, { org_id, search_query });
62
+ return toolResult(JSON.stringify(result, null, 2));
111
63
  }
112
64
  catch (e) {
113
65
  return toolError(`Failed to fetch seat usage: ${e.response?.status || e.message}`);
@@ -127,18 +79,8 @@ defineTool({
127
79
  },
128
80
  }, async ({ team_id }) => {
129
81
  try {
130
- const res = await internalClient(config).get(`/api/teams/${team_id}/members`);
131
- const members = (res.data?.meta || res.data || []).map((m) => ({
132
- id: m.id,
133
- name: m.name,
134
- email: m.email,
135
- img_url: m.img_url,
136
- last_active: m.last_active,
137
- team_role: m.team_role?.level || null,
138
- permission: m.team_user?.permission || null,
139
- seat_type: m.seat_type || null,
140
- }));
141
- return toolResult(JSON.stringify(members, null, 2));
82
+ const result = await listTeamMembers(config, { team_id });
83
+ return toolResult(JSON.stringify(result, null, 2));
142
84
  }
143
85
  catch (e) {
144
86
  return toolError(`Failed to list team members: ${e.response?.status || e.message}`);
@@ -158,17 +100,8 @@ defineTool({
158
100
  },
159
101
  }, async ({ org_id }) => {
160
102
  try {
161
- let orgId;
162
- try {
163
- orgId = requireOrgId(config, org_id);
164
- }
165
- catch (e) {
166
- return toolError(e.message);
167
- }
168
- const res = await internalClient(config).get(`/api/orgs/${orgId}/billing_data`);
169
- const data = res.data?.meta || res.data;
170
- const { shipping_address, ...billing } = data;
171
- return toolResult(JSON.stringify(billing, null, 2));
103
+ const result = await billingOverview(config, { org_id });
104
+ return toolResult(JSON.stringify(result, null, 2));
172
105
  }
173
106
  catch (e) {
174
107
  return toolError(`Failed to fetch billing overview: ${e.response?.status || e.message}`);
@@ -188,28 +121,8 @@ defineTool({
188
121
  },
189
122
  }, async ({ org_id }) => {
190
123
  try {
191
- let orgId;
192
- try {
193
- orgId = requireOrgId(config, org_id);
194
- }
195
- catch (e) {
196
- return toolError(e.message);
197
- }
198
- const client = internalClient(config);
199
- const [openResult, upcomingResult] = await Promise.allSettled([
200
- client.get(`/api/plans/organization/${orgId}/invoices/open`),
201
- client.get(`/api/plans/organization/${orgId}/invoices/upcoming`),
202
- ]);
203
- const response = {};
204
- if (openResult.status === 'fulfilled')
205
- response.open = openResult.value.data;
206
- else
207
- response.open_error = 'Failed to fetch open invoices';
208
- if (upcomingResult.status === 'fulfilled')
209
- response.upcoming = upcomingResult.value.data;
210
- else
211
- response.upcoming_error = 'Failed to fetch upcoming invoices';
212
- return toolResult(JSON.stringify(response, null, 2));
124
+ const result = await listInvoices(config, { org_id });
125
+ return toolResult(JSON.stringify(result, null, 2));
213
126
  }
214
127
  catch (e) {
215
128
  return toolError(`Failed to list invoices: ${e.response?.status || e.message}`);
@@ -229,28 +142,8 @@ defineTool({
229
142
  },
230
143
  }, async ({ org_id }) => {
231
144
  try {
232
- let orgId;
233
- try {
234
- orgId = requireOrgId(config, org_id);
235
- }
236
- catch (e) {
237
- return toolError(e.message);
238
- }
239
- const client = internalClient(config);
240
- const [domainsResult, ssoResult] = await Promise.allSettled([
241
- client.get(`/api/orgs/${orgId}/domains`),
242
- client.get(`/api/org/${orgId}/org_saml_config`),
243
- ]);
244
- const response = {};
245
- if (domainsResult.status === 'fulfilled')
246
- response.domains = domainsResult.value.data;
247
- else
248
- response.domains_error = 'Failed to fetch domains';
249
- if (ssoResult.status === 'fulfilled')
250
- response.sso = ssoResult.value.data;
251
- else
252
- response.sso_error = 'SSO config not available';
253
- return toolResult(JSON.stringify(response, null, 2));
145
+ const result = await orgDomains(config, { org_id });
146
+ return toolResult(JSON.stringify(result, null, 2));
254
147
  }
255
148
  catch (e) {
256
149
  return toolError(`Failed to fetch org domains: ${e.response?.status || e.message}`);
@@ -270,8 +163,8 @@ defineTool({
270
163
  },
271
164
  }, async ({ plan_id }) => {
272
165
  try {
273
- const res = await internalClient(config).get(`/api/plans/${plan_id}/ai_credits/plan_usage_summary`);
274
- return toolResult(JSON.stringify(res.data, null, 2));
166
+ const result = await aiCreditUsage(config, { plan_id });
167
+ return toolResult(JSON.stringify(result, null, 2));
275
168
  }
276
169
  catch (e) {
277
170
  return toolError(`Failed to fetch AI credit usage: ${e.response?.status || e.message}`);
@@ -292,15 +185,8 @@ defineTool({
292
185
  },
293
186
  }, async ({ org_id }) => {
294
187
  try {
295
- let orgId;
296
- try {
297
- orgId = requireOrgId(config, org_id);
298
- }
299
- catch (e) {
300
- return toolError(e.message);
301
- }
302
- await internalClient(config).post(`/api/orgs/${orgId}/export_members`);
303
- return toolResult('CSV export queued. It will be emailed to the org admin.');
188
+ const msg = await exportMembers(config, { org_id });
189
+ return toolResult(msg);
304
190
  }
305
191
  catch (e) {
306
192
  return toolError(`Failed to export members: ${e.response?.status || e.message}`);
@@ -321,28 +207,8 @@ defineTool({
321
207
  },
322
208
  }, async ({ org_id, search_query }) => {
323
209
  try {
324
- let orgId;
325
- try {
326
- orgId = requireOrgId(config, org_id);
327
- }
328
- catch (e) {
329
- return toolError(e.message);
330
- }
331
- const params = {};
332
- if (search_query)
333
- params.search_query = search_query;
334
- const res = await internalClient(config).get(`/api/v2/orgs/${orgId}/org_users`, { params });
335
- const users = res.data?.meta?.users || res.data?.meta || res.data || [];
336
- const members = (Array.isArray(users) ? users : []).map((m) => ({
337
- org_user_id: String(m.id),
338
- user_id: m.user_id,
339
- email: m.user?.email,
340
- name: m.user?.handle,
341
- permission: m.permission,
342
- seat_type: m.active_seat_type?.key || null,
343
- last_active: m.last_seen || null,
344
- }));
345
- return toolResult(JSON.stringify(members, null, 2));
210
+ const result = await listOrgMembers(config, { org_id, search_query });
211
+ return toolResult(JSON.stringify(result, null, 2));
346
212
  }
347
213
  catch (e) {
348
214
  return toolError(`Failed to list org members: ${e.response?.status || e.message}`);
@@ -362,24 +228,8 @@ defineTool({
362
228
  },
363
229
  }, async ({ org_id }) => {
364
230
  try {
365
- let orgId;
366
- try {
367
- orgId = requireOrgId(config, org_id);
368
- }
369
- catch (e) {
370
- return toolError(e.message);
371
- }
372
- const res = await internalClient(config).get(`/api/pricing/contract_rates`, { params: { plan_parent_id: orgId, plan_type: 'organization' } });
373
- const seatProducts = new Set(['expert', 'developer', 'collaborator']);
374
- const prices = res.data?.meta?.product_prices || [];
375
- const rates = prices
376
- .filter((r) => seatProducts.has(r.billable_product_key))
377
- .map((r) => ({
378
- product: r.billable_product_key,
379
- monthly_cents: r.amount,
380
- monthly_dollars: (r.amount / 100).toFixed(2),
381
- }));
382
- return toolResult(JSON.stringify(rates, null, 2));
231
+ const result = await contractRates(config, { org_id });
232
+ return toolResult(JSON.stringify(result, null, 2));
383
233
  }
384
234
  catch (e) {
385
235
  return toolError(`Failed to fetch contract rates: ${e.response?.status || e.message}`);
@@ -388,29 +238,6 @@ defineTool({
388
238
  },
389
239
  });
390
240
  // -- change_seat --
391
- const SEAT_HIERARCHY = {
392
- view: 0,
393
- collab: 1,
394
- dev: 2,
395
- full: 3,
396
- };
397
- const SEAT_LABELS = {
398
- view: 'Viewer (free)',
399
- collab: 'Collaborator ($5/mo)',
400
- dev: 'Developer ($25/mo)',
401
- full: 'Full ($55/mo)',
402
- };
403
- const PAID_STATUSES = {
404
- full: { expert: 'full' },
405
- dev: { developer: 'full' },
406
- collab: { collaborator: 'full' },
407
- view: { collaborator: 'starter', developer: 'starter', expert: 'starter' },
408
- };
409
- const SEAT_KEY_TO_TYPE = {
410
- expert: 'full',
411
- developer: 'dev',
412
- collaborator: 'collab',
413
- };
414
241
  defineTool({
415
242
  toolset: 'org',
416
243
  auth: 'cookie',
@@ -427,56 +254,13 @@ defineTool({
427
254
  },
428
255
  }, async ({ user_id, seat_type, org_id, confirm }) => {
429
256
  try {
430
- let orgId;
431
- try {
432
- orgId = requireOrgId(config, org_id);
433
- }
434
- catch (e) {
435
- return toolError(e.message);
436
- }
437
- const res = await internalClient(config).get(`/api/v2/orgs/${orgId}/org_users`, {
438
- params: user_id.includes('@') ? { search_query: user_id } : {},
439
- });
440
- const users = res.data?.meta?.users || res.data?.meta || res.data || [];
441
- const members = Array.isArray(users) ? users : [];
442
- const member = members.find((m) => user_id.includes('@')
443
- ? m.user?.email === user_id
444
- : String(m.user_id) === String(user_id));
445
- if (!member)
446
- return toolError(`User not found: ${user_id}`);
447
- if (String(member.user_id) === String(config.userId)) {
448
- return toolError('Cannot change your own seat type. Use the Figma admin panel.');
449
- }
450
- const currentKey = member.active_seat_type?.key || null;
451
- const currentType = currentKey ? (SEAT_KEY_TO_TYPE[currentKey] || 'view') : 'view';
452
- if (currentType === seat_type) {
453
- return toolResult(`Already on ${SEAT_LABELS[seat_type]} seat. No change needed.`);
454
- }
455
- const isUpgrade = (SEAT_HIERARCHY[seat_type] ?? 0) > (SEAT_HIERARCHY[currentType] ?? 0);
456
- if (isUpgrade && !confirm) {
457
- return toolError(`Upgrading from ${SEAT_LABELS[currentType]} to ${SEAT_LABELS[seat_type]} will increase billing. ` +
458
- `Set confirm: true to authorize.`);
459
- }
460
- await internalClient(config).put(`/api/orgs/${orgId}/org_users`, {
461
- org_user_ids: [String(member.id)],
462
- paid_statuses: PAID_STATUSES[seat_type],
463
- entry_point: 'members_tab',
464
- seat_increase_authorized: 'true',
465
- seat_swap_intended: 'false',
466
- latest_ou_update: member.updated_at,
467
- showing_billing_groups: 'true',
468
- }, {
469
- 'axios-retry': { retries: 0 },
470
- });
471
- return toolResult(JSON.stringify({
472
- user: member.user?.handle || member.user?.email,
473
- email: member.user?.email,
474
- old_seat: SEAT_LABELS[currentType],
475
- new_seat: SEAT_LABELS[seat_type],
476
- }, null, 2));
257
+ const result = await changeSeat(config, { user_id, seat_type, org_id, confirm });
258
+ if (typeof result === 'string')
259
+ return toolResult(result);
260
+ return toolResult(JSON.stringify(result, null, 2));
477
261
  }
478
262
  catch (e) {
479
- return toolError(`Failed to change seat: ${e.response?.status || e.message}`);
263
+ return toolError(e.response?.status ? `Failed to change seat: ${e.response.status}` : e.message);
480
264
  }
481
265
  });
482
266
  },
@@ -1,16 +1,6 @@
1
1
  import { z } from 'zod';
2
- import { internalClient } from '../clients/internal-api.js';
3
2
  import { defineTool, toolResult, toolError, figmaId } from './register.js';
4
- const LEVEL_MAP = { owner: 999, editor: 300, viewer: 100 };
5
- const LEVEL_NAMES = { 999: 'owner', 300: 'editor', 100: 'viewer' };
6
- function levelName(level) {
7
- return LEVEL_NAMES[level] || `level:${level}`;
8
- }
9
- async function getRoles(config, resourceType, resourceId) {
10
- const res = await internalClient(config).get(`/api/roles/${resourceType}/${resourceId}`);
11
- const meta = res.data?.meta;
12
- return Array.isArray(meta) ? meta : [];
13
- }
3
+ import { getPermissions, setPermissions, share, revokeAccess, listRoleRequests, approveRoleRequest, denyRoleRequest, } from '../operations/permissions.js';
14
4
  // -- get_permissions --
15
5
  defineTool({
16
6
  toolset: 'permissions',
@@ -24,17 +14,8 @@ defineTool({
24
14
  },
25
15
  }, async ({ resource_type, resource_id }) => {
26
16
  try {
27
- const roles = await getRoles(config, resource_type, resource_id);
28
- const users = roles.map((r) => ({
29
- role_id: String(r.id),
30
- user_id: r.user_id ? String(r.user_id) : undefined,
31
- handle: r.user?.handle,
32
- email: r.user?.email || r.pending_email,
33
- role: levelName(r.level),
34
- level: r.level,
35
- pending: !!r.pending_email,
36
- }));
37
- return toolResult(JSON.stringify(users, null, 2));
17
+ const result = await getPermissions(config, { resource_type, resource_id });
18
+ return toolResult(JSON.stringify(result, null, 2));
38
19
  }
39
20
  catch (e) {
40
21
  return toolError(`Failed to get permissions: ${e.response?.status || e.message}`);
@@ -58,17 +39,11 @@ defineTool({
58
39
  },
59
40
  }, async ({ resource_type, resource_id, user_id, role }) => {
60
41
  try {
61
- const roles = await getRoles(config, resource_type, resource_id);
62
- const target = roles.find((r) => String(r.user_id) === user_id);
63
- if (!target) {
64
- return toolError(`User ${user_id} has no role on this ${resource_type}. Use share to invite them first.`);
65
- }
66
- const level = LEVEL_MAP[role];
67
- await internalClient(config).put(`/api/roles/${target.id}`, { level });
68
- return toolResult(`Set ${role} (level ${level}) for user ${user_id} on ${resource_type} ${resource_id}`);
42
+ const msg = await setPermissions(config, { resource_type, resource_id, user_id, role });
43
+ return toolResult(msg);
69
44
  }
70
45
  catch (e) {
71
- return toolError(`Failed to set permissions: ${e.response?.status || e.message}`);
46
+ return toolError(e.response?.status ? `Failed to set permissions: ${e.response.status}` : e.message);
72
47
  }
73
48
  });
74
49
  },
@@ -89,18 +64,10 @@ defineTool({
89
64
  },
90
65
  }, async ({ resource_type, resource_id, email, role }) => {
91
66
  try {
92
- const level = LEVEL_MAP[role || 'viewer'];
93
- const res = await internalClient(config).post('/api/invites', {
94
- resource_type,
95
- resource_id_or_key: resource_id,
96
- emails: [email],
97
- level,
98
- });
99
- const invites = res.data?.meta?.invites || [];
100
- const roleId = invites[0]?.id;
101
- let msg = `Invited ${email} as ${role || 'viewer'} on ${resource_type} ${resource_id}`;
102
- if (roleId)
103
- msg += ` (role_id: ${roleId})`;
67
+ const result = await share(config, { resource_type, resource_id, email, role });
68
+ let msg = `Invited ${result.email} as ${result.role} on ${result.resource_type} ${result.resource_id}`;
69
+ if (result.role_id)
70
+ msg += ` (role_id: ${result.role_id})`;
104
71
  return toolResult(msg);
105
72
  }
106
73
  catch (e) {
@@ -125,16 +92,11 @@ defineTool({
125
92
  },
126
93
  }, async ({ resource_type, resource_id, user_id }) => {
127
94
  try {
128
- const roles = await getRoles(config, resource_type, resource_id);
129
- const target = roles.find((r) => String(r.user_id) === user_id);
130
- if (!target) {
131
- return toolError(`User ${user_id} has no role on this ${resource_type}.`);
132
- }
133
- await internalClient(config).delete(`/api/roles/${target.id}`);
134
- return toolResult(`Revoked access for user ${user_id} on ${resource_type} ${resource_id}`);
95
+ const msg = await revokeAccess(config, { resource_type, resource_id, user_id });
96
+ return toolResult(msg);
135
97
  }
136
98
  catch (e) {
137
- return toolError(`Failed to revoke access: ${e.response?.status || e.message}`);
99
+ return toolError(e.response?.status ? `Failed to revoke access: ${e.response.status}` : e.message);
138
100
  }
139
101
  });
140
102
  },
@@ -149,32 +111,8 @@ defineTool({
149
111
  inputSchema: {},
150
112
  }, async () => {
151
113
  try {
152
- const res = await internalClient(config).get('/api/user_notifications/server_driven/plan', {
153
- params: { current_plan_id: '-1', app_version: '1', client_type: 'web' },
154
- });
155
- const notifications = res.data?.meta || [];
156
- const requests = (Array.isArray(notifications) ? notifications : [])
157
- .filter((n) => n.notification_type === 'FileRoleRequestCreatedNotif')
158
- .map((n) => {
159
- const locals = n.notification?.locals || n.preferred_attachments?.[0]?.body || {};
160
- // Extract from notification locals if available, otherwise parse from display text
161
- const title = n.preferred_attachments?.[0]?.body?.title || [];
162
- const subtitle = n.preferred_attachments?.[0]?.body?.subtitle || [];
163
- const email = title[0]?.html_text?.replace(/<[^>]+>/g, '') || undefined;
164
- const action = subtitle[0]?.html_text || undefined;
165
- const fileName = subtitle[2]?.html_text || undefined;
166
- const hasActions = !!n.preferred_attachments?.[0]?.actions;
167
- return {
168
- notification_id: n.notification_id,
169
- email,
170
- action,
171
- file_name: fileName,
172
- pending: hasActions,
173
- is_unread: n.is_unread,
174
- created_at: n.preferred_attachments?.[0]?.body?.created_at,
175
- };
176
- });
177
- return toolResult(JSON.stringify(requests, null, 2));
114
+ const result = await listRoleRequests(config);
115
+ return toolResult(JSON.stringify(result, null, 2));
178
116
  }
179
117
  catch (e) {
180
118
  return toolError(`Failed to list role requests: ${e.response?.status || e.message}`);
@@ -195,19 +133,7 @@ defineTool({
195
133
  },
196
134
  }, async ({ notification_id }) => {
197
135
  try {
198
- const res = await internalClient(config).put('/api/user_notifications/accept', {
199
- id: notification_id,
200
- medium: 'web',
201
- appVersion: '1',
202
- clientType: 'web',
203
- });
204
- const notif = res.data?.meta?.notification;
205
- const locals = notif?.locals || {};
206
- let msg = `Approved access request ${notification_id}`;
207
- if (locals.file_name)
208
- msg += ` for ${locals.file_name}`;
209
- if (locals.user_id)
210
- msg += ` (user ${locals.user_id})`;
136
+ const msg = await approveRoleRequest(config, { notification_id });
211
137
  return toolResult(msg);
212
138
  }
213
139
  catch (e) {
@@ -229,13 +155,8 @@ defineTool({
229
155
  },
230
156
  }, async ({ notification_id }) => {
231
157
  try {
232
- await internalClient(config).put('/api/user_notifications/reject', {
233
- id: notification_id,
234
- medium: 'web',
235
- appVersion: '1',
236
- clientType: 'web',
237
- });
238
- return toolResult(`Declined access request ${notification_id}`);
158
+ const msg = await denyRoleRequest(config, { notification_id });
159
+ return toolResult(msg);
239
160
  }
240
161
  catch (e) {
241
162
  return toolError(`Failed to deny request: ${e.response?.status || e.message}`);
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod';
2
- import { internalClient } from '../clients/internal-api.js';
3
2
  import { defineTool, toolResult, toolError, figmaId } from './register.js';
3
+ import { createProject, renameProject, moveProject, trashProject, restoreProject, setProjectDescription, } from '../operations/projects.js';
4
4
  // -- create_project --
5
5
  defineTool({
6
6
  toolset: 'projects',
@@ -15,19 +15,8 @@ defineTool({
15
15
  },
16
16
  }, async ({ team_id, name }) => {
17
17
  try {
18
- const res = await internalClient(config).post('/api/folders', {
19
- team_id,
20
- path: name,
21
- sharing_audience_control: 'org_view',
22
- team_access: 'team_edit',
23
- });
24
- const meta = res.data?.meta;
25
- const p = Array.isArray(meta) ? meta[0] : (meta?.folder || meta || res.data);
26
- return toolResult(JSON.stringify({
27
- id: String(p.id),
28
- name: p.name || p.path || name,
29
- team_id,
30
- }, null, 2));
18
+ const result = await createProject(config, { team_id, name });
19
+ return toolResult(JSON.stringify(result, null, 2));
31
20
  }
32
21
  catch (e) {
33
22
  return toolError(`Failed to create project: ${e.response?.status || e.message}`);
@@ -49,10 +38,7 @@ defineTool({
49
38
  },
50
39
  }, async ({ project_id, name }) => {
51
40
  try {
52
- await internalClient(config).put('/api/folders/rename', {
53
- folder_id: project_id,
54
- name,
55
- });
41
+ await renameProject(config, { project_id, name });
56
42
  return toolResult(`Renamed project ${project_id} to "${name}"`);
57
43
  }
58
44
  catch (e) {
@@ -75,10 +61,7 @@ defineTool({
75
61
  },
76
62
  }, async ({ project_id, destination_team_id }) => {
77
63
  try {
78
- await internalClient(config).put('/api/folders/move', {
79
- folder_id: project_id,
80
- team_id: destination_team_id,
81
- });
64
+ await moveProject(config, { project_id, destination_team_id });
82
65
  return toolResult(`Moved project ${project_id} to team ${destination_team_id}`);
83
66
  }
84
67
  catch (e) {
@@ -101,7 +84,7 @@ defineTool({
101
84
  },
102
85
  }, async ({ project_id }) => {
103
86
  try {
104
- await internalClient(config).put(`/api/folders/trash/${project_id}`);
87
+ await trashProject(config, { project_id });
105
88
  return toolResult(`Trashed project ${project_id}`);
106
89
  }
107
90
  catch (e) {
@@ -123,7 +106,7 @@ defineTool({
123
106
  },
124
107
  }, async ({ project_id }) => {
125
108
  try {
126
- await internalClient(config).put(`/api/folders/restore/${project_id}`);
109
+ await restoreProject(config, { project_id });
127
110
  return toolResult(`Restored project ${project_id}`);
128
111
  }
129
112
  catch (e) {
@@ -146,9 +129,7 @@ defineTool({
146
129
  },
147
130
  }, async ({ project_id, description }) => {
148
131
  try {
149
- await internalClient(config).put(`/api/folders/${project_id}/description`, {
150
- description,
151
- });
132
+ await setProjectDescription(config, { project_id, description });
152
133
  return toolResult(`Updated description for project ${project_id}`);
153
134
  }
154
135
  catch (e) {
@@ -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 { getFile, getNodes } from '../operations/reading.js';
4
4
  // -- get_file --
5
5
  defineTool({
6
6
  toolset: 'reading',
@@ -15,13 +15,8 @@ defineTool({
15
15
  },
16
16
  }, async ({ file_key, depth, node_id }) => {
17
17
  try {
18
- const params = {};
19
- if (depth !== undefined)
20
- params.depth = String(depth);
21
- if (node_id)
22
- params['node-id'] = node_id;
23
- const res = await publicClient(config).get(`/v1/files/${file_key}`, { params });
24
- return toolResult(JSON.stringify(res.data, null, 2));
18
+ const result = await getFile(config, { file_key, depth, node_id });
19
+ return toolResult(JSON.stringify(result, null, 2));
25
20
  }
26
21
  catch (e) {
27
22
  return toolError(`Failed to get file: ${e.response?.status || e.message}`);
@@ -43,13 +38,8 @@ defineTool({
43
38
  },
44
39
  }, async ({ file_key, node_ids, depth }) => {
45
40
  try {
46
- const params = {
47
- ids: node_ids.join(','),
48
- };
49
- if (depth !== undefined)
50
- params.depth = String(depth);
51
- const res = await publicClient(config).get(`/v1/files/${file_key}/nodes`, { params });
52
- return toolResult(JSON.stringify(res.data, null, 2));
41
+ const result = await getNodes(config, { file_key, node_ids, depth });
42
+ return toolResult(JSON.stringify(result, null, 2));
53
43
  }
54
44
  catch (e) {
55
45
  return toolError(`Failed to get nodes: ${e.response?.status || e.message}`);