@skhema/cli 0.3.1
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 +163 -0
- package/bin/skhema.js +2 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +92 -0
- package/dist/commands/agent.d.ts +3 -0
- package/dist/commands/agent.d.ts.map +1 -0
- package/dist/commands/agent.js +154 -0
- package/dist/commands/api.d.ts +19 -0
- package/dist/commands/api.d.ts.map +1 -0
- package/dist/commands/api.js +97 -0
- package/dist/commands/auth.d.ts +3 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +403 -0
- package/dist/commands/component.d.ts +3 -0
- package/dist/commands/component.d.ts.map +1 -0
- package/dist/commands/component.js +221 -0
- package/dist/commands/contribute.d.ts +3 -0
- package/dist/commands/contribute.d.ts.map +1 -0
- package/dist/commands/contribute.js +318 -0
- package/dist/commands/element.d.ts +7 -0
- package/dist/commands/element.d.ts.map +1 -0
- package/dist/commands/element.js +167 -0
- package/dist/commands/export.d.ts +9 -0
- package/dist/commands/export.d.ts.map +1 -0
- package/dist/commands/export.js +42 -0
- package/dist/commands/init.d.ts +49 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +275 -0
- package/dist/commands/link.d.ts +8 -0
- package/dist/commands/link.d.ts.map +1 -0
- package/dist/commands/link.js +70 -0
- package/dist/commands/resource.d.ts +3 -0
- package/dist/commands/resource.d.ts.map +1 -0
- package/dist/commands/resource.js +88 -0
- package/dist/commands/skills.d.ts +3 -0
- package/dist/commands/skills.d.ts.map +1 -0
- package/dist/commands/skills.js +113 -0
- package/dist/commands/strategy.d.ts +3 -0
- package/dist/commands/strategy.d.ts.map +1 -0
- package/dist/commands/strategy.js +143 -0
- package/dist/commands/webhook.d.ts +3 -0
- package/dist/commands/webhook.d.ts.map +1 -0
- package/dist/commands/webhook.js +77 -0
- package/dist/commands/workspace.d.ts +8 -0
- package/dist/commands/workspace.d.ts.map +1 -0
- package/dist/commands/workspace.js +219 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/dist/lib/api/client.d.ts +27 -0
- package/dist/lib/api/client.d.ts.map +1 -0
- package/dist/lib/api/client.js +71 -0
- package/dist/lib/api/command-helpers.d.ts +27 -0
- package/dist/lib/api/command-helpers.d.ts.map +1 -0
- package/dist/lib/api/command-helpers.js +77 -0
- package/dist/lib/api/credential-context.d.ts +3 -0
- package/dist/lib/api/credential-context.d.ts.map +1 -0
- package/dist/lib/api/credential-context.js +13 -0
- package/dist/lib/api/export-cli.d.ts +14 -0
- package/dist/lib/api/export-cli.d.ts.map +1 -0
- package/dist/lib/api/export-cli.js +26 -0
- package/dist/lib/api/formats.d.ts +12 -0
- package/dist/lib/api/formats.d.ts.map +1 -0
- package/dist/lib/api/formats.js +18 -0
- package/dist/lib/api/passthrough.d.ts +34 -0
- package/dist/lib/api/passthrough.d.ts.map +1 -0
- package/dist/lib/api/passthrough.js +106 -0
- package/dist/lib/api/payload.d.ts +16 -0
- package/dist/lib/api/payload.d.ts.map +1 -0
- package/dist/lib/api/payload.js +53 -0
- package/dist/lib/api/run-command.d.ts +69 -0
- package/dist/lib/api/run-command.d.ts.map +1 -0
- package/dist/lib/api/run-command.js +127 -0
- package/dist/lib/api/scope.d.ts +8 -0
- package/dist/lib/api/scope.d.ts.map +1 -0
- package/dist/lib/api/scope.js +16 -0
- package/dist/lib/api/transfer.d.ts +57 -0
- package/dist/lib/api/transfer.d.ts.map +1 -0
- package/dist/lib/api/transfer.js +153 -0
- package/dist/lib/auth/api-key-admin.d.ts +39 -0
- package/dist/lib/auth/api-key-admin.d.ts.map +1 -0
- package/dist/lib/auth/api-key-admin.js +66 -0
- package/dist/lib/auth/api-key-store.d.ts +8 -0
- package/dist/lib/auth/api-key-store.d.ts.map +1 -0
- package/dist/lib/auth/api-key-store.js +109 -0
- package/dist/lib/auth/device-flow.d.ts +4 -0
- package/dist/lib/auth/device-flow.d.ts.map +1 -0
- package/dist/lib/auth/device-flow.js +109 -0
- package/dist/lib/auth/entitlements.d.ts +16 -0
- package/dist/lib/auth/entitlements.d.ts.map +1 -0
- package/dist/lib/auth/entitlements.js +89 -0
- package/dist/lib/auth/localhost-flow.d.ts +4 -0
- package/dist/lib/auth/localhost-flow.d.ts.map +1 -0
- package/dist/lib/auth/localhost-flow.js +134 -0
- package/dist/lib/auth/oauth-client.d.ts +33 -0
- package/dist/lib/auth/oauth-client.d.ts.map +1 -0
- package/dist/lib/auth/oauth-client.js +96 -0
- package/dist/lib/auth/token-store.d.ts +6 -0
- package/dist/lib/auth/token-store.d.ts.map +1 -0
- package/dist/lib/auth/token-store.js +136 -0
- package/dist/lib/auth/types.d.ts +49 -0
- package/dist/lib/auth/types.d.ts.map +1 -0
- package/dist/lib/auth/types.js +1 -0
- package/dist/lib/banner.d.ts +2 -0
- package/dist/lib/banner.d.ts.map +1 -0
- package/dist/lib/banner.js +82 -0
- package/dist/lib/config-store.d.ts +23 -0
- package/dist/lib/config-store.d.ts.map +1 -0
- package/dist/lib/config-store.js +53 -0
- package/dist/lib/config.d.ts +20 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +33 -0
- package/dist/lib/contribute/author-resolver.d.ts +13 -0
- package/dist/lib/contribute/author-resolver.d.ts.map +1 -0
- package/dist/lib/contribute/author-resolver.js +63 -0
- package/dist/lib/contribute/embed-generator.d.ts +4 -0
- package/dist/lib/contribute/embed-generator.d.ts.map +1 -0
- package/dist/lib/contribute/embed-generator.js +108 -0
- package/dist/lib/contribute/link-generator.d.ts +11 -0
- package/dist/lib/contribute/link-generator.d.ts.map +1 -0
- package/dist/lib/contribute/link-generator.js +119 -0
- package/dist/lib/contribute/method-schema.d.ts +239 -0
- package/dist/lib/contribute/method-schema.d.ts.map +1 -0
- package/dist/lib/contribute/method-schema.js +172 -0
- package/dist/lib/contribute/profile-resolver.d.ts +2 -0
- package/dist/lib/contribute/profile-resolver.d.ts.map +1 -0
- package/dist/lib/contribute/profile-resolver.js +25 -0
- package/dist/lib/contribute/types.d.ts +91 -0
- package/dist/lib/contribute/types.d.ts.map +1 -0
- package/dist/lib/contribute/types.js +1 -0
- package/dist/lib/mcp/registrar.d.ts +26 -0
- package/dist/lib/mcp/registrar.d.ts.map +1 -0
- package/dist/lib/mcp/registrar.js +69 -0
- package/dist/lib/mcp/registry.d.ts +54 -0
- package/dist/lib/mcp/registry.d.ts.map +1 -0
- package/dist/lib/mcp/registry.js +105 -0
- package/dist/lib/open.d.ts +2 -0
- package/dist/lib/open.d.ts.map +1 -0
- package/dist/lib/open.js +24 -0
- package/dist/lib/output.d.ts +25 -0
- package/dist/lib/output.d.ts.map +1 -0
- package/dist/lib/output.js +74 -0
- package/dist/lib/skills/installer.d.ts +4 -0
- package/dist/lib/skills/installer.d.ts.map +1 -0
- package/dist/lib/skills/installer.js +100 -0
- package/dist/lib/skills/lock.d.ts +4 -0
- package/dist/lib/skills/lock.d.ts.map +1 -0
- package/dist/lib/skills/lock.js +28 -0
- package/dist/lib/skills/platform-detect.d.ts +4 -0
- package/dist/lib/skills/platform-detect.d.ts.map +1 -0
- package/dist/lib/skills/platform-detect.js +89 -0
- package/dist/lib/skills/types.d.ts +29 -0
- package/dist/lib/skills/types.d.ts.map +1 -0
- package/dist/lib/skills/types.js +1 -0
- package/dist/postinstall.d.ts +2 -0
- package/dist/postinstall.d.ts.map +1 -0
- package/dist/postinstall.js +11 -0
- package/package.json +67 -0
- package/skills/.manifest.json +15 -0
- package/skills/skhema-challenge-framing/SKILL.md +28 -0
- package/skills/skhema-coherence-check/SKILL.md +28 -0
- package/skills/skhema-element-decomposition/SKILL.md +28 -0
- package/skills/skhema-element-writer/SKILL.md +20 -0
- package/skills/skhema-judgment-audit/SKILL.md +28 -0
- package/skills/skhema-semantic-sharpening/SKILL.md +28 -0
- package/skills/skhema-strategy-advisor/SKILL.md +20 -0
- package/skills/skhema-workspace-navigator/SKILL.md +20 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { resolveClient } from '../lib/api/client.js';
|
|
3
|
+
import { assertExportOutput, reportExport } from '../lib/api/export-cli.js';
|
|
4
|
+
import { assertFormat, DOCUMENT_FORMATS } from '../lib/api/formats.js';
|
|
5
|
+
import { resolveBody } from '../lib/api/payload.js';
|
|
6
|
+
import { runCommand } from '../lib/api/run-command.js';
|
|
7
|
+
import { resolveWorkspaceId } from '../lib/api/scope.js';
|
|
8
|
+
import { performExport } from '../lib/api/transfer.js';
|
|
9
|
+
import { log, logHeader, logTable } from '../lib/output.js';
|
|
10
|
+
function withWorkspace(cmd) {
|
|
11
|
+
return cmd.option('--workspace <id>', 'Workspace id (defaults to `skhema workspace use`)');
|
|
12
|
+
}
|
|
13
|
+
function renderStrategies(strategies = []) {
|
|
14
|
+
if (strategies.length === 0) {
|
|
15
|
+
log(chalk.dim('No strategies.'));
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
logHeader(`Strategies (${strategies.length})`);
|
|
19
|
+
logTable(['id', 'name', 'version', 'status'], strategies.map((s) => [
|
|
20
|
+
s.id,
|
|
21
|
+
s.name ?? '',
|
|
22
|
+
s.version === undefined ? '' : String(s.version),
|
|
23
|
+
s.status ?? '',
|
|
24
|
+
]));
|
|
25
|
+
}
|
|
26
|
+
export function registerStrategyCommands(program) {
|
|
27
|
+
const strategy = program.command('strategy').description('Strategy commands');
|
|
28
|
+
withWorkspace(strategy.command('list'))
|
|
29
|
+
.description('List strategies in a workspace')
|
|
30
|
+
.action(async (opts) => {
|
|
31
|
+
await runCommand('strategy list', async () => {
|
|
32
|
+
const { client } = await resolveClient();
|
|
33
|
+
const ws = resolveWorkspaceId(opts.workspace);
|
|
34
|
+
const data = await client.strategies.list(ws);
|
|
35
|
+
renderStrategies(data.strategies);
|
|
36
|
+
return data;
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
withWorkspace(strategy.command('get <strategyId>'))
|
|
40
|
+
.description('Get a strategy with its committed elements')
|
|
41
|
+
.action(async (strategyId, opts) => {
|
|
42
|
+
await runCommand('strategy get', async () => {
|
|
43
|
+
const { client } = await resolveClient();
|
|
44
|
+
const ws = resolveWorkspaceId(opts.workspace);
|
|
45
|
+
const data = await client.strategies.get(ws, strategyId);
|
|
46
|
+
if (data.strategy) {
|
|
47
|
+
logHeader(data.strategy.name ?? strategyId);
|
|
48
|
+
log(chalk.dim(`status: ${data.strategy.status ?? '—'} version: ${data.strategy.version ?? '—'}`));
|
|
49
|
+
}
|
|
50
|
+
return data;
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
withWorkspace(strategy.command('create'))
|
|
54
|
+
.description('Create a strategy')
|
|
55
|
+
.option('--name <name>', 'Strategy name')
|
|
56
|
+
.option('--description <text>', 'Description')
|
|
57
|
+
.option('--file <path>', 'JSON payload file (or - for stdin)')
|
|
58
|
+
.action(async (opts) => {
|
|
59
|
+
await runCommand('strategy create', async () => {
|
|
60
|
+
const { client } = await resolveClient();
|
|
61
|
+
const ws = resolveWorkspaceId(opts.workspace);
|
|
62
|
+
const body = resolveBody({
|
|
63
|
+
file: opts.file,
|
|
64
|
+
fields: { name: opts.name, description: opts.description },
|
|
65
|
+
});
|
|
66
|
+
const data = await client.strategies.create(ws, body);
|
|
67
|
+
log(chalk.green(`Created strategy ${chalk.cyan(data.strategy?.id)}`));
|
|
68
|
+
return data;
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
withWorkspace(strategy.command('update <strategyId>'))
|
|
72
|
+
.description('Update a strategy')
|
|
73
|
+
.option('--name <name>', 'New name')
|
|
74
|
+
.option('--description <text>', 'New description')
|
|
75
|
+
.option('--file <path>', 'JSON payload file (or - for stdin)')
|
|
76
|
+
.action(async (strategyId, opts) => {
|
|
77
|
+
await runCommand('strategy update', async () => {
|
|
78
|
+
const { client } = await resolveClient();
|
|
79
|
+
const ws = resolveWorkspaceId(opts.workspace);
|
|
80
|
+
const fields = {};
|
|
81
|
+
if (opts.name !== undefined)
|
|
82
|
+
fields.name = opts.name;
|
|
83
|
+
if (opts.description !== undefined)
|
|
84
|
+
fields.description = opts.description;
|
|
85
|
+
const body = resolveBody({ file: opts.file, fields });
|
|
86
|
+
const data = await client.strategies.update(ws, strategyId, body);
|
|
87
|
+
log(chalk.green(`Updated strategy ${chalk.cyan(strategyId)}`));
|
|
88
|
+
return data;
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
withWorkspace(strategy.command('commit <strategyId>'))
|
|
92
|
+
.description('Commit elements/components into a strategy')
|
|
93
|
+
.option('--component <id>', 'Commit all elements in this component')
|
|
94
|
+
.option('--file <path>', 'JSON payload file (or - for stdin)')
|
|
95
|
+
.action(async (strategyId, opts) => {
|
|
96
|
+
await runCommand('strategy commit', async () => {
|
|
97
|
+
const { client } = await resolveClient();
|
|
98
|
+
const ws = resolveWorkspaceId(opts.workspace);
|
|
99
|
+
const body = resolveBody({
|
|
100
|
+
file: opts.file,
|
|
101
|
+
fields: { componentId: opts.component },
|
|
102
|
+
});
|
|
103
|
+
const data = await client.strategies.commit(ws, strategyId, body);
|
|
104
|
+
log(chalk.green(`Committed to strategy ${chalk.cyan(strategyId)}`));
|
|
105
|
+
return data;
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
withWorkspace(strategy.command('activate <strategyId>'))
|
|
109
|
+
.description('Activate a strategy')
|
|
110
|
+
.action(async (strategyId, opts) => {
|
|
111
|
+
await runCommand('strategy activate', async () => {
|
|
112
|
+
const { client } = await resolveClient();
|
|
113
|
+
const ws = resolveWorkspaceId(opts.workspace);
|
|
114
|
+
const data = await client.strategies.activate(ws, strategyId);
|
|
115
|
+
log(chalk.green(`Activated strategy ${chalk.cyan(strategyId)}`));
|
|
116
|
+
return data;
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
withWorkspace(strategy.command('export <strategyId>'))
|
|
120
|
+
.description('Export a strategy (pdf/docx/md/json)')
|
|
121
|
+
.requiredOption('--format <format>', 'pdf | docx | md | json')
|
|
122
|
+
.option('--out <path>', 'Write to this path (- for stdout; default: server filename in cwd)')
|
|
123
|
+
.option('--email <recipient>', 'Email the export to an org member instead of downloading')
|
|
124
|
+
.action(async (strategyId, opts) => {
|
|
125
|
+
await runCommand('strategy export', async () => {
|
|
126
|
+
const { client } = await resolveClient();
|
|
127
|
+
const ws = resolveWorkspaceId(opts.workspace);
|
|
128
|
+
const format = assertFormat(opts.format, DOCUMENT_FORMATS);
|
|
129
|
+
assertExportOutput(opts);
|
|
130
|
+
const result = await performExport({
|
|
131
|
+
out: opts.email ? undefined : opts.out,
|
|
132
|
+
exec: () => client.strategies.export(ws, strategyId, {
|
|
133
|
+
format,
|
|
134
|
+
...(opts.email
|
|
135
|
+
? { delivery: 'email', recipientEmail: opts.email }
|
|
136
|
+
: {}),
|
|
137
|
+
}),
|
|
138
|
+
});
|
|
139
|
+
reportExport(result);
|
|
140
|
+
return result;
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../../src/commands/webhook.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAgCnC,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAwE9D"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { resolveClient } from '../lib/api/client.js';
|
|
3
|
+
import { runCommand, UsageError } from '../lib/api/run-command.js';
|
|
4
|
+
import { log, logHeader, logTable } from '../lib/output.js';
|
|
5
|
+
function renderEndpoints(endpoints = []) {
|
|
6
|
+
if (endpoints.length === 0) {
|
|
7
|
+
log(chalk.dim('No webhook subscriptions.'));
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
logHeader(`Webhook subscriptions (${endpoints.length})`);
|
|
11
|
+
logTable(['id', 'url', 'events', 'secret', 'status'], endpoints.map((e) => [
|
|
12
|
+
e.id,
|
|
13
|
+
e.url ?? '',
|
|
14
|
+
(e.subscribedEvents ?? []).join(',') || '*',
|
|
15
|
+
e.secretLast4 ? `••••${e.secretLast4}` : '',
|
|
16
|
+
e.status ?? '',
|
|
17
|
+
]));
|
|
18
|
+
}
|
|
19
|
+
export function registerWebhookCommands(program) {
|
|
20
|
+
const webhook = program
|
|
21
|
+
.command('webhook')
|
|
22
|
+
.description('Outbound webhook subscription commands');
|
|
23
|
+
webhook
|
|
24
|
+
.command('create')
|
|
25
|
+
.description('Create a webhook subscription (signing secret shown once)')
|
|
26
|
+
.requiredOption('--url <url>', 'Destination URL')
|
|
27
|
+
.option('--events <list>', 'Comma-separated event types (default: all)')
|
|
28
|
+
.option('--name <name>', 'Display name')
|
|
29
|
+
.option('--description <text>', 'Description')
|
|
30
|
+
.action(async (opts) => {
|
|
31
|
+
await runCommand('webhook create', async () => {
|
|
32
|
+
const { client } = await resolveClient();
|
|
33
|
+
const body = { url: opts.url };
|
|
34
|
+
if (opts.events) {
|
|
35
|
+
body.subscribedEvents = opts.events
|
|
36
|
+
.split(',')
|
|
37
|
+
.map((e) => e.trim())
|
|
38
|
+
.filter(Boolean);
|
|
39
|
+
}
|
|
40
|
+
if (opts.name)
|
|
41
|
+
body.displayName = opts.name;
|
|
42
|
+
if (opts.description)
|
|
43
|
+
body.description = opts.description;
|
|
44
|
+
const data = await client.webhooks.create(body);
|
|
45
|
+
log(chalk.green(`Created webhook ${chalk.cyan(data.endpoint?.id)}`));
|
|
46
|
+
log('');
|
|
47
|
+
log(chalk.yellow.bold('Signing secret (shown once — store it now):'));
|
|
48
|
+
log(chalk.bold(data.secret));
|
|
49
|
+
log(chalk.dim('It cannot be retrieved again; rotate if lost.'));
|
|
50
|
+
return data;
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
webhook
|
|
54
|
+
.command('list')
|
|
55
|
+
.description('List webhook subscriptions (secrets masked)')
|
|
56
|
+
.action(async () => {
|
|
57
|
+
await runCommand('webhook list', async () => {
|
|
58
|
+
const { client } = await resolveClient();
|
|
59
|
+
const data = await client.webhooks.list();
|
|
60
|
+
renderEndpoints(data.endpoints);
|
|
61
|
+
return data;
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
webhook
|
|
65
|
+
.command('delete <endpointId>')
|
|
66
|
+
.description('Delete a webhook subscription (idempotent)')
|
|
67
|
+
.action(async (endpointId) => {
|
|
68
|
+
await runCommand('webhook delete', async () => {
|
|
69
|
+
if (!endpointId)
|
|
70
|
+
throw new UsageError('endpointId is required.');
|
|
71
|
+
const { client } = await resolveClient();
|
|
72
|
+
const data = await client.webhooks.delete(endpointId);
|
|
73
|
+
log(chalk.green(`Deleted webhook ${chalk.cyan(endpointId)}`));
|
|
74
|
+
return data;
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
/**
|
|
3
|
+
* Workspace commands. `use` sets the default workspace consumed by scoped
|
|
4
|
+
* commands; the domain read/write verbs (list/get/create/update) and the
|
|
5
|
+
* `member` group wrap the `@skhema/sdk` workspaces resource.
|
|
6
|
+
*/
|
|
7
|
+
export declare function registerWorkspaceCommands(program: Command): void;
|
|
8
|
+
//# sourceMappingURL=workspace.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../../src/commands/workspace.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AA0BnC;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAyJhE"}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { resolveClient } from '../lib/api/client.js';
|
|
3
|
+
import { asRecord, buildBody, coerceList, readPayloadFile, resolveWorkspaceId, } from '../lib/api/command-helpers.js';
|
|
4
|
+
import { runCommand, UsageError } from '../lib/api/run-command.js';
|
|
5
|
+
import { getConfigLocation, getDefaultWorkspaceId, setDefaultWorkspaceId, } from '../lib/config-store.js';
|
|
6
|
+
import { getGlobalOptions, log, logHeader, logTable } from '../lib/output.js';
|
|
7
|
+
/** Print a single workspace as a label block (human output only). */
|
|
8
|
+
function printWorkspace(workspace) {
|
|
9
|
+
const w = asRecord(workspace);
|
|
10
|
+
logHeader(String(w.name ?? w.id ?? 'Workspace'));
|
|
11
|
+
log(` Id: ${chalk.cyan(String(w.id ?? '—'))}`);
|
|
12
|
+
if (w.visibility)
|
|
13
|
+
log(` Visibility: ${String(w.visibility)}`);
|
|
14
|
+
if (w.strategicContext)
|
|
15
|
+
log(` Context: ${String(w.strategicContext)}`);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Workspace commands. `use` sets the default workspace consumed by scoped
|
|
19
|
+
* commands; the domain read/write verbs (list/get/create/update) and the
|
|
20
|
+
* `member` group wrap the `@skhema/sdk` workspaces resource.
|
|
21
|
+
*/
|
|
22
|
+
export function registerWorkspaceCommands(program) {
|
|
23
|
+
const workspace = program
|
|
24
|
+
.command('workspace')
|
|
25
|
+
.description('Workspace commands');
|
|
26
|
+
workspace
|
|
27
|
+
.command('use [workspace-id]')
|
|
28
|
+
.description('Set (or, with --clear, unset) the default workspace')
|
|
29
|
+
.option('--clear', 'Clear the stored default workspace')
|
|
30
|
+
.action(async (workspaceId, options) => {
|
|
31
|
+
await runCommand('workspace use', async () => {
|
|
32
|
+
if (options.clear) {
|
|
33
|
+
setDefaultWorkspaceId(undefined);
|
|
34
|
+
log(chalk.green('Cleared the default workspace.'));
|
|
35
|
+
return { defaultWorkspaceId: null };
|
|
36
|
+
}
|
|
37
|
+
if (!workspaceId) {
|
|
38
|
+
const current = getDefaultWorkspaceId();
|
|
39
|
+
if (!current) {
|
|
40
|
+
throw new UsageError('Provide a workspace id, e.g. `skhema workspace use <id>` (or --clear).');
|
|
41
|
+
}
|
|
42
|
+
log(`Default workspace: ${chalk.cyan(current)}`);
|
|
43
|
+
return { defaultWorkspaceId: current };
|
|
44
|
+
}
|
|
45
|
+
setDefaultWorkspaceId(workspaceId);
|
|
46
|
+
log(chalk.green(`Default workspace set to ${chalk.cyan(workspaceId)}.`));
|
|
47
|
+
log(chalk.dim(` Stored in ${getConfigLocation()}`));
|
|
48
|
+
return { defaultWorkspaceId: workspaceId };
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
workspace
|
|
52
|
+
.command('list')
|
|
53
|
+
.description('List workspaces')
|
|
54
|
+
.action(async () => {
|
|
55
|
+
await runCommand('workspace list', async () => {
|
|
56
|
+
const { client } = await resolveClient();
|
|
57
|
+
const data = await client.workspaces.list();
|
|
58
|
+
const rows = coerceList(data, 'workspaces');
|
|
59
|
+
if (!getGlobalOptions().json) {
|
|
60
|
+
logHeader(`Workspaces (${rows.length})`);
|
|
61
|
+
if (rows.length === 0)
|
|
62
|
+
log(chalk.dim(' No workspaces.'));
|
|
63
|
+
else
|
|
64
|
+
logTable(['Id', 'Name', 'Visibility'], rows.map((r) => {
|
|
65
|
+
const w = asRecord(r);
|
|
66
|
+
return [
|
|
67
|
+
String(w.id ?? '—'),
|
|
68
|
+
String(w.name ?? '—'),
|
|
69
|
+
String(w.visibility ?? '—'),
|
|
70
|
+
];
|
|
71
|
+
}));
|
|
72
|
+
}
|
|
73
|
+
return data;
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
workspace
|
|
77
|
+
.command('get <workspace-id>')
|
|
78
|
+
.description('Get a workspace by id')
|
|
79
|
+
.action(async (workspaceId) => {
|
|
80
|
+
await runCommand('workspace get', async () => {
|
|
81
|
+
const { client } = await resolveClient();
|
|
82
|
+
const data = await client.workspaces.get(workspaceId);
|
|
83
|
+
if (!getGlobalOptions().json)
|
|
84
|
+
printWorkspace(asRecord(data).workspace ?? data);
|
|
85
|
+
return data;
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
workspace
|
|
89
|
+
.command('create')
|
|
90
|
+
.description('Create a workspace')
|
|
91
|
+
.option('--name <name>', 'Workspace name')
|
|
92
|
+
.option('--visibility <visibility>', 'Workspace visibility (e.g. global | restricted)')
|
|
93
|
+
.option('--file <path>', 'JSON payload file for the full body ("-" for stdin)')
|
|
94
|
+
.action(async (options) => {
|
|
95
|
+
await runCommand('workspace create', async () => {
|
|
96
|
+
const body = buildBody(readPayloadFile(options.file), {
|
|
97
|
+
name: options.name,
|
|
98
|
+
visibility: options.visibility,
|
|
99
|
+
});
|
|
100
|
+
if (!body.name) {
|
|
101
|
+
throw new UsageError('A workspace name is required. Pass --name <name> or include it in --file.');
|
|
102
|
+
}
|
|
103
|
+
const { client } = await resolveClient();
|
|
104
|
+
const data = await client.workspaces.create(body);
|
|
105
|
+
if (!getGlobalOptions().json) {
|
|
106
|
+
log(chalk.green('Workspace created.'));
|
|
107
|
+
printWorkspace(asRecord(data).workspace ?? data);
|
|
108
|
+
}
|
|
109
|
+
return data;
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
workspace
|
|
113
|
+
.command('update <workspace-id>')
|
|
114
|
+
.description('Update a workspace')
|
|
115
|
+
.option('--name <name>', 'Workspace name')
|
|
116
|
+
.option('--visibility <visibility>', 'Workspace visibility')
|
|
117
|
+
.option('--file <path>', 'JSON payload file for the full body ("-" for stdin)')
|
|
118
|
+
.action(async (workspaceId, options) => {
|
|
119
|
+
await runCommand('workspace update', async () => {
|
|
120
|
+
const body = buildBody(readPayloadFile(options.file), {
|
|
121
|
+
name: options.name,
|
|
122
|
+
visibility: options.visibility,
|
|
123
|
+
});
|
|
124
|
+
if (Object.keys(body).length === 0) {
|
|
125
|
+
throw new UsageError('Nothing to update. Pass --name/--visibility or a --file payload.');
|
|
126
|
+
}
|
|
127
|
+
const { client } = await resolveClient();
|
|
128
|
+
const data = await client.workspaces.update(workspaceId, body);
|
|
129
|
+
if (!getGlobalOptions().json) {
|
|
130
|
+
log(chalk.green('Workspace updated.'));
|
|
131
|
+
printWorkspace(asRecord(data).workspace ?? data);
|
|
132
|
+
}
|
|
133
|
+
return data;
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
registerMemberCommands(workspace);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* `skhema workspace member …` — the workspace membership verbs. All are
|
|
140
|
+
* workspace-scoped via `--workspace` (falling back to the default workspace).
|
|
141
|
+
*/
|
|
142
|
+
function registerMemberCommands(workspace) {
|
|
143
|
+
const member = workspace
|
|
144
|
+
.command('member')
|
|
145
|
+
.description('Manage workspace members');
|
|
146
|
+
member
|
|
147
|
+
.command('list')
|
|
148
|
+
.description('List workspace members')
|
|
149
|
+
.option('--workspace <id>', 'Workspace id (defaults to `skhema workspace use`)')
|
|
150
|
+
.action(async (options) => {
|
|
151
|
+
await runCommand('workspace member list', async () => {
|
|
152
|
+
const workspaceId = resolveWorkspaceId(options.workspace);
|
|
153
|
+
const { client } = await resolveClient();
|
|
154
|
+
const data = await client.workspaces.listMembers(workspaceId);
|
|
155
|
+
const rows = coerceList(data, 'members');
|
|
156
|
+
if (!getGlobalOptions().json) {
|
|
157
|
+
logHeader(`Members (${rows.length})`);
|
|
158
|
+
if (rows.length === 0)
|
|
159
|
+
log(chalk.dim(' No members.'));
|
|
160
|
+
else
|
|
161
|
+
logTable(['User', 'Role'], rows.map((r) => {
|
|
162
|
+
const m = asRecord(r);
|
|
163
|
+
return [String(m.userId ?? m.id ?? '—'), String(m.role ?? '—')];
|
|
164
|
+
}));
|
|
165
|
+
}
|
|
166
|
+
return data;
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
member
|
|
170
|
+
.command('add')
|
|
171
|
+
.description('Add a member to a workspace')
|
|
172
|
+
.requiredOption('--user <user-id>', 'User id to add')
|
|
173
|
+
.requiredOption('--role <role>', 'Role to grant')
|
|
174
|
+
.option('--workspace <id>', 'Workspace id (defaults to `skhema workspace use`)')
|
|
175
|
+
.action(async (options) => {
|
|
176
|
+
await runCommand('workspace member add', async () => {
|
|
177
|
+
const workspaceId = resolveWorkspaceId(options.workspace);
|
|
178
|
+
const { client } = await resolveClient();
|
|
179
|
+
const data = await client.workspaces.addMember(workspaceId, {
|
|
180
|
+
userId: options.user,
|
|
181
|
+
role: options.role,
|
|
182
|
+
});
|
|
183
|
+
if (!getGlobalOptions().json)
|
|
184
|
+
log(chalk.green(`Added ${options.user} as ${options.role}.`));
|
|
185
|
+
return data;
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
member
|
|
189
|
+
.command('remove')
|
|
190
|
+
.description('Remove a member from a workspace')
|
|
191
|
+
.requiredOption('--user <user-id>', 'User id to remove')
|
|
192
|
+
.option('--workspace <id>', 'Workspace id (defaults to `skhema workspace use`)')
|
|
193
|
+
.action(async (options) => {
|
|
194
|
+
await runCommand('workspace member remove', async () => {
|
|
195
|
+
const workspaceId = resolveWorkspaceId(options.workspace);
|
|
196
|
+
const { client } = await resolveClient();
|
|
197
|
+
const data = await client.workspaces.removeMember(workspaceId, options.user);
|
|
198
|
+
if (!getGlobalOptions().json)
|
|
199
|
+
log(chalk.green(`Removed ${options.user}.`));
|
|
200
|
+
return data;
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
member
|
|
204
|
+
.command('set-role')
|
|
205
|
+
.description("Update a member's role")
|
|
206
|
+
.requiredOption('--user <user-id>', 'User id to update')
|
|
207
|
+
.requiredOption('--role <role>', 'New role')
|
|
208
|
+
.option('--workspace <id>', 'Workspace id (defaults to `skhema workspace use`)')
|
|
209
|
+
.action(async (options) => {
|
|
210
|
+
await runCommand('workspace member set-role', async () => {
|
|
211
|
+
const workspaceId = resolveWorkspaceId(options.workspace);
|
|
212
|
+
const { client } = await resolveClient();
|
|
213
|
+
const data = await client.workspaces.updateMemberRole(workspaceId, options.user, { role: options.role });
|
|
214
|
+
if (!getGlobalOptions().json)
|
|
215
|
+
log(chalk.green(`Set ${options.user} to ${options.role}.`));
|
|
216
|
+
return data;
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { AUTH_BASE_URL, CLI_CLIENT_ID, OAUTH_SCOPES } from './lib/config.js';
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { SkhemaClient } from '@skhema/sdk';
|
|
2
|
+
/** Which credential source, in precedence order, is backing the client. */
|
|
3
|
+
export type CredentialLane = 'flag' | 'env' | 'stored-key' | 'oauth';
|
|
4
|
+
export interface CredentialDescriptor {
|
|
5
|
+
lane: CredentialLane;
|
|
6
|
+
/** `api-key` for the three key lanes; `oauth` for the stored session. */
|
|
7
|
+
kind: 'api-key' | 'oauth';
|
|
8
|
+
/** Masked key (`sk_live_...last4`), present for api-key lanes only. */
|
|
9
|
+
maskedKey?: string;
|
|
10
|
+
/** Organization id, when known without a network round-trip (OAuth lane). */
|
|
11
|
+
organizationId?: string;
|
|
12
|
+
/** Human-friendly source description for `auth status`. */
|
|
13
|
+
source: string;
|
|
14
|
+
}
|
|
15
|
+
export interface ResolvedClient {
|
|
16
|
+
client: SkhemaClient;
|
|
17
|
+
credential: CredentialDescriptor;
|
|
18
|
+
}
|
|
19
|
+
/** Mask a raw key to `sk_live_...abcd` — the only safe form to ever print. */
|
|
20
|
+
export declare function maskApiKey(rawKey: string): string;
|
|
21
|
+
/**
|
|
22
|
+
* Resolve the active credential using the fixed precedence (spec §3.1):
|
|
23
|
+
* `--api-key` flag → `SKHEMA_API_KEY` env → stored key → stored OAuth session.
|
|
24
|
+
* Throws {@link AuthRequiredError} (exit 3) when none is available.
|
|
25
|
+
*/
|
|
26
|
+
export declare function resolveClient(): Promise<ResolvedClient>;
|
|
27
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/lib/api/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,YAAY,EAAE,MAAM,aAAa,CAAA;AAO9D,2EAA2E;AAC3E,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,KAAK,GAAG,YAAY,GAAG,OAAO,CAAA;AAEpE,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,cAAc,CAAA;IACpB,yEAAyE;IACzE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAA;IACzB,uEAAuE;IACvE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,6EAA6E;IAC7E,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,2DAA2D;IAC3D,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,YAAY,CAAA;IACpB,UAAU,EAAE,oBAAoB,CAAA;CACjC;AAED,8EAA8E;AAC9E,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAGjD;AASD;;;;GAIG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,cAAc,CAAC,CA4D7D"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { createSkhemaClient } from '@skhema/sdk';
|
|
2
|
+
import { getStoredApiKey } from '../auth/api-key-store.js';
|
|
3
|
+
import { getStoredCredentials } from '../auth/token-store.js';
|
|
4
|
+
import { API_BASE_URL, API_KEY_ENV_VAR, AUTH_BASE_URL } from '../config.js';
|
|
5
|
+
import { getApiKeyFlag } from './credential-context.js';
|
|
6
|
+
import { AuthRequiredError } from './run-command.js';
|
|
7
|
+
/** Mask a raw key to `sk_live_...abcd` — the only safe form to ever print. */
|
|
8
|
+
export function maskApiKey(rawKey) {
|
|
9
|
+
const last4 = rawKey.slice(-4);
|
|
10
|
+
return `sk_live_...${last4}`;
|
|
11
|
+
}
|
|
12
|
+
function apiKeyClient(rawKey) {
|
|
13
|
+
return createSkhemaClient({ apiKey: rawKey }, { baseUrl: API_BASE_URL, authBaseUrl: AUTH_BASE_URL });
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Resolve the active credential using the fixed precedence (spec §3.1):
|
|
17
|
+
* `--api-key` flag → `SKHEMA_API_KEY` env → stored key → stored OAuth session.
|
|
18
|
+
* Throws {@link AuthRequiredError} (exit 3) when none is available.
|
|
19
|
+
*/
|
|
20
|
+
export async function resolveClient() {
|
|
21
|
+
const flagKey = getApiKeyFlag();
|
|
22
|
+
if (flagKey) {
|
|
23
|
+
return {
|
|
24
|
+
client: apiKeyClient(flagKey),
|
|
25
|
+
credential: {
|
|
26
|
+
lane: 'flag',
|
|
27
|
+
kind: 'api-key',
|
|
28
|
+
maskedKey: maskApiKey(flagKey),
|
|
29
|
+
source: '--api-key flag',
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
const envKey = process.env[API_KEY_ENV_VAR];
|
|
34
|
+
if (envKey) {
|
|
35
|
+
return {
|
|
36
|
+
client: apiKeyClient(envKey),
|
|
37
|
+
credential: {
|
|
38
|
+
lane: 'env',
|
|
39
|
+
kind: 'api-key',
|
|
40
|
+
maskedKey: maskApiKey(envKey),
|
|
41
|
+
source: `${API_KEY_ENV_VAR} environment variable`,
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
const storedKey = getStoredApiKey();
|
|
46
|
+
if (storedKey) {
|
|
47
|
+
return {
|
|
48
|
+
client: apiKeyClient(storedKey),
|
|
49
|
+
credential: {
|
|
50
|
+
lane: 'stored-key',
|
|
51
|
+
kind: 'api-key',
|
|
52
|
+
maskedKey: maskApiKey(storedKey),
|
|
53
|
+
source: 'stored API key (skhema auth key use)',
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
const creds = await getStoredCredentials();
|
|
58
|
+
if (creds?.access_token) {
|
|
59
|
+
return {
|
|
60
|
+
client: createSkhemaClient({ token: creds.access_token }, { baseUrl: API_BASE_URL, authBaseUrl: AUTH_BASE_URL }),
|
|
61
|
+
credential: {
|
|
62
|
+
lane: 'oauth',
|
|
63
|
+
kind: 'oauth',
|
|
64
|
+
organizationId: creds.organization?.id,
|
|
65
|
+
source: 'OAuth session (skhema auth login)',
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
throw new AuthRequiredError('No credentials found. Run `skhema auth login` for interactive use, or set ' +
|
|
70
|
+
`${API_KEY_ENV_VAR}=sk_live_… (or run \`skhema auth key use <key>\`) for headless/CI use.`);
|
|
71
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { RequestBody } from '@skhema/sdk';
|
|
2
|
+
/**
|
|
3
|
+
* Resolve the target workspace for a workspace-scoped command: the explicit
|
|
4
|
+
* `--workspace` flag wins, else the stored default (`skhema workspace use`).
|
|
5
|
+
* Throws a {@link UsageError} (exit 2) when neither is set.
|
|
6
|
+
*/
|
|
7
|
+
export declare function resolveWorkspaceId(flag?: string): string;
|
|
8
|
+
/**
|
|
9
|
+
* Read a full JSON request payload from `--file <path>` (or `-` for stdin).
|
|
10
|
+
* Returns `undefined` when no path is given. A malformed file is a usage error.
|
|
11
|
+
*/
|
|
12
|
+
export declare function readPayloadFile(filePath?: string): RequestBody | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* Build a request body from an optional `--file` base merged with field flags.
|
|
15
|
+
* Flag values that are `undefined` are dropped, so explicit flags override the
|
|
16
|
+
* file and unset flags leave the file's value intact.
|
|
17
|
+
*/
|
|
18
|
+
export declare function buildBody(base: RequestBody | undefined, fields: Record<string, unknown>): RequestBody;
|
|
19
|
+
/** Narrow an unknown value to a plain record for defensive field access. */
|
|
20
|
+
export declare function asRecord(value: unknown): Record<string, unknown>;
|
|
21
|
+
/**
|
|
22
|
+
* Coerce a list-shaped API response to an array. The gateway wraps collections
|
|
23
|
+
* under a resource key (`{ workspaces: [...] }`); fall back to the first array
|
|
24
|
+
* property, or the value itself when it is already an array.
|
|
25
|
+
*/
|
|
26
|
+
export declare function coerceList(value: unknown, key?: string): unknown[];
|
|
27
|
+
//# sourceMappingURL=command-helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command-helpers.d.ts","sourceRoot":"","sources":["../../../src/lib/api/command-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAKzC;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAQxD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAsB1E;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,WAAW,GAAG,SAAS,EAC7B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,WAAW,CAMb;AAED,4EAA4E;AAC5E,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAIhE;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,EAAE,CAQlE"}
|