@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.
- package/README.md +97 -986
- package/dist/cli/index.js +10 -6
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +75 -5
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/dev.d.ts +5 -0
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +222 -2
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/help-command.d.ts +1 -1
- package/dist/commands/help-command.d.ts.map +1 -1
- package/dist/commands/help-command.js +46 -15
- package/dist/commands/help-command.js.map +1 -1
- package/dist/commands/migrate.d.ts +10 -0
- package/dist/commands/migrate.d.ts.map +1 -1
- package/dist/commands/migrate.js +227 -4
- package/dist/commands/migrate.js.map +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/dev/invoke.d.ts +44 -0
- package/dist/dev/invoke.d.ts.map +1 -0
- package/dist/dev/invoke.js +155 -0
- package/dist/dev/invoke.js.map +1 -0
- package/dist/errors-registry.d.ts.map +1 -1
- package/dist/errors-registry.js +85 -0
- package/dist/errors-registry.js.map +1 -1
- package/dist/help.d.ts +10 -0
- package/dist/help.d.ts.map +1 -1
- package/dist/help.js +209 -3
- package/dist/help.js.map +1 -1
- package/dist/init/templates.d.ts.map +1 -1
- package/dist/init/templates.js +27 -8
- package/dist/init/templates.js.map +1 -1
- package/dist/reporter/style.d.ts +1 -0
- package/dist/reporter/style.d.ts.map +1 -1
- package/dist/reporter/style.js +4 -0
- package/dist/reporter/style.js.map +1 -1
- package/package.json +11 -1
package/README.md
CHANGED
|
@@ -1,1027 +1,138 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @hs-x/cli
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
341
|
-
|
|
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
|
-
#
|
|
345
|
-
#
|
|
346
|
-
hs-x
|
|
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
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
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
|
-
|
|
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
|
-
|
|
40
|
+
## Commands
|
|
370
41
|
|
|
371
|
-
|
|
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
|
-
|
|
44
|
+
Start:
|
|
377
45
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
hs-x
|
|
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
|
-
|
|
53
|
+
Develop:
|
|
384
54
|
|
|
385
|
-
|
|
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
|
-
|
|
62
|
+
Deploy:
|
|
388
63
|
|
|
389
|
-
|
|
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
|
-
|
|
392
|
-
hs-x login [--profile <name>] [--json]
|
|
393
|
-
hs-x logout [--profile <name>]
|
|
394
|
-
```
|
|
71
|
+
Observe:
|
|
395
72
|
|
|
396
|
-
|
|
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
|
-
|
|
399
|
-
hs-x accounts list [--json]
|
|
400
|
-
hs-x accounts current [--json]
|
|
401
|
-
hs-x accounts switch <account>
|
|
402
|
-
```
|
|
81
|
+
Account:
|
|
403
82
|
|
|
404
|
-
|
|
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
|
-
|
|
92
|
+
Config:
|
|
407
93
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
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
|
-
|
|
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
|
-
|
|
105
|
+
## Connecting accounts
|
|
521
106
|
|
|
522
|
-
|
|
107
|
+
`hs-x connect` walks HubSpot and Cloudflare setup back to back; each side is
|
|
108
|
+
also available on its own.
|
|
523
109
|
|
|
524
|
-
```
|
|
525
|
-
hs-x
|
|
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
|
-
|
|
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
|
-
|
|
122
|
+
## Migrating an existing app
|
|
968
123
|
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
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
|
-
##
|
|
130
|
+
## Docs
|
|
1009
131
|
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
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
|
-
##
|
|
136
|
+
## License
|
|
1021
137
|
|
|
1022
|
-
-
|
|
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
|