@hs-x/cli 0.2.0 → 0.2.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.
Files changed (39) hide show
  1. package/README.md +97 -986
  2. package/dist/cli/index.js +10 -6
  3. package/dist/cli/index.js.map +1 -1
  4. package/dist/commands/deploy.d.ts.map +1 -1
  5. package/dist/commands/deploy.js +75 -5
  6. package/dist/commands/deploy.js.map +1 -1
  7. package/dist/commands/dev.d.ts +5 -0
  8. package/dist/commands/dev.d.ts.map +1 -1
  9. package/dist/commands/dev.js +222 -2
  10. package/dist/commands/dev.js.map +1 -1
  11. package/dist/commands/help-command.d.ts +1 -1
  12. package/dist/commands/help-command.d.ts.map +1 -1
  13. package/dist/commands/help-command.js +46 -15
  14. package/dist/commands/help-command.js.map +1 -1
  15. package/dist/commands/migrate.d.ts +10 -0
  16. package/dist/commands/migrate.d.ts.map +1 -1
  17. package/dist/commands/migrate.js +227 -4
  18. package/dist/commands/migrate.js.map +1 -1
  19. package/dist/constants.d.ts +1 -1
  20. package/dist/constants.js +1 -1
  21. package/dist/dev/invoke.d.ts +44 -0
  22. package/dist/dev/invoke.d.ts.map +1 -0
  23. package/dist/dev/invoke.js +155 -0
  24. package/dist/dev/invoke.js.map +1 -0
  25. package/dist/errors-registry.d.ts.map +1 -1
  26. package/dist/errors-registry.js +85 -0
  27. package/dist/errors-registry.js.map +1 -1
  28. package/dist/help.d.ts +10 -0
  29. package/dist/help.d.ts.map +1 -1
  30. package/dist/help.js +209 -3
  31. package/dist/help.js.map +1 -1
  32. package/dist/init/templates.d.ts.map +1 -1
  33. package/dist/init/templates.js +27 -8
  34. package/dist/init/templates.js.map +1 -1
  35. package/dist/reporter/style.d.ts +1 -0
  36. package/dist/reporter/style.d.ts.map +1 -1
  37. package/dist/reporter/style.js +4 -0
  38. package/dist/reporter/style.js.map +1 -1
  39. package/package.json +11 -1
package/README.md CHANGED
@@ -1,1027 +1,138 @@
1
- # HS-X CLI Interfaces
1
+ # @hs-x/cli
2
2
 
3
- > **Status: design doc, not a current-state reference.**
4
- >
5
- > This file is the CLI *contract* — what `hs-x` will look like at completion. Many sections below describe commands, flags, or packages that are **not yet implemented**. For the actually-shipped CLI surface, run `hs-x --help` or read `packages/cli/src/index.ts` (the dispatch switch is the source of truth).
6
- >
7
- > Sections that drift from current implementation are marked inline with `[NOT YET IMPLEMENTED]` or `[PARTIAL]`. As of v1 (2026-06), shipped top-level commands are: `init` (alias `create`), `check`, `validate`, `deploy`, `connect`, `secrets`, `link`, `unlink`, `login`, `whoami`, `logout`, `completion`, `api`, `account`/`accounts`, `audit`, `drift`, `checkpoint`/`logs`, `routing`/`route`/`routes`, `promote`, `rollback`, `dev`, `migrate`, `flags`, `doctor`, `status`, `history`, `update`/`upgrade`. The planned monorepo package layout below does **not** match the actual `packages/` tree — see the workspace for the real list.
8
-
9
- *Working draft. This is the CLI contract for `hs-x` after the SDK/interface review pass. It assumes a monorepo implementation and keeps the CLI as orchestration over reusable packages. The CLI is the human surface, the CI surface, and the coding-agent surface; every command that matters must have stable JSON output and a matching MCP tool.*
10
-
11
- ## What changed in this pass
12
-
13
- This update folds in the root docs, `sdk/README.md`, and the current review findings that were missing from the first CLI draft:
14
-
15
- - first-run setup needs a single `hs-x connect` golden path;
16
- - portal-scoped dev routing requires an initial deployed runtime, so `dev` cannot pretend it is purely local;
17
- - the migration/card path should support Cloudflare-light or HubSpot-only first value where possible;
18
- - deploys use attestation-gated promotion, compatibility classification, rollback, and promotion;
19
- - metadata iteration needs an explicit command path (`dev upload` or `deploy --metadata-only`);
20
- - dev overrides should be exact capability matches in v1, not wildcard by default;
21
- - data-plane drift needs first-class commands (`doctor`, `drift`, `repair`);
22
- - sync needs operational commands (`status`, `run`, `pause`, `resume`, `reset`, `diff`);
23
- - CLI JSON must stay token-efficient for agents.
24
-
25
- ## Design goals
26
-
27
- - One CLI surface for humans, CI, and coding agents.
28
- - Commands are thin orchestration over reusable workspace packages.
29
- - Every project-bound command resolves account, project, environment, and runtime posture the same way.
30
- - Every high-value command has stable `--json` output and predictable exit codes.
31
- - Interactive prompts are optional; CI and agents can pass flags or environment variables instead.
32
- - The CLI understands monorepos without forcing every package to be an HS-X app.
33
- - Every command that mutates HubSpot, Cloudflare, or rollout state prints a plan in human mode and emits a structured plan in JSON mode.
34
- - The CLI is honest about external prerequisites: HubSpot developer/test portal, Cloudflare account, Workers Paid primitives, and Cloudflare DNS/custom-domain requirements.
35
-
36
- ## Monorepo package layout
37
-
38
- > **[PLANNED — does not match current `packages/` tree]** The actual workspace uses a smaller set of packages (`checkpoint`, `codegen`, `control-plane`, `design`, `e2e`, `email`, `fixtures`, `hubspot-cli`, `hubspot-client`, `hubspot-mock`, `mcp`, `runtime`, `sdk`, `studio`, `types`, `validator`). The breakdown below reflects the target decomposition, not what exists today.
39
-
40
- The CLI package owns command registration, option parsing, prompts, and rendering. It does not own the core logic.
41
-
42
- ```text
43
- packages/
44
- cli/ # `hs-x` binary; command registration, prompts, rendering
45
- common/ # ids, diagnostics, errors, result envelopes, exit codes
46
- config/ # workspace/project discovery, project links, account resolution
47
- sdk/ # public `@hs-x/sdk` authoring API
48
- compiler/ # source discovery, capability graph, hsmeta generation
49
- validator/ # `hs-x validate` rules and autofix engine
50
- types-generator/ # `hs-x types` implementation
51
- refs/ # generated @hs-x/refs support
52
- deployer/ # deploy state machine, attestation-gated promotion, rollback
53
- dev-server/ # dev loop, tunnel, override registration, wrangler/local-dev-lib orchestration
54
- hubspot/ # @hubspot/local-dev-lib wrapper and HubSpot API adapters
55
- cloudflare/ # Cloudflare account checks, route/domain checks, credential lease use
56
- control-client/ # typed control-plane API client
57
- observability/ # Checkpoint logs, AE/D1/R2 readers, live tail rendering
58
- sync-engine/ # sync operational controls exposed by `hs-x sync ...`
59
- migration/ # legacy app/card migration and rollout scaffolding
60
- ```
61
-
62
- The same packages should be usable by the MCP server, tests, Studio export/deploy flows, scripts in CI, and future programmatic APIs.
63
-
64
- ## Workspace and project discovery
65
-
66
- A monorepo can contain several HS-X apps plus shared packages.
67
-
68
- ```text
69
- repo/
70
- hsx.workspace.json
71
- packages/shared/
72
- apps/deal-enricher/
73
- hsx.config.ts
74
- hsx.account.json
75
- apps/support-copilot/
76
- hsx.config.ts
77
- hsx.account.json
78
- ```
79
-
80
- `hsx.workspace.json`:
81
-
82
- ```ts
83
- export interface HsxWorkspaceConfig {
84
- version: 1;
85
- projects: HsxWorkspaceProjectRef[];
86
- defaultProject?: string;
87
- }
88
-
89
- export interface HsxWorkspaceProjectRef {
90
- /** Stable local name used by `--project`. */
91
- name: string;
92
- /** Path relative to the workspace root. */
93
- path: string;
94
- /** Optional default environment for project-bound commands. */
95
- defaultEnv?: HsxEnvironmentName;
96
- }
97
- ```
98
-
99
- Example:
100
-
101
- ```json
102
- {
103
- "version": 1,
104
- "defaultProject": "deal-enricher",
105
- "projects": [
106
- { "name": "deal-enricher", "path": "apps/deal-enricher", "defaultEnv": "staging" },
107
- { "name": "support-copilot", "path": "apps/support-copilot" }
108
- ]
109
- }
110
- ```
111
-
112
- If `hsx.workspace.json` is absent, the resolver walks upward from `cwd` until it finds `hsx.config.ts`.
113
-
114
- ### Resolver rules
115
-
116
- Project-bound commands resolve the project in this order:
117
-
118
- 1. `--project <name-or-path>`
119
- 2. `HSX_PROJECT`
120
- 3. nearest parent containing `hsx.config.ts`
121
- 4. workspace `defaultProject`
122
- 5. fail with a list of known projects
123
-
124
- Project-bound commands include:
125
-
126
- ```text
127
- dev, deploy, promote, rollback, types, validate, rollout, logs,
128
- sync, upgrade, check, doctor, drift, checkpoint, repair, optimize
129
- ```
130
-
131
- Account resolution is project-bound once `hsx.account.json` exists. A globally active account is only a fallback for unlinked project creation/linking. If a user passes `--account` that conflicts with the linked project account, the CLI fails closed.
132
-
133
- ```ts
134
- export interface ResolvedProjectContext {
135
- workspaceRoot: string;
136
- projectRoot: string;
137
- projectName: string;
138
- configPath: string;
139
- link?: ProjectLinkFile;
140
- account: ResolvedAccount;
141
- environment: HsxEnvironmentName;
142
- runtimePosture: RuntimePosture;
143
- invokedFrom: string;
144
- }
145
-
146
- export type RuntimePosture = "byo-cloudflare" | "managed-cloudflare" | "hubspot-only";
147
- ```
148
-
149
- `hubspot-only` is for Cloudflare-light paths such as App Card migration or UI-only projects that do not need an external backend. If a command requires Workers, DOs, KV, D1, Queues, or routes, it upgrades the posture requirement and explains why.
150
-
151
- ## Local account/project link
152
-
153
- `hsx.account.json` is repository-local state, not app config. It is the link file written by `hs-x connect` and read by project-bound commands.
154
-
155
- ```ts
156
- export interface ProjectLinkFile {
157
- version: 1;
158
- accountId: string;
159
- accountSlug: string;
160
- projectId: string;
161
- projectSlug: string;
162
- hubspotAppId?: string;
163
- defaultEnvironment: HsxEnvironmentName;
164
- runtimePosture: RuntimePosture;
165
- environments: Record<HsxEnvironmentName, LinkedEnvironment>;
166
- linkedAt: string;
167
- }
168
-
169
- export type HsxEnvironmentName = "development" | "staging" | "production" | (string & {});
170
-
171
- export interface LinkedEnvironment {
172
- environmentId: string;
173
- hubspotProjectId?: string;
174
- cloudflareAccountId?: string;
175
- routeBaseUrl?: string;
176
- activeDeployId?: string;
177
- lastManifestHash?: string;
178
- }
179
- ```
180
-
181
- Human output starts with a resolved context header. JSON output includes the same context in `context`.
182
-
183
- ```text
184
- Account: client-a
185
- Project: deal-enricher
186
- Environment: staging
187
- Runtime: BYO Cloudflare
188
- Cloudflare: Client A Production CF
189
- Active deploy: deploy_abc123 / rev_green
190
- ```
191
-
192
- ## Shared command contract
193
-
194
- All commands build a `CommandContext` before doing work.
195
-
196
- ```ts
197
- export interface CommandContext {
198
- mode: "human" | "json";
199
- cwd: string;
200
- project?: ResolvedProjectContext;
201
- account?: ResolvedAccount;
202
- auth: AuthSession;
203
- io: CommandIO;
204
- control: ControlPlaneClient;
205
- clock: Clock;
206
- }
207
-
208
- export interface CommandIO {
209
- stdout(message: string): void;
210
- stderr(message: string): void;
211
- prompt<T>(request: PromptRequest<T>): Promise<T>;
212
- spinner<T>(label: string, task: () => Promise<T>): Promise<T>;
213
- }
214
- ```
215
-
216
- Commands return a typed result. The renderer converts the result to human text or JSON.
217
-
218
- ```ts
219
- export interface CommandResult<T = unknown> {
220
- ok: true;
221
- command: string;
222
- context: ResultContext;
223
- data: T;
224
- warnings?: HsxDiagnostic[];
225
- }
226
-
227
- export interface CommandFailure {
228
- ok: false;
229
- command: string;
230
- context?: Partial<ResultContext>;
231
- error: HsxErrorEnvelope;
232
- diagnostics?: HsxDiagnostic[];
233
- }
234
-
235
- export interface ResultContext {
236
- account?: { id: string; slug: string; name: string };
237
- project?: { id?: string; slug?: string; name: string; root: string };
238
- environment?: string;
239
- runtimePosture?: RuntimePosture;
240
- }
241
- ```
242
-
243
- JSON mode writes exactly one JSON document to stdout. Progress logs go to stderr or are suppressed.
244
-
245
- ## Global flags and environment variables
246
-
247
- ```text
248
- --project <name-or-path> Select a project in a monorepo.
249
- --account <slug-or-id> Select an HS-X account; project-bound commands fail if this conflicts with the linked project.
250
- --env <name> Select an environment; defaults from project link or workspace.
251
- --json Emit stable machine-readable JSON.
252
- --quiet, -q Suppress progress output without changing output format.
253
- --no-telemetry Disable CLI telemetry for this invocation.
254
- --no-update-check Disable the non-blocking version hint for this invocation.
255
- --no-color Disable ANSI output.
256
- --yes Accept safe prompts; never accepts breaking/semantic deploy changes.
257
- --non-interactive Disable prompts; missing required values become errors.
258
- --profile <name> Select local auth profile.
259
- --cwd <path> Run as if invoked from another directory; useful for agents/CI.
260
- ```
261
-
262
- Environment equivalents:
263
-
264
- ```text
265
- HSX_PROJECT
266
- HSX_ACCOUNT_ID
267
- HSX_ENV
268
- HSX_PROFILE
269
- HSX_TOKEN # CI/service auth
270
- HSX_HUBSPOT_PAK # HubSpot developer personal access key for CLI/local-dev-lib operations
271
- HSX_HUBSPOT_DEVELOPER_API_KEY
272
- # HubSpot developer API key for hapikey endpoints only
273
- HSX_HUBSPOT_APP_ID # HubSpot app id for app-scoped OAuth client secret setup
274
- HSX_HUBSPOT_CLIENT_ID # HubSpot OAuth client id from the app's projects page
275
- HSX_HUBSPOT_CLIENT_SECRET # HubSpot OAuth client secret from the app's projects page
276
- HSX_TELEMETRY_DISABLED=1
277
- HSX_TELEMETRY_OPTOUT=1
278
- HSX_UPDATE_CHECK_DISABLED=1
279
- HSX_NON_INTERACTIVE=1
280
- NO_COLOR=1
281
- ```
282
-
283
- Local state follows XDG paths by default:
284
-
285
- ```text
286
- $XDG_CONFIG_HOME/hs-x/config.json # connected accounts
287
- $XDG_CONFIG_HOME/hs-x/cloudflare-oauth.json # Cloudflare OAuth refresh tokens
288
- $XDG_STATE_HOME/hs-x/history.jsonl # command history
289
- $XDG_CACHE_HOME/hs-x/update-check.json # self-update check cache
290
- ```
291
-
292
- `HSX_STORE_PATH`, `HSX_CLOUDFLARE_OAUTH_STORE_PATH`,
293
- `HSX_HISTORY_PATH`, and `HSX_UPDATE_CHECK_CACHE_PATH` override the
294
- defaults for tests and local development.
295
-
296
- ## CLI telemetry
297
-
298
- The CLI sends one redacted command-completion event to the HS-X control plane
299
- after each packaged command run. The event includes command name, optional
300
- subcommand, CLI version, output mode, OS, architecture, runtime, CI boolean,
301
- exit code, and duration. It does not include args, paths, account ids, project
302
- ids, portal ids, env vars, request/response bodies, stack traces, or credential
303
- material. Delivery is best-effort, short-timeout, and never changes command
304
- behavior.
305
-
306
- Disable it with `--no-telemetry`, `HSX_TELEMETRY_DISABLED=1`,
307
- `HSX_TELEMETRY_OPTOUT=1`, or `DO_NOT_TRACK=1`.
308
-
309
- ## Self-update hints
310
-
311
- Packaged CLI runs check the npm registry at most once every 24 hours and append
312
- a one-line update hint to stderr when a newer version exists. The check is
313
- best-effort, short-timeout, skipped in `--json`, `--json-stream`, and
314
- `--quiet`, and disabled by `--no-update-check`,
315
- `HSX_UPDATE_CHECK_DISABLED=1`, or `NO_UPDATE_NOTIFIER=1`.
316
-
317
- ## HubSpot Developer Auth
318
-
319
- The CLI keeps HubSpot developer-account credentials distinct:
320
-
321
- - **Developer personal access key (PAK):** used as bearer auth for HubSpot CLI / `@hubspot/local-dev-lib` operations such as projects, builds, deploys, migration reads, and card-view swaps. Pass `--pak`, set `HSX_HUBSPOT_PAK`, or run HubSpot's `hs accounts auth` so `hs-x` can discover the PAK from local HubSpot CLI config.
322
- - **Developer API key (HAPIKEY):** used as the `hapikey` query parameter only for endpoints that explicitly document developer API key access. Pass `--developer-api-key` or set `HSX_HUBSPOT_DEVELOPER_API_KEY`.
323
- - **Per-install app tokens:** OAuth/static bearer tokens for installed portal runtime access. These are not developer-account CLI credentials and should not be used with `hs-x api hubspot`.
324
-
325
- For raw calls, `hs-x api hubspot <path>` accepts PAK-only, HAPIKEY-only, or both. When both are present, the request includes bearer auth and the `hapikey` query parameter.
3
+ The command-line tool for HS-X, a type-safe HubSpot app framework that deploys
4
+ to Cloudflare Workers. Installing this package puts the `hs-x` command on your
5
+ PATH: it scaffolds projects, runs the local dev loop, and deploys to your own
6
+ HubSpot developer app and your own Cloudflare account.
326
7
 
327
8
  ## Install
328
9
 
329
10
  ```bash
330
11
  npm i -g @hs-x/cli
12
+ hs-x --version
331
13
  ```
332
14
 
333
- This installs the `hs-x` command on your PATH. Both Node and Bun run it. You can also invoke it without a global install via `bunx @hs-x/cli <command>`.
15
+ Node 20+ and Bun can both run it. The npm package is `@hs-x/cli`; the installed
16
+ command is `hs-x`. If you prefer not to install anything globally, every command
17
+ also works through `bunx @hs-x/cli <command>`.
334
18
 
335
- ## First-run golden path
336
-
337
- This is the unlinked, direct-to-HubSpot path: scaffold a project, install its deps, point it at your HubSpot dev app and a Cloudflare API token, run the local loop, then deploy to your own Cloudflare and HubSpot.
19
+ ## Thirty seconds
338
20
 
339
21
  ```bash
340
- npm i -g @hs-x/cli
341
- hs-x init deal-enricher
342
- cd deal-enricher
22
+ hs-x init deal-tagger # scaffold a project (default template: workflow action)
23
+ cd deal-tagger
343
24
  bun install
344
- # set HubSpot dev-app credentials + a Cloudflare API token
345
- # (HSX_HUBSPOT_CLIENT_ID / HSX_HUBSPOT_CLIENT_SECRET, CLOUDFLARE_API_TOKEN, etc.)
346
- hs-x dev # local development loop
347
- hs-x deploy # deploy to your own Cloudflare + HubSpot
25
+ hs-x connect # bind a HubSpot developer account and a Cloudflare account
26
+ hs-x dev # local dev loop
27
+ hs-x deploy # deploy to your HubSpot app and your Cloudflare account
348
28
  ```
349
29
 
350
- The CLI works direct-to-HubSpot without the HS-X platform. The control plane, dashboard, and billing are opt-in. Deployed apps run in *your* Cloudflare account.
351
-
352
- ### Platform-linked setup with `hs-x connect`
353
-
354
- If you opt into the HS-X platform, `hs-x connect` is the broader golden path: it links the project to an HS-X account and walks HubSpot and Cloudflare setup. It is intentionally broad. It handles or guides:
355
-
356
- - local HS-X auth;
357
- - HS-X account selection;
358
- - HubSpot auth and developer/test portal selection;
359
- - Cloudflare setup based on tier: create or attach an HS-X-managed CF account, connect an existing CF account via OAuth, or accept a pasted API token;
360
- - Workers Paid / required primitive check, with an explicit downgrade path to the no-DO subset when possible;
361
- - `permittedUrls.fetch` strategy selection: custom domain on Cloudflare DNS, explicit `*.workers.dev` URLs with the refactor caveat, or deferred development-only setup;
362
- - environment creation;
363
- - first stub Worker deploy per declared runtime capability, so portal-scoped dev routing has a deployed interception point;
364
- - test-portal validation to avoid accidentally targeting a production customer portal;
365
- - `hsx.account.json` writing.
30
+ The CLI works direct-to-HubSpot out of the box. You provide the HubSpot and
31
+ Cloudflare credentials, the CLI talks to both platforms directly, and nothing
32
+ requires an HS-X platform account. The control plane, dashboard, and billing are
33
+ opt-in: `hs-x login` authenticates an HS-X account and `hs-x link` attaches an
34
+ already-deployed project to it.
366
35
 
367
- ### Cloudflare-light card migration path
36
+ Deployed Workers run in your Cloudflare account, and the generated
37
+ `.hs-x/alchemy.run.ts` (checked into your repo) describes the Cloudflare
38
+ resources your app owns — the app keeps running even if you stop using HS-X.
368
39
 
369
- For UI-only App Card migration, the CLI should not force Cloudflare before first value.
40
+ ## Commands
370
41
 
371
- ```bash
372
- hs-x migrate cards ./legacy-app --project migrated-cards
373
- hs-x deploy --hubspot-only --env staging
374
- ```
42
+ Grouped the way `hs-x help` prints them.
375
43
 
376
- If a migrated card needs a backend, the CLI explains the upgrade path:
44
+ Start:
377
45
 
378
- ```bash
379
- hs-x connect cloudflare
380
- hs-x deploy --env staging
381
- ```
46
+ | Command | What it does |
47
+ | --- | --- |
48
+ | `hs-x init [name]` | Create a new HS-X project |
49
+ | `hs-x connect` | Link this project to HubSpot and Cloudflare |
50
+ | `hs-x doctor` | Diagnose local setup |
51
+ | `hs-x check` | Validate project source |
382
52
 
383
- This path exists to keep the migration wedge honest: modern App Card preview/export/deploy first, Cloudflare only when runtime hosting is actually needed.
53
+ Develop:
384
54
 
385
- ## Account and project commands
55
+ | Command | What it does |
56
+ | --- | --- |
57
+ | `hs-x dev` | Start the local dev server |
58
+ | `hs-x api <path>` | Call HubSpot or the HS-X control plane |
59
+ | `hs-x flags` | Manage feature flags |
60
+ | `hs-x migrate` | Migrate legacy HubSpot apps |
386
61
 
387
- ### `hs-x login` / `hs-x logout`
62
+ Deploy:
388
63
 
389
- > **Shipped as `hs-x login` / `hs-x logout` (no `auth` prefix).**
64
+ | Command | What it does |
65
+ | --- | --- |
66
+ | `hs-x deploy` | Deploy the project |
67
+ | `hs-x promote` | Promote a deploy to an environment |
68
+ | `hs-x rollback` | Roll back an environment |
69
+ | `hs-x routes` | Show active deploy routes |
390
70
 
391
- ```text
392
- hs-x login [--profile <name>] [--json]
393
- hs-x logout [--profile <name>]
394
- ```
71
+ Observe:
395
72
 
396
- ### `hs-x accounts`
73
+ | Command | What it does |
74
+ | --- | --- |
75
+ | `hs-x status` | Show project health |
76
+ | `hs-x logs` | Stream runtime and deploy telemetry |
77
+ | `hs-x drift` | Show project drift |
78
+ | `hs-x audit list` | List redacted account audit rows |
79
+ | `hs-x history` | Show recent command runs |
397
80
 
398
- ```text
399
- hs-x accounts list [--json]
400
- hs-x accounts current [--json]
401
- hs-x accounts switch <account>
402
- ```
81
+ Account:
403
82
 
404
- ### `hs-x init`
83
+ | Command | What it does |
84
+ | --- | --- |
85
+ | `hs-x login` | Authenticate with an HS-X account token |
86
+ | `hs-x accounts` | Manage HS-X accounts |
87
+ | `hs-x whoami` | Show the active HS-X account |
88
+ | `hs-x logout` | Remove the active HS-X account |
89
+ | `hs-x link` | Attach this project to your HS-X account |
90
+ | `hs-x unlink` | Clear the session; keep local project state |
405
91
 
406
- Scaffolds a new HS-X project in a new directory named after the project. `hs-x create` is an accepted alias.
92
+ Config:
407
93
 
408
- ```text
409
- hs-x init [name] [--type workflow-action|sync-source|empty] [--app-name <name>] [--yes] [--no-install] [--force] [--cwd <dir>] [--quiet] [--json]
410
- ```
411
-
412
- Flags:
413
-
414
- - `--name <name>` — project directory and config name (a positional argument is also accepted).
415
- - `--type <type>` — app type: `workflow-action` (default), `sync-source`, or `empty`. `--template <id>` is also accepted (`deals-tagger` is the workflow-action template).
416
- - `--app-name <name>` — HubSpot app display name; defaults from the project name. `--display-name` is an accepted alias.
417
- - `--yes`, `-y` — accept defaults: install dependencies and skip prompts.
418
- - `--no-install` — skip the dependency install step.
419
- - `--force` — overwrite an existing `hsx.config.ts` at the target.
420
- - `--cwd <dir>` — run as if invoked from `<dir>`.
421
- - `--quiet`, `-q` — suppress progress output.
422
- - `--json` — emit a JSON result.
423
-
424
- A scaffold writes `package.json`, `tsconfig.json`, `.gitignore`, `hsx.config.ts`, and one or more `src/workers/*.ts` files. The generated `package.json` depends on `@hs-x/sdk` and `@hs-x/runtime` from npm (no `workspace:*`, no per-project `hs-x` devDependency — the CLI is installed globally). The `.gitignore` ignores `.hs-x/*` except `.hs-x/alchemy.run.ts`, which is tracked as the leaveability artifact.
425
-
426
- When invoked without `--type` in an interactive terminal, `init` prompts for the project name, the template, the HubSpot app name, and whether to install dependencies. In `--json` or non-interactive mode it falls back to defaults (project name `hsx-app`, the workflow-action template) and skips prompts.
427
-
428
- The JSON result has the shape:
429
-
430
- ```json
431
- {
432
- "ok": true,
433
- "command": "init",
434
- "project": {
435
- "name": "deal-enricher",
436
- "appName": "Deal Enricher",
437
- "root": "/abs/path/deal-enricher",
438
- "template": "deals-tagger",
439
- "installed": true
440
- }
441
- }
442
- ```
443
-
444
- ### `hs-x connect`
445
-
446
- Golden-path setup command.
447
-
448
- ```text
449
- hs-x connect [--project <name-or-path>] [--account <account>] [--hubspot-portal <portalId>] [--cloudflare-account <id>] [--runtime byo-cloudflare|managed-cloudflare|hubspot-only] [--env development] [--non-interactive]
450
- ```
451
-
452
- ```ts
453
- export interface ConnectOptions {
454
- projectSelector?: string;
455
- account?: string;
456
- hubspotPortalId?: string;
457
- cloudflareAccountId?: string;
458
- runtimePosture?: RuntimePosture;
459
- environment?: HsxEnvironmentName;
460
- provisionDevRuntime: boolean;
461
- }
462
-
463
- export interface ConnectResult {
464
- projectLinked: boolean;
465
- account: { id: string; slug: string };
466
- hubspot: { portalId?: string; appId?: string; projectId?: string };
467
- cloudflare?: { accountId: string; workersPaidRequired: boolean; routeBaseUrl?: string };
468
- environment: HsxEnvironmentName;
469
- devRuntimeReady: boolean;
470
- nextSteps: string[];
471
- }
472
- ```
473
-
474
- Subcommands for explicit setup:
475
-
476
- ```text
477
- hs-x connect hubspot --account-id <id> --developer-account-id <id> --display-name <name> [--pak <token>] [--developer-api-key <key>] # advanced alias for HubSpot-only setup
478
- hs-x connect cloudflare [--account <id>] [--domain <domain>] [--accept-workers-dev]
479
- hs-x connect domain [--domain <domain>] [--zone <zoneId>] # [NOT YET IMPLEMENTED]
480
- ```
481
-
482
- ### `hs-x secrets`
483
-
484
- Stores scoped app secrets that deploy copies into the tenant Cloudflare runtime. HubSpot install OAuth secrets are scoped by HS-X account, project, environment, and HubSpot app id so one Cloudflare account can host multiple HubSpot apps safely.
485
-
486
- ```text
487
- hs-x secrets hubspot-oauth set --account-id <id> --project-id <id> --hubspot-app-id <id> --client-id <id> --client-secret <secret> [--env production|staging|dev]
488
- ```
489
-
490
- The secret is never echoed. On deploy, HS-X also provisions or reuses the scoped tenant install KV namespace and writes `HSX_TOKEN_KEY` as a Worker secret for encrypted install-token storage. The same operation is available through the control-plane API, dashboard, and MCP.
491
-
492
- ### `hs-x project` — [NOT YET IMPLEMENTED]
493
-
494
- > No `project` command exists today. Linking is currently done through `hs-x connect`; project status flows live under other commands.
495
-
496
- ```text
497
- hs-x project link [--project <name-or-path>] --account <account> [--remote-project <slug-or-id>] [--env <name>]
498
- hs-x project status [--project <name>] [--json]
499
- hs-x project unlink [--project <name>]
500
- ```
501
-
502
- ```ts
503
- export interface ProjectStatusResult {
504
- linked: boolean;
505
- account?: { id: string; slug: string; cloudflareAccountId?: string };
506
- project?: { id: string; slug: string; root: string };
507
- environments: Array<{
508
- name: HsxEnvironmentName;
509
- hubspotProjectId?: string;
510
- routeBaseUrl?: string;
511
- activeDeployId?: string;
512
- driftState?: DriftState;
513
- }>;
514
- activeDevOverrides: DevOverrideSummary[];
515
- }
516
- ```
94
+ | Command | What it does |
95
+ | --- | --- |
96
+ | `hs-x secrets` | Store app secrets for runtime deploys |
97
+ | `hs-x completion <shell>` | Generate shell completion (bash, zsh, fish) |
98
+ | `hs-x update` | Check for a newer CLI release |
99
+ | `hs-x help` | Show command help |
517
100
 
518
- ## Development commands
101
+ Run `hs-x <command> --help` for command-specific flags. Most commands accept
102
+ `--json` for machine-readable output, and prompts can be skipped in scripts
103
+ with `--yes` where a command confirms before changing anything.
519
104
 
520
- ### `hs-x dev`
105
+ ## Connecting accounts
521
106
 
522
- Runs the local development loop. Default mode is portal-scoped proxy through the deployed runtime.
107
+ `hs-x connect` walks HubSpot and Cloudflare setup back to back; each side is
108
+ also available on its own.
523
109
 
524
- ```text
525
- hs-x dev --portal <portalId> [--project <name>] [--env development] [--capability <id> ...] [--ttl 2h] [--quick-tunnel] [--dev-deploy] [--mock] [--allow-production-portal]
526
- ```
527
-
528
- ```ts
529
- export interface DevOptions {
530
- portalId?: string;
531
- capabilities: string[]; // exact capability ids in v1
532
- ttlSeconds: number;
533
- tunnel: "named" | "quick" | "dev-deploy" | "mock";
534
- watch: boolean;
535
- openHubSpotPreview: boolean;
536
- allowProductionPortal: boolean;
537
- }
538
-
539
- export interface DevSessionResult {
540
- sessionId: string;
541
- portalId?: string;
542
- environment: HsxEnvironmentName;
543
- tunnelUrl?: string;
544
- overrides: DevOverrideSummary[];
545
- processes: Array<{ name: "wrangler" | "cloudflared" | "hubspot"; pid?: number; status: string }>;
546
- warnings: HsxDiagnostic[];
547
- }
548
- ```
549
-
550
- Rules:
551
-
552
- - v1 dev overrides are exact `(portalId, capabilityId, developerSession)` matches. No wildcard `*` override by default.
553
- - Production/customer portals are blocked unless `--allow-production-portal` is passed and the command is interactive or explicitly confirmed.
554
- - `hs-x dev` checks that a deployed dev runtime exists. If not, it offers to run the dev-runtime setup portion of `hs-x connect`.
555
- - DO-heavy paths warn in tunnel mode and recommend `--dev-deploy`.
556
- - UI extensions hot-reload through HubSpot's `hs project dev` / `@hubspot/local-dev-lib` underneath.
557
-
558
- Related commands:
559
-
560
- ```text
561
- hs-x dev status [--project <name>] [--portal <portalId>] [--json]
562
- hs-x dev stop --portal <portalId> [--capability <id>] # [NOT YET IMPLEMENTED]
563
- hs-x dev cleanup [--expired] [--all]
564
- hs-x dev doctor [--json] # use top-level `hs-x doctor`
565
- hs-x dev upload [--metadata-only] # [NOT YET IMPLEMENTED]
566
- ```
567
-
568
- `hs-x dev upload` is the metadata iteration escape hatch for workflow action inputs/outputs/labels, agent metadata, card metadata, and other `*-hsmeta.json` changes. Handler iteration should not require metadata upload; metadata iteration does.
569
-
570
- ## Deploy, promotion, and rollback
571
-
572
- ### `hs-x deploy`
573
-
574
- Runs the two-cloud deploy state machine. A deploy records a candidate, uploads Cloudflare and HubSpot assets through local execution, waits for runtime attestation to report healthy drift, then promotes the deploy in control-plane state. Full router-based revisioned routing is deferred.
575
-
576
- ```text
577
- hs-x deploy [--project <name>] [--env staging|production] [--plan] [--diff] [--resume <deployId>] [--breaking] [--semantic-change] [--expected-rps <n>] [--metadata-only] [--hubspot-only] [--json]
578
- ```
579
-
580
- > **[PARTIAL]** The shipped flag set diverges from this prototype. Actual flags include `--cloudflare-deploy`, `--cloudflare-dry-run`, `--hubspot-upload-only`, `--promote-when-healthy`, `--record-local`, `--local-control-plane`, `--portal-schema-fixture`, `--no-manage-schema`, `--json`. Run `hs-x deploy --help` for the current set; the flags marked `--breaking`, `--semantic-change`, `--resume`, `--expected-rps`, `--metadata-only` are design-only.
581
-
582
- ```ts
583
- export interface DeployOptions {
584
- environment: HsxEnvironmentName;
585
- planOnly: boolean;
586
- diffOnly: boolean;
587
- resumeDeployId?: string;
588
- allowBreaking: boolean;
589
- allowSemanticChange: boolean;
590
- expectedRps?: number;
591
- metadataOnly: boolean;
592
- hubspotOnly: boolean;
593
- }
594
-
595
- export interface DeployPlanResult {
596
- deployId: string;
597
- planOnly: true;
598
- compatibility: CompatibilityReport;
599
- stateMachine: DeployStepPreview[];
600
- resources: PlannedResourceChange[];
601
- routes: PlannedRouteChange[];
602
- hubspotAssets: PlannedHubSpotAssetChange[];
603
- estimatedCost: CostEstimate;
604
- drift?: DriftReport;
605
- requiredConfirmations: RequiredConfirmation[];
606
- }
607
-
608
- export interface DeployResult {
609
- deployId: string;
610
- environment: HsxEnvironmentName;
611
- activeDeployId: string;
612
- previousDeployId?: string;
613
- steps: DeployStepResult[];
614
- manifestPath: string;
615
- manifestHash: string;
616
- dashboardUrl?: string;
617
- }
618
- ```
619
-
620
- Deploy state machine:
621
-
622
- ```text
623
- plan_requested
624
- plan_received
625
- preflight
626
- apply_cloudflare
627
- deploy_worker
628
- upload_hubspot
629
- verify_hubspot
630
- record
631
- attest
632
- promote_when_healthy
633
- reconcile_flags
634
- active
635
- ```
636
-
637
- Compatibility classes:
638
-
639
- ```ts
640
- export type CompatibilityClass =
641
- | "additive-safe"
642
- | "compatible-with-shim"
643
- | "breaking"
644
- | "semantic";
645
- ```
646
-
647
- Rules:
648
-
649
- - `--yes` does not accept `breaking` or `semantic` changes. Those require explicit flags.
650
- - `--metadata-only` updates HubSpot metadata without changing Worker code when safe.
651
- - `--hubspot-only` is allowed only for projects/capabilities with no Cloudflare runtime needs.
652
- - `--plan` includes cost shape, Workers Paid requirements, streaming polling cost, custom-domain/permitted URL warnings, and drift status.
653
- - If drift is detected, deploy offers `hs-x repair` first rather than silently deploying on top of drift.
654
- - `--portal-schema-fixture <path>` runs the schema planner against deterministic observed schema JSON.
655
- - `--portal-schema-live` reads the target portal schema through the HubSpot developer client; `--apply-schema` applies only the managed `WILL CREATE` / `WILL ALTER` actions from that plan.
656
-
657
- ### `hs-x promote`
658
-
659
- Promotes an already-built/tested artifact between environments.
660
-
661
- ```text
662
- hs-x promote <from-env> <to-env> [--project <name>] [--deploy <deployId>] [--plan] [--json]
663
- ```
664
-
665
- ```ts
666
- export interface PromoteResult {
667
- fromEnvironment: HsxEnvironmentName;
668
- toEnvironment: HsxEnvironmentName;
669
- deployId: string;
670
- promotedDeployId: string;
671
- hubspotUploadRequired: boolean;
672
- steps: DeployStepResult[];
673
- }
674
- ```
675
-
676
- `promote` should preserve the artifact identity. It is not a rebuild unless the target environment requires environment-specific generated metadata.
677
-
678
- ### `hs-x rollback`
679
-
680
- ```text
681
- hs-x rollback [--project <name>] --env production [--to <deployId>] [--plan] [--json]
682
- ```
683
-
684
- ```ts
685
- export interface RollbackResult {
686
- environment: HsxEnvironmentName;
687
- fromDeployId: string;
688
- toDeployId: string;
689
- activeDeployId: string;
690
- warnings: HsxDiagnostic[];
691
- }
692
- ```
693
-
694
- Rollback promotes a prior recorded deploy through the control plane and marks the former active deploy rolled back. Rollbacks past one deploy boundary warn about card/backend contract drift and require explicit force.
695
-
696
- ## Drift and runtime health commands
697
-
698
- BYO Cloudflare means runtime resources can drift. These commands are project-bound.
699
-
700
- ```text
701
- hs-x doctor [--project <name>] [--env <name>] [--json]
702
- hs-x drift [--project <name>] [--env <name>] [--json]
703
- hs-x logs [--project <name>] [--env <name>] [--json]
704
- hs-x repair [--project <name>] [--env <name>] [--plan] [--json] # [NOT YET IMPLEMENTED]
705
- ```
706
-
707
- ```ts
708
- export type DriftState =
709
- | "healthy"
710
- | "drifted"
711
- | "credential_revoked"
712
- | "resource_missing"
713
- | "unknown_code"
714
- | "billing_untrusted"
715
- | "unknown";
716
-
717
- export interface DriftReport {
718
- state: DriftState;
719
- manifestHash?: string;
720
- observedManifestHash?: string;
721
- resources: DriftResourceFinding[];
722
- billingTrust: "trusted" | "ae-only" | "manual-review";
723
- }
724
- ```
725
-
726
- `doctor` runs checks and summarizes. `drift` shows resource-by-resource differences. `checkpoint` reads the project Checkpoint telemetry view: aggregate invocation counts, latency percentiles, recent failures, sampled successes, and the backing observability sources. `repair` offers to recreate missing HS-X resources or remove drift-introduced resources, with explicit confirmation for destructive changes.
727
-
728
- ## Type generation — [NOT YET IMPLEMENTED]
729
-
730
- > No `hs-x types` command exists today. Type generation runs implicitly via `hs-x check`/`hs-x validate`. The standalone command is planned.
731
-
732
- ```text
733
- hs-x types [--project <name>] [--portal <portalId>] [--out .hs-x/types.ts] [--watch] [--json]
734
- ```
735
-
736
- ```ts
737
- export interface TypesOptions {
738
- portalId?: string;
739
- outputPath: string;
740
- includeCustomObjects: boolean;
741
- includeAppObjects: boolean;
742
- }
743
-
744
- export interface TypesResult {
745
- outputPath: string;
746
- moduleAlias: "@hs-x/types";
747
- schemaVersion: string;
748
- objects: Array<{ name: string; properties: number; source: "standard" | "portal" | "app" }>;
749
- diagnostics: HsxDiagnostic[];
750
- }
751
- ```
752
-
753
- Rules:
754
-
755
- - Marketplace apps get strict standard/app object/event types and permissive custom-property access.
756
- - Private/single-portal apps can generate portal-exact types.
757
- - `hs-x upgrade` runs `hs-x types` automatically.
758
-
759
- ## Validation
760
-
761
- ```text
762
- hs-x validate [--project <name>] [--fix] [--format text|json|sarif] [--json]
763
- ```
764
-
765
- ```ts
766
- export interface ValidateOptions {
767
- fix: boolean;
768
- format: "text" | "json" | "sarif";
769
- rules?: string[];
770
- }
771
-
772
- export interface ValidateResult {
773
- status: "passed" | "passed-with-warnings" | "failed";
774
- diagnostics: HsxDiagnostic[];
775
- fixesApplied: FixSummary[];
776
- }
777
- ```
778
-
779
- Important rule groups:
780
-
781
- - declared scopes vs SDK-mediated calls and annotated raw calls;
782
- - direct HubSpot `fetch` warnings/errors;
783
- - explicit `agent.expose` required;
784
- - workflow resolved-input validity;
785
- - batched delivery invalid in no-DO profile;
786
- - card backend `hubspot.fetch` envelope;
787
- - streaming backend polling/cost warnings;
788
- - sync identity/tombstone/retry requirements;
789
- - generated ref alias (`@hs-x/refs`) usage;
790
- - dev override exact-match rules;
791
- - marketplace certification checks under `hs-x check`.
792
-
793
- ## Logs and observability
794
-
795
- ```text
796
- hs-x logs [--project <name>] [--env <name>] [--capability <id>] [--portal <portalId>] [--since 15m] [--follow] [--json]
797
- hs-x runs failures [--project <name>] [--env <name>] [--since 1h] [--json] # [NOT YET IMPLEMENTED]
798
- hs-x runs sampled [--project <name>] [--env <name>] [--since 15m] [--json] # [NOT YET IMPLEMENTED]
799
- hs-x metrics [--project <name>] [--env <name>] [--since 24h] [--json] # [NOT YET IMPLEMENTED]
800
- ```
801
-
802
- > `hs-x logs` is aliased to `hs-x checkpoint` today. `runs` and `metrics` top-level commands are planned.
803
-
804
- ```ts
805
- export interface LogRecord {
806
- timestamp: string;
807
- level: "debug" | "info" | "warn" | "error";
808
- deployId?: string;
809
- capabilityId?: string;
810
- portalId?: string;
811
- runId?: string;
812
- fingerprint?: string;
813
- message: string;
814
- fields?: Record<string, unknown>;
815
- }
816
- ```
817
-
818
- UI/CLI labels must be honest:
819
-
820
- - invocation totals: complete AE-backed aggregates;
821
- - recent failures: exemplar rows plus complete occurrence counts;
822
- - trace exemplars: persisted examples for a fingerprint;
823
- - recent sampled successes: sampled, labeled as sampled;
824
- - live tail: Cloudflare Workers Logs tail.
825
-
826
- Avoid promising “last 50 runs” unless the result is actually complete.
827
-
828
- ## Rollout and flags
829
-
830
- ```text
831
- hs-x flags list [--project <name>] [--env <name>] # [NOT YET IMPLEMENTED]
832
- hs-x flags set <flag> --portal <portalId> --value on|off|<json>
833
- hs-x flags get <flag> --portal <portalId>
834
- hs-x flags promote <flag> --percent <n> # [NOT YET IMPLEMENTED]
835
- hs-x flags history <flag> [--json] # [NOT YET IMPLEMENTED]
836
- ```
837
-
838
- > Today only `flags set` and `flags get` ship; `list`, `promote`, `history` are planned. The command is also aliased as `hs-x rollout`.
839
-
840
- ```ts
841
- export interface RolloutFlagState {
842
- flagKey: string;
843
- environment: HsxEnvironmentName;
844
- defaultValue: unknown;
845
- portalOverrides: number;
846
- rollout?: { percent: number; salt: string };
847
- hubspotCardFlagState?: "synced" | "pending" | "error";
848
- }
849
- ```
850
-
851
- Flag updates are per-portal rollout controls, not emergency global kill switches unless a stronger non-KV path is implemented.
852
-
853
- ## Migration commands
854
-
855
- ```text
856
- hs-x migrate inspect <path-or-app-id> [--json]
857
- hs-x migrate cards <path-or-app-id> [--project <name>] [--no-cloudflare] [--json]
858
- hs-x migrate app <path-or-app-id> [--project <name>] [--json]
859
- hs-x migrate report [--project <name>] [--json]
860
- ```
861
-
862
- ```ts
863
- export interface MigrationInspectResult {
864
- source: string;
865
- detected: Array<"classic-crm-card" | "project-card" | "serverless-function" | "workflow-action" | "settings-page">;
866
- automatable: MigrationFinding[];
867
- manualWork: MigrationFinding[];
868
- cloudflareRequired: boolean;
869
- }
870
-
871
- export interface MigrationResult {
872
- projectRoot: string;
873
- createdFiles: string[];
874
- rolloutFlags: string[];
875
- cloudflareRequired: boolean;
876
- nextSteps: string[];
877
- }
878
- ```
879
-
880
- The App Card migration wedge should work before the full runtime where possible. If a card backend or function migration requires Cloudflare, the CLI should say exactly which migrated feature caused that requirement.
881
-
882
- ## Sync operations — [NOT YET IMPLEMENTED]
883
-
884
- > No `hs-x sync` command exists today; the entire subcommand surface below is planned.
885
-
886
- Sync commands are operational controls over deployed sync capabilities.
887
-
888
- ```text
889
- hs-x sync list [--project <name>] [--env <name>] [--json]
890
- hs-x sync status <syncId> --portal <portalId> [--json]
891
- hs-x sync run <syncId> --portal <portalId> [--cursor <cursor>] [--json]
892
- hs-x sync pause <syncId> --portal <portalId>
893
- hs-x sync resume <syncId> --portal <portalId>
894
- hs-x sync reset <syncId> --portal <portalId> [--confirm]
895
- hs-x sync diff <syncId> --portal <portalId> [--limit <n>] [--json]
896
- hs-x sync deadletter <syncId> --portal <portalId> [--json]
897
- hs-x sync retry <syncId> --portal <portalId> <record-id> [--json]
898
- ```
899
-
900
- ```ts
901
- export interface SyncStatusResult {
902
- syncId: string;
903
- portalId: string;
904
- state: "idle" | "running" | "paused" | "error" | "backing_off";
905
- cursor?: unknown;
906
- lastRun?: string;
907
- recordsProcessed?: number;
908
- deadLetterCount: number;
909
- mappingVersion: string;
910
- }
911
- ```
912
-
913
- ## Certification and optimization
914
-
915
- ```text
916
- hs-x check [--project <name>] [--env <name>] [--marketplace] [--json]
917
- hs-x optimize [--project <name>] [--plan] [--write] [--json] # [NOT YET IMPLEMENTED]
918
- ```
919
-
920
- `check` audits marketplace readiness and should clearly separate automatable findings from human/process requirements such as demo videos, security questionnaire, verified domain, active install count, and review responses.
921
-
922
- `optimize` is reviewable, never silent. It can flag N+1 HubSpot calls, unbatched writes, missing cursors, over-broad scopes, and expensive streaming/card patterns, then offer a diff under `--write`.
923
-
924
- ## Upgrade — [PARTIAL]
925
-
926
- > `hs-x update` / `hs-x upgrade` ships as a self-update helper for the CLI binary itself. The platform-version migration flow described below (with `--to`, `--plan`) is **not yet implemented**.
927
-
928
- ```text
929
- hs-x upgrade [--project <name>] --to 2026.09 [--plan] [--json]
930
- ```
931
-
932
- Upgrade changes SDK channel, platform version, generated types, generated HubSpot metadata, and migration rules. It should emit a compatibility plan before writing.
933
-
934
- ## Diagnostics and exit codes
935
-
936
- ```ts
937
- export interface HsxDiagnostic {
938
- code: string;
939
- severity: "info" | "warning" | "error";
940
- message: string;
941
- file?: string;
942
- line?: number;
943
- column?: number;
944
- rule?: string;
945
- fix?: { title: string; safe: boolean };
946
- }
947
- ```
948
-
949
- Exit codes:
950
-
951
- ```text
952
- 0 success
953
- 1 generic failure
954
- 2 validation failed
955
- 3 authentication or authorization failed
956
- 4 project/account/environment resolution failed
957
- 5 deploy plan refused because of breaking or semantic changes
958
- 6 external dependency unavailable (HubSpot, Cloudflare, tunnel, DNS)
959
- 7 non-interactive command needed input
960
- 8 command interrupted; resume may be available
961
- 9 runtime drift detected; repair required before requested operation
962
- 10 command unsupported for current runtime posture
110
+ ```bash
111
+ hs-x connect hubspot # HubSpot developer account: PAK, or discovered from `hs accounts auth`
112
+ hs-x connect cloudflare # Cloudflare: browser OAuth flow, or --auth-method api-token
963
113
  ```
964
114
 
965
- ## JSON output example
115
+ For HubSpot, the CLI discovers credentials from the official HubSpot CLI config
116
+ when you have run `hs accounts auth`, or accepts a developer personal access key
117
+ via `--pak`. For Cloudflare, an interactive run uses a browser
118
+ authorization-code flow by default; `--auth-method api-token` accepts a scoped
119
+ API token you create in the Cloudflare dashboard and paste back, which also
120
+ suits CI and headless shells.
966
121
 
967
- `hs-x deploy --plan --json` emits one JSON document.
122
+ ## Migrating an existing app
968
123
 
969
- ```json
970
- {
971
- "ok": true,
972
- "command": "deploy",
973
- "context": {
974
- "account": { "id": "acct_123", "slug": "client-a", "name": "Client A" },
975
- "project": { "id": "proj_456", "slug": "deal-enricher", "name": "deal-enricher", "root": "/repo/apps/deal-enricher" },
976
- "environment": "staging",
977
- "runtimePosture": "byo-cloudflare"
978
- },
979
- "data": {
980
- "deployId": "deploy_abc123",
981
- "planOnly": true,
982
- "compatibility": {
983
- "additiveSafe": 4,
984
- "compatibleWithShim": [],
985
- "breaking": [],
986
- "semantic": []
987
- },
988
- "stateMachine": [
989
- { "name": "plan_received", "status": "pending" },
990
- { "name": "apply_cloudflare", "status": "pending" },
991
- { "name": "deploy_worker", "status": "pending" },
992
- { "name": "upload_hubspot", "status": "pending" },
993
- { "name": "record", "status": "pending" },
994
- { "name": "attest", "status": "pending" },
995
- { "name": "promote_when_healthy", "status": "pending" }
996
- ],
997
- "estimatedCost": {
998
- "currency": "USD",
999
- "monthly": 12.4,
1000
- "notes": ["streaming backend ai-summary: estimate assumes 100 concurrent streams at 400ms polling"]
1001
- },
1002
- "drift": { "state": "healthy" }
1003
- },
1004
- "warnings": []
1005
- }
1006
- ```
124
+ `hs-x migrate run` takes a legacy HubSpot app (platform versions 2023.2, 2025.1,
125
+ or 2025.2) and generates a 2026.03 HS-X project as a net-new duplicate you can
126
+ test with `hs-x dev`. Your original app keeps running untouched until you cut
127
+ over. See `hs-x migrate --help` for the inspect, report, cards, and swap-cards
128
+ subcommands.
1007
129
 
1008
- ## First implementation slice
130
+ ## Docs
1009
131
 
1010
- 1. Create `packages/common` with result envelopes, diagnostics, ids, error envelopes, and exit codes.
1011
- 2. Create `packages/config` with workspace discovery, project discovery, `hsx.account.json` parsing, runtime posture, and account conflict handling.
1012
- 3. Create `packages/cli` with global flag parsing, JSON/human renderers, and placeholder commands.
1013
- 4. Implement `hs-x project status --json` first because it exercises monorepo/project/account resolution without HubSpot or Cloudflare mutation.
1014
- 5. Implement `hs-x connect --plan` / dry-run checks next: auth state, HubSpot presence, Cloudflare prerequisite detection, route/domain choice, and what would be provisioned.
1015
- 6. Implement `hs-x validate --json` with config/workspace/SDK declaration checks.
1016
- 7. Implement `hs-x deploy --plan --json` using compiler manifests before mutating HubSpot or Cloudflare.
1017
- 8. Implement Cloudflare-light `hs-x migrate cards` and `hs-x deploy --hubspot-only` if the migration artifacts allow it.
1018
- 9. Wire real deploy execution, dev routing, drift/repair, and sync operations package by package behind the same command contracts.
132
+ Guides and reference live at [hs-x.dev/docs](https://hs-x.dev/docs). The
133
+ getting-started guide goes from an empty directory to a live HubSpot card in
134
+ about fifteen minutes.
1019
135
 
1020
- ## Open CLI questions
136
+ ## License
1021
137
 
1022
- - Managed-CF tier: does it ship early or remain an explicit future runtime posture?
1023
- - `hs-x dev upload` vs `hs-x deploy --metadata-only`: should both exist or should one alias the other?
1024
- - Exact first-run dev runtime: does `hs-x connect` always deploy a shell Worker, or only when a capability requires runtime hosting?
1025
- - HubSpot-only migration: which legacy card shapes can truly deploy without Cloudflare?
1026
- - Fresh-clone generated refs: should `hs-x init` install a stub `@hs-x/refs` package or rely on generated `.hs-x/refs` after first `dev`/`deploy`?
1027
- - How much of `hs-x check` belongs in `validate` vs a marketplace-specific command?
138
+ Apache-2.0