checkbox-cli 3.1.0 → 3.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 (2) hide show
  1. package/dist/checkbox.js +93 -33
  2. package/package.json +1 -1
package/dist/checkbox.js CHANGED
@@ -16,7 +16,7 @@ import { join } from 'path';
16
16
  // ── Config ──────────────────────────────────────────────────────────
17
17
  const CONFIG_DIR = join(homedir(), '.checkbox');
18
18
  const CONFIG_FILE = join(CONFIG_DIR, 'config.json');
19
- const VERSION = '3.1.0';
19
+ const VERSION = '3.2.0';
20
20
  function loadConfig() {
21
21
  try {
22
22
  if (!existsSync(CONFIG_FILE))
@@ -194,6 +194,37 @@ async function pickEntity(post, orgId, entityType, label, filters) {
194
194
  return null;
195
195
  }
196
196
  }
197
+ async function pickMember(post, orgId, label = 'team members') {
198
+ const s = p.spinner();
199
+ s.start(`Loading ${label}...`);
200
+ try {
201
+ const members = await listEntities(post, orgId, 'member');
202
+ s.stop(`${members.length} ${label}`);
203
+ if (members.length === 0) {
204
+ p.log.info(`No ${label} found.`);
205
+ return null;
206
+ }
207
+ const selected = await p.select({
208
+ message: `Choose a person`,
209
+ options: [
210
+ ...members.slice(0, 50).map((m) => ({
211
+ value: m.id,
212
+ label: m.name || m.metadata?.email || 'Unknown',
213
+ hint: m.metadata?.role || '',
214
+ })),
215
+ { value: '__back', label: pc.dim('\u2190 Back') },
216
+ ],
217
+ });
218
+ if (p.isCancel(selected) || selected === '__back')
219
+ return null;
220
+ return members.find((m) => m.id === selected) || null;
221
+ }
222
+ catch (err) {
223
+ s.stop(pc.red('Failed'));
224
+ p.log.error(err.message || `Could not load ${label}`);
225
+ return null;
226
+ }
227
+ }
197
228
  async function txt(message, required = false, placeholder) {
198
229
  const val = await p.text({
199
230
  message,
@@ -527,12 +558,10 @@ async function mTasks(cfg, post) {
527
558
  // Assignment
528
559
  const assign = await yn('Assign to a specific person?');
529
560
  if (assign) {
530
- const assignId = await txt('User ID to assign to', true);
531
- if (assignId) {
532
- payload.assigned_to = assignId;
533
- const assignName = await txt('Their display name (optional)');
534
- if (assignName)
535
- payload.assigned_to_name = assignName;
561
+ const member = await pickMember(post, cfg.orgId);
562
+ if (member) {
563
+ payload.assigned_to = member.id;
564
+ payload.assigned_to_name = member.name;
536
565
  }
537
566
  }
538
567
  // Privacy
@@ -620,9 +649,18 @@ async function mTasks(cfg, post) {
620
649
  // Share with specific users
621
650
  const share = await yn('Share with specific users?');
622
651
  if (share) {
623
- const ids = await txt('User IDs (comma-separated)', true);
624
- if (ids)
625
- payload.shared_with_ids = ids.split(',').map(s => s.trim()).filter(Boolean);
652
+ const shareIds = [];
653
+ let addMore = true;
654
+ while (addMore) {
655
+ const member = await pickMember(post, cfg.orgId);
656
+ if (member)
657
+ shareIds.push(member.id);
658
+ else
659
+ break;
660
+ addMore = !!(await yn('Add another person?'));
661
+ }
662
+ if (shareIds.length > 0)
663
+ payload.shared_with_ids = shareIds;
626
664
  }
627
665
  await cmd(post, cfg.orgId, 'create_task', payload, 'Creating task...');
628
666
  return mTasks(cfg, post);
@@ -693,12 +731,17 @@ async function mTasks(cfg, post) {
693
731
  break;
694
732
  }
695
733
  case 'assigned_to': {
696
- const aid = await txt('User ID to assign to (blank to unassign)');
697
- updates.assigned_to = aid || null;
698
- if (aid) {
699
- const aname = await txt('Their display name (optional)');
700
- if (aname)
701
- updates.assigned_to_name = aname;
734
+ const unassign = await yn('Assign to someone? (No to unassign)');
735
+ if (unassign) {
736
+ const member = await pickMember(post, cfg.orgId);
737
+ if (member) {
738
+ updates.assigned_to = member.id;
739
+ updates.assigned_to_name = member.name;
740
+ }
741
+ }
742
+ else {
743
+ updates.assigned_to = null;
744
+ updates.assigned_to_name = null;
702
745
  }
703
746
  break;
704
747
  }
@@ -844,9 +887,9 @@ async function mTasks(cfg, post) {
844
887
  const payload = { task_id: t.id };
845
888
  const specify = await yn('Specify an approver?');
846
889
  if (specify) {
847
- const aid = await txt('Approver user ID', true);
848
- if (aid)
849
- payload.approver_id = aid;
890
+ const member = await pickMember(post, cfg.orgId);
891
+ if (member)
892
+ payload.approver_id = member.id;
850
893
  }
851
894
  await cmd(post, cfg.orgId, 'request_approval', payload, 'Requesting approval...');
852
895
  return mTasks(cfg, post);
@@ -866,9 +909,9 @@ async function mTasks(cfg, post) {
866
909
  const t = await pickEntity(post, cfg.orgId, 'task', 'tasks');
867
910
  if (!t)
868
911
  return mTasks(cfg, post);
869
- const rid = await txt('Who should get the reminder? (user ID)', true);
870
- if (rid)
871
- await cmd(post, cfg.orgId, 'send_nudge', { task_id: t.id, receiver_id: rid }, 'Sending reminder...');
912
+ const member = await pickMember(post, cfg.orgId);
913
+ if (member)
914
+ await cmd(post, cfg.orgId, 'send_nudge', { task_id: t.id, receiver_id: member.id }, `Sending reminder to ${member.name}...`);
872
915
  return mTasks(cfg, post);
873
916
  }
874
917
  case 'corrective': {
@@ -1935,9 +1978,9 @@ async function mAutomation(cfg, post) {
1935
1978
  condition.approval_approver_role = role;
1936
1979
  }
1937
1980
  else if (approverType === 'user') {
1938
- const uid = await txt('Approver user ID', true);
1939
- if (uid)
1940
- condition.approval_approver_id = uid;
1981
+ const member = await pickMember(post, cfg.orgId);
1982
+ if (member)
1983
+ condition.approval_approver_id = member.id;
1941
1984
  }
1942
1985
  }
1943
1986
  else if (ruleType === 'time_window') {
@@ -2506,6 +2549,7 @@ async function mApprovals(cfg, post) {
2506
2549
  // ══════════════════════════════════════════════════════════════════════
2507
2550
  async function mTeam(cfg, post) {
2508
2551
  const a = await sel('Team', [
2552
+ { value: 'list', label: 'View team members' },
2509
2553
  { value: 'invite', label: 'Invite a member' },
2510
2554
  { value: 'revoke', label: 'Revoke an invitation' },
2511
2555
  { value: 'role', label: 'Change a member\u2019s role' },
@@ -2515,6 +2559,22 @@ async function mTeam(cfg, post) {
2515
2559
  if (!a || a === '__back')
2516
2560
  return interactiveMenu(cfg);
2517
2561
  switch (a) {
2562
+ case 'list': {
2563
+ const s = p.spinner();
2564
+ s.start('Loading team members...');
2565
+ const members = await listEntities(post, cfg.orgId, 'member');
2566
+ s.stop(`${members.length} team members`);
2567
+ if (members.length > 0) {
2568
+ const lines = members.map((m) => {
2569
+ const name = m.name || 'Unknown';
2570
+ const email = m.metadata?.email || '';
2571
+ const role = m.metadata?.role || '';
2572
+ return ` ${pc.cyan(pad(truncate(name, 30), 32))} ${pc.dim(pad(email, 30))} ${pc.dim(role)}`;
2573
+ });
2574
+ p.note(lines.join('\n'), 'Team Members');
2575
+ }
2576
+ return mTeam(cfg, post);
2577
+ }
2518
2578
  case 'invite': {
2519
2579
  const email = await txt('Email address', true);
2520
2580
  if (!email)
@@ -2537,26 +2597,26 @@ async function mTeam(cfg, post) {
2537
2597
  return mTeam(cfg, post);
2538
2598
  }
2539
2599
  case 'role': {
2540
- const userId = await txt('Member user ID', true);
2541
- if (!userId)
2600
+ const member = await pickMember(post, cfg.orgId);
2601
+ if (!member)
2542
2602
  return mTeam(cfg, post);
2543
- const role = await sel('New role', [
2603
+ const role = await sel(`New role for ${member.name}`, [
2544
2604
  { value: 'user', label: 'Member' },
2545
2605
  { value: 'admin', label: 'Admin' },
2546
2606
  { value: 'super_admin', label: 'Owner' },
2547
2607
  ]);
2548
2608
  if (!role)
2549
2609
  return mTeam(cfg, post);
2550
- await cmd(post, cfg.orgId, 'update_member_role', { user_id: userId, role }, 'Updating role...');
2610
+ await cmd(post, cfg.orgId, 'update_member_role', { user_id: member.id, role }, `Updating ${member.name}'s role...`);
2551
2611
  return mTeam(cfg, post);
2552
2612
  }
2553
2613
  case 'remove': {
2554
- const userId = await txt('Member user ID', true);
2555
- if (!userId)
2614
+ const member = await pickMember(post, cfg.orgId);
2615
+ if (!member)
2556
2616
  return mTeam(cfg, post);
2557
- const sure = await yn('Remove this member? They will lose access to the workspace.');
2617
+ const sure = await yn(`Remove ${member.name}? They will lose access to the workspace.`);
2558
2618
  if (sure)
2559
- await cmd(post, cfg.orgId, 'remove_member', { user_id: userId }, 'Removing member...');
2619
+ await cmd(post, cfg.orgId, 'remove_member', { user_id: member.id }, `Removing ${member.name}...`);
2560
2620
  return mTeam(cfg, post);
2561
2621
  }
2562
2622
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "checkbox-cli",
3
- "version": "3.1.0",
3
+ "version": "3.2.0",
4
4
  "description": "Beautiful, interactive CLI for Checkbox compliance management. Setup wizard, guided workflows, and full workspace control from your terminal.",
5
5
  "type": "module",
6
6
  "main": "dist/checkbox.js",