anhedral 0.10.1 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +77 -38
- package/assets/{anhedral-cli-init-technical.svg → anhedral-stack-technical.svg} +6 -6
- package/assets/{anhedral-cli-init.svg → anhedral-stack.svg} +7 -7
- package/dist/bin.js +271 -154
- package/dist/capabilities.d.ts +3 -3
- package/dist/capability-contract.d.ts +47 -0
- package/dist/capability-contract.js +136 -0
- package/dist/cli-arguments.d.ts +16 -0
- package/dist/cli-arguments.js +115 -0
- package/dist/cli-output.d.ts +8 -0
- package/dist/cli-output.js +211 -0
- package/dist/cloudflare.d.ts +7 -2
- package/dist/cloudflare.js +166 -36
- package/dist/command-schema.d.ts +21 -0
- package/dist/command-schema.js +374 -0
- package/dist/delivery.d.ts +2 -2
- package/dist/delivery.js +21 -4
- package/dist/index.d.ts +23 -1
- package/dist/index.js +15 -1
- package/dist/inspector-model.d.ts +115 -0
- package/dist/inspector-model.js +1 -0
- package/dist/lifecycle-model.d.ts +15 -0
- package/dist/lifecycle-model.js +20 -0
- package/dist/lifecycle.d.ts +12 -0
- package/dist/lifecycle.js +92 -0
- package/dist/mobile-delivery.d.ts +100 -0
- package/dist/mobile-delivery.js +319 -0
- package/dist/platforms/mobile.js +8 -5
- package/dist/project-inspector.d.ts +4 -0
- package/dist/project-inspector.js +646 -0
- package/dist/project-state.d.ts +17 -0
- package/dist/project-state.js +239 -11
- package/dist/provider-auth.d.ts +41 -0
- package/dist/provider-auth.js +210 -0
- package/dist/providers/adapters.d.ts +17 -0
- package/dist/providers/adapters.js +269 -0
- package/dist/providers/api.d.ts +28 -0
- package/dist/providers/api.js +252 -0
- package/dist/providers/config.d.ts +15 -0
- package/dist/providers/config.js +184 -0
- package/dist/providers/definitions.d.ts +318 -0
- package/dist/providers/definitions.js +127 -0
- package/dist/providers/provision.d.ts +24 -0
- package/dist/providers/provision.js +76 -0
- package/dist/providers/receipts.d.ts +20 -0
- package/dist/providers/receipts.js +66 -0
- package/dist/providers/recovery-verification.d.ts +7 -0
- package/dist/providers/recovery-verification.js +112 -0
- package/dist/providers/recovery.d.ts +30 -0
- package/dist/providers/recovery.js +34 -0
- package/dist/providers/runtime.d.ts +10 -0
- package/dist/providers/runtime.js +231 -0
- package/dist/providers/schema.d.ts +28 -0
- package/dist/providers/schema.js +49 -0
- package/dist/providers/state.d.ts +56 -0
- package/dist/providers/state.js +329 -0
- package/dist/provision.d.ts +1 -1
- package/dist/provision.js +61 -3
- package/dist/registry/add.d.ts +6 -0
- package/dist/registry/add.js +29 -1
- package/dist/registry/commands.d.ts +3 -2
- package/dist/registry/commands.js +53 -14
- package/dist/registry/options.js +3 -2
- package/dist/registry/preview.d.ts +11 -0
- package/dist/registry/preview.js +91 -0
- package/dist/registry/usage.d.ts +10 -0
- package/dist/registry/usage.js +22 -37
- package/dist/scaffold/auth.js +3 -19
- package/dist/scaffold/workspace.js +11 -0
- package/dist/ship.d.ts +320 -0
- package/dist/ship.js +211 -0
- package/dist/stack-registry.d.ts +2 -2
- package/dist/stack-registry.js +7 -1
- package/dist/standard.d.ts +2 -1
- package/dist/standard.js +11 -11
- package/dist/vercel.d.ts +3 -3
- package/dist/vercel.js +27 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/application-stack-standard.md +51 -80
- package/docs/cli-design.md +67 -0
- package/docs/delivery-workflow.md +29 -15
- package/docs/plugin-ui.md +61 -0
- package/docs/provider-configuration.md +117 -0
- package/package.json +16 -12
- package/dist/managed-hosting.d.ts +0 -49
- package/dist/managed-hosting.js +0 -373
- package/docs/managed-hosting-architecture.md +0 -87
- package/docs/sponsorships.md +0 -25
package/dist/bin.js
CHANGED
|
@@ -1,47 +1,159 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { shipCloudflareApplication, shipSelectedVercelApplication, shipSelectedStack, } from './ship.js';
|
|
3
|
+
import { integrationStatus, recoverIntegrationResource } from './providers/recovery.js';
|
|
4
|
+
import { INTEGRATION_PROVIDERS } from './providers/config.js';
|
|
5
|
+
import { recoverDeliveryLock } from './project-state.js';
|
|
6
|
+
import { readLifecycleOperation } from './lifecycle.js';
|
|
7
|
+
import { buildMobileApplication } from './mobile-delivery.js';
|
|
8
|
+
import { authenticateProviders } from './provider-auth.js';
|
|
2
9
|
import { runAdd, runRegistry } from './registry/commands.js';
|
|
3
|
-
import { registerManagedApplication, stageManagedVersion, saveManagedVersion, deployManagedVersion, managedApplicationStatus, } from './managed-hosting.js';
|
|
4
10
|
import { setupRepository } from './repository.js';
|
|
5
11
|
import path from 'node:path';
|
|
6
12
|
import { existsSync, readFileSync } from 'node:fs';
|
|
7
|
-
import { registerVercelApplication, connectVercelApplication,
|
|
8
|
-
import { registerApplication, connectApplication,
|
|
13
|
+
import { registerVercelApplication, connectVercelApplication, vercelApplicationStatus, verifyVercelApplication, rollbackVercelApplication, adoptVercelApplication, recoverVercelApplication, writeVercelOverview, } from './vercel.js';
|
|
14
|
+
import { registerApplication, connectApplication, applicationStatus, verifyApplication, rollbackApplication, recoverApplication, } from './delivery.js';
|
|
9
15
|
import { planProvisioning, provisionApplication } from './provision.js';
|
|
10
16
|
import { writeManagementOverview } from './management.js';
|
|
11
17
|
import { inspectProviderReadiness } from './provider-readiness.js';
|
|
12
18
|
import { inspectProject } from './readiness.js';
|
|
19
|
+
import { inspectApplication } from './project-inspector.js';
|
|
13
20
|
import { argv } from 'node:process';
|
|
14
21
|
import { STANDARD_USAGE, parseStandardOptions, scaffoldStandardProject } from './standard.js';
|
|
15
22
|
import { GENERATOR_VERSION } from './version.js';
|
|
16
23
|
import { PostCommitError } from './transaction.js';
|
|
17
|
-
|
|
18
|
-
|
|
24
|
+
import { CliArgumentError, globalArguments, inlineArguments, validateCommand, } from './cli-arguments.js';
|
|
25
|
+
import { printResult } from './cli-output.js';
|
|
26
|
+
import { commandHelp } from './registry/usage.js';
|
|
27
|
+
let [command, ...args] = argv.slice(2);
|
|
28
|
+
let json = argv.includes('--json');
|
|
29
|
+
let output = { json };
|
|
30
|
+
function argumentError(message) {
|
|
31
|
+
return new CliArgumentError(message, `Run anhedral ${command && commandHelp(command) ? command + ' ' : ''}--help.`);
|
|
32
|
+
}
|
|
19
33
|
async function main() {
|
|
20
|
-
|
|
21
|
-
|
|
34
|
+
const global = globalArguments(argv.slice(2));
|
|
35
|
+
[command, ...args] = global.args;
|
|
36
|
+
json = global.json;
|
|
37
|
+
if (json)
|
|
38
|
+
args.push('--json');
|
|
39
|
+
args = inlineArguments(command, args);
|
|
40
|
+
output = { json, quiet: global.quiet };
|
|
41
|
+
if ([undefined, '--help', '-h', 'help'].includes(command) ||
|
|
42
|
+
args.includes('--help') ||
|
|
43
|
+
args.includes('-h')) {
|
|
44
|
+
const target = command === 'help'
|
|
45
|
+
? args.filter((arg) => arg !== '--json')[0]
|
|
46
|
+
: ['--help', '-h'].includes(command)
|
|
47
|
+
? undefined
|
|
48
|
+
: command;
|
|
49
|
+
const help = target ? commandHelp(target) : STANDARD_USAGE;
|
|
50
|
+
if (!help)
|
|
51
|
+
throw new CliArgumentError('Unknown help topic.', 'Run anhedral --help.');
|
|
52
|
+
console.log(json ? JSON.stringify({ usage: help }) : help);
|
|
22
53
|
return;
|
|
23
54
|
}
|
|
55
|
+
validateCommand(command, args);
|
|
24
56
|
if (['--version', '-v'].includes(command)) {
|
|
25
57
|
console.log(json ? JSON.stringify({ version: GENERATOR_VERSION }) : GENERATOR_VERSION);
|
|
26
58
|
return;
|
|
27
59
|
}
|
|
28
|
-
if (
|
|
29
|
-
|
|
60
|
+
if (global.cwd) {
|
|
61
|
+
try {
|
|
62
|
+
process.chdir(global.cwd);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
throw new CliArgumentError('Working directory is unavailable.', 'Choose an existing directory with --cwd <directory>.');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (global.quiet || json)
|
|
69
|
+
process.env.ANHEDRAL_QUIET = '1';
|
|
70
|
+
if (global.verbose && !json && !global.quiet)
|
|
71
|
+
process.env.ANHEDRAL_VERBOSE = '1';
|
|
72
|
+
if (command === 'inspect') {
|
|
73
|
+
const directories = args.filter((arg) => arg !== '--json');
|
|
74
|
+
if (directories.length > 1 || directories.some((arg) => arg.startsWith('-')))
|
|
75
|
+
throw argumentError('Specify at most one project directory.');
|
|
76
|
+
printResult(command, inspectApplication(path.resolve(directories[0] ?? process.cwd())), output);
|
|
30
77
|
return;
|
|
31
78
|
}
|
|
32
|
-
if (command === '
|
|
33
|
-
|
|
79
|
+
if (command === 'integrations') {
|
|
80
|
+
const [action, ...input] = args.filter((arg) => arg !== '--json');
|
|
81
|
+
const directories = [];
|
|
82
|
+
const options = {};
|
|
83
|
+
for (let index = 0; index < input.length; index++) {
|
|
84
|
+
const flag = input[index];
|
|
85
|
+
if (['--provider', '--operation', '--resource'].includes(flag))
|
|
86
|
+
options[flag] = input[++index];
|
|
87
|
+
else if (flag.startsWith('-'))
|
|
88
|
+
throw argumentError('Unknown integration option.');
|
|
89
|
+
else
|
|
90
|
+
directories.push(flag);
|
|
91
|
+
}
|
|
92
|
+
if (directories.length > 1)
|
|
93
|
+
throw argumentError('Specify one application directory.');
|
|
94
|
+
const root = path.resolve(directories[0] ?? process.cwd());
|
|
95
|
+
if (action === 'status') {
|
|
96
|
+
if (Object.keys(options).length)
|
|
97
|
+
throw argumentError('Integration status accepts no recovery options.');
|
|
98
|
+
printResult(command, integrationStatus(root), output);
|
|
99
|
+
}
|
|
100
|
+
else if (action === 'recover') {
|
|
101
|
+
if (!INTEGRATION_PROVIDERS.includes(options['--provider']) ||
|
|
102
|
+
!options['--operation'] ||
|
|
103
|
+
!options['--resource'])
|
|
104
|
+
throw argumentError('Recovery requires --provider, --operation and --resource.');
|
|
105
|
+
printResult(command, await recoverIntegrationResource(root, {
|
|
106
|
+
provider: options['--provider'],
|
|
107
|
+
operation: options['--operation'],
|
|
108
|
+
resourceId: options['--resource'],
|
|
109
|
+
}), output);
|
|
110
|
+
}
|
|
111
|
+
else
|
|
112
|
+
throw argumentError('Use integrations status or integrations recover.');
|
|
34
113
|
return;
|
|
35
114
|
}
|
|
36
|
-
if (command === '
|
|
37
|
-
|
|
115
|
+
if (command === 'auth') {
|
|
116
|
+
let provider;
|
|
117
|
+
const directories = [];
|
|
118
|
+
for (let index = 0; index < args.length; index++) {
|
|
119
|
+
const flag = args[index];
|
|
120
|
+
if (['--json', '--status', '--non-interactive'].includes(flag))
|
|
121
|
+
continue;
|
|
122
|
+
if (flag === '--provider') {
|
|
123
|
+
const value = args[++index];
|
|
124
|
+
if (provider || !value || value.startsWith('-'))
|
|
125
|
+
throw argumentError('Specify one auth provider.');
|
|
126
|
+
provider = value;
|
|
127
|
+
}
|
|
128
|
+
else if (flag.startsWith('-'))
|
|
129
|
+
throw argumentError('Unknown auth option.');
|
|
130
|
+
else
|
|
131
|
+
directories.push(flag);
|
|
132
|
+
}
|
|
133
|
+
if (directories.length > 1)
|
|
134
|
+
throw argumentError('Specify one application directory.');
|
|
135
|
+
const result = await authenticateProviders(path.resolve(directories[0] ?? process.cwd()), {
|
|
136
|
+
provider,
|
|
137
|
+
status: args.includes('--status'),
|
|
138
|
+
interactive: !json &&
|
|
139
|
+
!output.quiet &&
|
|
140
|
+
!args.includes('--non-interactive') &&
|
|
141
|
+
Boolean(process.stdin.isTTY),
|
|
142
|
+
});
|
|
143
|
+
printResult('auth', result, output);
|
|
144
|
+
if (!result.authenticated)
|
|
145
|
+
process.exitCode = 1;
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (command === 'add') {
|
|
149
|
+
await runAdd(args, output);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (command === 'registry') {
|
|
153
|
+
await runRegistry(args, json, output);
|
|
38
154
|
return;
|
|
39
155
|
}
|
|
40
156
|
if (command === 'repository') {
|
|
41
|
-
if (args.includes('--help') || args.includes('-h')) {
|
|
42
|
-
console.log(STANDARD_USAGE);
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
157
|
let owner;
|
|
46
158
|
let name;
|
|
47
159
|
let visibility = 'private';
|
|
@@ -58,36 +170,36 @@ async function main() {
|
|
|
58
170
|
if (flag === '--owner' || flag === '--name' || flag === '--visibility') {
|
|
59
171
|
const value = args[++index];
|
|
60
172
|
if (!value || value.startsWith('-'))
|
|
61
|
-
throw
|
|
173
|
+
throw argumentError('Missing repository option value.');
|
|
62
174
|
if (flag === '--owner') {
|
|
63
175
|
if (owner)
|
|
64
|
-
throw
|
|
176
|
+
throw argumentError('Specify one GitHub owner.');
|
|
65
177
|
owner = value;
|
|
66
178
|
}
|
|
67
179
|
else if (flag === '--name') {
|
|
68
180
|
if (name)
|
|
69
|
-
throw
|
|
181
|
+
throw argumentError('Specify one repository name.');
|
|
70
182
|
name = value;
|
|
71
183
|
}
|
|
72
184
|
else {
|
|
73
185
|
if (!['private', 'public'].includes(value))
|
|
74
|
-
throw
|
|
186
|
+
throw argumentError('Visibility must be private or public.');
|
|
75
187
|
visibility = value;
|
|
76
188
|
}
|
|
77
189
|
continue;
|
|
78
190
|
}
|
|
79
191
|
if (flag.startsWith('-'))
|
|
80
|
-
throw
|
|
192
|
+
throw argumentError('Unknown repository option.');
|
|
81
193
|
directories.push(flag);
|
|
82
194
|
}
|
|
83
195
|
if (directories.length > 1)
|
|
84
|
-
throw
|
|
85
|
-
|
|
196
|
+
throw argumentError('Specify one application directory.');
|
|
197
|
+
printResult('repository', await setupRepository(path.resolve(directories[0] ?? process.cwd()), {
|
|
86
198
|
owner,
|
|
87
199
|
name,
|
|
88
200
|
visibility,
|
|
89
201
|
create,
|
|
90
|
-
}),
|
|
202
|
+
}), output);
|
|
91
203
|
return;
|
|
92
204
|
}
|
|
93
205
|
if ([
|
|
@@ -110,86 +222,13 @@ async function main() {
|
|
|
110
222
|
return;
|
|
111
223
|
}
|
|
112
224
|
if (command !== 'new' && command !== 'init') {
|
|
113
|
-
throw
|
|
114
|
-
}
|
|
115
|
-
if (args.some((arg) => ['--help', '-h'].includes(arg))) {
|
|
116
|
-
console.log(json ? JSON.stringify({ usage: STANDARD_USAGE }) : STANDARD_USAGE);
|
|
117
|
-
return;
|
|
225
|
+
throw argumentError(`Unknown command: ${command}. Use init, auth, ship, add, registry, inspect, doctor, repository, register, connect, provision, status, manage, verify, rollback, adopt, recover, new, --help, or --version.`);
|
|
118
226
|
}
|
|
119
227
|
const options = readOptions(command, args);
|
|
120
228
|
if (options)
|
|
121
|
-
await scaffoldStandardProject(options);
|
|
122
|
-
}
|
|
123
|
-
async function runCloud(args) {
|
|
124
|
-
const [action, ...flags] = args;
|
|
125
|
-
if (!action || flags.includes('--help') || action === '--help') {
|
|
126
|
-
console.log('anhedral cloud register|stage|save|deploy|status [directory] [--artifact directory] [--version stage-id] [--audience private|public] [--json]');
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
let artifact;
|
|
130
|
-
let version;
|
|
131
|
-
let audience = 'private';
|
|
132
|
-
const directories = [];
|
|
133
|
-
for (let index = 0; index < flags.length; index++) {
|
|
134
|
-
const flag = flags[index];
|
|
135
|
-
if (flag === '--json')
|
|
136
|
-
continue;
|
|
137
|
-
if ((flag === '--artifact' && action === 'stage') ||
|
|
138
|
-
(flag === '--version' && ['save', 'deploy'].includes(action)) ||
|
|
139
|
-
(flag === '--audience' && action === 'register')) {
|
|
140
|
-
const value = flags[++index];
|
|
141
|
-
if (!value || value.startsWith('-'))
|
|
142
|
-
throw new Error('Missing cloud option value.');
|
|
143
|
-
if (flag === '--artifact') {
|
|
144
|
-
if (artifact)
|
|
145
|
-
throw new Error('Specify one artifact.');
|
|
146
|
-
artifact = value;
|
|
147
|
-
}
|
|
148
|
-
else if (flag === '--version') {
|
|
149
|
-
if (version)
|
|
150
|
-
throw new Error('Specify one version.');
|
|
151
|
-
version = value;
|
|
152
|
-
}
|
|
153
|
-
else {
|
|
154
|
-
if (value !== 'private' && value !== 'public')
|
|
155
|
-
throw new Error('Audience must be private or public.');
|
|
156
|
-
audience = value;
|
|
157
|
-
}
|
|
158
|
-
continue;
|
|
159
|
-
}
|
|
160
|
-
if (flag.startsWith('-'))
|
|
161
|
-
throw new Error('Unknown cloud option.');
|
|
162
|
-
directories.push(flag);
|
|
163
|
-
}
|
|
164
|
-
if (directories.length > 1)
|
|
165
|
-
throw new Error('Specify one application directory.');
|
|
166
|
-
const root = path.resolve(directories[0] ?? process.cwd());
|
|
167
|
-
let result;
|
|
168
|
-
if (action === 'register')
|
|
169
|
-
result = registerManagedApplication(root, audience);
|
|
170
|
-
else if (action === 'stage') {
|
|
171
|
-
if (!artifact)
|
|
172
|
-
throw new Error('Stage requires --artifact <complete-build-directory>.');
|
|
173
|
-
result = await stageManagedVersion(root, path.resolve(artifact));
|
|
174
|
-
}
|
|
175
|
-
else if (action === 'save' || action === 'deploy') {
|
|
176
|
-
if (!version)
|
|
177
|
-
throw new Error('Save/deploy requires --version <stage-id>.');
|
|
178
|
-
result = await (action === 'save'
|
|
179
|
-
? saveManagedVersion(root, version)
|
|
180
|
-
: deployManagedVersion(root, version));
|
|
181
|
-
}
|
|
182
|
-
else if (action === 'status')
|
|
183
|
-
result = await managedApplicationStatus(root);
|
|
184
|
-
else
|
|
185
|
-
throw new Error('Unknown cloud action.');
|
|
186
|
-
console.log(JSON.stringify(result, null, 2));
|
|
229
|
+
await scaffoldStandardProject(options, undefined, output);
|
|
187
230
|
}
|
|
188
231
|
async function runDelivery(command, args) {
|
|
189
|
-
if (args.includes('--help') || args.includes('-h')) {
|
|
190
|
-
console.log(STANDARD_USAGE);
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
193
232
|
const positional = [];
|
|
194
233
|
let account;
|
|
195
234
|
let audience;
|
|
@@ -200,27 +239,47 @@ async function runDelivery(command, args) {
|
|
|
200
239
|
let use;
|
|
201
240
|
let create = false;
|
|
202
241
|
let git = false;
|
|
242
|
+
let platform;
|
|
243
|
+
let profile;
|
|
203
244
|
for (let index = 0; index < args.length; index++) {
|
|
204
245
|
const arg = args[index];
|
|
246
|
+
if (command === 'ship' && (arg === '--platform' || arg === '--profile')) {
|
|
247
|
+
const value = args[++index];
|
|
248
|
+
if (!value || value.startsWith('-'))
|
|
249
|
+
throw argumentError('Missing mobile build option.');
|
|
250
|
+
if (arg === '--platform') {
|
|
251
|
+
if (platform || !['ios', 'android'].includes(value))
|
|
252
|
+
throw argumentError('Select one mobile platform.');
|
|
253
|
+
platform = value;
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
if (profile)
|
|
257
|
+
throw argumentError('Select one build profile.');
|
|
258
|
+
profile = value;
|
|
259
|
+
}
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
if (command === 'recover' && arg === '--lock')
|
|
263
|
+
continue;
|
|
205
264
|
if (arg === '--provider') {
|
|
206
265
|
const value = args[++index];
|
|
207
266
|
if (!['vercel', 'cloudflare'].includes(value) || provider)
|
|
208
|
-
throw
|
|
267
|
+
throw argumentError('Specify one provider: vercel or cloudflare.');
|
|
209
268
|
provider = value;
|
|
210
269
|
continue;
|
|
211
270
|
}
|
|
212
271
|
if (command === 'connect' && arg === '--scope') {
|
|
213
272
|
if (scope)
|
|
214
|
-
throw
|
|
273
|
+
throw argumentError('Specify one Vercel scope.');
|
|
215
274
|
scope = args[++index];
|
|
216
275
|
if (!scope || scope.startsWith('-'))
|
|
217
|
-
throw
|
|
276
|
+
throw argumentError('Missing Vercel scope.');
|
|
218
277
|
continue;
|
|
219
278
|
}
|
|
220
279
|
if (command === 'connect' && arg === '--use') {
|
|
221
280
|
const value = args[++index];
|
|
222
281
|
if (!['personal', 'commercial'].includes(value) || use)
|
|
223
|
-
throw
|
|
282
|
+
throw argumentError('Specify --use personal or commercial.');
|
|
224
283
|
use = value;
|
|
225
284
|
continue;
|
|
226
285
|
}
|
|
@@ -235,7 +294,7 @@ async function runDelivery(command, args) {
|
|
|
235
294
|
if (command === 'rollback' && arg === '--release') {
|
|
236
295
|
releaseId = args[++index];
|
|
237
296
|
if (!releaseId || releaseId.startsWith('-'))
|
|
238
|
-
throw
|
|
297
|
+
throw argumentError('Missing release ID.');
|
|
239
298
|
continue;
|
|
240
299
|
}
|
|
241
300
|
if (command === 'rollback' && arg === '--data-compatible')
|
|
@@ -248,10 +307,10 @@ async function runDelivery(command, args) {
|
|
|
248
307
|
continue;
|
|
249
308
|
if (command === 'connect' && arg === '--account') {
|
|
250
309
|
if (account)
|
|
251
|
-
throw
|
|
310
|
+
throw argumentError('Specify one account.');
|
|
252
311
|
account = args[++index];
|
|
253
312
|
if (!account || account.startsWith('-'))
|
|
254
|
-
throw
|
|
313
|
+
throw argumentError('Missing account ID.');
|
|
255
314
|
continue;
|
|
256
315
|
}
|
|
257
316
|
if (command === 'connect' &&
|
|
@@ -264,18 +323,40 @@ async function runDelivery(command, args) {
|
|
|
264
323
|
if (command === 'connect' && arg === '--allow-email') {
|
|
265
324
|
const email = args[++index];
|
|
266
325
|
if (!email || email.startsWith('-'))
|
|
267
|
-
throw
|
|
326
|
+
throw argumentError('Missing allowed email address.');
|
|
268
327
|
emails.push(email);
|
|
269
328
|
continue;
|
|
270
329
|
}
|
|
271
330
|
if (arg.startsWith('-'))
|
|
272
|
-
throw
|
|
331
|
+
throw argumentError(`Unknown ${command} option: ${arg}`);
|
|
273
332
|
positional.push(arg);
|
|
274
333
|
}
|
|
275
334
|
if (positional.length > 1)
|
|
276
|
-
throw
|
|
335
|
+
throw argumentError('Specify at most one application directory.');
|
|
277
336
|
const root = path.resolve(positional[0] ?? process.cwd());
|
|
278
337
|
let result;
|
|
338
|
+
if (command === 'recover' && args.includes('--lock')) {
|
|
339
|
+
if (provider || args.includes('--refresh'))
|
|
340
|
+
throw argumentError('Local lock recovery cannot be combined with provider recovery or --refresh.');
|
|
341
|
+
printResult(command, recoverDeliveryLock(root), output);
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
if (platform || profile) {
|
|
345
|
+
if (!platform || !profile || provider)
|
|
346
|
+
throw argumentError('Mobile shipping requires --platform and --profile, without --provider.');
|
|
347
|
+
result = await buildMobileApplication(root, {
|
|
348
|
+
platform,
|
|
349
|
+
profile,
|
|
350
|
+
dryRun: args.includes('--dry-run'),
|
|
351
|
+
});
|
|
352
|
+
printResult(command, result, output);
|
|
353
|
+
if (result &&
|
|
354
|
+
typeof result === 'object' &&
|
|
355
|
+
'state' in result &&
|
|
356
|
+
['configuration-required', 'provider-configuration-required'].includes(String(result.state)))
|
|
357
|
+
process.exitCode = 1;
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
279
360
|
const web = provider === 'vercel' ||
|
|
280
361
|
(provider !== 'cloudflare' &&
|
|
281
362
|
(existsSync(path.join(root, 'anhedral.web.json')) ||
|
|
@@ -283,20 +364,28 @@ async function runDelivery(command, args) {
|
|
|
283
364
|
existsSync(path.join(root, 'anhedral.setup.json')) &&
|
|
284
365
|
JSON.parse(readFileSync(path.join(root, 'anhedral.setup.json'), 'utf8')).hosting ===
|
|
285
366
|
'vercel')));
|
|
367
|
+
if (command === 'ship' && !provider) {
|
|
368
|
+
result = await shipSelectedStack(root, args.includes('--dry-run'));
|
|
369
|
+
printResult(command, result, output);
|
|
370
|
+
if (result &&
|
|
371
|
+
typeof result === 'object' &&
|
|
372
|
+
'state' in result &&
|
|
373
|
+
['configuration-required', 'provider-configuration-required', 'partial'].includes(String(result.state)))
|
|
374
|
+
process.exitCode = 1;
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
286
377
|
if (web) {
|
|
287
378
|
if (account || emails.length)
|
|
288
|
-
throw
|
|
379
|
+
throw argumentError('Cloudflare account/member flags require --provider cloudflare.');
|
|
289
380
|
if (command === 'register')
|
|
290
381
|
result = registerVercelApplication(root);
|
|
291
382
|
else if (command === 'connect') {
|
|
292
383
|
if (!audience || !use)
|
|
293
|
-
throw
|
|
384
|
+
throw argumentError('Vercel connect requires --audience public|private and --use personal|commercial.');
|
|
294
385
|
result = await connectVercelApplication(root, { scope, audience, use, create, git });
|
|
295
386
|
}
|
|
296
387
|
else if (command === 'ship')
|
|
297
|
-
result = args.includes('--dry-run')
|
|
298
|
-
? planVercelDelivery(root)
|
|
299
|
-
: await shipVercelApplication(root);
|
|
388
|
+
result = await shipSelectedVercelApplication(root, args.includes('--dry-run'));
|
|
300
389
|
else if (command === 'adopt')
|
|
301
390
|
result = await adoptVercelApplication(root);
|
|
302
391
|
else if (command === 'recover')
|
|
@@ -305,7 +394,7 @@ async function runDelivery(command, args) {
|
|
|
305
394
|
result = await verifyVercelApplication(root);
|
|
306
395
|
else if (command === 'rollback') {
|
|
307
396
|
if (!releaseId)
|
|
308
|
-
throw
|
|
397
|
+
throw argumentError('Rollback requires --release <recorded-id>.');
|
|
309
398
|
result = await rollbackVercelApplication(root, releaseId, args.includes('--data-compatible'));
|
|
310
399
|
}
|
|
311
400
|
else if (command === 'status')
|
|
@@ -315,12 +404,19 @@ async function runDelivery(command, args) {
|
|
|
315
404
|
result = { file: writeVercelOverview(root, snapshot), evidence: snapshot.evidence };
|
|
316
405
|
}
|
|
317
406
|
else
|
|
318
|
-
throw
|
|
319
|
-
|
|
407
|
+
throw argumentError('Vercel project creation is handled by connect --create; Cloudflare resource provisioning requires --provider cloudflare.');
|
|
408
|
+
if (command === 'status')
|
|
409
|
+
result = { ...result, operation: readLifecycleOperation(root) };
|
|
410
|
+
printResult(command, result, output);
|
|
411
|
+
if (result &&
|
|
412
|
+
typeof result === 'object' &&
|
|
413
|
+
'state' in result &&
|
|
414
|
+
result.state === 'provider-configuration-required')
|
|
415
|
+
process.exitCode = 1;
|
|
320
416
|
return;
|
|
321
417
|
}
|
|
322
418
|
if (scope || use || create || git)
|
|
323
|
-
throw
|
|
419
|
+
throw argumentError('Vercel connect options require a Vercel web manifest or --provider vercel.');
|
|
324
420
|
if (command === 'register')
|
|
325
421
|
result = registerApplication(root);
|
|
326
422
|
else if (command === 'connect') {
|
|
@@ -329,16 +425,16 @@ async function runDelivery(command, args) {
|
|
|
329
425
|
else if (command === 'provision')
|
|
330
426
|
result = args.includes('--dry-run') ? planProvisioning(root) : await provisionApplication(root);
|
|
331
427
|
else if (command === 'ship')
|
|
332
|
-
result = args.includes('--dry-run')
|
|
428
|
+
result = await shipCloudflareApplication(root, args.includes('--dry-run'));
|
|
333
429
|
else if (command === 'adopt')
|
|
334
|
-
throw
|
|
430
|
+
throw argumentError('Git deployment adoption requires --provider vercel.');
|
|
335
431
|
else if (command === 'recover')
|
|
336
432
|
result = await recoverApplication(root);
|
|
337
433
|
else if (command === 'verify')
|
|
338
434
|
result = await verifyApplication(root);
|
|
339
435
|
else if (command === 'rollback') {
|
|
340
436
|
if (!releaseId)
|
|
341
|
-
throw
|
|
437
|
+
throw argumentError('Rollback requires --release <recorded-id>.');
|
|
342
438
|
result = await rollbackApplication(root, releaseId, args.includes('--data-compatible'));
|
|
343
439
|
}
|
|
344
440
|
else if (command === 'manage') {
|
|
@@ -347,17 +443,24 @@ async function runDelivery(command, args) {
|
|
|
347
443
|
}
|
|
348
444
|
else
|
|
349
445
|
result = await applicationStatus(root, args.includes('--refresh'));
|
|
350
|
-
|
|
446
|
+
if (command === 'status')
|
|
447
|
+
result = { ...result, operation: readLifecycleOperation(root) };
|
|
448
|
+
printResult(command, result, output);
|
|
449
|
+
if (result &&
|
|
450
|
+
typeof result === 'object' &&
|
|
451
|
+
'state' in result &&
|
|
452
|
+
['configuration-required', 'provider-configuration-required'].includes(String(result.state)))
|
|
453
|
+
process.exitCode = 1;
|
|
351
454
|
}
|
|
352
455
|
async function runDoctor(args) {
|
|
353
456
|
const positional = args.filter((arg) => arg !== '--json' && arg !== '--providers');
|
|
354
457
|
if (positional.length > 1 || positional.some((arg) => arg.startsWith('-')))
|
|
355
|
-
throw
|
|
458
|
+
throw argumentError('Usage: anhedral doctor [directory] [--providers] [--json]');
|
|
356
459
|
const root = path.resolve(positional[0] ?? process.cwd());
|
|
357
460
|
const report = args.includes('--providers')
|
|
358
461
|
? await inspectProviderReadiness(root)
|
|
359
462
|
: inspectProject(root);
|
|
360
|
-
|
|
463
|
+
printResult('doctor', report, output);
|
|
361
464
|
if (!report.localReady ||
|
|
362
465
|
('prerequisitesReady' in report &&
|
|
363
466
|
(!report.prerequisitesReady || report.providerVerification !== 'verified-existing-context')))
|
|
@@ -369,7 +472,13 @@ function readOptions(command, args) {
|
|
|
369
472
|
}
|
|
370
473
|
catch (error) {
|
|
371
474
|
const message = error instanceof Error ? error.message : String(error);
|
|
372
|
-
console.error(json
|
|
475
|
+
console.error(json
|
|
476
|
+
? JSON.stringify({
|
|
477
|
+
error: message,
|
|
478
|
+
code: 'INVALID_ARGUMENT',
|
|
479
|
+
nextAction: `Run anhedral ${command} --help.`,
|
|
480
|
+
})
|
|
481
|
+
: `Error: ${message}\nNext: Run anhedral ${command} --help.`);
|
|
373
482
|
process.exitCode = 1;
|
|
374
483
|
return;
|
|
375
484
|
}
|
|
@@ -379,33 +488,41 @@ try {
|
|
|
379
488
|
}
|
|
380
489
|
catch (error) {
|
|
381
490
|
const message = error instanceof Error ? error.message : String(error);
|
|
382
|
-
const code = error instanceof
|
|
383
|
-
?
|
|
384
|
-
:
|
|
385
|
-
'
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
491
|
+
const code = error instanceof CliArgumentError
|
|
492
|
+
? error.code
|
|
493
|
+
: error instanceof PostCommitError
|
|
494
|
+
? 'POST_COMMIT_FAILED'
|
|
495
|
+
: [
|
|
496
|
+
'repository',
|
|
497
|
+
'register',
|
|
498
|
+
'connect',
|
|
499
|
+
'provision',
|
|
500
|
+
'ship',
|
|
501
|
+
'status',
|
|
502
|
+
'manage',
|
|
503
|
+
'verify',
|
|
504
|
+
'rollback',
|
|
505
|
+
'adopt',
|
|
506
|
+
'recover',
|
|
507
|
+
'integrations',
|
|
508
|
+
].includes(command ?? '')
|
|
509
|
+
? 'DELIVERY_FAILED'
|
|
510
|
+
: command === 'auth'
|
|
511
|
+
? 'AUTH_FAILED'
|
|
512
|
+
: command === 'doctor'
|
|
513
|
+
? 'INVALID_ARGUMENT'
|
|
514
|
+
: command === 'new' || command === 'init'
|
|
515
|
+
? 'GENERATION_FAILED'
|
|
516
|
+
: command === 'add'
|
|
517
|
+
? 'ADDITION_FAILED'
|
|
518
|
+
: command === 'registry'
|
|
519
|
+
? 'REGISTRY_FAILED'
|
|
520
|
+
: 'UNKNOWN_COMMAND';
|
|
521
|
+
const nextAction = error instanceof CliArgumentError
|
|
522
|
+
? error.nextAction
|
|
523
|
+
: `Run anhedral ${command && commandHelp(command) ? command + ' ' : ''}--help.`;
|
|
524
|
+
console.error(json
|
|
525
|
+
? JSON.stringify({ error: message, code, nextAction })
|
|
526
|
+
: `Error: ${message}\nNext: ${nextAction}`);
|
|
410
527
|
process.exitCode = 1;
|
|
411
528
|
}
|
package/dist/capabilities.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export declare const CAPABILITIES: Record<StandardProduct, Capability>;
|
|
|
6
6
|
export declare const CAPABILITY_REGISTRY: {
|
|
7
7
|
schemaVersion: number;
|
|
8
8
|
cliVersion: string;
|
|
9
|
-
capabilities: Record<"realtime" | "
|
|
9
|
+
capabilities: Record<"realtime" | "d1" | "r2" | "kv" | "queues" | "workflows" | "electron" | "wxt" | "expo" | "vinext" | "next" | "styling" | "hono" | "neon" | "local-data" | "clerk" | "better-auth" | "cron" | "openai" | "ai-sdk" | "workers-ai" | "resend" | "stripe" | "revenuecat" | "sentry" | "basin" | "posthog", Capability>;
|
|
10
10
|
};
|
|
11
11
|
export declare function createSetupPlan(options: StandardOptions, registry?: import("./registry/resolve.js").ResolvedStackRegistry): {
|
|
12
12
|
schemaVersion: number;
|
|
@@ -15,7 +15,7 @@ export declare function createSetupPlan(options: StandardOptions, registry?: imp
|
|
|
15
15
|
standard: string;
|
|
16
16
|
root: string;
|
|
17
17
|
name: string;
|
|
18
|
-
products: ("realtime" | "
|
|
18
|
+
products: ("realtime" | "d1" | "r2" | "kv" | "queues" | "workflows" | "electron" | "wxt" | "expo" | "vinext" | "next" | "styling" | "hono" | "neon" | "local-data" | "clerk" | "better-auth" | "cron" | "openai" | "ai-sdk" | "workers-ai" | "resend" | "stripe" | "revenuecat" | "sentry" | "basin" | "posthog")[];
|
|
19
19
|
hosting: import("./registry/types.js").Hosting;
|
|
20
20
|
registry: import("./registry/resolve.js").ResolvedStackRegistry;
|
|
21
21
|
bootstrap: ("pnpm" | "next" | "shadcn@4.21.4" | "dlx" | "init" | "--monorepo" | "--template")[];
|
|
@@ -32,7 +32,7 @@ export declare function createSetupPlan(options: StandardOptions, registry?: imp
|
|
|
32
32
|
credentials: readonly string[];
|
|
33
33
|
resources: readonly string[];
|
|
34
34
|
verify: readonly string[];
|
|
35
|
-
id: "realtime" | "
|
|
35
|
+
id: "realtime" | "d1" | "r2" | "kv" | "queues" | "workflows" | "electron" | "wxt" | "expo" | "vinext" | "next" | "styling" | "hono" | "neon" | "local-data" | "clerk" | "better-auth" | "cron" | "openai" | "ai-sdk" | "workers-ai" | "resend" | "stripe" | "revenuecat" | "sentry" | "basin" | "posthog";
|
|
36
36
|
}[];
|
|
37
37
|
completion: string[];
|
|
38
38
|
resourcesProvisioned: boolean;
|