@girardmedia/bootspring 2.0.21 → 2.0.23
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/bin/bootspring.js +5 -0
- package/cli/org.js +474 -0
- package/cli/preseed/index.js +16 -0
- package/cli/preseed/interactive.js +143 -0
- package/cli/preseed/templates.js +227 -0
- package/cli/preseed.js +9 -301
- package/cli/seed/builders/ai-context-builder.js +85 -0
- package/cli/seed/builders/index.js +13 -0
- package/cli/seed/builders/seed-builder.js +272 -0
- package/cli/seed/extractors/content-extractors.js +383 -0
- package/cli/seed/extractors/index.js +47 -0
- package/cli/seed/extractors/metadata-extractors.js +167 -0
- package/cli/seed/extractors/section-extractor.js +54 -0
- package/cli/seed/extractors/stack-extractors.js +228 -0
- package/cli/seed/index.js +18 -0
- package/cli/seed/utils/folder-structure.js +84 -0
- package/cli/seed/utils/index.js +11 -0
- package/cli/seed.js +23 -1074
- package/core/api-client.js +77 -0
- package/core/entitlements.js +36 -0
- package/core/organizations.js +223 -0
- package/core/policies.js +51 -6
- package/core/policy-matrix.js +303 -0
- package/core/project-context.js +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.js +3220 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/context-McpJQa_2.d.ts +5710 -0
- package/dist/core/index.d.ts +635 -0
- package/dist/core/index.js +2593 -0
- package/dist/core/index.js.map +1 -0
- package/dist/index-QqbeEiDm.d.ts +857 -0
- package/dist/index-UiYCgwiH.d.ts +174 -0
- package/dist/index.d.ts +453 -0
- package/dist/index.js +44228 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/index.d.ts +1 -0
- package/dist/mcp/index.js +41173 -0
- package/dist/mcp/index.js.map +1 -0
- package/generators/index.ts +82 -0
- package/intelligence/orchestrator/config/failure-signatures.js +48 -0
- package/intelligence/orchestrator/config/index.js +23 -0
- package/intelligence/orchestrator/config/pack-lifecycle.js +262 -0
- package/intelligence/orchestrator/config/phases.js +111 -0
- package/intelligence/orchestrator/config/remediation.js +150 -0
- package/intelligence/orchestrator/config/workflows.js +168 -0
- package/intelligence/orchestrator/core/index.js +16 -0
- package/intelligence/orchestrator/core/state-manager.js +88 -0
- package/intelligence/orchestrator/core/telemetry.js +24 -0
- package/intelligence/orchestrator/index.js +17 -0
- package/intelligence/orchestrator.js +17 -512
- package/mcp/contracts/mcp-contract.v1.json +1 -1
- package/package.json +16 -3
- package/src/cli/agent.ts +703 -0
- package/src/cli/analyze.ts +640 -0
- package/src/cli/audit.ts +707 -0
- package/src/cli/auth.ts +930 -0
- package/src/cli/billing.ts +364 -0
- package/src/cli/build.ts +1089 -0
- package/src/cli/business.ts +508 -0
- package/src/cli/checkpoint-utils.ts +236 -0
- package/src/cli/checkpoint.ts +757 -0
- package/src/cli/cloud-sync.ts +534 -0
- package/src/cli/content.ts +273 -0
- package/src/cli/context.ts +667 -0
- package/src/cli/dashboard.ts +133 -0
- package/src/cli/deploy.ts +704 -0
- package/src/cli/doctor.ts +480 -0
- package/src/cli/fundraise.ts +494 -0
- package/src/cli/generate.ts +346 -0
- package/src/cli/github-cmd.ts +566 -0
- package/src/cli/health.ts +599 -0
- package/src/cli/index.ts +113 -0
- package/src/cli/init.ts +838 -0
- package/src/cli/legal.ts +495 -0
- package/src/cli/log.ts +316 -0
- package/src/cli/loop.ts +1660 -0
- package/src/cli/manager.ts +878 -0
- package/src/cli/mcp.ts +275 -0
- package/src/cli/memory.ts +346 -0
- package/src/cli/metrics.ts +590 -0
- package/src/cli/monitor.ts +960 -0
- package/src/cli/mvp.ts +662 -0
- package/src/cli/onboard.ts +663 -0
- package/src/cli/orchestrator.ts +622 -0
- package/src/cli/plugin.ts +483 -0
- package/src/cli/prd.ts +671 -0
- package/src/cli/preseed-start.ts +1633 -0
- package/src/cli/preseed.ts +2434 -0
- package/src/cli/project.ts +526 -0
- package/src/cli/quality.ts +885 -0
- package/src/cli/security.ts +1079 -0
- package/src/cli/seed.ts +1224 -0
- package/src/cli/skill.ts +537 -0
- package/src/cli/suggest.ts +1225 -0
- package/src/cli/switch.ts +518 -0
- package/src/cli/task.ts +780 -0
- package/src/cli/telemetry.ts +172 -0
- package/src/cli/todo.ts +627 -0
- package/src/cli/types.ts +15 -0
- package/src/cli/update.ts +334 -0
- package/src/cli/visualize.ts +609 -0
- package/src/cli/watch.ts +895 -0
- package/src/cli/workspace.ts +709 -0
- package/src/core/action-recorder.ts +673 -0
- package/src/core/analyze-workflow.ts +1453 -0
- package/src/core/api-client.ts +1120 -0
- package/src/core/audit-workflow.ts +1681 -0
- package/src/core/auth.ts +471 -0
- package/src/core/build-orchestrator.ts +509 -0
- package/src/core/build-state.ts +621 -0
- package/src/core/checkpoint-engine.ts +482 -0
- package/src/core/config.ts +1285 -0
- package/src/core/context-loader.ts +694 -0
- package/src/core/context.ts +410 -0
- package/src/core/deploy-workflow.ts +1085 -0
- package/src/core/entitlements.ts +322 -0
- package/src/core/github-sync.ts +720 -0
- package/src/core/index.ts +981 -0
- package/src/core/ingest.ts +1186 -0
- package/src/core/metrics-engine.ts +886 -0
- package/src/core/mvp.ts +847 -0
- package/src/core/onboard-workflow.ts +1293 -0
- package/src/core/policies.ts +81 -0
- package/src/core/preseed-workflow.ts +1163 -0
- package/src/core/preseed.ts +1826 -0
- package/src/core/project-context.ts +380 -0
- package/src/core/project-state.ts +699 -0
- package/src/core/r2-sync.ts +691 -0
- package/src/core/scaffold.ts +1715 -0
- package/src/core/session.ts +286 -0
- package/src/core/task-extractor.ts +799 -0
- package/src/core/telemetry.ts +371 -0
- package/src/core/tier-enforcement.ts +737 -0
- package/src/core/utils.ts +437 -0
- package/src/index.ts +29 -0
- package/src/intelligence/agent-collab.ts +2376 -0
- package/src/intelligence/auto-suggest.ts +713 -0
- package/src/intelligence/content-gen.ts +1351 -0
- package/src/intelligence/cross-project.ts +1692 -0
- package/src/intelligence/git-memory.ts +529 -0
- package/src/intelligence/index.ts +318 -0
- package/src/intelligence/orchestrator.ts +534 -0
- package/src/intelligence/prd.ts +466 -0
- package/src/intelligence/recommendations.ts +982 -0
- package/src/intelligence/workflow-composer.ts +1472 -0
- package/src/mcp/capabilities.ts +233 -0
- package/src/mcp/index.ts +37 -0
- package/src/mcp/registry.ts +1268 -0
- package/src/mcp/response-formatter.ts +797 -0
- package/src/mcp/server.ts +240 -0
- package/src/types/agent.ts +69 -0
- package/src/types/config.ts +86 -0
- package/src/types/context.ts +77 -0
- package/src/types/index.ts +53 -0
- package/src/types/mcp.ts +91 -0
- package/src/types/skills.ts +47 -0
- package/src/types/workflow.ts +155 -0
- package/generators/index.js +0 -18
package/bin/bootspring.js
CHANGED
|
@@ -139,6 +139,11 @@ const COMMANDS = {
|
|
|
139
139
|
description: 'Manage subscription and view usage',
|
|
140
140
|
usage: 'bootspring billing [status|usage|upgrade|portal]'
|
|
141
141
|
},
|
|
142
|
+
org: {
|
|
143
|
+
script: '../cli/org.js',
|
|
144
|
+
description: 'Manage organization policies and members',
|
|
145
|
+
usage: 'bootspring org [list|info|policy|members]'
|
|
146
|
+
},
|
|
142
147
|
manager: {
|
|
143
148
|
script: '../cli/manager.js',
|
|
144
149
|
description: 'Overview of all projects with health scores',
|
package/cli/org.js
ADDED
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bootspring Organization Command
|
|
3
|
+
* Manage organization policies and members
|
|
4
|
+
*
|
|
5
|
+
* Commands:
|
|
6
|
+
* list List your organizations
|
|
7
|
+
* info [orgId] Show organization details
|
|
8
|
+
* policy get Get current policy settings
|
|
9
|
+
* policy set <profile> Set policy profile (startup/regulated/enterprise)
|
|
10
|
+
* policy scopes List available policy scopes
|
|
11
|
+
* members List organization members
|
|
12
|
+
* member <userId> policy View/set member policy overrides
|
|
13
|
+
*
|
|
14
|
+
* @package bootspring
|
|
15
|
+
* @command org
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const utils = require('../core/utils');
|
|
19
|
+
const auth = require('../core/auth');
|
|
20
|
+
const apiClient = require('../core/api-client');
|
|
21
|
+
const policies = require('../core/policies');
|
|
22
|
+
const organizations = require('../core/organizations');
|
|
23
|
+
const policyMatrix = require('../core/policy-matrix');
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Run the org command
|
|
27
|
+
*/
|
|
28
|
+
async function run(args = []) {
|
|
29
|
+
const subcommand = args[0] || 'list';
|
|
30
|
+
|
|
31
|
+
switch (subcommand) {
|
|
32
|
+
case 'list':
|
|
33
|
+
return listOrganizations();
|
|
34
|
+
case 'info':
|
|
35
|
+
return showOrgInfo(args[1]);
|
|
36
|
+
case 'policy':
|
|
37
|
+
return handlePolicyCommand(args.slice(1));
|
|
38
|
+
case 'members':
|
|
39
|
+
return listMembers(args[1]);
|
|
40
|
+
case 'member':
|
|
41
|
+
return handleMemberCommand(args.slice(1));
|
|
42
|
+
case 'help':
|
|
43
|
+
case '--help':
|
|
44
|
+
case '-h':
|
|
45
|
+
return showHelp();
|
|
46
|
+
default:
|
|
47
|
+
console.log(`${utils.COLORS.red}Unknown command: ${subcommand}${utils.COLORS.reset}`);
|
|
48
|
+
showHelp();
|
|
49
|
+
return 1;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* List user's organizations
|
|
55
|
+
*/
|
|
56
|
+
async function listOrganizations() {
|
|
57
|
+
if (!auth.isAuthenticated()) {
|
|
58
|
+
console.log(`${utils.COLORS.yellow}Not authenticated. Run: bootspring auth login${utils.COLORS.reset}`);
|
|
59
|
+
return 1;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const spinner = utils.createSpinner('Fetching organizations...').start();
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
const orgs = await apiClient.listOrganizations();
|
|
66
|
+
|
|
67
|
+
if (!orgs || orgs.length === 0) {
|
|
68
|
+
spinner.info('No organizations found');
|
|
69
|
+
console.log(`\n${utils.COLORS.dim}Create an organization at bootspring.com/dashboard${utils.COLORS.reset}`);
|
|
70
|
+
return 0;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
spinner.succeed(`Found ${orgs.length} organization(s)`);
|
|
74
|
+
|
|
75
|
+
console.log(`\n${utils.COLORS.bold}Your Organizations:${utils.COLORS.reset}\n`);
|
|
76
|
+
|
|
77
|
+
for (const org of orgs) {
|
|
78
|
+
const tierBadge = getTierBadge(org.tier);
|
|
79
|
+
const profileBadge = getProfileBadge(org.policyProfile);
|
|
80
|
+
console.log(` ${utils.COLORS.cyan}${org.name}${utils.COLORS.reset} ${tierBadge} ${profileBadge}`);
|
|
81
|
+
console.log(` ${utils.COLORS.dim}ID: ${org.id}${utils.COLORS.reset}`);
|
|
82
|
+
console.log(` ${utils.COLORS.dim}Members: ${org.memberCount || '?'}${utils.COLORS.reset}`);
|
|
83
|
+
console.log();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return 0;
|
|
87
|
+
} catch (error) {
|
|
88
|
+
spinner.fail(`Failed to fetch organizations: ${error.message}`);
|
|
89
|
+
return 1;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Show organization info
|
|
95
|
+
*/
|
|
96
|
+
async function showOrgInfo(orgId) {
|
|
97
|
+
const resolvedOrgId = orgId || process.env.BOOTSPRING_ORG_ID || auth.getCredentials()?.orgId;
|
|
98
|
+
|
|
99
|
+
if (!resolvedOrgId) {
|
|
100
|
+
console.log(`${utils.COLORS.yellow}No organization specified.${utils.COLORS.reset}`);
|
|
101
|
+
console.log(`Usage: bootspring org info <orgId>`);
|
|
102
|
+
console.log(`Or set BOOTSPRING_ORG_ID environment variable`);
|
|
103
|
+
return 1;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const spinner = utils.createSpinner('Fetching organization...').start();
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
const org = await apiClient.getOrganization(resolvedOrgId);
|
|
110
|
+
spinner.succeed('Organization loaded');
|
|
111
|
+
|
|
112
|
+
console.log(`
|
|
113
|
+
${utils.COLORS.cyan}${utils.COLORS.bold}${org.name}${utils.COLORS.reset}
|
|
114
|
+
${utils.COLORS.dim}${'─'.repeat(50)}${utils.COLORS.reset}
|
|
115
|
+
|
|
116
|
+
${utils.COLORS.bold}Details:${utils.COLORS.reset}
|
|
117
|
+
ID: ${org.id}
|
|
118
|
+
Tier: ${getTierBadge(org.tier)}
|
|
119
|
+
Profile: ${getProfileBadge(org.policyProfile)}
|
|
120
|
+
Created: ${new Date(org.createdAt).toLocaleDateString()}
|
|
121
|
+
|
|
122
|
+
${utils.COLORS.bold}Policy:${utils.COLORS.reset}
|
|
123
|
+
External Skills: ${org.policy?.allowExternalSkills ? '✓ Allowed' : '✗ Blocked'}
|
|
124
|
+
Blocked Workflows: ${org.policy?.blockedWorkflows?.length || 0}
|
|
125
|
+
|
|
126
|
+
${utils.COLORS.bold}Members:${utils.COLORS.reset} ${org.memberCount || org.members?.length || '?'}
|
|
127
|
+
`);
|
|
128
|
+
|
|
129
|
+
if (org.members && org.members.length > 0) {
|
|
130
|
+
console.log(`${utils.COLORS.bold}Member List:${utils.COLORS.reset}`);
|
|
131
|
+
for (const member of org.members.slice(0, 10)) {
|
|
132
|
+
const roleBadge = getRoleBadge(member.role);
|
|
133
|
+
console.log(` ${member.email || member.userId} ${roleBadge}`);
|
|
134
|
+
}
|
|
135
|
+
if (org.members.length > 10) {
|
|
136
|
+
console.log(` ${utils.COLORS.dim}... and ${org.members.length - 10} more${utils.COLORS.reset}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return 0;
|
|
141
|
+
} catch (error) {
|
|
142
|
+
spinner.fail(`Failed to fetch organization: ${error.message}`);
|
|
143
|
+
return 1;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Handle policy subcommands
|
|
149
|
+
*/
|
|
150
|
+
async function handlePolicyCommand(args) {
|
|
151
|
+
const action = args[0] || 'get';
|
|
152
|
+
|
|
153
|
+
switch (action) {
|
|
154
|
+
case 'get':
|
|
155
|
+
return showPolicy(args[1]);
|
|
156
|
+
case 'set':
|
|
157
|
+
return setPolicy(args[1], args[2]);
|
|
158
|
+
case 'scopes':
|
|
159
|
+
return showScopes();
|
|
160
|
+
case 'profiles':
|
|
161
|
+
return showProfiles();
|
|
162
|
+
default:
|
|
163
|
+
console.log(`${utils.COLORS.red}Unknown policy action: ${action}${utils.COLORS.reset}`);
|
|
164
|
+
console.log('Available: get, set, scopes, profiles');
|
|
165
|
+
return 1;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Show current policy
|
|
171
|
+
*/
|
|
172
|
+
async function showPolicy(orgId) {
|
|
173
|
+
try {
|
|
174
|
+
const summary = await organizations.getOrgPolicySummary({ orgId });
|
|
175
|
+
|
|
176
|
+
if (!summary.hasOrg) {
|
|
177
|
+
// Show user-level policy
|
|
178
|
+
const profile = policies.resolvePolicyProfile();
|
|
179
|
+
const policy = policies.getPolicyProfile(profile);
|
|
180
|
+
|
|
181
|
+
console.log(`
|
|
182
|
+
${utils.COLORS.cyan}${utils.COLORS.bold}Current Policy (User-Level)${utils.COLORS.reset}
|
|
183
|
+
${utils.COLORS.dim}${'─'.repeat(50)}${utils.COLORS.reset}
|
|
184
|
+
|
|
185
|
+
Profile: ${getProfileBadge(profile)}
|
|
186
|
+
External Skills: ${policy.allowExternalSkills ? '✓ Allowed' : '✗ Blocked'}
|
|
187
|
+
Blocked Workflows: ${policy.blockedWorkflows.length > 0 ? policy.blockedWorkflows.join(', ') : 'None'}
|
|
188
|
+
|
|
189
|
+
${utils.COLORS.dim}No organization context. Set BOOTSPRING_ORG_ID for org-level policy.${utils.COLORS.reset}
|
|
190
|
+
`);
|
|
191
|
+
return 0;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
console.log(`
|
|
195
|
+
${utils.COLORS.cyan}${utils.COLORS.bold}Organization Policy${utils.COLORS.reset}
|
|
196
|
+
${utils.COLORS.dim}${'─'.repeat(50)}${utils.COLORS.reset}
|
|
197
|
+
|
|
198
|
+
Organization: ${summary.orgName || summary.orgId}
|
|
199
|
+
Tier: ${getTierBadge(summary.tier)}
|
|
200
|
+
Profile: ${getProfileBadge(summary.profile)}
|
|
201
|
+
Your Role: ${getRoleBadge(summary.role)}
|
|
202
|
+
|
|
203
|
+
${utils.COLORS.bold}Scope Summary:${utils.COLORS.reset}
|
|
204
|
+
Allowed Scopes: ${utils.COLORS.green}${summary.allowedScopes}${utils.COLORS.reset}
|
|
205
|
+
Blocked Scopes: ${utils.COLORS.red}${summary.blockedScopes}${utils.COLORS.reset}
|
|
206
|
+
|
|
207
|
+
${utils.COLORS.bold}Limits:${utils.COLORS.reset}
|
|
208
|
+
Skills/Day: ${formatLimit(summary.limits.skillsPerDay)}
|
|
209
|
+
Workflows/Day: ${formatLimit(summary.limits.workflowsPerDay)}
|
|
210
|
+
Agent Calls/Day: ${formatLimit(summary.limits.agentInvocationsPerDay)}
|
|
211
|
+
Team Members: ${formatLimit(summary.limits.teamMembers)}
|
|
212
|
+
|
|
213
|
+
${summary.overrides.length > 0 ? `${utils.COLORS.bold}Active Overrides:${utils.COLORS.reset} ${summary.overrides.join(', ')}` : ''}
|
|
214
|
+
`);
|
|
215
|
+
|
|
216
|
+
return 0;
|
|
217
|
+
} catch (error) {
|
|
218
|
+
console.log(`${utils.COLORS.red}Failed to get policy: ${error.message}${utils.COLORS.reset}`);
|
|
219
|
+
return 1;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Set policy profile
|
|
225
|
+
*/
|
|
226
|
+
async function setPolicy(profile, orgId) {
|
|
227
|
+
if (!profile) {
|
|
228
|
+
console.log(`${utils.COLORS.yellow}Please specify a profile: startup, regulated, or enterprise${utils.COLORS.reset}`);
|
|
229
|
+
return 1;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const validProfiles = ['startup', 'regulated', 'enterprise'];
|
|
233
|
+
if (!validProfiles.includes(profile.toLowerCase())) {
|
|
234
|
+
console.log(`${utils.COLORS.red}Invalid profile: ${profile}${utils.COLORS.reset}`);
|
|
235
|
+
console.log(`Valid profiles: ${validProfiles.join(', ')}`);
|
|
236
|
+
return 1;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const resolvedOrgId = orgId || process.env.BOOTSPRING_ORG_ID;
|
|
240
|
+
|
|
241
|
+
if (!resolvedOrgId) {
|
|
242
|
+
// Set user-level via env var hint
|
|
243
|
+
console.log(`
|
|
244
|
+
${utils.COLORS.yellow}No organization specified.${utils.COLORS.reset}
|
|
245
|
+
|
|
246
|
+
To set user-level policy, add to your environment:
|
|
247
|
+
${utils.COLORS.cyan}export BOOTSPRING_POLICY_PROFILE=${profile}${utils.COLORS.reset}
|
|
248
|
+
|
|
249
|
+
To set org-level policy, specify an org ID:
|
|
250
|
+
${utils.COLORS.cyan}bootspring org policy set ${profile} <orgId>${utils.COLORS.reset}
|
|
251
|
+
`);
|
|
252
|
+
return 0;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const spinner = utils.createSpinner(`Setting policy to "${profile}"...`).start();
|
|
256
|
+
|
|
257
|
+
try {
|
|
258
|
+
await apiClient.updateOrgPolicy(resolvedOrgId, { policyProfile: profile });
|
|
259
|
+
organizations.clearOrgCache(resolvedOrgId);
|
|
260
|
+
spinner.succeed(`Policy updated to "${profile}"`);
|
|
261
|
+
return 0;
|
|
262
|
+
} catch (error) {
|
|
263
|
+
spinner.fail(`Failed to update policy: ${error.message}`);
|
|
264
|
+
return 1;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Show available scopes
|
|
270
|
+
*/
|
|
271
|
+
function showScopes() {
|
|
272
|
+
const scopes = policies.getPolicyScopes();
|
|
273
|
+
|
|
274
|
+
console.log(`
|
|
275
|
+
${utils.COLORS.cyan}${utils.COLORS.bold}Policy Scopes${utils.COLORS.reset}
|
|
276
|
+
${utils.COLORS.dim}${'─'.repeat(50)}${utils.COLORS.reset}
|
|
277
|
+
`);
|
|
278
|
+
|
|
279
|
+
for (const [category, categoryScopes] of Object.entries(scopes)) {
|
|
280
|
+
console.log(`${utils.COLORS.bold}${category}:${utils.COLORS.reset}`);
|
|
281
|
+
for (const [name, scope] of Object.entries(categoryScopes)) {
|
|
282
|
+
console.log(` ${utils.COLORS.cyan}${scope}${utils.COLORS.reset} ${utils.COLORS.dim}(${name})${utils.COLORS.reset}`);
|
|
283
|
+
}
|
|
284
|
+
console.log();
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return 0;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Show available profiles
|
|
292
|
+
*/
|
|
293
|
+
function showProfiles() {
|
|
294
|
+
const profiles = policies.listPolicyProfiles();
|
|
295
|
+
|
|
296
|
+
console.log(`
|
|
297
|
+
${utils.COLORS.cyan}${utils.COLORS.bold}Policy Profiles${utils.COLORS.reset}
|
|
298
|
+
${utils.COLORS.dim}${'─'.repeat(50)}${utils.COLORS.reset}
|
|
299
|
+
`);
|
|
300
|
+
|
|
301
|
+
for (const profile of profiles) {
|
|
302
|
+
console.log(` ${getProfileBadge(profile.id)} ${utils.COLORS.dim}(${profile.tier} tier+)${utils.COLORS.reset}`);
|
|
303
|
+
console.log(` ${utils.COLORS.dim}${profile.description}${utils.COLORS.reset}`);
|
|
304
|
+
console.log();
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return 0;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* List organization members
|
|
312
|
+
*/
|
|
313
|
+
async function listMembers(orgId) {
|
|
314
|
+
const resolvedOrgId = orgId || process.env.BOOTSPRING_ORG_ID;
|
|
315
|
+
|
|
316
|
+
if (!resolvedOrgId) {
|
|
317
|
+
console.log(`${utils.COLORS.yellow}No organization specified.${utils.COLORS.reset}`);
|
|
318
|
+
return 1;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const spinner = utils.createSpinner('Fetching members...').start();
|
|
322
|
+
|
|
323
|
+
try {
|
|
324
|
+
const members = await apiClient.listOrgMembers(resolvedOrgId);
|
|
325
|
+
spinner.succeed(`Found ${members.length} member(s)`);
|
|
326
|
+
|
|
327
|
+
console.log(`\n${utils.COLORS.bold}Organization Members:${utils.COLORS.reset}\n`);
|
|
328
|
+
|
|
329
|
+
for (const member of members) {
|
|
330
|
+
const roleBadge = getRoleBadge(member.role);
|
|
331
|
+
const hasOverrides = member.policyOverrides && Object.keys(member.policyOverrides).length > 0;
|
|
332
|
+
console.log(` ${member.email || member.userId} ${roleBadge}${hasOverrides ? ' (custom policy)' : ''}`);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return 0;
|
|
336
|
+
} catch (error) {
|
|
337
|
+
spinner.fail(`Failed to fetch members: ${error.message}`);
|
|
338
|
+
return 1;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Handle member subcommands
|
|
344
|
+
*/
|
|
345
|
+
async function handleMemberCommand(args) {
|
|
346
|
+
const userId = args[0];
|
|
347
|
+
const action = args[1] || 'info';
|
|
348
|
+
|
|
349
|
+
if (!userId) {
|
|
350
|
+
console.log(`${utils.COLORS.yellow}Please specify a user ID${utils.COLORS.reset}`);
|
|
351
|
+
return 1;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
switch (action) {
|
|
355
|
+
case 'info':
|
|
356
|
+
case 'policy':
|
|
357
|
+
return showMemberPolicy(userId, args[2]);
|
|
358
|
+
default:
|
|
359
|
+
console.log(`${utils.COLORS.red}Unknown member action: ${action}${utils.COLORS.reset}`);
|
|
360
|
+
return 1;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Show member policy
|
|
366
|
+
*/
|
|
367
|
+
async function showMemberPolicy(userId, orgId) {
|
|
368
|
+
const resolvedOrgId = orgId || process.env.BOOTSPRING_ORG_ID;
|
|
369
|
+
|
|
370
|
+
if (!resolvedOrgId) {
|
|
371
|
+
console.log(`${utils.COLORS.yellow}No organization specified.${utils.COLORS.reset}`);
|
|
372
|
+
return 1;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const spinner = utils.createSpinner('Fetching member policy...').start();
|
|
376
|
+
|
|
377
|
+
try {
|
|
378
|
+
const policy = await apiClient.getMemberPolicy(resolvedOrgId, userId);
|
|
379
|
+
spinner.succeed('Member policy loaded');
|
|
380
|
+
|
|
381
|
+
console.log(`
|
|
382
|
+
${utils.COLORS.cyan}${utils.COLORS.bold}Member Policy${utils.COLORS.reset}
|
|
383
|
+
${utils.COLORS.dim}${'─'.repeat(50)}${utils.COLORS.reset}
|
|
384
|
+
|
|
385
|
+
User: ${userId}
|
|
386
|
+
Role: ${getRoleBadge(policy.role)}
|
|
387
|
+
|
|
388
|
+
${utils.COLORS.bold}Policy Overrides:${utils.COLORS.reset}
|
|
389
|
+
${policy.overrides && Object.keys(policy.overrides).length > 0
|
|
390
|
+
? JSON.stringify(policy.overrides, null, 2).split('\n').join('\n ')
|
|
391
|
+
: 'No custom overrides (using org defaults)'}
|
|
392
|
+
`);
|
|
393
|
+
|
|
394
|
+
return 0;
|
|
395
|
+
} catch (error) {
|
|
396
|
+
spinner.fail(`Failed to fetch member policy: ${error.message}`);
|
|
397
|
+
return 1;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Helper functions
|
|
402
|
+
|
|
403
|
+
function getTierBadge(tier) {
|
|
404
|
+
const badges = {
|
|
405
|
+
free: `${utils.COLORS.dim}[free]${utils.COLORS.reset}`,
|
|
406
|
+
pro: `${utils.COLORS.blue}[pro]${utils.COLORS.reset}`,
|
|
407
|
+
team: `${utils.COLORS.green}[team]${utils.COLORS.reset}`,
|
|
408
|
+
enterprise: `${utils.COLORS.magenta}[enterprise]${utils.COLORS.reset}`
|
|
409
|
+
};
|
|
410
|
+
return badges[tier] || `[${tier}]`;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
function getProfileBadge(profile) {
|
|
414
|
+
const badges = {
|
|
415
|
+
startup: `${utils.COLORS.cyan}startup${utils.COLORS.reset}`,
|
|
416
|
+
regulated: `${utils.COLORS.yellow}regulated${utils.COLORS.reset}`,
|
|
417
|
+
enterprise: `${utils.COLORS.magenta}enterprise${utils.COLORS.reset}`
|
|
418
|
+
};
|
|
419
|
+
return badges[profile] || profile;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function getRoleBadge(role) {
|
|
423
|
+
const badges = {
|
|
424
|
+
owner: `${utils.COLORS.red}(owner)${utils.COLORS.reset}`,
|
|
425
|
+
admin: `${utils.COLORS.yellow}(admin)${utils.COLORS.reset}`,
|
|
426
|
+
member: `${utils.COLORS.dim}(member)${utils.COLORS.reset}`,
|
|
427
|
+
viewer: `${utils.COLORS.dim}(viewer)${utils.COLORS.reset}`
|
|
428
|
+
};
|
|
429
|
+
return badges[role] || `(${role})`;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function formatLimit(value) {
|
|
433
|
+
if (value === -1) return `${utils.COLORS.green}Unlimited${utils.COLORS.reset}`;
|
|
434
|
+
if (value === undefined) return `${utils.COLORS.dim}N/A${utils.COLORS.reset}`;
|
|
435
|
+
return value.toLocaleString();
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
function showHelp() {
|
|
439
|
+
console.log(`
|
|
440
|
+
${utils.COLORS.cyan}${utils.COLORS.bold}⚡ Bootspring Organization${utils.COLORS.reset}
|
|
441
|
+
${utils.COLORS.dim}Manage organization policies and members${utils.COLORS.reset}
|
|
442
|
+
|
|
443
|
+
${utils.COLORS.bold}Usage:${utils.COLORS.reset}
|
|
444
|
+
bootspring org <command> [options]
|
|
445
|
+
|
|
446
|
+
${utils.COLORS.bold}Commands:${utils.COLORS.reset}
|
|
447
|
+
${utils.COLORS.cyan}list${utils.COLORS.reset} List your organizations
|
|
448
|
+
${utils.COLORS.cyan}info${utils.COLORS.reset} [orgId] Show organization details
|
|
449
|
+
${utils.COLORS.cyan}policy get${utils.COLORS.reset} Get current policy settings
|
|
450
|
+
${utils.COLORS.cyan}policy set <profile>${utils.COLORS.reset} Set policy profile
|
|
451
|
+
${utils.COLORS.cyan}policy scopes${utils.COLORS.reset} List available policy scopes
|
|
452
|
+
${utils.COLORS.cyan}policy profiles${utils.COLORS.reset} List policy profiles
|
|
453
|
+
${utils.COLORS.cyan}members${utils.COLORS.reset} [orgId] List organization members
|
|
454
|
+
${utils.COLORS.cyan}member <userId> policy${utils.COLORS.reset} View member policy overrides
|
|
455
|
+
|
|
456
|
+
${utils.COLORS.bold}Policy Profiles:${utils.COLORS.reset}
|
|
457
|
+
${utils.COLORS.cyan}startup${utils.COLORS.reset} Permissive policy for fast iteration
|
|
458
|
+
${utils.COLORS.cyan}regulated${utils.COLORS.reset} Compliance-focused, external skills blocked
|
|
459
|
+
${utils.COLORS.cyan}enterprise${utils.COLORS.reset} Full control with SSO and audit
|
|
460
|
+
|
|
461
|
+
${utils.COLORS.bold}Environment:${utils.COLORS.reset}
|
|
462
|
+
BOOTSPRING_ORG_ID Default organization ID
|
|
463
|
+
BOOTSPRING_POLICY_PROFILE User-level policy profile
|
|
464
|
+
|
|
465
|
+
${utils.COLORS.bold}Examples:${utils.COLORS.reset}
|
|
466
|
+
bootspring org list
|
|
467
|
+
bootspring org info org_abc123
|
|
468
|
+
bootspring org policy get
|
|
469
|
+
bootspring org policy set regulated
|
|
470
|
+
bootspring org members
|
|
471
|
+
`);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
module.exports = { run };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Preseed Module Index
|
|
3
|
+
* Re-exports all preseed submodules
|
|
4
|
+
* @package bootspring
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const interactive = require('./interactive');
|
|
8
|
+
const templates = require('./templates');
|
|
9
|
+
|
|
10
|
+
module.exports = {
|
|
11
|
+
interactive,
|
|
12
|
+
templates,
|
|
13
|
+
// Re-export commonly used functions at top level
|
|
14
|
+
...interactive,
|
|
15
|
+
...templates
|
|
16
|
+
};
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive Helpers
|
|
3
|
+
* Readline and prompting utilities for preseed wizard
|
|
4
|
+
* @package bootspring
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const readline = require('readline');
|
|
8
|
+
const utils = require('../../core/utils');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Create readline interface
|
|
12
|
+
* @returns {readline.Interface}
|
|
13
|
+
*/
|
|
14
|
+
function createInterface() {
|
|
15
|
+
return readline.createInterface({
|
|
16
|
+
input: process.stdin,
|
|
17
|
+
output: process.stdout
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Ask a text question
|
|
23
|
+
* @param {readline.Interface} rl - readline interface
|
|
24
|
+
* @param {string} question - the question to ask
|
|
25
|
+
* @param {string} defaultValue - default value if empty
|
|
26
|
+
* @returns {Promise<string>}
|
|
27
|
+
*/
|
|
28
|
+
function askText(rl, question, defaultValue = '') {
|
|
29
|
+
const prompt = defaultValue
|
|
30
|
+
? `${question} ${utils.COLORS.dim}[${defaultValue}]${utils.COLORS.reset}: `
|
|
31
|
+
: `${question}: `;
|
|
32
|
+
|
|
33
|
+
return new Promise((resolve) => {
|
|
34
|
+
rl.question(prompt, (answer) => {
|
|
35
|
+
resolve(answer.trim() || defaultValue);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Ask a multi-line text question
|
|
42
|
+
* @param {readline.Interface} rl - readline interface
|
|
43
|
+
* @param {string} question - the question to ask
|
|
44
|
+
* @param {string} hint - hint text
|
|
45
|
+
* @returns {Promise<string>}
|
|
46
|
+
*/
|
|
47
|
+
function askMultiLine(rl, question, hint = 'Enter text (empty line to finish)') {
|
|
48
|
+
return new Promise((resolve) => {
|
|
49
|
+
console.log(`\n${utils.COLORS.bold}${question}${utils.COLORS.reset}`);
|
|
50
|
+
console.log(`${utils.COLORS.dim}${hint}${utils.COLORS.reset}`);
|
|
51
|
+
|
|
52
|
+
const lines = [];
|
|
53
|
+
const onLine = (line) => {
|
|
54
|
+
if (line.trim() === '') {
|
|
55
|
+
rl.removeListener('line', onLine);
|
|
56
|
+
resolve(lines.join('\n'));
|
|
57
|
+
} else {
|
|
58
|
+
lines.push(line);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
rl.on('line', onLine);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Ask a list question (comma-separated)
|
|
67
|
+
* @param {readline.Interface} rl - readline interface
|
|
68
|
+
* @param {string} question - the question to ask
|
|
69
|
+
* @param {string} hint - hint text
|
|
70
|
+
* @returns {Promise<string[]>}
|
|
71
|
+
*/
|
|
72
|
+
function askList(rl, question, hint = 'Enter comma-separated values') {
|
|
73
|
+
return new Promise((resolve) => {
|
|
74
|
+
console.log(`\n${utils.COLORS.bold}${question}${utils.COLORS.reset}`);
|
|
75
|
+
console.log(`${utils.COLORS.dim}${hint}${utils.COLORS.reset}`);
|
|
76
|
+
|
|
77
|
+
rl.question('> ', (answer) => {
|
|
78
|
+
const items = answer
|
|
79
|
+
.split(',')
|
|
80
|
+
.map(s => s.trim())
|
|
81
|
+
.filter(s => s.length > 0);
|
|
82
|
+
resolve(items);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Ask a choice question
|
|
89
|
+
* @param {readline.Interface} rl - readline interface
|
|
90
|
+
* @param {string} question - the question to ask
|
|
91
|
+
* @param {Array} options - array of options (strings or {label, value, description})
|
|
92
|
+
* @param {number} defaultIndex - default selected index
|
|
93
|
+
* @returns {Promise<string>}
|
|
94
|
+
*/
|
|
95
|
+
function askChoice(rl, question, options, defaultIndex = 0) {
|
|
96
|
+
return new Promise((resolve) => {
|
|
97
|
+
console.log(`\n${utils.COLORS.bold}${question}${utils.COLORS.reset}`);
|
|
98
|
+
|
|
99
|
+
options.forEach((opt, i) => {
|
|
100
|
+
const marker = i === defaultIndex
|
|
101
|
+
? `${utils.COLORS.cyan}>${utils.COLORS.reset}`
|
|
102
|
+
: ' ';
|
|
103
|
+
const label = typeof opt === 'object' ? opt.label : opt;
|
|
104
|
+
const desc = typeof opt === 'object' && opt.description
|
|
105
|
+
? ` ${utils.COLORS.dim}- ${opt.description}${utils.COLORS.reset}`
|
|
106
|
+
: '';
|
|
107
|
+
console.log(` ${marker} ${i + 1}. ${label}${desc}`);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
rl.question(`\n${utils.COLORS.dim}Select [1-${options.length}]${utils.COLORS.reset}: `, (answer) => {
|
|
111
|
+
const index = parseInt(answer, 10) - 1;
|
|
112
|
+
const selectedIndex = (index >= 0 && index < options.length) ? index : defaultIndex;
|
|
113
|
+
const selected = options[selectedIndex];
|
|
114
|
+
resolve(typeof selected === 'object' ? selected.value : selected);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Display section header
|
|
121
|
+
* @param {number} number - current step number
|
|
122
|
+
* @param {number} total - total steps
|
|
123
|
+
* @param {string} title - section title
|
|
124
|
+
* @param {string} description - section description
|
|
125
|
+
*/
|
|
126
|
+
function displaySection(number, total, title, description) {
|
|
127
|
+
console.log('\n');
|
|
128
|
+
console.log(`${utils.COLORS.cyan}${'━'.repeat(60)}${utils.COLORS.reset}`);
|
|
129
|
+
console.log(`${utils.COLORS.bold}Step ${number}/${total}: ${title}${utils.COLORS.reset}`);
|
|
130
|
+
if (description) {
|
|
131
|
+
console.log(`${utils.COLORS.dim}${description}${utils.COLORS.reset}`);
|
|
132
|
+
}
|
|
133
|
+
console.log(`${utils.COLORS.cyan}${'━'.repeat(60)}${utils.COLORS.reset}`);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
module.exports = {
|
|
137
|
+
createInterface,
|
|
138
|
+
askText,
|
|
139
|
+
askMultiLine,
|
|
140
|
+
askList,
|
|
141
|
+
askChoice,
|
|
142
|
+
displaySection
|
|
143
|
+
};
|