@girardmedia/bootspring 2.0.24 → 2.0.26

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": "1.0.0",
3
- "last_analysis": "2026-02-21T18:13:21.290Z",
3
+ "last_analysis": "2026-02-26T07:41:06.886Z",
4
4
  "insights": [
5
5
  {
6
6
  "type": "cold_start",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "contractVersion": "v1",
3
3
  "packageName": "@girardmedia/bootspring",
4
- "packageVersion": "2.0.24",
4
+ "packageVersion": "2.0.26",
5
5
  "tools": [
6
6
  {
7
7
  "name": "bootspring_assist",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@girardmedia/bootspring",
3
- "version": "2.0.24",
3
+ "version": "2.0.26",
4
4
  "description": "Development scaffolding with intelligence - AI-powered context, agents, and workflows for any MCP-compatible assistant",
5
5
  "keywords": [
6
6
  "ai",
@@ -68,9 +68,11 @@
68
68
  "start": "node bin/bootspring.js",
69
69
  "dashboard": "node bin/bootspring.js dashboard",
70
70
  "mcp": "node mcp/server.js",
71
- "test": "jest",
72
- "test:watch": "jest --watch",
73
- "test:coverage": "jest --coverage",
71
+ "test": "vitest run",
72
+ "test:watch": "vitest",
73
+ "test:coverage": "vitest run --coverage",
74
+ "test:jest": "jest",
75
+ "test:jest:watch": "jest --watch",
74
76
  "lint": "eslint .",
75
77
  "lint:fix": "eslint . --fix",
76
78
  "typecheck": "tsc --noEmit",
@@ -90,6 +92,7 @@
90
92
  "@swc/core": "^1.15.13",
91
93
  "@swc/jest": "^0.2.39",
92
94
  "@types/node": "^25.3.1",
95
+ "@vitest/coverage-v8": "^4.0.18",
93
96
  "eslint": "^9.39.2",
94
97
  "globals": "^17.3.0",
95
98
  "jest": "^29.7.0",
package/src/cli/org.ts CHANGED
@@ -1,4 +1,3 @@
1
- // @ts-nocheck
2
1
  /**
3
2
  * Bootspring Organization Command
4
3
  * Manage organization policies and members
@@ -16,17 +15,44 @@
16
15
  * @command org
17
16
  */
18
17
 
19
- const utils = require('../core/utils');
20
- const auth = require('../core/auth');
21
- const apiClient = require('../core/api-client');
22
- const policies = require('../core/policies');
23
- const organizations = require('../core/organizations');
24
- const policyMatrix = require('../core/policy-matrix');
18
+ import type {
19
+ Organization,
20
+ OrgMember,
21
+ Tier,
22
+ PolicyProfile,
23
+ MemberRole,
24
+ PolicyScopes,
25
+ } from '../types/policy';
26
+
27
+ import * as utils from '../core/utils';
28
+ import * as auth from '../core/auth';
29
+ import * as organizations from '../core/organizations';
30
+
31
+ // Type for API client methods
32
+ interface ApiClient {
33
+ listOrganizations: () => Promise<Organization[]>;
34
+ getOrganization: (orgId: string) => Promise<Organization>;
35
+ updateOrgPolicy: (orgId: string, data: { policyProfile: string }) => Promise<void>;
36
+ listOrgMembers: (orgId: string) => Promise<OrgMember[]>;
37
+ getMemberPolicy: (orgId: string, userId: string) => Promise<{ role: MemberRole; overrides?: Record<string, unknown> }>;
38
+ }
39
+
40
+ // Type for policies module
41
+ interface PoliciesModule {
42
+ resolvePolicyProfile: () => PolicyProfile;
43
+ getPolicyProfile: (profile: PolicyProfile) => { allowExternalSkills: boolean; blockedWorkflows: string[] };
44
+ getPolicyScopes: () => PolicyScopes;
45
+ listPolicyProfiles: () => Array<{ id: PolicyProfile; tier: string; description: string }>;
46
+ }
47
+
48
+ // Dynamic imports for modules that may not have proper types yet
49
+ const apiClient = require('../core/api-client') as ApiClient;
50
+ const policies = require('../core/policies') as PoliciesModule;
25
51
 
26
52
  /**
27
53
  * Run the org command
28
54
  */
29
- async function run(args = []) {
55
+ export async function run(args: string[] = []): Promise<number> {
30
56
  const subcommand = args[0] || 'list';
31
57
 
32
58
  switch (subcommand) {
@@ -43,7 +69,8 @@ async function run(args = []) {
43
69
  case 'help':
44
70
  case '--help':
45
71
  case '-h':
46
- return showHelp();
72
+ showHelp();
73
+ return 0;
47
74
  default:
48
75
  console.log(`${utils.COLORS.red}Unknown command: ${subcommand}${utils.COLORS.reset}`);
49
76
  showHelp();
@@ -54,8 +81,8 @@ async function run(args = []) {
54
81
  /**
55
82
  * List user's organizations
56
83
  */
57
- async function listOrganizations() {
58
- if (!auth.isAuthenticated()) {
84
+ async function listOrganizations(): Promise<number> {
85
+ if (!(auth as { isAuthenticated?: () => boolean }).isAuthenticated?.()) {
59
86
  console.log(`${utils.COLORS.yellow}Not authenticated. Run: bootspring auth login${utils.COLORS.reset}`);
60
87
  return 1;
61
88
  }
@@ -86,7 +113,8 @@ async function listOrganizations() {
86
113
 
87
114
  return 0;
88
115
  } catch (error) {
89
- spinner.fail(`Failed to fetch organizations: ${error.message}`);
116
+ const err = error as Error;
117
+ spinner.fail(`Failed to fetch organizations: ${err.message}`);
90
118
  return 1;
91
119
  }
92
120
  }
@@ -94,8 +122,9 @@ async function listOrganizations() {
94
122
  /**
95
123
  * Show organization info
96
124
  */
97
- async function showOrgInfo(orgId) {
98
- const resolvedOrgId = orgId || process.env.BOOTSPRING_ORG_ID || auth.getCredentials()?.orgId;
125
+ async function showOrgInfo(orgId?: string): Promise<number> {
126
+ const credentials = (auth as { getCredentials?: () => { orgId?: string } | null }).getCredentials?.();
127
+ const resolvedOrgId = orgId || process.env.BOOTSPRING_ORG_ID || credentials?.orgId;
99
128
 
100
129
  if (!resolvedOrgId) {
101
130
  console.log(`${utils.COLORS.yellow}No organization specified.${utils.COLORS.reset}`);
@@ -118,7 +147,7 @@ ${utils.COLORS.bold}Details:${utils.COLORS.reset}
118
147
  ID: ${org.id}
119
148
  Tier: ${getTierBadge(org.tier)}
120
149
  Profile: ${getProfileBadge(org.policyProfile)}
121
- Created: ${new Date(org.createdAt).toLocaleDateString()}
150
+ Created: ${org.createdAt ? new Date(org.createdAt).toLocaleDateString() : 'N/A'}
122
151
 
123
152
  ${utils.COLORS.bold}Policy:${utils.COLORS.reset}
124
153
  External Skills: ${org.policy?.allowExternalSkills ? '✓ Allowed' : '✗ Blocked'}
@@ -140,7 +169,8 @@ ${utils.COLORS.bold}Members:${utils.COLORS.reset} ${org.memberCount || org.membe
140
169
 
141
170
  return 0;
142
171
  } catch (error) {
143
- spinner.fail(`Failed to fetch organization: ${error.message}`);
172
+ const err = error as Error;
173
+ spinner.fail(`Failed to fetch organization: ${err.message}`);
144
174
  return 1;
145
175
  }
146
176
  }
@@ -148,7 +178,7 @@ ${utils.COLORS.bold}Members:${utils.COLORS.reset} ${org.memberCount || org.membe
148
178
  /**
149
179
  * Handle policy subcommands
150
180
  */
151
- async function handlePolicyCommand(args) {
181
+ async function handlePolicyCommand(args: string[]): Promise<number> {
152
182
  const action = args[0] || 'get';
153
183
 
154
184
  switch (action) {
@@ -170,7 +200,7 @@ async function handlePolicyCommand(args) {
170
200
  /**
171
201
  * Show current policy
172
202
  */
173
- async function showPolicy(orgId) {
203
+ async function showPolicy(orgId?: string): Promise<number> {
174
204
  try {
175
205
  const summary = await organizations.getOrgPolicySummary({ orgId });
176
206
 
@@ -197,26 +227,27 @@ ${utils.COLORS.cyan}${utils.COLORS.bold}Organization Policy${utils.COLORS.reset}
197
227
  ${utils.COLORS.dim}${'─'.repeat(50)}${utils.COLORS.reset}
198
228
 
199
229
  Organization: ${summary.orgName || summary.orgId}
200
- Tier: ${getTierBadge(summary.tier)}
201
- Profile: ${getProfileBadge(summary.profile)}
202
- Your Role: ${getRoleBadge(summary.role)}
230
+ Tier: ${getTierBadge(summary.tier!)}
231
+ Profile: ${getProfileBadge(summary.profile!)}
232
+ Your Role: ${getRoleBadge(summary.role!)}
203
233
 
204
234
  ${utils.COLORS.bold}Scope Summary:${utils.COLORS.reset}
205
235
  Allowed Scopes: ${utils.COLORS.green}${summary.allowedScopes}${utils.COLORS.reset}
206
236
  Blocked Scopes: ${utils.COLORS.red}${summary.blockedScopes}${utils.COLORS.reset}
207
237
 
208
238
  ${utils.COLORS.bold}Limits:${utils.COLORS.reset}
209
- Skills/Day: ${formatLimit(summary.limits.skillsPerDay)}
210
- Workflows/Day: ${formatLimit(summary.limits.workflowsPerDay)}
211
- Agent Calls/Day: ${formatLimit(summary.limits.agentInvocationsPerDay)}
212
- Team Members: ${formatLimit(summary.limits.teamMembers)}
239
+ Skills/Day: ${formatLimit(summary.limits?.skillsPerDay)}
240
+ Workflows/Day: ${formatLimit(summary.limits?.workflowsPerDay)}
241
+ Agent Calls/Day: ${formatLimit(summary.limits?.agentInvocationsPerDay)}
242
+ Team Members: ${formatLimit(summary.limits?.teamMembers)}
213
243
 
214
- ${summary.overrides.length > 0 ? `${utils.COLORS.bold}Active Overrides:${utils.COLORS.reset} ${summary.overrides.join(', ')}` : ''}
244
+ ${summary.overrides && summary.overrides.length > 0 ? `${utils.COLORS.bold}Active Overrides:${utils.COLORS.reset} ${summary.overrides.join(', ')}` : ''}
215
245
  `);
216
246
 
217
247
  return 0;
218
248
  } catch (error) {
219
- console.log(`${utils.COLORS.red}Failed to get policy: ${error.message}${utils.COLORS.reset}`);
249
+ const err = error as Error;
250
+ console.log(`${utils.COLORS.red}Failed to get policy: ${err.message}${utils.COLORS.reset}`);
220
251
  return 1;
221
252
  }
222
253
  }
@@ -224,7 +255,7 @@ ${summary.overrides.length > 0 ? `${utils.COLORS.bold}Active Overrides:${utils.C
224
255
  /**
225
256
  * Set policy profile
226
257
  */
227
- async function setPolicy(profile, orgId) {
258
+ async function setPolicy(profile?: string, orgId?: string): Promise<number> {
228
259
  if (!profile) {
229
260
  console.log(`${utils.COLORS.yellow}Please specify a profile: startup, regulated, or enterprise${utils.COLORS.reset}`);
230
261
  return 1;
@@ -261,7 +292,8 @@ To set org-level policy, specify an org ID:
261
292
  spinner.succeed(`Policy updated to "${profile}"`);
262
293
  return 0;
263
294
  } catch (error) {
264
- spinner.fail(`Failed to update policy: ${error.message}`);
295
+ const err = error as Error;
296
+ spinner.fail(`Failed to update policy: ${err.message}`);
265
297
  return 1;
266
298
  }
267
299
  }
@@ -269,7 +301,7 @@ To set org-level policy, specify an org ID:
269
301
  /**
270
302
  * Show available scopes
271
303
  */
272
- function showScopes() {
304
+ function showScopes(): number {
273
305
  const scopes = policies.getPolicyScopes();
274
306
 
275
307
  console.log(`
@@ -279,7 +311,7 @@ ${utils.COLORS.dim}${'─'.repeat(50)}${utils.COLORS.reset}
279
311
 
280
312
  for (const [category, categoryScopes] of Object.entries(scopes)) {
281
313
  console.log(`${utils.COLORS.bold}${category}:${utils.COLORS.reset}`);
282
- for (const [name, scope] of Object.entries(categoryScopes)) {
314
+ for (const [name, scope] of Object.entries(categoryScopes as Record<string, string>)) {
283
315
  console.log(` ${utils.COLORS.cyan}${scope}${utils.COLORS.reset} ${utils.COLORS.dim}(${name})${utils.COLORS.reset}`);
284
316
  }
285
317
  console.log();
@@ -291,15 +323,15 @@ ${utils.COLORS.dim}${'─'.repeat(50)}${utils.COLORS.reset}
291
323
  /**
292
324
  * Show available profiles
293
325
  */
294
- function showProfiles() {
295
- const profiles = policies.listPolicyProfiles();
326
+ function showProfiles(): number {
327
+ const profilesList = policies.listPolicyProfiles();
296
328
 
297
329
  console.log(`
298
330
  ${utils.COLORS.cyan}${utils.COLORS.bold}Policy Profiles${utils.COLORS.reset}
299
331
  ${utils.COLORS.dim}${'─'.repeat(50)}${utils.COLORS.reset}
300
332
  `);
301
333
 
302
- for (const profile of profiles) {
334
+ for (const profile of profilesList) {
303
335
  console.log(` ${getProfileBadge(profile.id)} ${utils.COLORS.dim}(${profile.tier} tier+)${utils.COLORS.reset}`);
304
336
  console.log(` ${utils.COLORS.dim}${profile.description}${utils.COLORS.reset}`);
305
337
  console.log();
@@ -311,7 +343,7 @@ ${utils.COLORS.dim}${'─'.repeat(50)}${utils.COLORS.reset}
311
343
  /**
312
344
  * List organization members
313
345
  */
314
- async function listMembers(orgId) {
346
+ async function listMembers(orgId?: string): Promise<number> {
315
347
  const resolvedOrgId = orgId || process.env.BOOTSPRING_ORG_ID;
316
348
 
317
349
  if (!resolvedOrgId) {
@@ -335,7 +367,8 @@ async function listMembers(orgId) {
335
367
 
336
368
  return 0;
337
369
  } catch (error) {
338
- spinner.fail(`Failed to fetch members: ${error.message}`);
370
+ const err = error as Error;
371
+ spinner.fail(`Failed to fetch members: ${err.message}`);
339
372
  return 1;
340
373
  }
341
374
  }
@@ -343,7 +376,7 @@ async function listMembers(orgId) {
343
376
  /**
344
377
  * Handle member subcommands
345
378
  */
346
- async function handleMemberCommand(args) {
379
+ async function handleMemberCommand(args: string[]): Promise<number> {
347
380
  const userId = args[0];
348
381
  const action = args[1] || 'info';
349
382
 
@@ -365,7 +398,7 @@ async function handleMemberCommand(args) {
365
398
  /**
366
399
  * Show member policy
367
400
  */
368
- async function showMemberPolicy(userId, orgId) {
401
+ async function showMemberPolicy(userId: string, orgId?: string): Promise<number> {
369
402
  const resolvedOrgId = orgId || process.env.BOOTSPRING_ORG_ID;
370
403
 
371
404
  if (!resolvedOrgId) {
@@ -394,15 +427,16 @@ ${utils.COLORS.bold}Policy Overrides:${utils.COLORS.reset}
394
427
 
395
428
  return 0;
396
429
  } catch (error) {
397
- spinner.fail(`Failed to fetch member policy: ${error.message}`);
430
+ const err = error as Error;
431
+ spinner.fail(`Failed to fetch member policy: ${err.message}`);
398
432
  return 1;
399
433
  }
400
434
  }
401
435
 
402
436
  // Helper functions
403
437
 
404
- function getTierBadge(tier) {
405
- const badges = {
438
+ function getTierBadge(tier: Tier): string {
439
+ const badges: Record<Tier, string> = {
406
440
  free: `${utils.COLORS.dim}[free]${utils.COLORS.reset}`,
407
441
  pro: `${utils.COLORS.blue}[pro]${utils.COLORS.reset}`,
408
442
  team: `${utils.COLORS.green}[team]${utils.COLORS.reset}`,
@@ -411,17 +445,17 @@ function getTierBadge(tier) {
411
445
  return badges[tier] || `[${tier}]`;
412
446
  }
413
447
 
414
- function getProfileBadge(profile) {
415
- const badges = {
448
+ function getProfileBadge(profile: PolicyProfile): string {
449
+ const badges: Record<PolicyProfile, string> = {
416
450
  startup: `${utils.COLORS.cyan}startup${utils.COLORS.reset}`,
417
451
  regulated: `${utils.COLORS.yellow}regulated${utils.COLORS.reset}`,
418
452
  enterprise: `${utils.COLORS.magenta}enterprise${utils.COLORS.reset}`
419
453
  };
420
- return badges[profile] || profile;
454
+ return badges[profile] || String(profile);
421
455
  }
422
456
 
423
- function getRoleBadge(role) {
424
- const badges = {
457
+ function getRoleBadge(role: MemberRole): string {
458
+ const badges: Record<MemberRole, string> = {
425
459
  owner: `${utils.COLORS.red}(owner)${utils.COLORS.reset}`,
426
460
  admin: `${utils.COLORS.yellow}(admin)${utils.COLORS.reset}`,
427
461
  member: `${utils.COLORS.dim}(member)${utils.COLORS.reset}`,
@@ -430,13 +464,13 @@ function getRoleBadge(role) {
430
464
  return badges[role] || `(${role})`;
431
465
  }
432
466
 
433
- function formatLimit(value) {
467
+ function formatLimit(value?: number): string {
434
468
  if (value === -1) return `${utils.COLORS.green}Unlimited${utils.COLORS.reset}`;
435
469
  if (value === undefined) return `${utils.COLORS.dim}N/A${utils.COLORS.reset}`;
436
470
  return value.toLocaleString();
437
471
  }
438
472
 
439
- function showHelp() {
473
+ function showHelp(): void {
440
474
  console.log(`
441
475
  ${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Bootspring Organization${utils.COLORS.reset}
442
476
  ${utils.COLORS.dim}Manage organization policies and members${utils.COLORS.reset}
@@ -471,5 +505,3 @@ ${utils.COLORS.bold}Examples:${utils.COLORS.reset}
471
505
  bootspring org members
472
506
  `);
473
507
  }
474
-
475
- export { run };
@@ -1,45 +1,35 @@
1
- // @ts-nocheck
2
1
  /**
3
2
  * Organizations Module
4
3
  * Org-level policy resolution and caching
5
4
  * @package bootspring
6
5
  */
7
6
 
8
- const apiClient = require('./api-client');
9
- const policyMatrix = require('./policy-matrix');
10
- const auth = require('./auth');
7
+ import type {
8
+ Organization,
9
+ OrgMember,
10
+ OrgContext,
11
+ OrgPolicyAccessResult,
12
+ OrgPolicySummary,
13
+ OrgOptions,
14
+ CacheEntry,
15
+ MemberRole,
16
+ } from '../types/policy';
17
+
18
+ import * as apiClient from './api-client';
19
+ import * as policyMatrix from './policy-matrix';
20
+ import * as auth from './auth';
11
21
 
12
22
  // Cache for org data (5 minute TTL)
13
23
  const ORG_CACHE_TTL = 5 * 60 * 1000;
14
- const orgCache = new Map();
15
-
16
- /**
17
- * Organization structure
18
- * @typedef {object} Organization
19
- * @property {string} id - Organization ID
20
- * @property {string} name - Organization name
21
- * @property {string} tier - Subscription tier (team/enterprise)
22
- * @property {string} policyProfile - Default policy profile
23
- * @property {object} settings - Org settings
24
- * @property {OrgMember[]} members - Organization members
25
- */
26
-
27
- /**
28
- * Organization member structure
29
- * @typedef {object} OrgMember
30
- * @property {string} userId - User ID
31
- * @property {string} email - User email
32
- * @property {string} role - Member role (owner/admin/member/viewer)
33
- * @property {object} policyOverrides - Per-member policy overrides
34
- */
24
+ const orgCache = new Map<string, CacheEntry<Organization>>();
35
25
 
36
26
  /**
37
27
  * Get organization from cache or API
38
- * @param {string} orgId - Organization ID
39
- * @param {object} options - Options including apiKey
40
- * @returns {Promise<Organization|null>}
41
28
  */
42
- async function getOrganization(orgId, options = {}) {
29
+ export async function getOrganization(
30
+ orgId: string,
31
+ options: OrgOptions = {}
32
+ ): Promise<Organization | null> {
43
33
  if (!orgId) return null;
44
34
 
45
35
  const cacheKey = `org:${orgId}`;
@@ -50,11 +40,11 @@ async function getOrganization(orgId, options = {}) {
50
40
  }
51
41
 
52
42
  try {
53
- const org = await apiClient.getOrganization(orgId, options);
43
+ const org = await (apiClient as { getOrganization?: (id: string, opts?: OrgOptions) => Promise<Organization | null> }).getOrganization?.(orgId, options);
54
44
  if (org) {
55
45
  orgCache.set(cacheKey, { data: org, timestamp: Date.now() });
56
46
  }
57
- return org;
47
+ return org ?? null;
58
48
  } catch {
59
49
  // Return cached data if available, even if stale
60
50
  return cached?.data || null;
@@ -63,12 +53,12 @@ async function getOrganization(orgId, options = {}) {
63
53
 
64
54
  /**
65
55
  * Get organization member
66
- * @param {string} orgId - Organization ID
67
- * @param {string} userId - User ID
68
- * @param {object} options - Options
69
- * @returns {Promise<OrgMember|null>}
70
56
  */
71
- async function getOrgMember(orgId, userId, options = {}) {
57
+ export async function getOrgMember(
58
+ orgId: string,
59
+ userId: string,
60
+ options: OrgOptions = {}
61
+ ): Promise<OrgMember | null> {
72
62
  const org = await getOrganization(orgId, options);
73
63
  if (!org?.members) return null;
74
64
  return org.members.find(m => m.userId === userId) || null;
@@ -76,14 +66,13 @@ async function getOrgMember(orgId, userId, options = {}) {
76
66
 
77
67
  /**
78
68
  * Resolve organization context from credentials/env
79
- * @param {object} options - Options
80
- * @returns {Promise<object>} Org context
81
69
  */
82
- async function resolveOrgContext(options = {}) {
70
+ export async function resolveOrgContext(options: OrgOptions = {}): Promise<OrgContext> {
83
71
  // Try to get org ID from various sources
72
+ const credentials = (auth as { getCredentials?: () => { orgId?: string; userId?: string } | null }).getCredentials?.();
84
73
  const orgId = options.orgId
85
74
  || process.env.BOOTSPRING_ORG_ID
86
- || auth.getCredentials()?.orgId;
75
+ || credentials?.orgId;
87
76
 
88
77
  if (!orgId) {
89
78
  return {
@@ -107,7 +96,7 @@ async function resolveOrgContext(options = {}) {
107
96
  }
108
97
 
109
98
  // Get current user's membership
110
- const userId = options.userId || auth.getCredentials()?.userId;
99
+ const userId = options.userId || credentials?.userId;
111
100
  const member = userId ? await getOrgMember(orgId, userId, options) : null;
112
101
 
113
102
  // Build effective policy
@@ -129,27 +118,24 @@ async function resolveOrgContext(options = {}) {
129
118
 
130
119
  /**
131
120
  * Check if user has permission in org
132
- * @param {string} permission - Permission to check
133
- * @param {object} orgContext - Organization context
134
- * @returns {boolean}
135
121
  */
136
- function hasOrgPermission(permission, orgContext) {
122
+ export function hasOrgPermission(permission: string, orgContext: OrgContext | null): boolean {
137
123
  if (!orgContext?.hasOrg) return false;
138
124
 
139
- const role = orgContext.role || 'viewer';
125
+ const role: MemberRole = orgContext.role || 'viewer';
140
126
  const rolePerms = policyMatrix.ROLE_PERMISSIONS[role];
141
127
 
142
128
  if (!rolePerms) return false;
143
- return rolePerms[permission] === true;
129
+ return (rolePerms as Record<string, boolean>)[permission] === true;
144
130
  }
145
131
 
146
132
  /**
147
133
  * Check policy access for a scope
148
- * @param {string} scope - Scope to check (e.g., 'skills.external')
149
- * @param {object} options - Options including orgId
150
- * @returns {Promise<object>} Access result
151
134
  */
152
- async function checkOrgPolicyAccess(scope, options = {}) {
135
+ export async function checkOrgPolicyAccess(
136
+ scope: string,
137
+ options: OrgOptions = {}
138
+ ): Promise<OrgPolicyAccessResult> {
153
139
  const orgContext = await resolveOrgContext(options);
154
140
 
155
141
  // If no org, fall back to user-level policy
@@ -165,7 +151,7 @@ async function checkOrgPolicyAccess(scope, options = {}) {
165
151
  return {
166
152
  ...result,
167
153
  hasOrgPolicy: true,
168
- orgId: orgContext.orgId,
154
+ orgId: orgContext.orgId ?? undefined,
169
155
  tier: orgContext.policy.tier,
170
156
  profile: orgContext.policy.profile
171
157
  };
@@ -173,10 +159,8 @@ async function checkOrgPolicyAccess(scope, options = {}) {
173
159
 
174
160
  /**
175
161
  * Get org policy summary for display
176
- * @param {object} options - Options
177
- * @returns {Promise<object>} Policy summary
178
162
  */
179
- async function getOrgPolicySummary(options = {}) {
163
+ export async function getOrgPolicySummary(options: OrgOptions = {}): Promise<OrgPolicySummary> {
180
164
  const orgContext = await resolveOrgContext(options);
181
165
 
182
166
  if (!orgContext.hasOrg) {
@@ -186,10 +170,10 @@ async function getOrgPolicySummary(options = {}) {
186
170
  };
187
171
  }
188
172
 
189
- const policy = orgContext.policy;
173
+ const policy = orgContext.policy!;
190
174
  return {
191
175
  hasOrg: true,
192
- orgId: orgContext.orgId,
176
+ orgId: orgContext.orgId ?? undefined,
193
177
  orgName: orgContext.org?.name,
194
178
  tier: policy.tier,
195
179
  profile: policy.profile,
@@ -203,22 +187,11 @@ async function getOrgPolicySummary(options = {}) {
203
187
 
204
188
  /**
205
189
  * Clear organization cache
206
- * @param {string} [orgId] - Specific org to clear, or all if not specified
207
190
  */
208
- function clearOrgCache(orgId) {
191
+ export function clearOrgCache(orgId?: string): void {
209
192
  if (orgId) {
210
193
  orgCache.delete(`org:${orgId}`);
211
194
  } else {
212
195
  orgCache.clear();
213
196
  }
214
197
  }
215
-
216
- module.exports = {
217
- getOrganization,
218
- getOrgMember,
219
- resolveOrgContext,
220
- hasOrgPermission,
221
- checkOrgPolicyAccess,
222
- getOrgPolicySummary,
223
- clearOrgCache
224
- };