figmanage 1.3.7 → 1.4.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 +181 -132
- package/dist/auth/client.d.ts +1 -0
- package/dist/auth/health.js +2 -1
- package/dist/cli/comments.js +79 -2
- package/dist/cli/components.js +66 -0
- package/dist/cli/compound-commands.js +2 -0
- package/dist/cli/org.js +143 -5
- package/dist/cli/teams.js +41 -1
- package/dist/cli/webhooks.js +20 -1
- package/dist/clients/internal-api.js +1 -1
- package/dist/clients/public-api.js +1 -1
- package/dist/mcp.d.ts +1 -0
- package/dist/mcp.js +10 -4
- package/dist/operations/comments.d.ts +25 -0
- package/dist/operations/comments.js +22 -0
- package/dist/operations/compound-manager.d.ts +1 -0
- package/dist/operations/compound-manager.js +17 -3
- package/dist/operations/dev-resources.d.ts +25 -0
- package/dist/operations/dev-resources.js +31 -0
- package/dist/operations/navigate.d.ts +5 -0
- package/dist/operations/navigate.js +20 -0
- package/dist/operations/org.d.ts +68 -1
- package/dist/operations/org.js +112 -1
- package/dist/operations/teams.d.ts +9 -0
- package/dist/operations/teams.js +23 -0
- package/dist/operations/webhooks.d.ts +14 -0
- package/dist/operations/webhooks.js +12 -0
- package/dist/tools/analytics.js +2 -0
- package/dist/tools/comments.js +99 -3
- package/dist/tools/compound-manager.js +7 -2
- package/dist/tools/compound.js +3 -0
- package/dist/tools/dev-resources.d.ts +2 -0
- package/dist/tools/dev-resources.js +78 -0
- package/dist/tools/org.js +203 -8
- package/dist/tools/permissions.js +3 -0
- package/dist/tools/register.d.ts +2 -1
- package/dist/tools/register.js +4 -1
- package/dist/tools/teams.js +53 -1
- package/dist/tools/webhooks.js +25 -2
- package/package.json +1 -1
package/dist/cli/org.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import { listAdmins, listOrgTeams, seatUsage, listTeamMembers, billingOverview, listInvoices, orgDomains, aiCreditUsage, exportMembers, listOrgMembers, contractRates, changeSeat, } from '../operations/org.js';
|
|
2
|
+
import { listAdmins, listOrgTeams, seatUsage, listTeamMembers, billingOverview, listInvoices, orgDomains, aiCreditUsage, exportMembers, listOrgMembers, contractRates, changeSeat, activityLog, listPayments, removeOrgMember, createUserGroup, deleteUserGroups, addUserGroupMembers, removeUserGroupMembers, } from '../operations/org.js';
|
|
3
3
|
import { output, error } from './format.js';
|
|
4
4
|
import { formatApiError } from '../helpers.js';
|
|
5
5
|
import { requireCookie } from './helpers.js';
|
|
@@ -130,13 +130,14 @@ export function orgCommand() {
|
|
|
130
130
|
}
|
|
131
131
|
});
|
|
132
132
|
org
|
|
133
|
-
.command('ai-credits <
|
|
134
|
-
.description('AI credit usage summary
|
|
133
|
+
.command('ai-credits <team-id>')
|
|
134
|
+
.description('AI credit usage summary (resolves billing plan from team)')
|
|
135
|
+
.option('--plan-id <id>', 'Plan ID override (skips team folder lookup)')
|
|
135
136
|
.option('--json', 'Force JSON output')
|
|
136
|
-
.action(async (
|
|
137
|
+
.action(async (teamId, options) => {
|
|
137
138
|
try {
|
|
138
139
|
const config = requireCookie();
|
|
139
|
-
const result = await aiCreditUsage(config, { plan_id: planId });
|
|
140
|
+
const result = await aiCreditUsage(config, { team_id: teamId, plan_id: options.planId });
|
|
140
141
|
output(result, options);
|
|
141
142
|
}
|
|
142
143
|
catch (e) {
|
|
@@ -223,6 +224,143 @@ export function orgCommand() {
|
|
|
223
224
|
process.exit(1);
|
|
224
225
|
}
|
|
225
226
|
});
|
|
227
|
+
org
|
|
228
|
+
.command('activity-log')
|
|
229
|
+
.description('Org audit log. Filter by email for per-user activity.')
|
|
230
|
+
.option('--org-id <id>', 'Org ID override')
|
|
231
|
+
.option('--emails <list>', 'Comma-separated emails to filter')
|
|
232
|
+
.option('--start <date>', 'Start date (ISO or YYYY-MM-DD)')
|
|
233
|
+
.option('--end <date>', 'End date (ISO or YYYY-MM-DD)')
|
|
234
|
+
.option('--page-size <n>', 'Entries per page')
|
|
235
|
+
.option('--after <cursor>', 'Pagination cursor from previous response')
|
|
236
|
+
.option('--json', 'Force JSON output')
|
|
237
|
+
.action(async (options) => {
|
|
238
|
+
try {
|
|
239
|
+
const config = requireCookie();
|
|
240
|
+
const result = await activityLog(config, {
|
|
241
|
+
org_id: options.orgId,
|
|
242
|
+
emails: options.emails,
|
|
243
|
+
start_time: options.start,
|
|
244
|
+
end_time: options.end,
|
|
245
|
+
page_size: options.pageSize ? parseInt(options.pageSize, 10) : undefined,
|
|
246
|
+
after: options.after,
|
|
247
|
+
});
|
|
248
|
+
output(result, options);
|
|
249
|
+
}
|
|
250
|
+
catch (e) {
|
|
251
|
+
error(formatApiError(e));
|
|
252
|
+
process.exit(1);
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
org
|
|
256
|
+
.command('payments')
|
|
257
|
+
.description('List paid invoices / payment history')
|
|
258
|
+
.option('--org-id <id>', 'Org ID override')
|
|
259
|
+
.option('--json', 'Force JSON output')
|
|
260
|
+
.action(async (options) => {
|
|
261
|
+
try {
|
|
262
|
+
const config = requireCookie();
|
|
263
|
+
const result = await listPayments(config, { org_id: options.orgId });
|
|
264
|
+
output(result, options);
|
|
265
|
+
}
|
|
266
|
+
catch (e) {
|
|
267
|
+
error(formatApiError(e));
|
|
268
|
+
process.exit(1);
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
org
|
|
272
|
+
.command('remove-org-member <user>')
|
|
273
|
+
.description('Permanently remove a member from the org (cannot be undone)')
|
|
274
|
+
.option('--org-id <id>', 'Org ID override')
|
|
275
|
+
.option('--confirm', 'Confirm permanent removal')
|
|
276
|
+
.option('--json', 'Force JSON output')
|
|
277
|
+
.action(async (user, options) => {
|
|
278
|
+
try {
|
|
279
|
+
const config = requireCookie();
|
|
280
|
+
const msg = await removeOrgMember(config, {
|
|
281
|
+
user_identifier: user,
|
|
282
|
+
org_id: options.orgId,
|
|
283
|
+
confirm: options.confirm,
|
|
284
|
+
});
|
|
285
|
+
output({ message: msg }, options);
|
|
286
|
+
}
|
|
287
|
+
catch (e) {
|
|
288
|
+
error(formatApiError(e));
|
|
289
|
+
process.exit(1);
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
org
|
|
293
|
+
.command('create-user-group <name>')
|
|
294
|
+
.description('Create a user group')
|
|
295
|
+
.option('--description <text>', 'Group description')
|
|
296
|
+
.option('--team-id <id>', 'Team ID (resolves billing plan)')
|
|
297
|
+
.option('--plan-id <id>', 'Plan ID override')
|
|
298
|
+
.option('--emails <list>', 'Comma-separated emails to add as initial members')
|
|
299
|
+
.option('--no-notify', 'Skip member notification')
|
|
300
|
+
.option('--json', 'Force JSON output')
|
|
301
|
+
.action(async (name, options) => {
|
|
302
|
+
try {
|
|
303
|
+
const config = requireCookie();
|
|
304
|
+
const result = await createUserGroup(config, {
|
|
305
|
+
name,
|
|
306
|
+
description: options.description,
|
|
307
|
+
team_id: options.teamId,
|
|
308
|
+
plan_id: options.planId,
|
|
309
|
+
emails: options.emails ? options.emails.split(',').map(e => e.trim()) : undefined,
|
|
310
|
+
should_notify: options.notify,
|
|
311
|
+
});
|
|
312
|
+
output(result, options);
|
|
313
|
+
}
|
|
314
|
+
catch (e) {
|
|
315
|
+
error(formatApiError(e));
|
|
316
|
+
process.exit(1);
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
org
|
|
320
|
+
.command('delete-user-groups <ids...>')
|
|
321
|
+
.description('Delete one or more user groups')
|
|
322
|
+
.option('--json', 'Force JSON output')
|
|
323
|
+
.action(async (ids, options) => {
|
|
324
|
+
try {
|
|
325
|
+
const config = requireCookie();
|
|
326
|
+
const msg = await deleteUserGroups(config, { user_group_ids: ids });
|
|
327
|
+
output({ message: msg }, options);
|
|
328
|
+
}
|
|
329
|
+
catch (e) {
|
|
330
|
+
error(formatApiError(e));
|
|
331
|
+
process.exit(1);
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
org
|
|
335
|
+
.command('add-user-group-members <group-id> <emails...>')
|
|
336
|
+
.description('Add members to a user group by email')
|
|
337
|
+
.option('--json', 'Force JSON output')
|
|
338
|
+
.action(async (groupId, emails, options) => {
|
|
339
|
+
try {
|
|
340
|
+
const config = requireCookie();
|
|
341
|
+
const result = await addUserGroupMembers(config, { user_group_id: groupId, emails });
|
|
342
|
+
output(result, options);
|
|
343
|
+
}
|
|
344
|
+
catch (e) {
|
|
345
|
+
error(formatApiError(e));
|
|
346
|
+
process.exit(1);
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
org
|
|
350
|
+
.command('remove-user-group-members <group-id> <user-ids...>')
|
|
351
|
+
.description('Remove members from a user group')
|
|
352
|
+
.option('--json', 'Force JSON output')
|
|
353
|
+
.action(async (groupId, userIds, options) => {
|
|
354
|
+
try {
|
|
355
|
+
const config = requireCookie();
|
|
356
|
+
const result = await removeUserGroupMembers(config, { user_group_id: groupId, user_ids: userIds });
|
|
357
|
+
output({ message: result }, options);
|
|
358
|
+
}
|
|
359
|
+
catch (e) {
|
|
360
|
+
error(formatApiError(e));
|
|
361
|
+
process.exit(1);
|
|
362
|
+
}
|
|
363
|
+
});
|
|
226
364
|
return org;
|
|
227
365
|
}
|
|
228
366
|
//# sourceMappingURL=org.js.map
|
package/dist/cli/teams.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import { createTeam, renameTeam, deleteTeam } from '../operations/teams.js';
|
|
2
|
+
import { createTeam, renameTeam, deleteTeam, addTeamMember, removeTeamMember } from '../operations/teams.js';
|
|
3
3
|
import { output, error } from './format.js';
|
|
4
4
|
import { formatApiError } from '../helpers.js';
|
|
5
5
|
import { requireCookie } from './helpers.js';
|
|
@@ -57,6 +57,46 @@ export function teamsCommand() {
|
|
|
57
57
|
process.exit(1);
|
|
58
58
|
}
|
|
59
59
|
});
|
|
60
|
+
teams
|
|
61
|
+
.command('add-member <team-id> <email>')
|
|
62
|
+
.description('Add a member to a team by email')
|
|
63
|
+
.option('--level <n>', 'Permission level: 100 = view (default), 300 = edit, 999 = admin')
|
|
64
|
+
.option('--json', 'Force JSON output')
|
|
65
|
+
.action(async (teamId, email, options) => {
|
|
66
|
+
try {
|
|
67
|
+
const config = requireCookie();
|
|
68
|
+
const msg = await addTeamMember(config, {
|
|
69
|
+
team_id: teamId,
|
|
70
|
+
email,
|
|
71
|
+
level: options.level ? parseInt(options.level, 10) : undefined,
|
|
72
|
+
});
|
|
73
|
+
output({ message: msg }, options);
|
|
74
|
+
}
|
|
75
|
+
catch (e) {
|
|
76
|
+
error(formatApiError(e));
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
teams
|
|
81
|
+
.command('remove-member <team-id> <user-id>')
|
|
82
|
+
.description('Remove a member from a team')
|
|
83
|
+
.option('--json', 'Force JSON output')
|
|
84
|
+
.action(async (teamId, userId, options) => {
|
|
85
|
+
try {
|
|
86
|
+
const config = requireCookie();
|
|
87
|
+
const { confirmAction } = await import('./helpers.js');
|
|
88
|
+
if (!await confirmAction(`Remove user ${userId} from team ${teamId}?`)) {
|
|
89
|
+
console.log('Cancelled.');
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const msg = await removeTeamMember(config, { team_id: teamId, user_id: userId });
|
|
93
|
+
output({ message: msg }, options);
|
|
94
|
+
}
|
|
95
|
+
catch (e) {
|
|
96
|
+
error(formatApiError(e));
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
60
100
|
return teams;
|
|
61
101
|
}
|
|
62
102
|
//# sourceMappingURL=teams.js.map
|
package/dist/cli/webhooks.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import { listWebhooks, createWebhook, updateWebhook, deleteWebhook, } from '../operations/webhooks.js';
|
|
2
|
+
import { listWebhooks, createWebhook, updateWebhook, deleteWebhook, webhookRequests, } from '../operations/webhooks.js';
|
|
3
3
|
import { output, error } from './format.js';
|
|
4
4
|
import { formatApiError } from '../helpers.js';
|
|
5
5
|
import { requirePat } from './helpers.js';
|
|
@@ -85,6 +85,25 @@ export function webhooksCommand() {
|
|
|
85
85
|
process.exit(1);
|
|
86
86
|
}
|
|
87
87
|
});
|
|
88
|
+
webhooks
|
|
89
|
+
.command('requests <webhook-id>')
|
|
90
|
+
.description('List recent webhook delivery attempts (last 7 days)')
|
|
91
|
+
.option('--json', 'Force JSON output')
|
|
92
|
+
.action(async (webhookId, options) => {
|
|
93
|
+
try {
|
|
94
|
+
const config = requirePat();
|
|
95
|
+
const result = await webhookRequests(config, { webhook_id: webhookId });
|
|
96
|
+
if (result.requests.length === 0) {
|
|
97
|
+
console.log('No webhook deliveries in the last 7 days.');
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
output(result, options);
|
|
101
|
+
}
|
|
102
|
+
catch (e) {
|
|
103
|
+
error(formatApiError(e));
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
88
107
|
webhooks
|
|
89
108
|
.command('delete <webhook-id>')
|
|
90
109
|
.description('Delete a webhook')
|
|
@@ -8,7 +8,7 @@ export function internalClient(config) {
|
|
|
8
8
|
if (existing)
|
|
9
9
|
return existing;
|
|
10
10
|
const client = axios.create({
|
|
11
|
-
baseURL: 'https://www.figma.com',
|
|
11
|
+
baseURL: process.env.FIGMA_INTERNAL_BASE_URL || 'https://www.figma.com',
|
|
12
12
|
httpsAgent,
|
|
13
13
|
headers: {
|
|
14
14
|
'Cookie': `__Host-figma.authn=${config.cookie || ''}`,
|
|
@@ -8,7 +8,7 @@ export function publicClient(config) {
|
|
|
8
8
|
if (existing)
|
|
9
9
|
return existing;
|
|
10
10
|
const client = axios.create({
|
|
11
|
-
baseURL: 'https://api.figma.com',
|
|
11
|
+
baseURL: process.env.FIGMA_PUBLIC_BASE_URL || 'https://api.figma.com',
|
|
12
12
|
httpsAgent,
|
|
13
13
|
headers: {
|
|
14
14
|
'X-Figma-Token': config.pat || '',
|
package/dist/mcp.d.ts
CHANGED
package/dist/mcp.js
CHANGED
|
@@ -7,6 +7,7 @@ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/
|
|
|
7
7
|
import { loadAuthConfig, hasPat, hasCookie } from './auth/client.js';
|
|
8
8
|
import { registerTools } from './tools/register.js';
|
|
9
9
|
import { registerSetupTools } from './tools/setup.js';
|
|
10
|
+
import { checkIsAdmin } from './operations/navigate.js';
|
|
10
11
|
// Import tool modules (side-effect: registers via defineTool)
|
|
11
12
|
import './tools/navigate.js';
|
|
12
13
|
import './tools/files.js';
|
|
@@ -17,6 +18,7 @@ import './tools/export.js';
|
|
|
17
18
|
import './tools/versions.js';
|
|
18
19
|
import './tools/branching.js';
|
|
19
20
|
import './tools/components.js';
|
|
21
|
+
import './tools/dev-resources.js';
|
|
20
22
|
import './tools/webhooks.js';
|
|
21
23
|
import './tools/reading.js';
|
|
22
24
|
import './tools/analytics.js';
|
|
@@ -73,19 +75,23 @@ export async function startMcpServer() {
|
|
|
73
75
|
version,
|
|
74
76
|
});
|
|
75
77
|
const fullyConfigured = hasPat(config) && hasCookie(config);
|
|
78
|
+
const isAdmin = hasCookie(config) ? await checkIsAdmin(config) : false;
|
|
79
|
+
config.isAdmin = isAdmin;
|
|
76
80
|
if (fullyConfigured) {
|
|
77
|
-
registerTools(server, config, enabledToolsets, readOnly);
|
|
81
|
+
registerTools(server, config, enabledToolsets, readOnly, isAdmin);
|
|
78
82
|
}
|
|
79
83
|
else {
|
|
80
84
|
// No auth or partial auth: register setup tools.
|
|
81
85
|
// If env vars provide some auth, also register whatever tools are available.
|
|
82
86
|
if (hasPat(config) || hasCookie(config)) {
|
|
83
|
-
registerTools(server, config, enabledToolsets, readOnly);
|
|
87
|
+
registerTools(server, config, enabledToolsets, readOnly, isAdmin);
|
|
84
88
|
}
|
|
85
|
-
registerSetupTools(server, () => {
|
|
89
|
+
registerSetupTools(server, async () => {
|
|
86
90
|
// Re-load config after setup wrote credentials to disk
|
|
87
91
|
const newConfig = loadAuthConfig();
|
|
88
|
-
|
|
92
|
+
const newIsAdmin = await checkIsAdmin(newConfig);
|
|
93
|
+
newConfig.isAdmin = newIsAdmin;
|
|
94
|
+
registerTools(server, newConfig, enabledToolsets, readOnly, newIsAdmin);
|
|
89
95
|
server.server.sendToolListChanged();
|
|
90
96
|
});
|
|
91
97
|
}
|
|
@@ -36,6 +36,31 @@ export declare function deleteComment(config: AuthConfig, params: {
|
|
|
36
36
|
file_key: string;
|
|
37
37
|
comment_id: string;
|
|
38
38
|
}): Promise<void>;
|
|
39
|
+
export declare function resolveComment(config: AuthConfig, params: {
|
|
40
|
+
file_key: string;
|
|
41
|
+
comment_id: string;
|
|
42
|
+
resolved?: boolean;
|
|
43
|
+
}): Promise<string>;
|
|
44
|
+
export declare function editComment(config: AuthConfig, params: {
|
|
45
|
+
file_key: string;
|
|
46
|
+
comment_id: string;
|
|
47
|
+
message: string;
|
|
48
|
+
}): Promise<string>;
|
|
49
|
+
export interface AddedReaction {
|
|
50
|
+
emoji: string;
|
|
51
|
+
user: string;
|
|
52
|
+
created_at: string;
|
|
53
|
+
}
|
|
54
|
+
export declare function addCommentReaction(config: AuthConfig, params: {
|
|
55
|
+
file_key: string;
|
|
56
|
+
comment_id: string;
|
|
57
|
+
emoji: string;
|
|
58
|
+
}): Promise<AddedReaction>;
|
|
59
|
+
export declare function removeCommentReaction(config: AuthConfig, params: {
|
|
60
|
+
file_key: string;
|
|
61
|
+
comment_id: string;
|
|
62
|
+
emoji: string;
|
|
63
|
+
}): Promise<void>;
|
|
39
64
|
export declare function listCommentReactions(config: AuthConfig, params: {
|
|
40
65
|
file_key: string;
|
|
41
66
|
comment_id: string;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { publicClient } from '../clients/public-api.js';
|
|
2
|
+
import { internalClient } from '../clients/internal-api.js';
|
|
2
3
|
export async function listComments(config, params) {
|
|
3
4
|
const res = await publicClient(config).get(`/v1/files/${params.file_key}/comments`);
|
|
4
5
|
const comments = res.data?.comments || [];
|
|
@@ -53,6 +54,27 @@ export async function postComment(config, params) {
|
|
|
53
54
|
export async function deleteComment(config, params) {
|
|
54
55
|
await publicClient(config).delete(`/v1/files/${params.file_key}/comments/${params.comment_id}`);
|
|
55
56
|
}
|
|
57
|
+
export async function resolveComment(config, params) {
|
|
58
|
+
const resolve = params.resolved !== false;
|
|
59
|
+
await internalClient(config).put(`/api/file/${params.file_key}/comments/${params.comment_id}`, { resolved_at: resolve ? 'true' : null });
|
|
60
|
+
return `Comment ${params.comment_id} ${resolve ? 'resolved' : 'unresolved'}.`;
|
|
61
|
+
}
|
|
62
|
+
export async function editComment(config, params) {
|
|
63
|
+
await internalClient(config).put(`/api/file/${params.file_key}/comments/${params.comment_id}`, { message_meta: [{ t: params.message }] });
|
|
64
|
+
return `Comment ${params.comment_id} updated.`;
|
|
65
|
+
}
|
|
66
|
+
export async function addCommentReaction(config, params) {
|
|
67
|
+
const res = await publicClient(config).post(`/v1/files/${params.file_key}/comments/${params.comment_id}/reactions`, { emoji: params.emoji });
|
|
68
|
+
const r = res.data;
|
|
69
|
+
return {
|
|
70
|
+
emoji: r.emoji || params.emoji,
|
|
71
|
+
user: r.user?.handle || '',
|
|
72
|
+
created_at: r.created_at || new Date().toISOString(),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
export async function removeCommentReaction(config, params) {
|
|
76
|
+
await publicClient(config).delete(`/v1/files/${params.file_key}/comments/${params.comment_id}/reactions`, { params: { emoji: params.emoji } });
|
|
77
|
+
}
|
|
56
78
|
export async function listCommentReactions(config, params) {
|
|
57
79
|
const res = await publicClient(config).get(`/v1/files/${params.file_key}/comments/${params.comment_id}/reactions`);
|
|
58
80
|
const reactions = res.data?.reactions || [];
|
|
@@ -22,7 +22,7 @@ const PAID_STATUSES = {
|
|
|
22
22
|
const LEVEL_MAP = { editor: 300, viewer: 100 };
|
|
23
23
|
// -- offboard_user --
|
|
24
24
|
export async function offboardUser(config, params) {
|
|
25
|
-
const { user_identifier, execute, transfer_to } = params;
|
|
25
|
+
const { user_identifier, execute, transfer_to, remove_from_org } = params;
|
|
26
26
|
const orgId = requireOrgId(config, params.org_id);
|
|
27
27
|
const api = internalClient(config);
|
|
28
28
|
// Step 1: Resolve user
|
|
@@ -175,7 +175,7 @@ export async function offboardUser(config, params) {
|
|
|
175
175
|
file_ownership: fileOwnership,
|
|
176
176
|
summary,
|
|
177
177
|
transfer_plan: transferPlan,
|
|
178
|
-
note: 'Run with execute=true to perform offboarding. Provide transfer_to if the user owns files.',
|
|
178
|
+
note: 'Run with execute=true to perform offboarding. Provide transfer_to if the user owns files. Add remove_from_org=true to fully remove from the org (permanent).',
|
|
179
179
|
};
|
|
180
180
|
}
|
|
181
181
|
// --- Execute mode ---
|
|
@@ -315,6 +315,18 @@ export async function offboardUser(config, params) {
|
|
|
315
315
|
actions.push({ action: 'downgrade_seat', status: 'failed', detail: formatApiError(e) });
|
|
316
316
|
}
|
|
317
317
|
}
|
|
318
|
+
// Step F: Remove from org (permanent, requires remove_from_org flag)
|
|
319
|
+
if (remove_from_org) {
|
|
320
|
+
try {
|
|
321
|
+
await api.delete(`/api/orgs/${orgId}/org_users`, {
|
|
322
|
+
data: { org_user_ids: [user.org_user_id] },
|
|
323
|
+
});
|
|
324
|
+
actions.push({ action: 'remove_from_org', status: 'done', detail: 'Permanently removed from org' });
|
|
325
|
+
}
|
|
326
|
+
catch (e) {
|
|
327
|
+
actions.push({ action: 'remove_from_org', status: 'failed', detail: formatApiError(e) });
|
|
328
|
+
}
|
|
329
|
+
}
|
|
318
330
|
const succeeded = actions.filter(a => a.status === 'done').length;
|
|
319
331
|
const failed = actions.filter(a => a.status === 'failed').length;
|
|
320
332
|
return {
|
|
@@ -324,7 +336,9 @@ export async function offboardUser(config, params) {
|
|
|
324
336
|
summary: { succeeded, failed, total: actions.length },
|
|
325
337
|
note: failed > 0
|
|
326
338
|
? `${failed} action(s) failed. Review and retry manually.`
|
|
327
|
-
:
|
|
339
|
+
: remove_from_org
|
|
340
|
+
? 'Offboarding complete. User permanently removed from org.'
|
|
341
|
+
: 'Offboarding complete. User remains in org (use remove_from_org=true to fully remove).',
|
|
328
342
|
};
|
|
329
343
|
}
|
|
330
344
|
// -- onboard_user --
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { AuthConfig } from '../auth/client.js';
|
|
2
|
+
export interface DevResource {
|
|
3
|
+
id: string;
|
|
4
|
+
name: string;
|
|
5
|
+
url: string;
|
|
6
|
+
file_key: string;
|
|
7
|
+
node_id: string;
|
|
8
|
+
dev_status?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function listDevResources(config: AuthConfig, params: {
|
|
11
|
+
file_key: string;
|
|
12
|
+
node_ids?: string[];
|
|
13
|
+
}): Promise<DevResource[]>;
|
|
14
|
+
export interface CreateDevResourceParams {
|
|
15
|
+
file_key: string;
|
|
16
|
+
node_id: string;
|
|
17
|
+
name: string;
|
|
18
|
+
url: string;
|
|
19
|
+
}
|
|
20
|
+
export declare function createDevResource(config: AuthConfig, params: CreateDevResourceParams): Promise<Record<string, any>>;
|
|
21
|
+
export declare function deleteDevResource(config: AuthConfig, params: {
|
|
22
|
+
file_key: string;
|
|
23
|
+
dev_resource_id: string;
|
|
24
|
+
}): Promise<void>;
|
|
25
|
+
//# sourceMappingURL=dev-resources.d.ts.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { publicClient } from '../clients/public-api.js';
|
|
2
|
+
export async function listDevResources(config, params) {
|
|
3
|
+
const reqParams = {};
|
|
4
|
+
if (params.node_ids?.length)
|
|
5
|
+
reqParams.node_ids = params.node_ids.join(',');
|
|
6
|
+
const res = await publicClient(config).get(`/v1/files/${params.file_key}/dev_resources`, { params: reqParams });
|
|
7
|
+
const resources = res.data?.dev_resources || [];
|
|
8
|
+
return resources.map((r) => ({
|
|
9
|
+
id: r.id,
|
|
10
|
+
name: r.name,
|
|
11
|
+
url: r.url,
|
|
12
|
+
file_key: r.file_key || params.file_key,
|
|
13
|
+
node_id: r.node_id,
|
|
14
|
+
dev_status: r.dev_status,
|
|
15
|
+
}));
|
|
16
|
+
}
|
|
17
|
+
export async function createDevResource(config, params) {
|
|
18
|
+
const res = await publicClient(config).post('/v1/dev_resources', {
|
|
19
|
+
dev_resources: [{
|
|
20
|
+
file_key: params.file_key,
|
|
21
|
+
node_id: params.node_id,
|
|
22
|
+
name: params.name,
|
|
23
|
+
url: params.url,
|
|
24
|
+
}],
|
|
25
|
+
});
|
|
26
|
+
return res.data;
|
|
27
|
+
}
|
|
28
|
+
export async function deleteDevResource(config, params) {
|
|
29
|
+
await publicClient(config).delete(`/v1/files/${params.file_key}/dev_resources/${params.dev_resource_id}`);
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=dev-resources.js.map
|
|
@@ -72,6 +72,11 @@ export interface Favorite {
|
|
|
72
72
|
name: string;
|
|
73
73
|
type?: string;
|
|
74
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* Check if the current user is an org admin.
|
|
77
|
+
* Returns false on any error (no org, 403, network) -- safe default.
|
|
78
|
+
*/
|
|
79
|
+
export declare function checkIsAdmin(config: AuthConfig): Promise<boolean>;
|
|
75
80
|
export declare function checkAuthStatus(config: AuthConfig): Promise<AuthCheckResult>;
|
|
76
81
|
export declare function listOrgs(config: AuthConfig): Promise<OrgListEntry[]>;
|
|
77
82
|
export declare function switchOrg(config: AuthConfig, params: {
|
|
@@ -10,8 +10,28 @@ function requireOrgId(config, explicit) {
|
|
|
10
10
|
throw new Error('Invalid org ID format');
|
|
11
11
|
return id;
|
|
12
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* Check if the current user is an org admin.
|
|
15
|
+
* Returns false on any error (no org, 403, network) -- safe default.
|
|
16
|
+
*/
|
|
17
|
+
export async function checkIsAdmin(config) {
|
|
18
|
+
const orgId = config.orgId || process.env.FIGMA_ORG_ID;
|
|
19
|
+
if (!orgId || !config.cookie)
|
|
20
|
+
return false;
|
|
21
|
+
try {
|
|
22
|
+
const res = await internalClient(config).get(`/api/orgs/${orgId}/admins`, { params: { include_license_admins: false } });
|
|
23
|
+
const admins = res.data?.meta?.admins || res.data?.meta || [];
|
|
24
|
+
return admins.some(a => (a.user_id || a.user?.id) === config.userId);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
13
30
|
export async function checkAuthStatus(config) {
|
|
14
31
|
const status = await checkAuth(config);
|
|
32
|
+
if (config.isAdmin === undefined && config.cookie) {
|
|
33
|
+
config.isAdmin = await checkIsAdmin(config);
|
|
34
|
+
}
|
|
15
35
|
const formatted = formatAuthStatus(status, config);
|
|
16
36
|
return { status, formatted };
|
|
17
37
|
}
|
package/dist/operations/org.d.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import type { AuthConfig } from '../auth/client.js';
|
|
2
|
+
import { internalClient } from '../clients/internal-api.js';
|
|
2
3
|
export declare const SEAT_HIERARCHY: Record<string, number>;
|
|
3
4
|
export declare const SEAT_LABELS: Record<string, string>;
|
|
4
5
|
export declare const PAID_STATUSES: Record<string, Record<string, string>>;
|
|
5
6
|
export declare const SEAT_KEY_TO_TYPE: Record<string, string>;
|
|
7
|
+
/** Resolve a billing plan_id from a team's folders. */
|
|
8
|
+
export declare function resolvePlanId(client: ReturnType<typeof internalClient>, teamId: string): Promise<string>;
|
|
6
9
|
export interface Admin {
|
|
7
10
|
user_id: string;
|
|
8
11
|
email: string | undefined;
|
|
@@ -54,7 +57,8 @@ export declare function orgDomains(config: AuthConfig, params: {
|
|
|
54
57
|
org_id?: string;
|
|
55
58
|
}): Promise<Record<string, any>>;
|
|
56
59
|
export declare function aiCreditUsage(config: AuthConfig, params: {
|
|
57
|
-
|
|
60
|
+
team_id: string;
|
|
61
|
+
plan_id?: string;
|
|
58
62
|
}): Promise<any>;
|
|
59
63
|
export declare function exportMembers(config: AuthConfig, params: {
|
|
60
64
|
org_id?: string;
|
|
@@ -92,4 +96,67 @@ export declare function changeSeat(config: AuthConfig, params: {
|
|
|
92
96
|
org_id?: string;
|
|
93
97
|
confirm?: boolean;
|
|
94
98
|
}): Promise<ChangeSeatResult | string>;
|
|
99
|
+
export interface ActivityLogEntry {
|
|
100
|
+
id: string;
|
|
101
|
+
timestamp: string;
|
|
102
|
+
event: string;
|
|
103
|
+
actor: {
|
|
104
|
+
id: string;
|
|
105
|
+
email: string;
|
|
106
|
+
name: string;
|
|
107
|
+
} | null;
|
|
108
|
+
team: string | null;
|
|
109
|
+
ip_address: string | null;
|
|
110
|
+
target: {
|
|
111
|
+
type: string;
|
|
112
|
+
id_or_key: string;
|
|
113
|
+
} | null;
|
|
114
|
+
metadata: Record<string, any> | null;
|
|
115
|
+
}
|
|
116
|
+
export interface ActivityLogResult {
|
|
117
|
+
entries: ActivityLogEntry[];
|
|
118
|
+
pagination?: {
|
|
119
|
+
after: string;
|
|
120
|
+
column: string;
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
export declare function activityLog(config: AuthConfig, params: {
|
|
124
|
+
org_id?: string;
|
|
125
|
+
emails?: string;
|
|
126
|
+
start_time?: string;
|
|
127
|
+
end_time?: string;
|
|
128
|
+
page_size?: number;
|
|
129
|
+
after?: string;
|
|
130
|
+
}): Promise<ActivityLogResult>;
|
|
131
|
+
export declare function listPayments(config: AuthConfig, params: {
|
|
132
|
+
org_id?: string;
|
|
133
|
+
}): Promise<any[]>;
|
|
134
|
+
export declare function removeOrgMember(config: AuthConfig, params: {
|
|
135
|
+
user_identifier: string;
|
|
136
|
+
org_id?: string;
|
|
137
|
+
confirm?: boolean;
|
|
138
|
+
}): Promise<string>;
|
|
139
|
+
export interface CreateUserGroupResult {
|
|
140
|
+
user_group_id: string;
|
|
141
|
+
add_user_results: any;
|
|
142
|
+
}
|
|
143
|
+
export declare function createUserGroup(config: AuthConfig, params: {
|
|
144
|
+
name: string;
|
|
145
|
+
description?: string;
|
|
146
|
+
team_id?: string;
|
|
147
|
+
plan_id?: string;
|
|
148
|
+
emails?: string[];
|
|
149
|
+
should_notify?: boolean;
|
|
150
|
+
}): Promise<CreateUserGroupResult>;
|
|
151
|
+
export declare function deleteUserGroups(config: AuthConfig, params: {
|
|
152
|
+
user_group_ids: string[];
|
|
153
|
+
}): Promise<string>;
|
|
154
|
+
export declare function addUserGroupMembers(config: AuthConfig, params: {
|
|
155
|
+
user_group_id: string;
|
|
156
|
+
emails: string[];
|
|
157
|
+
}): Promise<any>;
|
|
158
|
+
export declare function removeUserGroupMembers(config: AuthConfig, params: {
|
|
159
|
+
user_group_id: string;
|
|
160
|
+
user_ids: string[];
|
|
161
|
+
}): Promise<string>;
|
|
95
162
|
//# sourceMappingURL=org.d.ts.map
|