@hs-x/cli 0.1.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 +1001 -0
- package/dist/account-store.d.ts +51 -0
- package/dist/account-store.d.ts.map +1 -0
- package/dist/account-store.js +138 -0
- package/dist/account-store.js.map +1 -0
- package/dist/bin/hs-x.d.ts +3 -0
- package/dist/bin/hs-x.d.ts.map +1 -0
- package/dist/bin/hs-x.js +47 -0
- package/dist/bin/hs-x.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +595 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli-error.d.ts +36 -0
- package/dist/cli-error.d.ts.map +1 -0
- package/dist/cli-error.js +40 -0
- package/dist/cli-error.js.map +1 -0
- package/dist/cloudflare-auth.d.ts +25 -0
- package/dist/cloudflare-auth.d.ts.map +1 -0
- package/dist/cloudflare-auth.js +251 -0
- package/dist/cloudflare-auth.js.map +1 -0
- package/dist/cloudflare-kv.d.ts +23 -0
- package/dist/cloudflare-kv.d.ts.map +1 -0
- package/dist/cloudflare-kv.js +101 -0
- package/dist/cloudflare-kv.js.map +1 -0
- package/dist/cloudflare-oauth-store.d.ts +16 -0
- package/dist/cloudflare-oauth-store.d.ts.map +1 -0
- package/dist/cloudflare-oauth-store.js +80 -0
- package/dist/cloudflare-oauth-store.js.map +1 -0
- package/dist/cloudflare-oauth.d.ts +82 -0
- package/dist/cloudflare-oauth.d.ts.map +1 -0
- package/dist/cloudflare-oauth.js +336 -0
- package/dist/cloudflare-oauth.js.map +1 -0
- package/dist/cloudflare-pointer.d.ts +13 -0
- package/dist/cloudflare-pointer.d.ts.map +1 -0
- package/dist/cloudflare-pointer.js +46 -0
- package/dist/cloudflare-pointer.js.map +1 -0
- package/dist/command-history.d.ts +7 -0
- package/dist/command-history.d.ts.map +1 -0
- package/dist/command-history.js +34 -0
- package/dist/command-history.js.map +1 -0
- package/dist/commands/account.d.ts +7 -0
- package/dist/commands/account.d.ts.map +1 -0
- package/dist/commands/account.js +315 -0
- package/dist/commands/account.js.map +1 -0
- package/dist/commands/api.d.ts +36 -0
- package/dist/commands/api.d.ts.map +1 -0
- package/dist/commands/api.js +521 -0
- package/dist/commands/api.js.map +1 -0
- package/dist/commands/completion.d.ts +7 -0
- package/dist/commands/completion.d.ts.map +1 -0
- package/dist/commands/completion.js +121 -0
- package/dist/commands/completion.js.map +1 -0
- package/dist/commands/connect.d.ts +7 -0
- package/dist/commands/connect.d.ts.map +1 -0
- package/dist/commands/connect.js +1123 -0
- package/dist/commands/connect.js.map +1 -0
- package/dist/commands/control-plane-read.d.ts +22 -0
- package/dist/commands/control-plane-read.d.ts.map +1 -0
- package/dist/commands/control-plane-read.js +350 -0
- package/dist/commands/control-plane-read.js.map +1 -0
- package/dist/commands/deploy-promote.d.ts +14 -0
- package/dist/commands/deploy-promote.d.ts.map +1 -0
- package/dist/commands/deploy-promote.js +105 -0
- package/dist/commands/deploy-promote.js.map +1 -0
- package/dist/commands/deploy.d.ts +18 -0
- package/dist/commands/deploy.d.ts.map +1 -0
- package/dist/commands/deploy.js +2764 -0
- package/dist/commands/deploy.js.map +1 -0
- package/dist/commands/dev.d.ts +7 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +913 -0
- package/dist/commands/dev.js.map +1 -0
- package/dist/commands/doctor.d.ts +8 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +258 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/flags.d.ts +22 -0
- package/dist/commands/flags.d.ts.map +1 -0
- package/dist/commands/flags.js +185 -0
- package/dist/commands/flags.js.map +1 -0
- package/dist/commands/help-command.d.ts +13 -0
- package/dist/commands/help-command.d.ts.map +1 -0
- package/dist/commands/help-command.js +482 -0
- package/dist/commands/help-command.js.map +1 -0
- package/dist/commands/history.d.ts +6 -0
- package/dist/commands/history.d.ts.map +1 -0
- package/dist/commands/history.js +42 -0
- package/dist/commands/history.js.map +1 -0
- package/dist/commands/init.d.ts +8 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +233 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/link.d.ts +26 -0
- package/dist/commands/link.d.ts.map +1 -0
- package/dist/commands/link.js +441 -0
- package/dist/commands/link.js.map +1 -0
- package/dist/commands/login.d.ts +8 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +381 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/migrate.d.ts +8 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +258 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/rollback.d.ts +21 -0
- package/dist/commands/rollback.d.ts.map +1 -0
- package/dist/commands/rollback.js +301 -0
- package/dist/commands/rollback.js.map +1 -0
- package/dist/commands/secrets.d.ts +7 -0
- package/dist/commands/secrets.d.ts.map +1 -0
- package/dist/commands/secrets.js +230 -0
- package/dist/commands/secrets.js.map +1 -0
- package/dist/commands/status.d.ts +7 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +241 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/unlink.d.ts +21 -0
- package/dist/commands/unlink.d.ts.map +1 -0
- package/dist/commands/unlink.js +83 -0
- package/dist/commands/unlink.js.map +1 -0
- package/dist/commands/update.d.ts +11 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +154 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/validate.d.ts +9 -0
- package/dist/commands/validate.d.ts.map +1 -0
- package/dist/commands/validate.js +39 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/config.d.ts +12 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +64 -0
- package/dist/config.js.map +1 -0
- package/dist/constants.d.ts +4 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +4 -0
- package/dist/constants.js.map +1 -0
- package/dist/control-plane-fetch.d.ts +34 -0
- package/dist/control-plane-fetch.d.ts.map +1 -0
- package/dist/control-plane-fetch.js +73 -0
- package/dist/control-plane-fetch.js.map +1 -0
- package/dist/control-plane-loader.d.ts +16 -0
- package/dist/control-plane-loader.d.ts.map +1 -0
- package/dist/control-plane-loader.js +24 -0
- package/dist/control-plane-loader.js.map +1 -0
- package/dist/dev/compat-shim.d.ts +40 -0
- package/dist/dev/compat-shim.d.ts.map +1 -0
- package/dist/dev/compat-shim.js +65 -0
- package/dist/dev/compat-shim.js.map +1 -0
- package/dist/dev/event-bus.d.ts +27 -0
- package/dist/dev/event-bus.d.ts.map +1 -0
- package/dist/dev/event-bus.js +32 -0
- package/dist/dev/event-bus.js.map +1 -0
- package/dist/dev/log-server.d.ts +52 -0
- package/dist/dev/log-server.d.ts.map +1 -0
- package/dist/dev/log-server.js +216 -0
- package/dist/dev/log-server.js.map +1 -0
- package/dist/dev/session-manager.d.ts +33 -0
- package/dist/dev/session-manager.d.ts.map +1 -0
- package/dist/dev/session-manager.js +132 -0
- package/dist/dev/session-manager.js.map +1 -0
- package/dist/dev/stream-renderer.d.ts +22 -0
- package/dist/dev/stream-renderer.d.ts.map +1 -0
- package/dist/dev/stream-renderer.js +65 -0
- package/dist/dev/stream-renderer.js.map +1 -0
- package/dist/dev/tunnel.d.ts +40 -0
- package/dist/dev/tunnel.d.ts.map +1 -0
- package/dist/dev/tunnel.js +139 -0
- package/dist/dev/tunnel.js.map +1 -0
- package/dist/effect-http.d.ts +10 -0
- package/dist/effect-http.d.ts.map +1 -0
- package/dist/effect-http.js +38 -0
- package/dist/effect-http.js.map +1 -0
- package/dist/errors-registry.d.ts +11 -0
- package/dist/errors-registry.d.ts.map +1 -0
- package/dist/errors-registry.js +554 -0
- package/dist/errors-registry.js.map +1 -0
- package/dist/errors.d.ts +58 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +30 -0
- package/dist/errors.js.map +1 -0
- package/dist/help.d.ts +6 -0
- package/dist/help.d.ts.map +1 -0
- package/dist/help.js +100 -0
- package/dist/help.js.map +1 -0
- package/dist/history.d.ts +15 -0
- package/dist/history.d.ts.map +1 -0
- package/dist/history.js +69 -0
- package/dist/history.js.map +1 -0
- package/dist/hubspot-auth.d.ts +53 -0
- package/dist/hubspot-auth.d.ts.map +1 -0
- package/dist/hubspot-auth.js +301 -0
- package/dist/hubspot-auth.js.map +1 -0
- package/dist/hubspot-developer-client.d.ts +10 -0
- package/dist/hubspot-developer-client.d.ts.map +1 -0
- package/dist/hubspot-developer-client.js +212 -0
- package/dist/hubspot-developer-client.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/init/templates.d.ts +18 -0
- package/dist/init/templates.d.ts.map +1 -0
- package/dist/init/templates.js +239 -0
- package/dist/init/templates.js.map +1 -0
- package/dist/load-env.d.ts +16 -0
- package/dist/load-env.d.ts.map +1 -0
- package/dist/load-env.js +69 -0
- package/dist/load-env.js.map +1 -0
- package/dist/machine-id.d.ts +3 -0
- package/dist/machine-id.d.ts.map +1 -0
- package/dist/machine-id.js +41 -0
- package/dist/machine-id.js.map +1 -0
- package/dist/paths.d.ts +4 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +19 -0
- package/dist/paths.js.map +1 -0
- package/dist/prompt.d.ts +43 -0
- package/dist/prompt.d.ts.map +1 -0
- package/dist/prompt.js +379 -0
- package/dist/prompt.js.map +1 -0
- package/dist/reporter/human.d.ts +28 -0
- package/dist/reporter/human.d.ts.map +1 -0
- package/dist/reporter/human.js +126 -0
- package/dist/reporter/human.js.map +1 -0
- package/dist/reporter/index.d.ts +14 -0
- package/dist/reporter/index.d.ts.map +1 -0
- package/dist/reporter/index.js +37 -0
- package/dist/reporter/index.js.map +1 -0
- package/dist/reporter/json.d.ts +43 -0
- package/dist/reporter/json.d.ts.map +1 -0
- package/dist/reporter/json.js +146 -0
- package/dist/reporter/json.js.map +1 -0
- package/dist/reporter/style.d.ts +34 -0
- package/dist/reporter/style.d.ts.map +1 -0
- package/dist/reporter/style.js +97 -0
- package/dist/reporter/style.js.map +1 -0
- package/dist/reporter/types.d.ts +41 -0
- package/dist/reporter/types.d.ts.map +1 -0
- package/dist/reporter/types.js +2 -0
- package/dist/reporter/types.js.map +1 -0
- package/dist/result.d.ts +4 -0
- package/dist/result.d.ts.map +1 -0
- package/dist/result.js +2 -0
- package/dist/result.js.map +1 -0
- package/dist/services/account-store.d.ts +31 -0
- package/dist/services/account-store.d.ts.map +1 -0
- package/dist/services/account-store.js +135 -0
- package/dist/services/account-store.js.map +1 -0
- package/dist/services/app-paths.d.ts +25 -0
- package/dist/services/app-paths.d.ts.map +1 -0
- package/dist/services/app-paths.js +34 -0
- package/dist/services/app-paths.js.map +1 -0
- package/dist/services/cloudflare-auth.d.ts +83 -0
- package/dist/services/cloudflare-auth.d.ts.map +1 -0
- package/dist/services/cloudflare-auth.js +30 -0
- package/dist/services/cloudflare-auth.js.map +1 -0
- package/dist/services/cloudflare-kv.d.ts +45 -0
- package/dist/services/cloudflare-kv.d.ts.map +1 -0
- package/dist/services/cloudflare-kv.js +151 -0
- package/dist/services/cloudflare-kv.js.map +1 -0
- package/dist/services/command-history.d.ts +29 -0
- package/dist/services/command-history.d.ts.map +1 -0
- package/dist/services/command-history.js +62 -0
- package/dist/services/command-history.js.map +1 -0
- package/dist/services/control-plane.d.ts +32 -0
- package/dist/services/control-plane.d.ts.map +1 -0
- package/dist/services/control-plane.js +57 -0
- package/dist/services/control-plane.js.map +1 -0
- package/dist/services/env-loader.d.ts +18 -0
- package/dist/services/env-loader.d.ts.map +1 -0
- package/dist/services/env-loader.js +34 -0
- package/dist/services/env-loader.js.map +1 -0
- package/dist/services/http.d.ts +19 -0
- package/dist/services/http.d.ts.map +1 -0
- package/dist/services/http.js +9 -0
- package/dist/services/http.js.map +1 -0
- package/dist/services/live.d.ts +16 -0
- package/dist/services/live.d.ts.map +1 -0
- package/dist/services/live.js +26 -0
- package/dist/services/live.js.map +1 -0
- package/dist/services/machine-id.d.ts +18 -0
- package/dist/services/machine-id.d.ts.map +1 -0
- package/dist/services/machine-id.js +39 -0
- package/dist/services/machine-id.js.map +1 -0
- package/dist/services/reporter.d.ts +55 -0
- package/dist/services/reporter.d.ts.map +1 -0
- package/dist/services/reporter.js +49 -0
- package/dist/services/reporter.js.map +1 -0
- package/dist/state-store.d.ts +39 -0
- package/dist/state-store.d.ts.map +1 -0
- package/dist/state-store.js +89 -0
- package/dist/state-store.js.map +1 -0
- package/dist/telemetry.d.ts +13 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +129 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/tenant-state.d.ts +69 -0
- package/dist/tenant-state.d.ts.map +1 -0
- package/dist/tenant-state.js +161 -0
- package/dist/tenant-state.js.map +1 -0
- package/package.json +38 -0
package/README.md
ADDED
|
@@ -0,0 +1,1001 @@
|
|
|
1
|
+
# HS-X CLI Interfaces
|
|
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 2026-05-20, shipped top-level commands are: `init`/`create`, `check`, `validate`, `deploy`, `promote`, `connect`, `secrets`, `login`, `whoami`, `logout`, `completion`, `api`, `account`, `audit`, `drift`, `checkpoint`/`logs`, `routing`, `rollback`, `dev`, `migrate`, `rollout`/`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 ADR-009 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`, `stream-relay`, `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.
|
|
326
|
+
|
|
327
|
+
## First-run golden paths
|
|
328
|
+
|
|
329
|
+
### Full runtime path
|
|
330
|
+
|
|
331
|
+
This path creates or links a project, connects HubSpot and Cloudflare, creates a development environment, deploys the first runtime shell needed for portal-scoped dev routing, and then starts local dev.
|
|
332
|
+
|
|
333
|
+
```bash
|
|
334
|
+
npx hs-x create deal-enricher
|
|
335
|
+
cd deal-enricher
|
|
336
|
+
hs-x connect
|
|
337
|
+
hs-x dev --portal 12345
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
`hs-x connect` is intentionally broad. It handles or guides:
|
|
341
|
+
|
|
342
|
+
- local HS-X auth;
|
|
343
|
+
- HS-X account selection;
|
|
344
|
+
- HubSpot auth and developer/test portal selection;
|
|
345
|
+
- 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;
|
|
346
|
+
- Workers Paid / required primitive check, with an explicit downgrade path to the no-DO subset when possible;
|
|
347
|
+
- `permittedUrls.fetch` strategy selection: custom domain on Cloudflare DNS, explicit `*.workers.dev` URLs with the refactor caveat, or deferred development-only setup;
|
|
348
|
+
- environment creation;
|
|
349
|
+
- first stub Worker deploy per declared runtime capability, so portal-scoped dev routing has a deployed interception point;
|
|
350
|
+
- test-portal validation to avoid accidentally targeting a production customer portal;
|
|
351
|
+
- `hsx.account.json` writing.
|
|
352
|
+
|
|
353
|
+
### Cloudflare-light card migration path
|
|
354
|
+
|
|
355
|
+
For UI-only App Card migration, the CLI should not force Cloudflare before first value.
|
|
356
|
+
|
|
357
|
+
```bash
|
|
358
|
+
hs-x migrate cards ./legacy-app --project migrated-cards
|
|
359
|
+
hs-x deploy --hubspot-only --env staging
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
If a migrated card needs a backend, the CLI explains the upgrade path:
|
|
363
|
+
|
|
364
|
+
```bash
|
|
365
|
+
hs-x connect cloudflare
|
|
366
|
+
hs-x deploy --env staging
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
This path exists to keep the migration wedge honest: modern App Card preview/export/deploy first, Cloudflare only when runtime hosting is actually needed.
|
|
370
|
+
|
|
371
|
+
## Account and project commands
|
|
372
|
+
|
|
373
|
+
### `hs-x login` / `hs-x logout`
|
|
374
|
+
|
|
375
|
+
> **Shipped as `hs-x login` / `hs-x logout` (no `auth` prefix).**
|
|
376
|
+
|
|
377
|
+
```text
|
|
378
|
+
hs-x login [--profile <name>] [--json]
|
|
379
|
+
hs-x logout [--profile <name>]
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
### `hs-x accounts`
|
|
383
|
+
|
|
384
|
+
```text
|
|
385
|
+
hs-x accounts list [--json]
|
|
386
|
+
hs-x accounts current [--json]
|
|
387
|
+
hs-x accounts switch <account>
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### `hs-x create`
|
|
391
|
+
|
|
392
|
+
Workspace command. Creates a new project and optionally adds it to `hsx.workspace.json`.
|
|
393
|
+
|
|
394
|
+
```text
|
|
395
|
+
hs-x create <name> [--type workflow-action|sync-source|empty] [--app-name <name>] [--dir <path>] [--account <account>] [--monorepo] [--runtime byo-cloudflare|managed-cloudflare|hubspot-only]
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
```ts
|
|
399
|
+
export interface CreateOptions {
|
|
400
|
+
name: string;
|
|
401
|
+
type: "workflow-action" | "sync-source" | "empty";
|
|
402
|
+
targetDir: string;
|
|
403
|
+
account?: string;
|
|
404
|
+
runtimePosture?: RuntimePosture;
|
|
405
|
+
addToWorkspace: boolean;
|
|
406
|
+
installDeps: boolean;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
export interface CreateResult {
|
|
410
|
+
projectRoot: string;
|
|
411
|
+
packageManager: "pnpm" | "npm" | "yarn" | "bun";
|
|
412
|
+
filesCreated: string[];
|
|
413
|
+
workspaceUpdated: boolean;
|
|
414
|
+
nextSteps: string[];
|
|
415
|
+
}
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
### `hs-x connect`
|
|
419
|
+
|
|
420
|
+
Golden-path setup command.
|
|
421
|
+
|
|
422
|
+
```text
|
|
423
|
+
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]
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
```ts
|
|
427
|
+
export interface ConnectOptions {
|
|
428
|
+
projectSelector?: string;
|
|
429
|
+
account?: string;
|
|
430
|
+
hubspotPortalId?: string;
|
|
431
|
+
cloudflareAccountId?: string;
|
|
432
|
+
runtimePosture?: RuntimePosture;
|
|
433
|
+
environment?: HsxEnvironmentName;
|
|
434
|
+
provisionDevRuntime: boolean;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
export interface ConnectResult {
|
|
438
|
+
projectLinked: boolean;
|
|
439
|
+
account: { id: string; slug: string };
|
|
440
|
+
hubspot: { portalId?: string; appId?: string; projectId?: string };
|
|
441
|
+
cloudflare?: { accountId: string; workersPaidRequired: boolean; routeBaseUrl?: string };
|
|
442
|
+
environment: HsxEnvironmentName;
|
|
443
|
+
devRuntimeReady: boolean;
|
|
444
|
+
nextSteps: string[];
|
|
445
|
+
}
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
Subcommands for explicit setup:
|
|
449
|
+
|
|
450
|
+
```text
|
|
451
|
+
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
|
|
452
|
+
hs-x connect cloudflare [--account <id>] [--domain <domain>] [--accept-workers-dev]
|
|
453
|
+
hs-x connect domain [--domain <domain>] [--zone <zoneId>] # [NOT YET IMPLEMENTED]
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
### `hs-x secrets`
|
|
457
|
+
|
|
458
|
+
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.
|
|
459
|
+
|
|
460
|
+
```text
|
|
461
|
+
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]
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
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.
|
|
465
|
+
|
|
466
|
+
### `hs-x project` — [NOT YET IMPLEMENTED]
|
|
467
|
+
|
|
468
|
+
> No `project` command exists today. Linking is currently done through `hs-x connect`; project status flows live under other commands.
|
|
469
|
+
|
|
470
|
+
```text
|
|
471
|
+
hs-x project link [--project <name-or-path>] --account <account> [--remote-project <slug-or-id>] [--env <name>]
|
|
472
|
+
hs-x project status [--project <name>] [--json]
|
|
473
|
+
hs-x project unlink [--project <name>]
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
```ts
|
|
477
|
+
export interface ProjectStatusResult {
|
|
478
|
+
linked: boolean;
|
|
479
|
+
account?: { id: string; slug: string; cloudflareAccountId?: string };
|
|
480
|
+
project?: { id: string; slug: string; root: string };
|
|
481
|
+
environments: Array<{
|
|
482
|
+
name: HsxEnvironmentName;
|
|
483
|
+
hubspotProjectId?: string;
|
|
484
|
+
routeBaseUrl?: string;
|
|
485
|
+
activeDeployId?: string;
|
|
486
|
+
driftState?: DriftState;
|
|
487
|
+
}>;
|
|
488
|
+
activeDevOverrides: DevOverrideSummary[];
|
|
489
|
+
}
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
## Development commands
|
|
493
|
+
|
|
494
|
+
### `hs-x dev`
|
|
495
|
+
|
|
496
|
+
Runs the local development loop. Default mode is portal-scoped proxy through the deployed runtime.
|
|
497
|
+
|
|
498
|
+
```text
|
|
499
|
+
hs-x dev --portal <portalId> [--project <name>] [--env development] [--capability <id> ...] [--ttl 2h] [--quick-tunnel] [--dev-deploy] [--mock] [--allow-production-portal]
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
```ts
|
|
503
|
+
export interface DevOptions {
|
|
504
|
+
portalId?: string;
|
|
505
|
+
capabilities: string[]; // exact capability ids in v1
|
|
506
|
+
ttlSeconds: number;
|
|
507
|
+
tunnel: "named" | "quick" | "dev-deploy" | "mock";
|
|
508
|
+
watch: boolean;
|
|
509
|
+
openHubSpotPreview: boolean;
|
|
510
|
+
allowProductionPortal: boolean;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
export interface DevSessionResult {
|
|
514
|
+
sessionId: string;
|
|
515
|
+
portalId?: string;
|
|
516
|
+
environment: HsxEnvironmentName;
|
|
517
|
+
tunnelUrl?: string;
|
|
518
|
+
overrides: DevOverrideSummary[];
|
|
519
|
+
processes: Array<{ name: "wrangler" | "cloudflared" | "hubspot"; pid?: number; status: string }>;
|
|
520
|
+
warnings: HsxDiagnostic[];
|
|
521
|
+
}
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
Rules:
|
|
525
|
+
|
|
526
|
+
- v1 dev overrides are exact `(portalId, capabilityId, developerSession)` matches. No wildcard `*` override by default.
|
|
527
|
+
- Production/customer portals are blocked unless `--allow-production-portal` is passed and the command is interactive or explicitly confirmed.
|
|
528
|
+
- `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`.
|
|
529
|
+
- DO-heavy paths warn in tunnel mode and recommend `--dev-deploy`.
|
|
530
|
+
- UI extensions hot-reload through HubSpot's `hs project dev` / `@hubspot/local-dev-lib` underneath.
|
|
531
|
+
|
|
532
|
+
Related commands:
|
|
533
|
+
|
|
534
|
+
```text
|
|
535
|
+
hs-x dev status [--project <name>] [--portal <portalId>] [--json]
|
|
536
|
+
hs-x dev stop --portal <portalId> [--capability <id>] # [NOT YET IMPLEMENTED]
|
|
537
|
+
hs-x dev cleanup [--expired] [--all]
|
|
538
|
+
hs-x dev doctor [--json] # use top-level `hs-x doctor`
|
|
539
|
+
hs-x dev upload [--metadata-only] # [NOT YET IMPLEMENTED]
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
`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.
|
|
543
|
+
|
|
544
|
+
## Deploy, promotion, and rollback
|
|
545
|
+
|
|
546
|
+
### `hs-x deploy`
|
|
547
|
+
|
|
548
|
+
Runs the Phase 1 two-cloud deploy state machine from ADR-009. 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.
|
|
549
|
+
|
|
550
|
+
```text
|
|
551
|
+
hs-x deploy [--project <name>] [--env staging|production] [--plan] [--diff] [--resume <deployId>] [--breaking] [--semantic-change] [--expected-rps <n>] [--metadata-only] [--hubspot-only] [--json]
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
> **[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.
|
|
555
|
+
|
|
556
|
+
```ts
|
|
557
|
+
export interface DeployOptions {
|
|
558
|
+
environment: HsxEnvironmentName;
|
|
559
|
+
planOnly: boolean;
|
|
560
|
+
diffOnly: boolean;
|
|
561
|
+
resumeDeployId?: string;
|
|
562
|
+
allowBreaking: boolean;
|
|
563
|
+
allowSemanticChange: boolean;
|
|
564
|
+
expectedRps?: number;
|
|
565
|
+
metadataOnly: boolean;
|
|
566
|
+
hubspotOnly: boolean;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
export interface DeployPlanResult {
|
|
570
|
+
deployId: string;
|
|
571
|
+
planOnly: true;
|
|
572
|
+
compatibility: CompatibilityReport;
|
|
573
|
+
stateMachine: DeployStepPreview[];
|
|
574
|
+
resources: PlannedResourceChange[];
|
|
575
|
+
routes: PlannedRouteChange[];
|
|
576
|
+
hubspotAssets: PlannedHubSpotAssetChange[];
|
|
577
|
+
estimatedCost: CostEstimate;
|
|
578
|
+
drift?: DriftReport;
|
|
579
|
+
requiredConfirmations: RequiredConfirmation[];
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
export interface DeployResult {
|
|
583
|
+
deployId: string;
|
|
584
|
+
environment: HsxEnvironmentName;
|
|
585
|
+
activeDeployId: string;
|
|
586
|
+
previousDeployId?: string;
|
|
587
|
+
steps: DeployStepResult[];
|
|
588
|
+
manifestPath: string;
|
|
589
|
+
manifestHash: string;
|
|
590
|
+
dashboardUrl?: string;
|
|
591
|
+
}
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
Phase 1 state machine:
|
|
595
|
+
|
|
596
|
+
```text
|
|
597
|
+
plan_requested
|
|
598
|
+
plan_received
|
|
599
|
+
preflight
|
|
600
|
+
apply_cloudflare
|
|
601
|
+
deploy_worker
|
|
602
|
+
upload_hubspot
|
|
603
|
+
verify_hubspot
|
|
604
|
+
record
|
|
605
|
+
attest
|
|
606
|
+
promote_when_healthy
|
|
607
|
+
reconcile_flags
|
|
608
|
+
active
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
Compatibility classes:
|
|
612
|
+
|
|
613
|
+
```ts
|
|
614
|
+
export type CompatibilityClass =
|
|
615
|
+
| "additive-safe"
|
|
616
|
+
| "compatible-with-shim"
|
|
617
|
+
| "breaking"
|
|
618
|
+
| "semantic";
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
Rules:
|
|
622
|
+
|
|
623
|
+
- `--yes` does not accept `breaking` or `semantic` changes. Those require explicit flags.
|
|
624
|
+
- `--metadata-only` updates HubSpot metadata without changing Worker code when safe.
|
|
625
|
+
- `--hubspot-only` is allowed only for projects/capabilities with no Cloudflare runtime needs.
|
|
626
|
+
- `--plan` includes cost shape, Workers Paid requirements, streaming polling cost, custom-domain/permitted URL warnings, and drift status.
|
|
627
|
+
- If drift is detected, deploy offers `hs-x repair` first rather than silently deploying on top of drift.
|
|
628
|
+
- `--portal-schema-fixture <path>` runs the ADR-008 schema planner against deterministic observed schema JSON.
|
|
629
|
+
- `--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.
|
|
630
|
+
|
|
631
|
+
### `hs-x promote`
|
|
632
|
+
|
|
633
|
+
Promotes an already-built/tested artifact between environments.
|
|
634
|
+
|
|
635
|
+
```text
|
|
636
|
+
hs-x promote <from-env> <to-env> [--project <name>] [--deploy <deployId>] [--plan] [--json]
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
```ts
|
|
640
|
+
export interface PromoteResult {
|
|
641
|
+
fromEnvironment: HsxEnvironmentName;
|
|
642
|
+
toEnvironment: HsxEnvironmentName;
|
|
643
|
+
deployId: string;
|
|
644
|
+
promotedDeployId: string;
|
|
645
|
+
hubspotUploadRequired: boolean;
|
|
646
|
+
steps: DeployStepResult[];
|
|
647
|
+
}
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
`promote` should preserve the artifact identity. It is not a rebuild unless the target environment requires environment-specific generated metadata.
|
|
651
|
+
|
|
652
|
+
### `hs-x rollback`
|
|
653
|
+
|
|
654
|
+
```text
|
|
655
|
+
hs-x rollback [--project <name>] --env production [--to <deployId>] [--plan] [--json]
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
```ts
|
|
659
|
+
export interface RollbackResult {
|
|
660
|
+
environment: HsxEnvironmentName;
|
|
661
|
+
fromDeployId: string;
|
|
662
|
+
toDeployId: string;
|
|
663
|
+
activeDeployId: string;
|
|
664
|
+
warnings: HsxDiagnostic[];
|
|
665
|
+
}
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
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.
|
|
669
|
+
|
|
670
|
+
## Drift and runtime health commands
|
|
671
|
+
|
|
672
|
+
BYO Cloudflare means runtime resources can drift. These commands are project-bound.
|
|
673
|
+
|
|
674
|
+
```text
|
|
675
|
+
hs-x doctor [--project <name>] [--env <name>] [--json]
|
|
676
|
+
hs-x drift [--project <name>] [--env <name>] [--json]
|
|
677
|
+
hs-x logs [--project <name>] [--env <name>] [--json]
|
|
678
|
+
hs-x repair [--project <name>] [--env <name>] [--plan] [--json] # [NOT YET IMPLEMENTED]
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
```ts
|
|
682
|
+
export type DriftState =
|
|
683
|
+
| "healthy"
|
|
684
|
+
| "drifted"
|
|
685
|
+
| "credential_revoked"
|
|
686
|
+
| "resource_missing"
|
|
687
|
+
| "unknown_code"
|
|
688
|
+
| "billing_untrusted"
|
|
689
|
+
| "unknown";
|
|
690
|
+
|
|
691
|
+
export interface DriftReport {
|
|
692
|
+
state: DriftState;
|
|
693
|
+
manifestHash?: string;
|
|
694
|
+
observedManifestHash?: string;
|
|
695
|
+
resources: DriftResourceFinding[];
|
|
696
|
+
billingTrust: "trusted" | "ae-only" | "manual-review";
|
|
697
|
+
}
|
|
698
|
+
```
|
|
699
|
+
|
|
700
|
+
`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.
|
|
701
|
+
|
|
702
|
+
## Type generation — [NOT YET IMPLEMENTED]
|
|
703
|
+
|
|
704
|
+
> No `hs-x types` command exists today. Type generation runs implicitly via `hs-x check`/`hs-x validate`. The standalone command is planned.
|
|
705
|
+
|
|
706
|
+
```text
|
|
707
|
+
hs-x types [--project <name>] [--portal <portalId>] [--out .hs-x/types.ts] [--watch] [--json]
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
```ts
|
|
711
|
+
export interface TypesOptions {
|
|
712
|
+
portalId?: string;
|
|
713
|
+
outputPath: string;
|
|
714
|
+
includeCustomObjects: boolean;
|
|
715
|
+
includeAppObjects: boolean;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
export interface TypesResult {
|
|
719
|
+
outputPath: string;
|
|
720
|
+
moduleAlias: "@hs-x/types";
|
|
721
|
+
schemaVersion: string;
|
|
722
|
+
objects: Array<{ name: string; properties: number; source: "standard" | "portal" | "app" }>;
|
|
723
|
+
diagnostics: HsxDiagnostic[];
|
|
724
|
+
}
|
|
725
|
+
```
|
|
726
|
+
|
|
727
|
+
Rules:
|
|
728
|
+
|
|
729
|
+
- Marketplace apps get strict standard/app object/event types and permissive custom-property access.
|
|
730
|
+
- Private/single-portal apps can generate portal-exact types.
|
|
731
|
+
- `hs-x upgrade` runs `hs-x types` automatically.
|
|
732
|
+
|
|
733
|
+
## Validation
|
|
734
|
+
|
|
735
|
+
```text
|
|
736
|
+
hs-x validate [--project <name>] [--fix] [--format text|json|sarif] [--json]
|
|
737
|
+
```
|
|
738
|
+
|
|
739
|
+
```ts
|
|
740
|
+
export interface ValidateOptions {
|
|
741
|
+
fix: boolean;
|
|
742
|
+
format: "text" | "json" | "sarif";
|
|
743
|
+
rules?: string[];
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
export interface ValidateResult {
|
|
747
|
+
status: "passed" | "passed-with-warnings" | "failed";
|
|
748
|
+
diagnostics: HsxDiagnostic[];
|
|
749
|
+
fixesApplied: FixSummary[];
|
|
750
|
+
}
|
|
751
|
+
```
|
|
752
|
+
|
|
753
|
+
Important rule groups:
|
|
754
|
+
|
|
755
|
+
- declared scopes vs SDK-mediated calls and annotated raw calls;
|
|
756
|
+
- direct HubSpot `fetch` warnings/errors;
|
|
757
|
+
- explicit `agent.expose` required;
|
|
758
|
+
- workflow resolved-input validity;
|
|
759
|
+
- batched delivery invalid in no-DO profile;
|
|
760
|
+
- card backend `hubspot.fetch` envelope;
|
|
761
|
+
- streaming backend polling/cost warnings;
|
|
762
|
+
- sync identity/tombstone/retry requirements;
|
|
763
|
+
- generated ref alias (`@hs-x/refs`) usage;
|
|
764
|
+
- dev override exact-match rules;
|
|
765
|
+
- marketplace certification checks under `hs-x check`.
|
|
766
|
+
|
|
767
|
+
## Logs and observability
|
|
768
|
+
|
|
769
|
+
```text
|
|
770
|
+
hs-x logs [--project <name>] [--env <name>] [--capability <id>] [--portal <portalId>] [--since 15m] [--follow] [--json]
|
|
771
|
+
hs-x runs failures [--project <name>] [--env <name>] [--since 1h] [--json] # [NOT YET IMPLEMENTED]
|
|
772
|
+
hs-x runs sampled [--project <name>] [--env <name>] [--since 15m] [--json] # [NOT YET IMPLEMENTED]
|
|
773
|
+
hs-x metrics [--project <name>] [--env <name>] [--since 24h] [--json] # [NOT YET IMPLEMENTED]
|
|
774
|
+
```
|
|
775
|
+
|
|
776
|
+
> `hs-x logs` is aliased to `hs-x checkpoint` today. `runs` and `metrics` top-level commands are planned.
|
|
777
|
+
|
|
778
|
+
```ts
|
|
779
|
+
export interface LogRecord {
|
|
780
|
+
timestamp: string;
|
|
781
|
+
level: "debug" | "info" | "warn" | "error";
|
|
782
|
+
deployId?: string;
|
|
783
|
+
capabilityId?: string;
|
|
784
|
+
portalId?: string;
|
|
785
|
+
runId?: string;
|
|
786
|
+
fingerprint?: string;
|
|
787
|
+
message: string;
|
|
788
|
+
fields?: Record<string, unknown>;
|
|
789
|
+
}
|
|
790
|
+
```
|
|
791
|
+
|
|
792
|
+
UI/CLI labels must be honest:
|
|
793
|
+
|
|
794
|
+
- invocation totals: complete AE-backed aggregates;
|
|
795
|
+
- recent failures: exemplar rows plus complete occurrence counts;
|
|
796
|
+
- trace exemplars: persisted examples for a fingerprint;
|
|
797
|
+
- recent sampled successes: sampled, labeled as sampled;
|
|
798
|
+
- live tail: Cloudflare Workers Logs tail.
|
|
799
|
+
|
|
800
|
+
Avoid promising “last 50 runs” unless the result is actually complete.
|
|
801
|
+
|
|
802
|
+
## Rollout and flags
|
|
803
|
+
|
|
804
|
+
```text
|
|
805
|
+
hs-x flags list [--project <name>] [--env <name>] # [NOT YET IMPLEMENTED]
|
|
806
|
+
hs-x flags set <flag> --portal <portalId> --value on|off|<json>
|
|
807
|
+
hs-x flags get <flag> --portal <portalId>
|
|
808
|
+
hs-x flags promote <flag> --percent <n> # [NOT YET IMPLEMENTED]
|
|
809
|
+
hs-x flags history <flag> [--json] # [NOT YET IMPLEMENTED]
|
|
810
|
+
```
|
|
811
|
+
|
|
812
|
+
> Today only `flags set` and `flags get` ship; `list`, `promote`, `history` are planned. The command is also aliased as `hs-x rollout`.
|
|
813
|
+
|
|
814
|
+
```ts
|
|
815
|
+
export interface RolloutFlagState {
|
|
816
|
+
flagKey: string;
|
|
817
|
+
environment: HsxEnvironmentName;
|
|
818
|
+
defaultValue: unknown;
|
|
819
|
+
portalOverrides: number;
|
|
820
|
+
rollout?: { percent: number; salt: string };
|
|
821
|
+
hubspotCardFlagState?: "synced" | "pending" | "error";
|
|
822
|
+
}
|
|
823
|
+
```
|
|
824
|
+
|
|
825
|
+
Flag updates are per-portal rollout controls, not emergency global kill switches unless a stronger non-KV path is implemented.
|
|
826
|
+
|
|
827
|
+
## Migration commands
|
|
828
|
+
|
|
829
|
+
```text
|
|
830
|
+
hs-x migrate inspect <path-or-app-id> [--json]
|
|
831
|
+
hs-x migrate cards <path-or-app-id> [--project <name>] [--no-cloudflare] [--json]
|
|
832
|
+
hs-x migrate app <path-or-app-id> [--project <name>] [--json]
|
|
833
|
+
hs-x migrate report [--project <name>] [--json]
|
|
834
|
+
```
|
|
835
|
+
|
|
836
|
+
```ts
|
|
837
|
+
export interface MigrationInspectResult {
|
|
838
|
+
source: string;
|
|
839
|
+
detected: Array<"classic-crm-card" | "project-card" | "serverless-function" | "workflow-action" | "settings-page">;
|
|
840
|
+
automatable: MigrationFinding[];
|
|
841
|
+
manualWork: MigrationFinding[];
|
|
842
|
+
cloudflareRequired: boolean;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
export interface MigrationResult {
|
|
846
|
+
projectRoot: string;
|
|
847
|
+
createdFiles: string[];
|
|
848
|
+
rolloutFlags: string[];
|
|
849
|
+
cloudflareRequired: boolean;
|
|
850
|
+
nextSteps: string[];
|
|
851
|
+
}
|
|
852
|
+
```
|
|
853
|
+
|
|
854
|
+
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.
|
|
855
|
+
|
|
856
|
+
## Sync operations — [NOT YET IMPLEMENTED]
|
|
857
|
+
|
|
858
|
+
> No `hs-x sync` command exists today; the entire subcommand surface below is planned.
|
|
859
|
+
|
|
860
|
+
Sync commands are operational controls over deployed sync capabilities.
|
|
861
|
+
|
|
862
|
+
```text
|
|
863
|
+
hs-x sync list [--project <name>] [--env <name>] [--json]
|
|
864
|
+
hs-x sync status <syncId> --portal <portalId> [--json]
|
|
865
|
+
hs-x sync run <syncId> --portal <portalId> [--cursor <cursor>] [--json]
|
|
866
|
+
hs-x sync pause <syncId> --portal <portalId>
|
|
867
|
+
hs-x sync resume <syncId> --portal <portalId>
|
|
868
|
+
hs-x sync reset <syncId> --portal <portalId> [--confirm]
|
|
869
|
+
hs-x sync diff <syncId> --portal <portalId> [--limit <n>] [--json]
|
|
870
|
+
hs-x sync deadletter <syncId> --portal <portalId> [--json]
|
|
871
|
+
hs-x sync retry <syncId> --portal <portalId> <record-id> [--json]
|
|
872
|
+
```
|
|
873
|
+
|
|
874
|
+
```ts
|
|
875
|
+
export interface SyncStatusResult {
|
|
876
|
+
syncId: string;
|
|
877
|
+
portalId: string;
|
|
878
|
+
state: "idle" | "running" | "paused" | "error" | "backing_off";
|
|
879
|
+
cursor?: unknown;
|
|
880
|
+
lastRun?: string;
|
|
881
|
+
recordsProcessed?: number;
|
|
882
|
+
deadLetterCount: number;
|
|
883
|
+
mappingVersion: string;
|
|
884
|
+
}
|
|
885
|
+
```
|
|
886
|
+
|
|
887
|
+
## Certification and optimization
|
|
888
|
+
|
|
889
|
+
```text
|
|
890
|
+
hs-x check [--project <name>] [--env <name>] [--marketplace] [--json]
|
|
891
|
+
hs-x optimize [--project <name>] [--plan] [--write] [--json] # [NOT YET IMPLEMENTED]
|
|
892
|
+
```
|
|
893
|
+
|
|
894
|
+
`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.
|
|
895
|
+
|
|
896
|
+
`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`.
|
|
897
|
+
|
|
898
|
+
## Upgrade — [PARTIAL]
|
|
899
|
+
|
|
900
|
+
> `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**.
|
|
901
|
+
|
|
902
|
+
```text
|
|
903
|
+
hs-x upgrade [--project <name>] --to 2026.09 [--plan] [--json]
|
|
904
|
+
```
|
|
905
|
+
|
|
906
|
+
Upgrade changes SDK channel, platform version, generated types, generated HubSpot metadata, and migration rules. It should emit a compatibility plan before writing.
|
|
907
|
+
|
|
908
|
+
## Diagnostics and exit codes
|
|
909
|
+
|
|
910
|
+
```ts
|
|
911
|
+
export interface HsxDiagnostic {
|
|
912
|
+
code: string;
|
|
913
|
+
severity: "info" | "warning" | "error";
|
|
914
|
+
message: string;
|
|
915
|
+
file?: string;
|
|
916
|
+
line?: number;
|
|
917
|
+
column?: number;
|
|
918
|
+
rule?: string;
|
|
919
|
+
fix?: { title: string; safe: boolean };
|
|
920
|
+
}
|
|
921
|
+
```
|
|
922
|
+
|
|
923
|
+
Exit codes:
|
|
924
|
+
|
|
925
|
+
```text
|
|
926
|
+
0 success
|
|
927
|
+
1 generic failure
|
|
928
|
+
2 validation failed
|
|
929
|
+
3 authentication or authorization failed
|
|
930
|
+
4 project/account/environment resolution failed
|
|
931
|
+
5 deploy plan refused because of breaking or semantic changes
|
|
932
|
+
6 external dependency unavailable (HubSpot, Cloudflare, tunnel, DNS)
|
|
933
|
+
7 non-interactive command needed input
|
|
934
|
+
8 command interrupted; resume may be available
|
|
935
|
+
9 runtime drift detected; repair required before requested operation
|
|
936
|
+
10 command unsupported for current runtime posture
|
|
937
|
+
```
|
|
938
|
+
|
|
939
|
+
## JSON output example
|
|
940
|
+
|
|
941
|
+
`hs-x deploy --plan --json` emits one JSON document.
|
|
942
|
+
|
|
943
|
+
```json
|
|
944
|
+
{
|
|
945
|
+
"ok": true,
|
|
946
|
+
"command": "deploy",
|
|
947
|
+
"context": {
|
|
948
|
+
"account": { "id": "acct_123", "slug": "client-a", "name": "Client A" },
|
|
949
|
+
"project": { "id": "proj_456", "slug": "deal-enricher", "name": "deal-enricher", "root": "/repo/apps/deal-enricher" },
|
|
950
|
+
"environment": "staging",
|
|
951
|
+
"runtimePosture": "byo-cloudflare"
|
|
952
|
+
},
|
|
953
|
+
"data": {
|
|
954
|
+
"deployId": "deploy_abc123",
|
|
955
|
+
"planOnly": true,
|
|
956
|
+
"compatibility": {
|
|
957
|
+
"additiveSafe": 4,
|
|
958
|
+
"compatibleWithShim": [],
|
|
959
|
+
"breaking": [],
|
|
960
|
+
"semantic": []
|
|
961
|
+
},
|
|
962
|
+
"stateMachine": [
|
|
963
|
+
{ "name": "plan_received", "status": "pending" },
|
|
964
|
+
{ "name": "apply_cloudflare", "status": "pending" },
|
|
965
|
+
{ "name": "deploy_worker", "status": "pending" },
|
|
966
|
+
{ "name": "upload_hubspot", "status": "pending" },
|
|
967
|
+
{ "name": "record", "status": "pending" },
|
|
968
|
+
{ "name": "attest", "status": "pending" },
|
|
969
|
+
{ "name": "promote_when_healthy", "status": "pending" }
|
|
970
|
+
],
|
|
971
|
+
"estimatedCost": {
|
|
972
|
+
"currency": "USD",
|
|
973
|
+
"monthly": 12.4,
|
|
974
|
+
"notes": ["streaming backend ai-summary: estimate assumes 100 concurrent streams at 400ms polling"]
|
|
975
|
+
},
|
|
976
|
+
"drift": { "state": "healthy" }
|
|
977
|
+
},
|
|
978
|
+
"warnings": []
|
|
979
|
+
}
|
|
980
|
+
```
|
|
981
|
+
|
|
982
|
+
## First implementation slice
|
|
983
|
+
|
|
984
|
+
1. Create `packages/common` with result envelopes, diagnostics, ids, error envelopes, and exit codes.
|
|
985
|
+
2. Create `packages/config` with workspace discovery, project discovery, `hsx.account.json` parsing, runtime posture, and account conflict handling.
|
|
986
|
+
3. Create `packages/cli` with global flag parsing, JSON/human renderers, and placeholder commands.
|
|
987
|
+
4. Implement `hs-x project status --json` first because it exercises monorepo/project/account resolution without HubSpot or Cloudflare mutation.
|
|
988
|
+
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.
|
|
989
|
+
6. Implement `hs-x validate --json` with config/workspace/SDK declaration checks.
|
|
990
|
+
7. Implement `hs-x deploy --plan --json` using compiler manifests before mutating HubSpot or Cloudflare.
|
|
991
|
+
8. Implement Cloudflare-light `hs-x migrate cards` and `hs-x deploy --hubspot-only` if the migration artifacts allow it.
|
|
992
|
+
9. Wire real deploy execution, dev routing, drift/repair, and sync operations package by package behind the same command contracts.
|
|
993
|
+
|
|
994
|
+
## Open CLI questions
|
|
995
|
+
|
|
996
|
+
- Managed-CF tier: does it ship early or remain an explicit future runtime posture?
|
|
997
|
+
- `hs-x dev upload` vs `hs-x deploy --metadata-only`: should both exist or should one alias the other?
|
|
998
|
+
- Exact first-run dev runtime: does `hs-x connect` always deploy a shell Worker, or only when a capability requires runtime hosting?
|
|
999
|
+
- HubSpot-only migration: which legacy card shapes can truly deploy without Cloudflare?
|
|
1000
|
+
- Fresh-clone generated refs: should `hs-x create` install a stub `@hs-x/refs` package or rely on generated `.hs-x/refs` after first `dev`/`deploy`?
|
|
1001
|
+
- How much of `hs-x check` belongs in `validate` vs a marketplace-specific command?
|