hub-launch 1.23.0 → 1.25.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/CHANGELOG.md +35 -0
- package/README.md +59 -0
- package/dist/commands/errorWatcher.d.ts +147 -0
- package/dist/commands/errorWatcher.d.ts.map +1 -0
- package/dist/commands/errorWatcher.fixture.d.ts +32 -0
- package/dist/commands/errorWatcher.fixture.d.ts.map +1 -0
- package/dist/commands/errorWatcher.fixture.js +32 -0
- package/dist/commands/errorWatcher.fixture.js.map +1 -0
- package/dist/commands/errorWatcher.js +1017 -0
- package/dist/commands/errorWatcher.js.map +1 -0
- package/dist/commands/launch.d.ts.map +1 -1
- package/dist/commands/launch.js +94 -19
- package/dist/commands/launch.js.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/templates/proceed-instructions.md +2 -0
- package/dist/templates/skills/hula-confirm/SKILL.md +1 -1
- package/dist/templates/skills/hula-plan/SKILL.md +1 -0
- package/dist/types/config.schema.d.ts +3 -0
- package/dist/types/config.schema.d.ts.map +1 -1
- package/dist/types/config.schema.js +4 -0
- package/dist/types/config.schema.js.map +1 -1
- package/dist/types/errorWatcher.schema.d.ts +72 -0
- package/dist/types/errorWatcher.schema.d.ts.map +1 -0
- package/dist/types/errorWatcher.schema.js +18 -0
- package/dist/types/errorWatcher.schema.js.map +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -0
- package/dist/types/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,41 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.25.0] - 2026-08-23
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- New `hula error-watcher` command to manage production **Error Watchers** — inbound error webhooks that deduplicate reported errors and auto-launch a fix (PR, plan, or feedback run). Seven modes: `--create`, `--list`, `--show`, `--events`, `--update`, `--delete`, and `--print-setup`. `--create` and `--print-setup` print the steps to mint a project ingest key, a paste-ready `.env` block, and a hard-coded signed-request snippet that signs the exact `` `${timestamp}.${body}` `` payload the server verifies. Credentials resolve flags → `.hublaunch/hublaunch.config.js` → env, identical to `hula schedule`, and are never written to any file. `--events` shows each error's disposition (`deduped`, `skipped_environment`, `skipped_budget`, `skipped_credits`, `skipped_open_pr`, `launched`, `failed`) so "why was there no PR?" is answerable from the terminal; with no watcher id it covers every watcher on the project. Requires a hula-server with Error Watcher support; older servers return a clear "not supported yet" message. See [docs/error-watcher.md](./docs/error-watcher.md).
|
|
15
|
+
- `--instructions`, `--error-env KEY=VALUE`, and `--secret <literal>` flags on `--create`/`--update` for the three watcher fields that shape what the fix agent sees: trusted guidance rendered into the agent's brief, environment variables injected into the fix sandbox, and extra literal values redacted from every payload. Each replaces the stored value wholesale, with matching `--clear-instructions` / `--clear-error-env` / `--clear-secrets` flags. Values are write-only — the server never echoes them back, and the CLI never prints them.
|
|
16
|
+
- Optional `errorWebhookUrl` config key to override the Error Watcher ingest URL for self-hosted servers whose webhook host differs from `hulaProjectUrl`.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- **Error Watcher ingest now authenticates on a project ingest key.** Matching the server, the credential a reporting app presents moved from the per-watcher `hew_` token to a project-level `hik_` key signing with its own `his_` secret. The generated `.env` block emits `HULA_INGEST_KEY` / `HULA_INGEST_SECRET` (was `HULA_ERROR_WATCHER_TOKEN` / `HULA_ERROR_WATCHER_SECRET`) and the snippet sends `X-Hula-Api-Key` (was `X-Hula-Watcher`). The watcher now holds configuration only; `--create` prints no credential of its own and instead directs you to **Projects → gear icon → Error reporting API keys**, the only place a key can be minted. **Any reporter wired up against an earlier build of this command must be updated or it will 401.**
|
|
21
|
+
- The printed snippet now sends `errorName` and `errorCode` alongside `key`. The server's dedupe fingerprint is built from those three identity fields and nothing else, so a report carrying none of them shares a single bucket with every other such report — one fix per watcher per dedupe window.
|
|
22
|
+
- `--events` with no watcher id lists the project's watchers and queries each one, merging the results newest-first. It previously requested a project-wide events route that does not exist on the server and always returned 404.
|
|
23
|
+
- `--update` now rejects `--container-cpu`, `--container-memory`, `--container-disk`, `--update-notification-url`, and `--update-notification-name-tag` up front. The update endpoint ignores those fields, so sending them reported success while changing nothing; they remain settable at `--create`.
|
|
24
|
+
- `--delete` no longer claims reports will start returning 401. Deleting a watcher revokes no credential — ingest keys keep authenticating, and the webhook answers 409 once the project has no enabled watcher left.
|
|
25
|
+
- `--show` displays the watcher's `instructions` and no longer prints the vestigial watcher token prefix.
|
|
26
|
+
- A `409` from the ingest webhook now maps to a specific message naming the cause ("no enabled error watcher is configured for this project") and the commands that fix it, instead of falling through to a generic status line.
|
|
27
|
+
|
|
28
|
+
### Removed
|
|
29
|
+
|
|
30
|
+
- `hula error-watcher --rotate`. It rotated the watcher's signing secret, which the ingest webhook no longer verifies, and its response handling did not match the server's shape. The signing secret that matters belongs to the project ingest key and is rotated from the dashboard settings page, which has no REST route for the CLI to call.
|
|
31
|
+
|
|
32
|
+
## [1.24.0] - 2026-08-18
|
|
33
|
+
|
|
34
|
+
### Fixed
|
|
35
|
+
|
|
36
|
+
- Entitlement is now verified with the server on every launch instead of being cached indefinitely in local config. A cancelled subscription, an expired free trial, or exhausted PR credits no longer slips past the local gate only to fail later against the server. `usageTier` is now treated as a refreshable cache of the last server answer, consulted only when the server is unreachable.
|
|
37
|
+
- Access-denied messages now reflect the account's actual subscription status — never subscribed (offers the free trial), cancelled, payment failed, or the server's status verbatim — instead of always claiming the user is on the free tier.
|
|
38
|
+
- Billing and upgrade links point at the configured server's own dashboard rather than a hardcoded hublaunch.site, so preview and self-hosted deployments send users to their own billing page.
|
|
39
|
+
- Chained hula skills (`/hula-plan` → confirm → launch) now execute inline explicitly, fixing the "blocked by disable-model-invocation" break under newer Claude Code versions that enforce that flag on model-initiated Skill-tool calls.
|
|
40
|
+
|
|
41
|
+
### Changed
|
|
42
|
+
|
|
43
|
+
- Prefer the server's own 402 response message and its `upgradeUrl` / `buyCreditsUrl` over anything the CLI infers, in both the single-repo and `--folder` launch paths.
|
|
44
|
+
|
|
10
45
|
## [1.23.0] - 2026-08-13
|
|
11
46
|
|
|
12
47
|
### Added
|
package/README.md
CHANGED
|
@@ -128,6 +128,7 @@ they're there when you want them:
|
|
|
128
128
|
| `/hula-confirm` | Re-validate a plan you've edited by hand |
|
|
129
129
|
| `/hula-upload` | Sync a plan to `origin/main` (normally automatic during launch) |
|
|
130
130
|
| `/hula-schedule` | Run or schedule autonomous actions (e.g. a nightly `harden` security audit) |
|
|
131
|
+
| `hula error-watcher` | Manage production Error Watchers — inbound error webhooks that auto-launch fix PRs |
|
|
131
132
|
| `/hula-help` | Interactive onboarding and reference — walks through setup, the workflow, or any command/skill |
|
|
132
133
|
| `/hula-create` | Legacy: create an issue without launching (the modern flow is `/hula-plan` → launch) |
|
|
133
134
|
|
|
@@ -135,6 +136,64 @@ Full details: [Commands Reference](./docs/commands.md) ·
|
|
|
135
136
|
[Advanced Usage](./docs/advanced.md) (launch pipeline internals, resume,
|
|
136
137
|
test mode, env forwarding, scheduling).
|
|
137
138
|
|
|
139
|
+
## Error Watchers
|
|
140
|
+
|
|
141
|
+
An **Error Watcher** is an inbound webhook: your production app reports an error
|
|
142
|
+
to HubLaunch, HubLaunch deduplicates it, and — when the error is new — auto-launches
|
|
143
|
+
a fix (a PR, a plan, or a feedback run). Manage watchers from the CLI:
|
|
144
|
+
|
|
145
|
+
Reporting an error needs **two** pieces, created in different places:
|
|
146
|
+
|
|
147
|
+
- the **watcher** — the configuration (dedupe window, environments, PR policy,
|
|
148
|
+
agent instructions), managed from the CLI;
|
|
149
|
+
- a **project ingest key** — the credential your app presents (`hik_…` plus its
|
|
150
|
+
own signing secret `his_…`), created in the dashboard under **Projects → gear
|
|
151
|
+
icon → Error reporting API keys**.
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
# Create a watcher; prints where to mint the ingest key, a .env block, and a
|
|
155
|
+
# ready-to-paste signed-request snippet.
|
|
156
|
+
hula error-watcher --create --name api-prod
|
|
157
|
+
|
|
158
|
+
# See what your watchers have been doing, and why each error did or didn't
|
|
159
|
+
# launch a fix (omit the id to cover every watcher on the project)
|
|
160
|
+
hula error-watcher --events
|
|
161
|
+
|
|
162
|
+
# List, inspect, patch tuning, or delete
|
|
163
|
+
hula error-watcher --list
|
|
164
|
+
hula error-watcher --show <watcherId>
|
|
165
|
+
hula error-watcher --update <watcherId> --max-fixes-per-day 10
|
|
166
|
+
hula error-watcher --update <watcherId> --instructions "Never touch billing/"
|
|
167
|
+
hula error-watcher --delete <watcherId>
|
|
168
|
+
|
|
169
|
+
# Re-print the setup snippet at any time (placeholders only — no secrets, works offline)
|
|
170
|
+
hula error-watcher --print-setup
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Add the printed block to your production environment, filling in the key and
|
|
174
|
+
secret from the dashboard:
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
HULA_ERROR_WEBHOOK_URL=https://www.hublaunch.site/api/v1/webhooks/error
|
|
178
|
+
HULA_INGEST_KEY=hik_…
|
|
179
|
+
HULA_INGEST_SECRET=his_…
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
The **key and secret are shown only once** and are never handled by the CLI —
|
|
183
|
+
copy them into your deployment's secret store immediately. Every report is signed
|
|
184
|
+
with `HMAC-SHA256` over `` `${timestamp}.${body}` `` (the exact payload the printed
|
|
185
|
+
snippet produces) and sent as `X-Hula-Api-Key` + `X-Hula-Signature`. Rotate the
|
|
186
|
+
secret from the same settings page; the previous one keeps verifying for 24 hours.
|
|
187
|
+
|
|
188
|
+
Send at least a `key`, `errorName`, or `errorCode` with every report — the dedupe
|
|
189
|
+
fingerprint is built from those three fields alone, and a report carrying none of
|
|
190
|
+
them shares one bucket with every other such report.
|
|
191
|
+
|
|
192
|
+
A `409` means the key is valid but the project has no enabled watcher; create one
|
|
193
|
+
with `--create`. Requires a hula-server with Error Watcher support; older servers
|
|
194
|
+
return a clear "not supported yet" message. Full reference:
|
|
195
|
+
[docs/error-watcher.md](./docs/error-watcher.md).
|
|
196
|
+
|
|
138
197
|
## Multi-repo feature groups
|
|
139
198
|
|
|
140
199
|
A feature that spans several repositories — say a mobile app plus its backend
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
import type { Config } from '../types/index.js';
|
|
3
|
+
export interface ErrorWatcherOptions {
|
|
4
|
+
create?: boolean;
|
|
5
|
+
list?: boolean;
|
|
6
|
+
show?: string;
|
|
7
|
+
/** `--events [watcherId]`: `true` (all watchers) or a specific watcher id. */
|
|
8
|
+
events?: string | boolean;
|
|
9
|
+
update?: string;
|
|
10
|
+
delete?: string;
|
|
11
|
+
/** `--print-setup [watcherId]`: the id is informational only. */
|
|
12
|
+
printSetup?: string | boolean;
|
|
13
|
+
name?: string;
|
|
14
|
+
dedupeWindow?: number;
|
|
15
|
+
maxFixesPerDay?: number;
|
|
16
|
+
environments?: string;
|
|
17
|
+
outcomeType?: string;
|
|
18
|
+
prPolicy?: string;
|
|
19
|
+
enabled?: string;
|
|
20
|
+
/** Trusted guidance rendered into the fix agent's brief. */
|
|
21
|
+
instructions?: string;
|
|
22
|
+
/** Clear the stored instructions (sends `instructions: null`). */
|
|
23
|
+
clearInstructions?: boolean;
|
|
24
|
+
/** Repeatable `KEY=VALUE` env vars injected into the fix sandbox. */
|
|
25
|
+
errorEnv?: string[];
|
|
26
|
+
/** Clear all errorEnvVars (sends an empty map). */
|
|
27
|
+
clearErrorEnv?: boolean;
|
|
28
|
+
/** Repeatable literal values redacted from every payload for this watcher. */
|
|
29
|
+
secret?: string[];
|
|
30
|
+
/** Clear the extra redaction list (sends an empty array). */
|
|
31
|
+
clearSecrets?: boolean;
|
|
32
|
+
status?: string;
|
|
33
|
+
limit?: number;
|
|
34
|
+
provider?: string;
|
|
35
|
+
providerKey?: string;
|
|
36
|
+
containerCpu?: number;
|
|
37
|
+
containerMemory?: number;
|
|
38
|
+
containerDisk?: number;
|
|
39
|
+
updateNotificationUrl?: string;
|
|
40
|
+
updateNotificationNameTag?: string;
|
|
41
|
+
project?: string;
|
|
42
|
+
url?: string;
|
|
43
|
+
apiKey?: string;
|
|
44
|
+
json?: boolean;
|
|
45
|
+
yes?: boolean;
|
|
46
|
+
}
|
|
47
|
+
/** The mutually-exclusive command modes, in dispatch order. */
|
|
48
|
+
export type WatcherMode = 'create' | 'list' | 'show' | 'events' | 'update' | 'delete' | 'print-setup';
|
|
49
|
+
/**
|
|
50
|
+
* Manage production Error Watchers — thin REST client over
|
|
51
|
+
* `/api/v1/error-watchers`, structured like `hula schedule`.
|
|
52
|
+
*/
|
|
53
|
+
export declare function errorWatcherCommand(program: Command, config: Config): void;
|
|
54
|
+
/**
|
|
55
|
+
* Commander collector for a repeatable flag. `previous` is undefined on the
|
|
56
|
+
* first occurrence, which is what lets a caller distinguish "flag omitted"
|
|
57
|
+
* (undefined) from "flag given" — the distinction PATCH relies on to leave a
|
|
58
|
+
* field untouched rather than clearing it.
|
|
59
|
+
*/
|
|
60
|
+
export declare function collectRepeatable(value: string, previous?: string[]): string[];
|
|
61
|
+
/**
|
|
62
|
+
* Which mode flags are active. `--events` and `--print-setup` count when
|
|
63
|
+
* present even without a value (`true`); every other flag counts when truthy.
|
|
64
|
+
*/
|
|
65
|
+
export declare function selectModes(options: ErrorWatcherOptions): WatcherMode[];
|
|
66
|
+
/**
|
|
67
|
+
* Dispatch a single mode. Enforces mutual exclusion BEFORE any network call, so
|
|
68
|
+
* two mode flags fail without issuing a request. With no mode flag, prints help.
|
|
69
|
+
*/
|
|
70
|
+
export declare function runErrorWatcher(config: Config, options: ErrorWatcherOptions, showHelp?: () => void): Promise<void>;
|
|
71
|
+
/**
|
|
72
|
+
* The ingest webhook URL the customer's production app POSTs to. Defaults to
|
|
73
|
+
* `<serverUrl>/api/v1/webhooks/error`, overridable via `config.errorWebhookUrl`
|
|
74
|
+
* for self-hosted servers whose webhook host differs from `hulaProjectUrl`.
|
|
75
|
+
*/
|
|
76
|
+
export declare function resolveWebhookUrl(serverUrl: string, config: Config): string;
|
|
77
|
+
/**
|
|
78
|
+
* Where a project ingest key (`hik_…`) is minted. Key CRUD is exposed over
|
|
79
|
+
* tRPC only — there is no REST route for the CLI to call — so every setup path
|
|
80
|
+
* points the user at this page rather than pretending to issue a credential.
|
|
81
|
+
*/
|
|
82
|
+
export declare function resolveProjectSettingsUrl(serverUrl: string): string;
|
|
83
|
+
/**
|
|
84
|
+
* Validate tuning flags client-side so the common mistakes are caught without a
|
|
85
|
+
* network round trip. Returns a list of error strings (empty when all valid).
|
|
86
|
+
* The server validates independently; this is a convenience, not the boundary.
|
|
87
|
+
*/
|
|
88
|
+
export declare function validateTuningOptions(options: ErrorWatcherOptions): string[];
|
|
89
|
+
/**
|
|
90
|
+
* Validate the repeatable `--error-env KEY=VALUE` pairs. Reserved names are
|
|
91
|
+
* rejected here because the server returns a 400 for them, and the platform
|
|
92
|
+
* owns those variables inside the fix sandbox.
|
|
93
|
+
*/
|
|
94
|
+
export declare function validateErrorEnv(entries: string[] | undefined): string[];
|
|
95
|
+
/**
|
|
96
|
+
* Validate the repeatable `--secret` values. The minimum length is the server's
|
|
97
|
+
* floor: a very short literal would match everywhere and shred the very error
|
|
98
|
+
* text the fix agent needs.
|
|
99
|
+
*/
|
|
100
|
+
export declare function validateSecrets(entries: string[] | undefined): string[];
|
|
101
|
+
/** Parse validated `KEY=VALUE` entries into the map the server expects. */
|
|
102
|
+
export declare function parseErrorEnv(entries: string[]): Record<string, string>;
|
|
103
|
+
/**
|
|
104
|
+
* The signed-request snippet. HARD-CODED (never fetched) so `--print-setup`
|
|
105
|
+
* works with the server unreachable. Built line-by-line from single/double
|
|
106
|
+
* quoted strings so the literal `${timestamp}.${body}` signing payload survives
|
|
107
|
+
* verbatim into the printed output (it must NOT be interpolated here).
|
|
108
|
+
*/
|
|
109
|
+
export declare function buildSigningSnippet(): string;
|
|
110
|
+
/**
|
|
111
|
+
* The `.env` block for the customer's production environment.
|
|
112
|
+
*
|
|
113
|
+
* The credential is the PROJECT ingest key (`hik_…`) and its own signing secret
|
|
114
|
+
* (`his_…`), minted in the dashboard settings page — not the watcher. The CLI
|
|
115
|
+
* never holds either value, so both are always placeholders.
|
|
116
|
+
*/
|
|
117
|
+
export declare function buildEnvBlock(webhookUrl: string): string;
|
|
118
|
+
/**
|
|
119
|
+
* Print the wiring steps for a watcher: mint the project ingest key, set the
|
|
120
|
+
* env vars, paste the signed-request snippet.
|
|
121
|
+
*
|
|
122
|
+
* The CLI deliberately prints no credential of its own. Ingest keys are created
|
|
123
|
+
* over tRPC from the dashboard and have no REST route, so the only honest thing
|
|
124
|
+
* the CLI can do is say where to get one. The watcher `hew_`/`hes_` values the
|
|
125
|
+
* create endpoint still returns authenticate nothing and are never shown.
|
|
126
|
+
*/
|
|
127
|
+
export declare function printSetupInstructions(serverUrl: string, webhookUrl: string): void;
|
|
128
|
+
/**
|
|
129
|
+
* One-line rendering of the watcher's `instructions`. Long guidance is elided
|
|
130
|
+
* rather than wrapped so the field stays one row in the detail block.
|
|
131
|
+
*/
|
|
132
|
+
export declare function summarizeInstructions(instructions: string | null | undefined, maxLen?: number): string;
|
|
133
|
+
/** Humanize a duration in seconds: 300 → 5m, 3600 → 1h, 86400 → 24h, 604800 → 7d. */
|
|
134
|
+
export declare function humanizeDuration(seconds: number): string;
|
|
135
|
+
/** Relative time such as "12m ago" for an ISO timestamp. */
|
|
136
|
+
export declare function relativeTime(iso: string): string;
|
|
137
|
+
/**
|
|
138
|
+
* Map server responses to actionable messages. NEVER echoes the Authorization
|
|
139
|
+
* header, the watcher token, or the secret — none of those are read from the
|
|
140
|
+
* error object, so no redaction pass is needed. Re-throws non-axios errors for
|
|
141
|
+
* the caller's outer handler.
|
|
142
|
+
*/
|
|
143
|
+
export declare function handleWatcherApiError(error: unknown, serverUrl: string, ctx?: {
|
|
144
|
+
project?: string;
|
|
145
|
+
watcherId?: string;
|
|
146
|
+
}): void;
|
|
147
|
+
//# sourceMappingURL=errorWatcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errorWatcher.d.ts","sourceRoot":"","sources":["../../src/commands/errorWatcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Signing fixture for the reporter snippet the CLI prints.
|
|
3
|
+
*
|
|
4
|
+
* The CLI does not sign anything itself — it prints a snippet the customer
|
|
5
|
+
* pastes into their app. This fixture pins the algorithm that snippet
|
|
6
|
+
* documents, so a careless edit to `buildSigningSnippet()` fails here rather
|
|
7
|
+
* than surfacing as an unexplained production `401`.
|
|
8
|
+
*
|
|
9
|
+
* Signing payload: `${timestamp}.${body}` — the request timestamp, a literal
|
|
10
|
+
* `.`, then the raw JSON body. Signing the body alone (dropping the timestamp
|
|
11
|
+
* prefix) removes replay protection and is the exact mistake the snippet and
|
|
12
|
+
* this fixture guard against.
|
|
13
|
+
*
|
|
14
|
+
* The secret is the PROJECT ingest key's signing secret (`his_…`), minted with
|
|
15
|
+
* the key in the dashboard settings page. The per-watcher `hes_…` secret this
|
|
16
|
+
* fixture used to model no longer authenticates anything.
|
|
17
|
+
*/
|
|
18
|
+
export declare const WATCHER_SIGNATURE_FIXTURE: {
|
|
19
|
+
/** Test signing secret. NEVER a real credential. */
|
|
20
|
+
readonly secret: "his_test_secret_do_not_use_in_production";
|
|
21
|
+
/** Unix seconds, as sent in the `X-Hula-Timestamp` header. */
|
|
22
|
+
readonly timestamp: "1734556800";
|
|
23
|
+
/**
|
|
24
|
+
* The exact raw request body that was signed. Carries all three dedupe
|
|
25
|
+
* identity fields (`key`, `errorName`, `errorCode`) because those — and only
|
|
26
|
+
* those — build the server's fingerprint.
|
|
27
|
+
*/
|
|
28
|
+
readonly body: "{\"errorDescription\":\"Payment gateway timed out\",\"key\":\"orders.checkout.timeout\",\"errorName\":\"TimeoutError\",\"errorCode\":\"ETIMEDOUT\",\"environment\":\"production\"}";
|
|
29
|
+
/** HMAC-SHA256(secret, `${timestamp}.${body}`) as lowercase hex. */
|
|
30
|
+
readonly signatureHex: "fe747ddcc36a1405b9ecc417328fbfdb0459d5c7d4d3b0528b47eed0c0b17045";
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=errorWatcher.fixture.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errorWatcher.fixture.d.ts","sourceRoot":"","sources":["../../src/commands/errorWatcher.fixture.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,yBAAyB;IACpC,oDAAoD;;IAEpD,8DAA8D;;IAE9D;;;;OAIG;;IAEH,oEAAoE;;CAG5D,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Signing fixture for the reporter snippet the CLI prints.
|
|
3
|
+
*
|
|
4
|
+
* The CLI does not sign anything itself — it prints a snippet the customer
|
|
5
|
+
* pastes into their app. This fixture pins the algorithm that snippet
|
|
6
|
+
* documents, so a careless edit to `buildSigningSnippet()` fails here rather
|
|
7
|
+
* than surfacing as an unexplained production `401`.
|
|
8
|
+
*
|
|
9
|
+
* Signing payload: `${timestamp}.${body}` — the request timestamp, a literal
|
|
10
|
+
* `.`, then the raw JSON body. Signing the body alone (dropping the timestamp
|
|
11
|
+
* prefix) removes replay protection and is the exact mistake the snippet and
|
|
12
|
+
* this fixture guard against.
|
|
13
|
+
*
|
|
14
|
+
* The secret is the PROJECT ingest key's signing secret (`his_…`), minted with
|
|
15
|
+
* the key in the dashboard settings page. The per-watcher `hes_…` secret this
|
|
16
|
+
* fixture used to model no longer authenticates anything.
|
|
17
|
+
*/
|
|
18
|
+
export const WATCHER_SIGNATURE_FIXTURE = {
|
|
19
|
+
/** Test signing secret. NEVER a real credential. */
|
|
20
|
+
secret: 'his_test_secret_do_not_use_in_production',
|
|
21
|
+
/** Unix seconds, as sent in the `X-Hula-Timestamp` header. */
|
|
22
|
+
timestamp: '1734556800',
|
|
23
|
+
/**
|
|
24
|
+
* The exact raw request body that was signed. Carries all three dedupe
|
|
25
|
+
* identity fields (`key`, `errorName`, `errorCode`) because those — and only
|
|
26
|
+
* those — build the server's fingerprint.
|
|
27
|
+
*/
|
|
28
|
+
body: '{"errorDescription":"Payment gateway timed out","key":"orders.checkout.timeout","errorName":"TimeoutError","errorCode":"ETIMEDOUT","environment":"production"}',
|
|
29
|
+
/** HMAC-SHA256(secret, `${timestamp}.${body}`) as lowercase hex. */
|
|
30
|
+
signatureHex: 'fe747ddcc36a1405b9ecc417328fbfdb0459d5c7d4d3b0528b47eed0c0b17045',
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=errorWatcher.fixture.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errorWatcher.fixture.js","sourceRoot":"","sources":["../../src/commands/errorWatcher.fixture.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG;IACvC,oDAAoD;IACpD,MAAM,EAAE,0CAA0C;IAClD,8DAA8D;IAC9D,SAAS,EAAE,YAAY;IACvB;;;;OAIG;IACH,IAAI,EAAE,gKAAgK;IACtK,oEAAoE;IACpE,YAAY,EACV,kEAAkE;CAC5D,CAAC"}
|