@msn-control/liftoff 0.2.1 → 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 +165 -8
- package/dist/args.d.ts +19 -0
- package/dist/args.js +216 -39
- package/dist/args.js.map +1 -1
- package/dist/catalogs.d.ts +7 -1
- package/dist/catalogs.js +55 -0
- package/dist/catalogs.js.map +1 -1
- package/dist/cli.js +12 -7
- package/dist/cli.js.map +1 -1
- package/dist/commands.js +172 -26
- package/dist/commands.js.map +1 -1
- package/dist/file-system.d.ts +3 -0
- package/dist/file-system.js +306 -24
- package/dist/file-system.js.map +1 -1
- package/dist/genai-templates.d.ts +10 -0
- package/dist/genai-templates.js +10 -0
- package/dist/genai-templates.js.map +1 -0
- package/dist/interactive.js +21 -6
- package/dist/interactive.js.map +1 -1
- package/dist/migrate-plan.d.ts +1 -1
- package/dist/migrate-plan.js +38 -10
- package/dist/migrate-plan.js.map +1 -1
- package/dist/planner.js +199 -11
- package/dist/planner.js.map +1 -1
- package/dist/reconcile.d.ts +1 -0
- package/dist/reconcile.js +58 -12
- package/dist/reconcile.js.map +1 -1
- package/dist/scan.d.ts +2 -1
- package/dist/scan.js +112 -3
- package/dist/scan.js.map +1 -1
- package/dist/standard-templates.d.ts +5 -0
- package/dist/standard-templates.js +942 -0
- package/dist/standard-templates.js.map +1 -0
- package/dist/template-types.d.ts +1 -0
- package/dist/template-types.js +2 -0
- package/dist/template-types.js.map +1 -0
- package/dist/templates.d.ts +18 -0
- package/dist/templates.js +1363 -142
- package/dist/templates.js.map +1 -1
- package/dist/types.d.ts +24 -2
- package/package.json +6 -4
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Status: implemented.
|
|
4
4
|
|
|
5
|
-
Liftoff is the Mission Control Launchpad Engine package under `tools/liftoff-cli`. It exposes the `liftoff` command and scaffolds
|
|
5
|
+
Liftoff is the Mission Control Launchpad Engine package under `tools/liftoff-cli`. It exposes the `liftoff` command and scaffolds governed GenAI applications or standard APIs with Docker Compose, OpenTofu, and spec-driven governance assets.
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
@@ -12,20 +12,177 @@ Install the latest stable CLI globally:
|
|
|
12
12
|
npm install -g @msn-control/liftoff@latest
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
After installation,
|
|
15
|
+
After installation, confirm the command is available:
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
18
|
liftoff help
|
|
19
|
-
liftoff create
|
|
20
19
|
```
|
|
21
20
|
|
|
22
|
-
##
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
Preview a generated project before writing files:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
liftoff plan --pattern rag --cloud azure --region eastus --frontend
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Create a project non-interactively:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
liftoff create claims-copilot --pattern rag --cloud azure --region eastus --spec openspec --frontend --yes
|
|
33
|
+
cd claims-copilot
|
|
34
|
+
cp .env.example .env
|
|
35
|
+
liftoff validate
|
|
36
|
+
liftoff doctor
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Create a standard Node.js API without GenAI components:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
liftoff plan --no-genai --api node --cloud azure --region eastus
|
|
43
|
+
liftoff create claims-api --no-genai --api node --cloud azure --region eastus --spec openspec --no-frontend --yes
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Standard API choices are `python` (FastAPI), `node` (Fastify with TypeScript), and `go` (Huma v2 with Chi). Existing GenAI commands that provide `--pattern` continue to infer the GenAI project type and Python/FastAPI stack.
|
|
47
|
+
|
|
48
|
+
Use the local development helper to print the Docker Compose command, then run the printed command:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
liftoff dev up
|
|
52
|
+
docker compose up --build
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
For an existing project, create a Liftoff-shaped sibling scaffold and staged migration plan without modifying the source project:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
liftoff migrate ../legacy-app --region eastus --yes
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## How Liftoff Works
|
|
62
|
+
|
|
63
|
+
Liftoff turns a small set of project decisions into a deterministic scaffold and records what it wrote so future CLI versions can reconcile safely.
|
|
64
|
+
|
|
65
|
+
```text
|
|
66
|
+
plan -> create or migrate -> validate and doctor -> update -> dev and infra helpers
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
- `liftoff plan` resolves flags or config into a project plan, renders the artifact list, and writes nothing.
|
|
70
|
+
- `liftoff create` first determines whether the project is GenAI or standard. GenAI projects select a pattern and use Python/FastAPI/PydanticAI; standard projects select an approved Python, Node.js, or Go API stack. Both flows collect cloud, region, frontend, environments, and spec workflow.
|
|
71
|
+
- `liftoff migrate <path>` scans a legacy project, generates a fresh sibling Liftoff scaffold, stages a filtered legacy copy under `migration/legacy`, and emits an OpenSpec migration change or `MIGRATION.md`.
|
|
72
|
+
- `liftoff validate` checks that `liftoff.manifest.json` can be loaded and every durable manifest artifact exists on disk.
|
|
73
|
+
- `liftoff doctor` runs read-only readiness checks. Outside a generated project it checks the local environment; inside a generated project it also checks manifest health, scaffold drift, runtime configuration, cloud auth, and Azure Functions tooling for worker-enabled Azure projects.
|
|
74
|
+
- `liftoff update` checks scaffold drift by default and writes nothing. It exits 0 when clean and 2 when drift exists, which makes it usable as a CI drift gate.
|
|
75
|
+
- `liftoff update --apply` writes safe changes such as new, missing, moved, or untouched upgraded artifacts. It skips conflicts unless `--force` is also provided, and it reports orphaned files without deleting them automatically.
|
|
76
|
+
- `liftoff dev` prints Docker Compose helper commands such as `docker compose up --build`, `docker compose logs -f`, and `docker compose down --volumes`.
|
|
77
|
+
- `liftoff infra` prints OpenTofu helper commands such as `tofu init`, `tofu plan -var-file=environments/dev.tfvars`, and `tofu apply -var-file=environments/dev.tfvars`.
|
|
78
|
+
|
|
79
|
+
Discovery commands are available for the catalogs that drive generation:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
liftoff patterns
|
|
83
|
+
liftoff providers
|
|
84
|
+
liftoff regions
|
|
85
|
+
liftoff regions search korea --cloud azure
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Azure is the available V1 provider. AWS and GCP are listed as planned provider adapters and are rejected before generation.
|
|
89
|
+
|
|
90
|
+
## Strict Commands And Safe Recovery
|
|
91
|
+
|
|
92
|
+
Liftoff validates each command before running it. Unknown flags or subcommands, missing flag values, invalid booleans, incompatible duplicates, and extra positional arguments exit 1 without generating files or printing a fallback helper command. Use command-specific help to see the accepted syntax:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
liftoff create --help
|
|
96
|
+
liftoff update --help
|
|
97
|
+
liftoff regions --help
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
`liftoff update --apply` preflights every path and destination before its first mutation. A new or moved artifact is adopted when the destination already contains the rendered bytes; different pre-existing bytes are reported as a conflict and skipped. `--force` overwrites reviewed conflicts. Orphans are reported but never deleted automatically. Any write, replacement, cleanup, or manifest failure exits 1 without a success summary or false manifest state, so fixing the filesystem issue and retrying is safe.
|
|
101
|
+
|
|
102
|
+
Manifest paths must be portable path-part arrays confined to the project. Traversal, absolute, drive-qualified, UNC, embedded-separator, empty, and symlink-escaping paths are rejected before artifact access. If validation reports an unsafe or malformed manifest, restore `liftoff.manifest.json` from version control or regenerate a fresh project with the matching Liftoff version. Do not repair the issue by weakening path validation or by retaining a hand-edited unsafe path.
|
|
103
|
+
|
|
104
|
+
## Generated Integration Configuration
|
|
105
|
+
|
|
106
|
+
GenAI starters contain executable, offline-testable integration boundaries:
|
|
107
|
+
|
|
108
|
+
- `PYDANTIC_AI_MODEL` selects the production PydanticAI model. Invoking an unconfigured production agent raises a clear configuration error rather than returning a successful placeholder.
|
|
109
|
+
- Redis Streams publishing uses `REDIS_URL` and `REDIS_STREAM_NAME`.
|
|
110
|
+
- Azure Service Bus publishing uses `SERVICE_BUS_QUEUE_NAME` and either `SERVICE_BUS_CONNECTION_STRING` or `SERVICE_BUS_FULLY_QUALIFIED_NAMESPACE`; `AZURE_CLIENT_ID` selects a user-assigned managed identity.
|
|
111
|
+
- Langfuse tracing requires both `LANGFUSE_PUBLIC_KEY` and `LANGFUSE_SECRET_KEY`, with optional `LANGFUSE_HOST`. Without both keys, tracing is explicitly disabled and reports no remote trace ID.
|
|
112
|
+
- Generated frontends read `VITE_API_BASE_URL` from `frontend/.env`, call the route selected by the project pattern or API stack, and expose loading, response, and failure states.
|
|
113
|
+
- Generated backends allow the local frontend origin by default. Set the comma-separated `CORS_ALLOWED_ORIGINS` value whenever `VITE_API_BASE_URL` points at a frontend on another origin; generated Azure infrastructure sets it to the deployed frontend URL.
|
|
114
|
+
|
|
115
|
+
Generated backend, messaging, tracing, and orchestration tests require no external model, Redis, Service Bus, or Langfuse service. Each generated README includes the fresh-project install, build, and test commands for its selected stack.
|
|
116
|
+
|
|
117
|
+
## Azure Deployment Contracts
|
|
118
|
+
|
|
119
|
+
Environment tfvars contain a deterministic 12-character lowercase alphanumeric `resource_suffix` for globally scoped Azure names. If Azure reports a collision, replace that environment's suffix with another unique value matching `^[a-z0-9]{12}$`; `tofu validate` rejects invalid overrides.
|
|
120
|
+
|
|
121
|
+
Worker-enabled projects configure `ServiceBusConnection__fullyQualifiedNamespace` and `ServiceBusConnection__clientId` for the attached user-assigned identity, and grant that identity's principal the Service Bus Data Receiver role. `function_worker_queue_name` drives the provisioned queue, Function setting, and output. Function host storage uses one complete key-backed `AzureWebJobsStorage` configuration rather than mixed partial identity settings.
|
|
122
|
+
|
|
123
|
+
## Generated Project Structure
|
|
124
|
+
|
|
125
|
+
Generated paths below are logical project structure examples. The CLI writes them using platform-correct filesystem handling on macOS, Linux, and Windows, and machine-readable manifests store path parts rather than joined path strings.
|
|
126
|
+
|
|
127
|
+
```text
|
|
128
|
+
claims-copilot/
|
|
129
|
+
|-- README.md
|
|
130
|
+
|-- liftoff.config.json
|
|
131
|
+
|-- liftoff.manifest.json
|
|
132
|
+
|-- .env.example
|
|
133
|
+
|-- Dockerfile
|
|
134
|
+
|-- docker-compose.yml
|
|
135
|
+
|-- backend/ # internals depend on the selected API stack
|
|
136
|
+
|-- database/
|
|
137
|
+
| |-- alembic.ini
|
|
138
|
+
| |-- migrations/
|
|
139
|
+
| `-- models/
|
|
140
|
+
|-- environments/
|
|
141
|
+
| |-- dev/
|
|
142
|
+
| |-- test/
|
|
143
|
+
| `-- prod/
|
|
144
|
+
|-- infrastructure/
|
|
145
|
+
| `-- opentofu/
|
|
146
|
+
| `-- azure/
|
|
147
|
+
|-- openspec/ or .specify/
|
|
148
|
+
|-- frontend/ # only when frontend generation is selected
|
|
149
|
+
|-- functions/<worker-name>/ # only for worker-enabled Azure patterns
|
|
150
|
+
`-- migration/legacy/ # only for projects created by liftoff migrate
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Core generated areas:
|
|
154
|
+
|
|
155
|
+
- `backend` contains the selected API stack and Scalar/OpenAPI wiring. Python uses `backend/apis`, Node.js uses `backend/src`, and Go uses `backend/cmd/api` plus `backend/internal`.
|
|
156
|
+
- `backend/orchestration` appears only in GenAI projects and contains PydanticAI agents, prompts, model configuration, and integration boundaries.
|
|
157
|
+
- `database` contains stack-native migrations and SQL schema assets: SQLAlchemy/Alembic for Python, Drizzle for Node.js, or pgx/Goose for Go.
|
|
158
|
+
- `environments/<env>` contains environment-specific backend settings, and Functions settings when a worker is generated.
|
|
159
|
+
- `docker-compose.yml` starts the selected backend runtime, PostgreSQL, Redis, Azurite, and Mailpit. GenAI projects use pgvector when required and include the optional Langfuse observability profile.
|
|
160
|
+
- `infrastructure/opentofu/azure` contains Azure OpenTofu modules, environment tfvars, local state configuration, and a remote state example.
|
|
161
|
+
- `openspec` is generated for the OpenSpec workflow; `.specify` and `specs` are generated for the Spec Kit workflow.
|
|
162
|
+
|
|
163
|
+
Conditional areas:
|
|
164
|
+
|
|
165
|
+
- `frontend` is generated only when `--frontend` is selected. It uses Vue 3 and Tailwind with a generic API starter for standard projects or an experience matched to the selected GenAI pattern.
|
|
166
|
+
- `functions/<worker-name>` is generated only for worker-enabled Azure GenAI patterns such as RAG, agent, multi-agent, and workflow. Azure Functions trigger adapters live there; reusable GenAI orchestration stays under `backend/orchestration`.
|
|
167
|
+
- `backend/workers` appears for worker-backed patterns as backend-adjacent or containerized worker code, separate from Azure Functions runtime files.
|
|
168
|
+
- `migration/legacy` appears only after `liftoff migrate` and contains a filtered copy of the source project for guided migration work.
|
|
169
|
+
|
|
170
|
+
## Configuration And Manifest
|
|
171
|
+
|
|
172
|
+
Generated projects contain two root files with different ownership models:
|
|
173
|
+
|
|
174
|
+
- `liftoff.config.json` is user-owned desired state after creation. Liftoff writes it once during generation and does not machine-rewrite it afterwards. Supported changes, such as adding an environment or enabling frontend output, are reconciled by `liftoff update`. Project type, API stack, and GenAI pattern changes are migrations and should use `liftoff migrate`.
|
|
175
|
+
- `liftoff.manifest.json` is the CLI-owned compatibility record. Manifest schema v2 records `artifactVersion`, the generating `liftoffVersion`, project type, API stack, applicable GenAI pattern, durable generated artifact `logicalName`s, categories, OS-neutral path parts, and `sha256:` content hashes.
|
|
176
|
+
|
|
177
|
+
The manifest lets `liftoff validate`, `liftoff doctor`, and `liftoff update` distinguish clean generated files from local edits. Seed content, such as the initial OpenSpec bootstrap change, is written once and intentionally omitted from the durable manifest so it can follow its own lifecycle. Treat the manifest as CLI-owned: restore or regenerate it when validation fails rather than hand-editing artifact paths or hashes.
|
|
178
|
+
|
|
179
|
+
## Contract Conventions
|
|
23
180
|
|
|
24
181
|
Generated projects contain persistent files that outlive any CLI release. The following rules are the compatibility contract, enforced by `tests/contract.test.ts` where possible:
|
|
25
182
|
|
|
26
|
-
- **Manifest schema**: `liftoff.manifest.json` uses `artifactVersion` 2
|
|
27
|
-
- **Append-only identifiers**: artifact `logicalName`s and catalog ids (patterns, providers, environments, spec workflows) are never renamed or removed, only added. The contract test snapshots the logical-name sets.
|
|
28
|
-
- **Deterministic rendering**: artifact content depends only on the project plan and the template code
|
|
183
|
+
- **Manifest schema**: `liftoff.manifest.json` uses `artifactVersion` 2, the first supported schema version, recording the generating CLI version (`liftoffVersion`) and a `sha256:`-prefixed `contentHash` per artifact. Readers accept every supported version and reject others with a remedy; writers always write the latest version.
|
|
184
|
+
- **Append-only identifiers**: artifact `logicalName`s and catalog ids (project types, API stacks, patterns, providers, environments, spec workflows) are never renamed or removed, only added. The contract test snapshots the logical-name sets.
|
|
185
|
+
- **Deterministic rendering**: artifact content depends only on the project plan and the template code, with no timestamps, randomness, or environment leakage. Verified by a double-render byte-equality test.
|
|
29
186
|
- **Reserved namespaces**: `.liftoff/` in generated projects is reserved for future CLI-managed state; no new CLI-managed root-level files beyond `liftoff.config.json` and `liftoff.manifest.json`. `liftoff.config.json` is written once at generation and never machine-written afterwards.
|
|
30
187
|
- **Portable paths**: machine-readable files store OS-neutral path-part arrays, never joined path strings.
|
|
31
188
|
- **Exit codes**: 0 = success or clean check, 1 = failure, 2 = a check mode found drift.
|
|
@@ -57,4 +214,4 @@ npm run smoke:package --workspace @msn-control/liftoff
|
|
|
57
214
|
|
|
58
215
|
The `Release Liftoff` workflow builds, tests, packs, smoke-installs, and publishes only the `@msn-control/liftoff` workspace package. Stable versions publish with the `latest` npm dist-tag; prerelease versions publish with `next`.
|
|
59
216
|
|
|
60
|
-
Prefer npm trusted publishing
|
|
217
|
+
Prefer npm trusted publishing for release authentication. The workflow disables provenance while the source repository is private because npm accepts GitHub Actions provenance only from public repositories. If trusted publishing is not configured for the repository and npm organization, create a scoped npm automation token and store it as the repository secret `NPM_TOKEN`.
|
package/dist/args.d.ts
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
import type { ParsedArgs } from './types.js';
|
|
2
|
+
type FlagKind = 'boolean' | 'value';
|
|
3
|
+
interface FlagDefinition {
|
|
4
|
+
kind: FlagKind;
|
|
5
|
+
negatable?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface CommandDefinition {
|
|
8
|
+
description: string;
|
|
9
|
+
usage: string;
|
|
10
|
+
flags: Readonly<Record<string, FlagDefinition>>;
|
|
11
|
+
subcommands?: readonly string[];
|
|
12
|
+
defaultMaxPositionals: number;
|
|
13
|
+
subcommandMaxPositionals?: Readonly<Record<string, number>>;
|
|
14
|
+
}
|
|
15
|
+
export declare const commandDefinitions: Readonly<Record<string, CommandDefinition>>;
|
|
16
|
+
export declare class UsageError extends Error {
|
|
17
|
+
constructor(message: string);
|
|
18
|
+
}
|
|
2
19
|
export declare function parseArgs(argv: string[]): ParsedArgs;
|
|
20
|
+
export declare function formatCommandHelp(command: string): string;
|
|
3
21
|
export declare function readStringFlag(flags: ParsedArgs['flags'], name: string): string | undefined;
|
|
4
22
|
export declare function readBooleanFlag(flags: ParsedArgs['flags'], name: string): boolean | undefined;
|
|
5
23
|
export declare function readListFlag(flags: ParsedArgs['flags'], name: string): string[] | undefined;
|
|
24
|
+
export {};
|
package/dist/args.js
CHANGED
|
@@ -1,65 +1,242 @@
|
|
|
1
|
-
const
|
|
2
|
-
const
|
|
3
|
-
const
|
|
1
|
+
const booleanFlag = (negatable = false) => ({ kind: 'boolean', negatable });
|
|
2
|
+
const valueFlag = () => ({ kind: 'value' });
|
|
3
|
+
const helpFlag = { help: booleanFlag() };
|
|
4
|
+
const projectFlags = {
|
|
5
|
+
project: valueFlag(),
|
|
6
|
+
genai: booleanFlag(true),
|
|
7
|
+
api: valueFlag(),
|
|
8
|
+
pattern: valueFlag(),
|
|
9
|
+
cloud: valueFlag(),
|
|
10
|
+
region: valueFlag(),
|
|
11
|
+
frontend: booleanFlag(true),
|
|
12
|
+
environments: valueFlag(),
|
|
13
|
+
spec: valueFlag(),
|
|
14
|
+
config: valueFlag()
|
|
15
|
+
};
|
|
16
|
+
export const commandDefinitions = {
|
|
17
|
+
help: {
|
|
18
|
+
description: 'Show general or command-specific help',
|
|
19
|
+
usage: '[command]',
|
|
20
|
+
flags: helpFlag,
|
|
21
|
+
defaultMaxPositionals: 1
|
|
22
|
+
},
|
|
23
|
+
create: {
|
|
24
|
+
description: 'Generate a new project',
|
|
25
|
+
usage: '[project-name]',
|
|
26
|
+
flags: { ...projectFlags, yes: booleanFlag(), ...helpFlag },
|
|
27
|
+
defaultMaxPositionals: 1
|
|
28
|
+
},
|
|
29
|
+
plan: {
|
|
30
|
+
description: 'Preview generated artifacts',
|
|
31
|
+
usage: '',
|
|
32
|
+
flags: { ...projectFlags, ...helpFlag },
|
|
33
|
+
defaultMaxPositionals: 0
|
|
34
|
+
},
|
|
35
|
+
patterns: {
|
|
36
|
+
description: 'List GenAI patterns',
|
|
37
|
+
usage: '',
|
|
38
|
+
flags: helpFlag,
|
|
39
|
+
defaultMaxPositionals: 0
|
|
40
|
+
},
|
|
41
|
+
providers: {
|
|
42
|
+
description: 'List cloud providers',
|
|
43
|
+
usage: '',
|
|
44
|
+
flags: helpFlag,
|
|
45
|
+
defaultMaxPositionals: 0
|
|
46
|
+
},
|
|
47
|
+
regions: {
|
|
48
|
+
description: 'List or search provider regions',
|
|
49
|
+
usage: '[search <query>]',
|
|
50
|
+
flags: { cloud: valueFlag(), region: valueFlag(), ...helpFlag },
|
|
51
|
+
subcommands: ['search'],
|
|
52
|
+
defaultMaxPositionals: 0,
|
|
53
|
+
subcommandMaxPositionals: { search: 1 }
|
|
54
|
+
},
|
|
55
|
+
validate: {
|
|
56
|
+
description: 'Validate a generated project manifest',
|
|
57
|
+
usage: '[project-path]',
|
|
58
|
+
flags: { project: valueFlag(), ...helpFlag },
|
|
59
|
+
defaultMaxPositionals: 1
|
|
60
|
+
},
|
|
61
|
+
update: {
|
|
62
|
+
description: 'Reconcile a project with current templates',
|
|
63
|
+
usage: '[project-path]',
|
|
64
|
+
flags: {
|
|
65
|
+
project: valueFlag(),
|
|
66
|
+
apply: booleanFlag(),
|
|
67
|
+
force: booleanFlag(),
|
|
68
|
+
json: booleanFlag(),
|
|
69
|
+
...helpFlag
|
|
70
|
+
},
|
|
71
|
+
defaultMaxPositionals: 1
|
|
72
|
+
},
|
|
73
|
+
migrate: {
|
|
74
|
+
description: 'Adopt an existing project',
|
|
75
|
+
usage: '<source-path>',
|
|
76
|
+
flags: { ...projectFlags, yes: booleanFlag(), ...helpFlag },
|
|
77
|
+
defaultMaxPositionals: 1
|
|
78
|
+
},
|
|
79
|
+
doctor: {
|
|
80
|
+
description: 'Check local and project readiness',
|
|
81
|
+
usage: '',
|
|
82
|
+
flags: { cloud: valueFlag(), json: booleanFlag(), ...helpFlag },
|
|
83
|
+
defaultMaxPositionals: 0
|
|
84
|
+
},
|
|
85
|
+
dev: {
|
|
86
|
+
description: 'Print Docker Compose helper commands',
|
|
87
|
+
usage: '[up|down|logs|reset]',
|
|
88
|
+
flags: { profile: valueFlag(), ...helpFlag },
|
|
89
|
+
subcommands: ['up', 'down', 'logs', 'reset'],
|
|
90
|
+
defaultMaxPositionals: 0,
|
|
91
|
+
subcommandMaxPositionals: { up: 0, down: 0, logs: 0, reset: 0 }
|
|
92
|
+
},
|
|
93
|
+
infra: {
|
|
94
|
+
description: 'Print OpenTofu helper commands',
|
|
95
|
+
usage: '[init|plan|apply|output]',
|
|
96
|
+
flags: { env: valueFlag(), ...helpFlag },
|
|
97
|
+
subcommands: ['init', 'plan', 'apply', 'output'],
|
|
98
|
+
defaultMaxPositionals: 0,
|
|
99
|
+
subcommandMaxPositionals: { init: 0, plan: 0, apply: 0, output: 0 }
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
export class UsageError extends Error {
|
|
103
|
+
constructor(message) {
|
|
104
|
+
super(message);
|
|
105
|
+
this.name = 'UsageError';
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function assignFlag(flags, name, value) {
|
|
109
|
+
if (Object.hasOwn(flags, name)) {
|
|
110
|
+
throw new UsageError(`Flag --${name} may be provided only once.`);
|
|
111
|
+
}
|
|
112
|
+
flags[name] = value;
|
|
113
|
+
}
|
|
114
|
+
function parseBooleanValue(name, value) {
|
|
115
|
+
if (value === 'true') {
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
if (value === 'false') {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
throw new UsageError(`Flag --${name} expects true or false.`);
|
|
122
|
+
}
|
|
4
123
|
export function parseArgs(argv) {
|
|
5
|
-
|
|
124
|
+
if (argv.length === 0) {
|
|
125
|
+
return { positional: [], flags: {} };
|
|
126
|
+
}
|
|
127
|
+
if (argv[0] === '--help') {
|
|
128
|
+
return { command: 'help', positional: [], flags: {} };
|
|
129
|
+
}
|
|
130
|
+
const command = argv[0];
|
|
131
|
+
if (command.startsWith('-')) {
|
|
132
|
+
throw new UsageError(`Unknown option: ${command}. Run \`liftoff help\` for usage.`);
|
|
133
|
+
}
|
|
134
|
+
const definition = commandDefinitions[command];
|
|
135
|
+
if (!definition) {
|
|
136
|
+
throw new UsageError(`Unknown command: ${command}. Run \`liftoff help\` for usage.`);
|
|
137
|
+
}
|
|
138
|
+
const tokens = argv.slice(1);
|
|
139
|
+
let subcommand;
|
|
140
|
+
if (definition.subcommands && tokens[0] && !tokens[0].startsWith('-')) {
|
|
141
|
+
const candidate = tokens.shift();
|
|
142
|
+
if (!definition.subcommands.includes(candidate)) {
|
|
143
|
+
throw new UsageError(`Unsupported ${command} subcommand: ${candidate}. Use one of: ${definition.subcommands.join(', ')}.`);
|
|
144
|
+
}
|
|
145
|
+
subcommand = candidate;
|
|
146
|
+
}
|
|
6
147
|
const positional = [];
|
|
7
148
|
const flags = {};
|
|
8
|
-
|
|
9
|
-
const tokens = hasSubcommand ? rest : argv.slice(1);
|
|
10
|
-
const subcommand = hasSubcommand ? maybeSubcommand : undefined;
|
|
149
|
+
let positionalOnly = false;
|
|
11
150
|
for (let index = 0; index < tokens.length; index += 1) {
|
|
12
151
|
const token = tokens[index];
|
|
13
|
-
if (
|
|
14
|
-
|
|
152
|
+
if (token === '--') {
|
|
153
|
+
positionalOnly = true;
|
|
15
154
|
continue;
|
|
16
155
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
flags[withoutPrefix.slice(3)] = false;
|
|
156
|
+
if (positionalOnly || !token.startsWith('-')) {
|
|
157
|
+
positional.push(token);
|
|
20
158
|
continue;
|
|
21
159
|
}
|
|
160
|
+
if (!token.startsWith('--')) {
|
|
161
|
+
throw new UsageError(`Unknown option: ${token}. Liftoff options use --long-name syntax.`);
|
|
162
|
+
}
|
|
163
|
+
const withoutPrefix = token.slice(2);
|
|
22
164
|
const equalsIndex = withoutPrefix.indexOf('=');
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
165
|
+
const rawName = equalsIndex >= 0 ? withoutPrefix.slice(0, equalsIndex) : withoutPrefix;
|
|
166
|
+
const inlineValue = equalsIndex >= 0 ? withoutPrefix.slice(equalsIndex + 1) : undefined;
|
|
167
|
+
const negated = rawName.startsWith('no-');
|
|
168
|
+
const name = negated ? rawName.slice(3) : rawName;
|
|
169
|
+
const flagDefinition = definition.flags[name];
|
|
170
|
+
if (!flagDefinition) {
|
|
171
|
+
throw new UsageError(`Unknown flag for ${command}: --${rawName}.`);
|
|
172
|
+
}
|
|
173
|
+
if (negated) {
|
|
174
|
+
if (inlineValue !== undefined || flagDefinition.kind !== 'boolean' || !flagDefinition.negatable) {
|
|
175
|
+
throw new UsageError(`Flag --${name} does not support the --no-${name} form.`);
|
|
176
|
+
}
|
|
177
|
+
assignFlag(flags, name, false);
|
|
26
178
|
continue;
|
|
27
179
|
}
|
|
28
|
-
if (
|
|
29
|
-
flags
|
|
180
|
+
if (flagDefinition.kind === 'boolean') {
|
|
181
|
+
assignFlag(flags, name, inlineValue === undefined ? true : parseBooleanValue(name, inlineValue));
|
|
30
182
|
continue;
|
|
31
183
|
}
|
|
32
|
-
if (
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
throw new Error(`Missing value for --${withoutPrefix}.`);
|
|
184
|
+
if (inlineValue !== undefined) {
|
|
185
|
+
if (inlineValue.length === 0) {
|
|
186
|
+
throw new UsageError(`Missing value for --${name}.`);
|
|
36
187
|
}
|
|
37
|
-
flags
|
|
38
|
-
index += 1;
|
|
188
|
+
assignFlag(flags, name, inlineValue);
|
|
39
189
|
continue;
|
|
40
190
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
191
|
+
const next = tokens[index + 1];
|
|
192
|
+
if (!next || next.startsWith('-')) {
|
|
193
|
+
throw new UsageError(`Missing value for --${name}.`);
|
|
194
|
+
}
|
|
195
|
+
assignFlag(flags, name, next);
|
|
196
|
+
index += 1;
|
|
197
|
+
}
|
|
198
|
+
const maxPositionals = subcommand
|
|
199
|
+
? definition.subcommandMaxPositionals?.[subcommand] ?? 0
|
|
200
|
+
: definition.defaultMaxPositionals;
|
|
201
|
+
if (positional.length > maxPositionals) {
|
|
202
|
+
throw new UsageError(`Too many positional arguments for ${command}${subcommand ? ` ${subcommand}` : ''}. ` +
|
|
203
|
+
`Usage: liftoff ${command}${definition.usage ? ` ${definition.usage}` : ''}`);
|
|
204
|
+
}
|
|
205
|
+
if (command === 'help' && positional[0] && !commandDefinitions[positional[0]]) {
|
|
206
|
+
throw new UsageError(`Unknown command for help: ${positional[0]}.`);
|
|
207
|
+
}
|
|
208
|
+
return { command, subcommand, positional, flags };
|
|
209
|
+
}
|
|
210
|
+
export function formatCommandHelp(command) {
|
|
211
|
+
const definition = commandDefinitions[command];
|
|
212
|
+
if (!definition) {
|
|
213
|
+
throw new UsageError(`Unknown command for help: ${command}.`);
|
|
214
|
+
}
|
|
215
|
+
const lines = [
|
|
216
|
+
`${command} - ${definition.description}`,
|
|
217
|
+
'',
|
|
218
|
+
`Usage: liftoff ${command}${definition.usage ? ` ${definition.usage}` : ''}`
|
|
219
|
+
];
|
|
220
|
+
if (definition.subcommands) {
|
|
221
|
+
lines.push('', `Subcommands: ${definition.subcommands.join(', ')}`);
|
|
222
|
+
}
|
|
223
|
+
const flagNames = Object.entries(definition.flags).map(([name, flag]) => {
|
|
224
|
+
const value = flag.kind === 'value' ? ' <value>' : '';
|
|
225
|
+
const negated = flag.negatable ? ` / --no-${name}` : '';
|
|
226
|
+
return ` --${name}${value}${negated}`;
|
|
227
|
+
});
|
|
228
|
+
if (flagNames.length > 0) {
|
|
229
|
+
lines.push('', 'Options:', ...flagNames);
|
|
230
|
+
}
|
|
231
|
+
return `${lines.join('\n')}\n`;
|
|
49
232
|
}
|
|
50
233
|
export function readStringFlag(flags, name) {
|
|
51
234
|
const value = flags[name];
|
|
52
|
-
|
|
53
|
-
return value;
|
|
54
|
-
}
|
|
55
|
-
return undefined;
|
|
235
|
+
return typeof value === 'string' ? value : undefined;
|
|
56
236
|
}
|
|
57
237
|
export function readBooleanFlag(flags, name) {
|
|
58
238
|
const value = flags[name];
|
|
59
|
-
|
|
60
|
-
return value;
|
|
61
|
-
}
|
|
62
|
-
return undefined;
|
|
239
|
+
return typeof value === 'boolean' ? value : undefined;
|
|
63
240
|
}
|
|
64
241
|
export function readListFlag(flags, name) {
|
|
65
242
|
const value = flags[name];
|
package/dist/args.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"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"}
|
package/dist/catalogs.d.ts
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
import type { EnvironmentDefinition, EnvironmentId, PatternDefinition, PatternId, ProviderDefinition, ProviderId, RegionDefinition, SpecWorkflowDefinition, SpecWorkflowId } from './types.js';
|
|
1
|
+
import type { ApiStackDefinition, ApiStackId, EnvironmentDefinition, EnvironmentId, PatternDefinition, PatternId, ProviderDefinition, ProviderId, ProjectTypeDefinition, ProjectTypeId, RegionDefinition, SpecWorkflowDefinition, SpecWorkflowId } from './types.js';
|
|
2
2
|
export declare const approvedStack: string[];
|
|
3
|
+
export declare const projectTypes: ProjectTypeDefinition[];
|
|
4
|
+
export declare const apiStacks: ApiStackDefinition[];
|
|
3
5
|
export declare const patterns: PatternDefinition[];
|
|
4
6
|
export declare const providers: ProviderDefinition[];
|
|
5
7
|
export declare const azureRegions: RegionDefinition[];
|
|
6
8
|
export declare const environments: EnvironmentDefinition[];
|
|
7
9
|
export declare const specWorkflows: SpecWorkflowDefinition[];
|
|
8
10
|
export declare function getPattern(value: string): PatternDefinition | undefined;
|
|
11
|
+
export declare function getProjectType(value: string): ProjectTypeDefinition | undefined;
|
|
12
|
+
export declare function getApiStack(value: string): ApiStackDefinition | undefined;
|
|
9
13
|
export declare function getProvider(value: string): ProviderDefinition | undefined;
|
|
10
14
|
export declare function getSpecWorkflow(value: string): SpecWorkflowDefinition | undefined;
|
|
11
15
|
export declare function getEnvironment(value: string): EnvironmentDefinition | undefined;
|
|
@@ -24,6 +28,8 @@ export type RegionResolution = {
|
|
|
24
28
|
};
|
|
25
29
|
export declare function resolveRegion(provider: ProviderId, input?: string): RegionResolution;
|
|
26
30
|
export declare function isPatternId(value: string): value is PatternId;
|
|
31
|
+
export declare function isProjectTypeId(value: string): value is ProjectTypeId;
|
|
32
|
+
export declare function isApiStackId(value: string): value is ApiStackId;
|
|
27
33
|
export declare function isProviderId(value: string): value is ProviderId;
|
|
28
34
|
export declare function isEnvironmentId(value: string): value is EnvironmentId;
|
|
29
35
|
export declare function isSpecWorkflowId(value: string): value is SpecWorkflowId;
|
package/dist/catalogs.js
CHANGED
|
@@ -14,6 +14,47 @@ export const approvedStack = [
|
|
|
14
14
|
'Docker Compose',
|
|
15
15
|
'OpenTofu'
|
|
16
16
|
];
|
|
17
|
+
export const projectTypes = [
|
|
18
|
+
{
|
|
19
|
+
id: 'genai',
|
|
20
|
+
label: 'GenAI application',
|
|
21
|
+
description: 'Python/FastAPI application with PydanticAI and a selected GenAI pattern.'
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
id: 'standard',
|
|
25
|
+
label: 'Standard application',
|
|
26
|
+
description: 'Non-GenAI API application using an approved Python, Node.js, or Go stack.'
|
|
27
|
+
}
|
|
28
|
+
];
|
|
29
|
+
export const apiStacks = [
|
|
30
|
+
{
|
|
31
|
+
id: 'python-fastapi',
|
|
32
|
+
label: 'Python / FastAPI',
|
|
33
|
+
aliases: ['python', 'py', 'fastapi', 'python-fastapi'],
|
|
34
|
+
language: 'Python',
|
|
35
|
+
framework: 'FastAPI',
|
|
36
|
+
databaseTooling: 'SQLAlchemy + Alembic',
|
|
37
|
+
testFramework: 'pytest'
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
id: 'node-fastify',
|
|
41
|
+
label: 'Node.js / Fastify / TypeScript',
|
|
42
|
+
aliases: ['node', 'nodejs', 'node.js', 'fastify', 'typescript', 'node-fastify'],
|
|
43
|
+
language: 'Node.js',
|
|
44
|
+
framework: 'Fastify with TypeScript',
|
|
45
|
+
databaseTooling: 'Drizzle',
|
|
46
|
+
testFramework: 'Vitest'
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
id: 'go-huma',
|
|
50
|
+
label: 'Go / Huma / Chi',
|
|
51
|
+
aliases: ['go', 'golang', 'huma', 'chi', 'go-huma'],
|
|
52
|
+
language: 'Go',
|
|
53
|
+
framework: 'Huma v2 with Chi',
|
|
54
|
+
databaseTooling: 'pgx + Goose',
|
|
55
|
+
testFramework: 'go test'
|
|
56
|
+
}
|
|
57
|
+
];
|
|
17
58
|
export const patterns = [
|
|
18
59
|
{
|
|
19
60
|
id: 'rag',
|
|
@@ -191,6 +232,14 @@ export function getPattern(value) {
|
|
|
191
232
|
const normalized = normalize(value);
|
|
192
233
|
return patterns.find((pattern) => normalize(pattern.id) === normalized || pattern.aliases.some((alias) => normalize(alias) === normalized));
|
|
193
234
|
}
|
|
235
|
+
export function getProjectType(value) {
|
|
236
|
+
const normalized = normalize(value);
|
|
237
|
+
return projectTypes.find((projectType) => normalize(projectType.id) === normalized || normalize(projectType.label) === normalized);
|
|
238
|
+
}
|
|
239
|
+
export function getApiStack(value) {
|
|
240
|
+
const normalized = normalize(value);
|
|
241
|
+
return apiStacks.find((stack) => normalize(stack.id) === normalized || stack.aliases.some((alias) => normalize(alias) === normalized));
|
|
242
|
+
}
|
|
194
243
|
export function getProvider(value) {
|
|
195
244
|
const normalized = normalize(value);
|
|
196
245
|
return providers.find((provider) => normalize(provider.id) === normalized || normalize(provider.label) === normalized);
|
|
@@ -250,6 +299,12 @@ export function resolveRegion(provider, input) {
|
|
|
250
299
|
export function isPatternId(value) {
|
|
251
300
|
return patterns.some((pattern) => pattern.id === value);
|
|
252
301
|
}
|
|
302
|
+
export function isProjectTypeId(value) {
|
|
303
|
+
return projectTypes.some((projectType) => projectType.id === value);
|
|
304
|
+
}
|
|
305
|
+
export function isApiStackId(value) {
|
|
306
|
+
return apiStacks.some((stack) => stack.id === value);
|
|
307
|
+
}
|
|
253
308
|
export function isProviderId(value) {
|
|
254
309
|
return providers.some((provider) => provider.id === value);
|
|
255
310
|
}
|