@goliapkg/sentori-cli 1.4.1 → 1.6.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.
Files changed (54) hide show
  1. package/README.md +268 -26
  2. package/lib/artifacts.d.ts +7 -0
  3. package/lib/artifacts.d.ts.map +1 -1
  4. package/lib/artifacts.js +5 -1
  5. package/lib/artifacts.js.map +1 -1
  6. package/lib/compatibility.d.ts +6 -0
  7. package/lib/compatibility.d.ts.map +1 -0
  8. package/lib/compatibility.js +21 -0
  9. package/lib/compatibility.js.map +1 -0
  10. package/lib/index.js +97 -65
  11. package/lib/index.js.map +1 -1
  12. package/lib/lenient.js +1 -1
  13. package/lib/lenient.js.map +1 -1
  14. package/lib/mcp.js +1 -1
  15. package/lib/mcp.js.map +1 -1
  16. package/lib/native-artifacts.d.ts.map +1 -1
  17. package/lib/native-artifacts.js +5 -0
  18. package/lib/native-artifacts.js.map +1 -1
  19. package/lib/probes.js +1 -1
  20. package/lib/probes.js.map +1 -1
  21. package/lib/srcbundle.d.ts +37 -2
  22. package/lib/srcbundle.d.ts.map +1 -1
  23. package/lib/srcbundle.js +181 -58
  24. package/lib/srcbundle.js.map +1 -1
  25. package/lib/upload.d.ts +47 -2
  26. package/lib/upload.d.ts.map +1 -1
  27. package/lib/upload.js +65 -2
  28. package/lib/upload.js.map +1 -1
  29. package/lib/workflow.d.ts +47 -0
  30. package/lib/workflow.d.ts.map +1 -0
  31. package/lib/workflow.js +289 -0
  32. package/lib/workflow.js.map +1 -0
  33. package/package.json +11 -7
  34. package/src/__tests__/artifacts.test.ts +1 -1
  35. package/src/__tests__/compatibility.test.ts +80 -0
  36. package/src/__tests__/expand-sourcemaps.test.ts +56 -0
  37. package/src/__tests__/rn-upload-hook.test.ts +35 -0
  38. package/src/__tests__/srcbundle.test.ts +66 -0
  39. package/src/__tests__/upload-verdict.test.ts +77 -0
  40. package/src/__tests__/workflow.test.ts +102 -0
  41. package/src/artifacts.ts +12 -1
  42. package/src/compatibility.ts +21 -0
  43. package/src/index.ts +83 -66
  44. package/src/lenient.ts +1 -1
  45. package/src/mcp.ts +1 -1
  46. package/src/native-artifacts.ts +5 -0
  47. package/src/probes.ts +1 -1
  48. package/src/srcbundle.ts +150 -61
  49. package/src/upload.ts +88 -3
  50. package/src/workflow.ts +240 -0
  51. package/lib/source-bundle.d.ts +0 -28
  52. package/lib/source-bundle.d.ts.map +0 -1
  53. package/lib/source-bundle.js +0 -193
  54. package/lib/source-bundle.js.map +0 -1
package/README.md CHANGED
@@ -1,49 +1,291 @@
1
- # @goliapkg/sentori-cli
1
+ # Sentori CLI
2
2
 
3
- Node-based CLI for [Sentori](https://sentori.golia.jp). Two surfaces:
3
+ Configure, collect, upload and verify the debug files from an application build.
4
+ The package is `@goliapkg/sentori-cli`; its command is **`sentori`**.
5
+ `sentori-cli` remains an alias for existing scripts. The CLI runs on the build
6
+ machine and does not add runtime work to the application.
4
7
 
5
- - **Source-map upload** for web bundlers + Hermes (React Native) builds
6
- - **Issue triage** (list / resolve / silence / comment) — usable as
7
- a Model Context Protocol (MCP) server so Claude / Cursor / Aider
8
- can act on your error inbox
8
+ ## Install the correct version
9
9
 
10
- ## Install
10
+ This guide requires **CLI 1.6.0**. Check registry availability before updating
11
+ your build pipeline. The older 1.5.0 package has only the
12
+ `sentori-cli` executable and does not have `init`, `doctor`, `upload all`, ZIP
13
+ packaging or exact-file `verify`. Do not install 1.5.0 and run this guide's commands.
14
+
15
+ Use Node **18.17 or newer**; use a maintained Node release for CI. Pin the CLI as a
16
+ development dependency and commit the package lock:
17
+
18
+ ```sh
19
+ npm view @goliapkg/sentori-cli@1.6.0 version
20
+ npm install --save-dev --save-exact @goliapkg/sentori-cli@1.6.0
21
+ npx --no-install sentori --version
22
+ ```
23
+
24
+ The last command must print `sentori 1.6.0`. If npm cannot find that version, use
25
+ the tested package supplied by the Sentori team, without assuming `latest` has
26
+ these features:
27
+
28
+ ```sh
29
+ npm install --save-dev --save-exact /absolute/path/goliapkg-sentori-cli-1.6.0.tgz
30
+ npx --no-install sentori --version
31
+ ```
32
+
33
+ For maintainers preparing that package, from the Sentori repository root with
34
+ its pinned Bun installed:
35
+
36
+ ```sh
37
+ bun install --frozen-lockfile
38
+ bun run --cwd sdk/cli build
39
+ npm pack ./sdk/cli --ignore-scripts --pack-destination /tmp
40
+ ```
41
+
42
+ Consumers need Node/npm, not Bun. A local dev dependency is not added to an
43
+ interactive shell's PATH: use `npx --no-install sentori` as below, or `sentori`
44
+ inside an npm script. `--no-install` prevents npx from downloading a different
45
+ package if the local executable is missing. The server maintenance binary
46
+ `sentori-operator` is a separate tool.
47
+
48
+ ## Compatibility and version identity
49
+
50
+ | Value | Meaning |
51
+ | --- | --- |
52
+ | CLI `1.6.0` | Build-tool package version, shown by `sentori --version` |
53
+ | Server version | Running server binary, reported by `sentori doctor` |
54
+ | SDK version | Independently versioned app library; it need not equal the CLI or server |
55
+ | Config `version: 1` | Upload configuration schema, not a product version |
56
+ | App `release` | Exact string reported by the SDK, including build number |
57
+ | Git tag | Optional source revision; it does not set the app release |
58
+
59
+ The current server advertises artifact protocol `1`, source formats `zip` and
60
+ `json`, and `sha256`/`format` verification on the existing authenticated artifact
61
+ inventory API. These fields are additive: old clients keep using that API and
62
+ legacy path-to-content JSON remains accepted.
63
+
64
+ CLI 1.6 checks capabilities rather than guessing from a server version. An older
65
+ build can have the same displayed server version without ZIP support. This
66
+ repository's server changes must be deployed before using source ZIPs against
67
+ that instance; changing the CLI alone is not enough.
68
+
69
+ | Server response | CLI 1.6 behavior |
70
+ | --- | --- |
71
+ | Protocol 1, ZIP and verification supported | Unified upload and verification, including sources |
72
+ | No capability advertisement | Source-map/dSYM/mapping workflows remain available; a ZIP plan stops before any upload |
73
+ | ZIP absent, unsupported protocol or incomplete verification support | Relevant workflow fails with an upgrade/compatibility message before uploading |
74
+ | Missing/false `usable`, wrong hash or wrong filename | Exact-file verification fails; stored bytes alone are not success |
75
+
76
+ Keep an existing CLI 1.5 pipeline pinned until its server is upgraded if it needs
77
+ legacy JSON sources. CLI 1.6 does not silently convert ZIPs to JSON or drop sources.
78
+ A new CLI also cannot reconstruct missing debug files from an SDK version: the
79
+ files must come from the actual application build being diagnosed.
80
+
81
+ ## Cloud and self-hosted destinations
82
+
83
+ The same package and commands support both editions. Choose the destination
84
+ before running `init`:
85
+
86
+ | Edition | `--api-url` | Credential |
87
+ | --- | --- | --- |
88
+ | Sentori Cloud | The target workspace's runtime URL, shown when opening that workspace | API-scope token created in that workspace's project |
89
+ | Self-hosted | Your instance's public HTTPS base URL | API-scope token created in that instance's project |
90
+
91
+ The Cloud account/billing website is not the upload destination. Do not use its
92
+ login cookie, the SDK ingest hostname, or a token from another workspace. Open
93
+ the project, create an API token in its token settings, and supply it through
94
+ `SENTORI_TOKEN` or the configured `tokenEnv`. `doctor` must accept that exact
95
+ URL/token pair before uploading. Cloud storage/traffic quotas still apply;
96
+ 429 is a failed upload, not a verified artifact. Resolve the quota, rerun the
97
+ same release, and require `verify` to pass. Revoking an API token stops future
98
+ CLI operations; rotate CI secrets before revocation.
99
+
100
+ Self-hosted operators must upgrade their server to a compatible release before
101
+ using ZIP sources. Cloud operators deploy the compatible data-plane image;
102
+ customers only need to update the CLI. Both use the capability checks below,
103
+ without assuming the CLI, SDK and server have matching version numbers.
104
+
105
+ ## Configure once per build target
106
+
107
+ Run in the application or monorepo root. Choose only the files that target
108
+ produces. For a web build that writes maps under `dist`:
109
+
110
+ ```sh
111
+ npx --no-install sentori init --api-url https://sentori.your-domain.com \
112
+ --sourcemap dist --sources src
113
+ ```
114
+
115
+ For iOS, use `--dsym build/MyApp.app.dSYM --sources Sources`. For Android, use
116
+ `--mapping app/build/outputs/mapping/release/mapping.txt --sources app/src/main`.
117
+ For React Native, also select the build's final composed JS source map; source
118
+ ZIPs cannot replace it. Adjust these example paths to the actual build outputs.
119
+
120
+ `init` creates `sentori.config.json` and refuses to overwrite it. A minimal web
121
+ configuration is:
122
+
123
+ ```json
124
+ {
125
+ "version": 1,
126
+ "apiUrl": "https://sentori.your-domain.com",
127
+ "tokenEnv": "SENTORI_TOKEN",
128
+ "artifacts": { "sourcemap": ["dist"] },
129
+ "sources": { "root": ".", "include": ["src", "shared"], "exclude": ["shared/tests"] }
130
+ }
131
+ ```
132
+
133
+ Remove `shared` if it does not exist. Select only application/shared sources
134
+ needed for stack context. Dependencies, hidden paths, common build outputs and
135
+ symlinks are excluded; never select an entire monorepo indiscriminately. Keep
136
+ `.sentori/` out of version control because it contains the generated source ZIP.
137
+ Use `sourceArchive: "sources.zip"` instead of `sources` for an existing ZIP.
138
+
139
+ Paths in configuration are relative to the configuration file. Source includes
140
+ and excludes are relative to `sources.root`. For a configuration in an app
141
+ subdirectory that needs shared monorepo files, set `sources.root` to the monorepo
142
+ root relative to that configuration and use root-relative include paths.
143
+
144
+ Use separate configurations such as `sentori.ios.config.json` and
145
+ `sentori.android.config.json` when platforms have different releases or outputs.
146
+ Pass `--config` to **every** command, including `doctor` and `verify`. The API-scope
147
+ token determines the project; these commands do not need a project UUID. Different
148
+ projects require their own tokens.
149
+
150
+ ## Check, build, upload, verify
151
+
152
+ Supply an **API-scope** token through the environment or the CI secret store. An
153
+ app's public ingest token cannot upload debug files. Never commit the token;
154
+ `init` stores only the environment variable name.
155
+
156
+ ```sh
157
+ export SENTORI_TOKEN='your-api-scope-token'
158
+ export SENTORI_RELEASE='my-app@1.2.3+42'
159
+ npx --no-install sentori doctor
160
+ # Build the app here, with the same release string and source revision.
161
+ npx --no-install sentori config validate
162
+ npx --no-install sentori upload all --dry-run
163
+ npx --no-install sentori upload all
164
+ npx --no-install sentori verify
165
+ ```
166
+
167
+ `doctor` checks authentication and configured server capabilities, even before
168
+ build outputs exist. It does not upload files or check build identity.
169
+ `config validate` and `upload all --dry-run` check local files and print the plan;
170
+ they do not contact the server. `upload all` checks compatibility before the first
171
+ write, attempts all selected files, then reads the server inventory back.
172
+ `verify` independently requires matching file kinds, names (except server-named
173
+ dSYM slices), SHA-256 bytes and successful format validation for the same local
174
+ plan. An older file of the same kind does not satisfy it.
175
+
176
+ Release precedence is `--release`, then `SENTORI_RELEASE`, then config `release`.
177
+ URL precedence is `--api-url`, then `SENTORI_API_URL`, then config `apiUrl`.
178
+ A Git override `--git-tag` takes precedence over `sources.gitTag`. Repeat the same
179
+ overrides for upload and verification; otherwise you may check a different build.
180
+ Individual legacy commands take their own arguments and do not read this config.
181
+
182
+ The app release must exactly match the SDK, including build number; it is never
183
+ guessed from `package.json`. Successful byte/format verification does **not** prove
184
+ that a binary and source revision match, or that every event can be symbolicated.
185
+ Native frames still need matching dSYM/debug IDs or mappings; source context needs
186
+ a resolved file path and line number. Supported servers reprocess stored events
187
+ after late uploads.
188
+
189
+ ## CI and exit codes
190
+
191
+ Ordinary upload failures return zero to protect the host build, while printing
192
+ what failed. `--strict` opts into a nonzero upload result. `doctor`, `config
193
+ validate` and `verify` return nonzero when their checks fail. `--dry-run` is valid
194
+ only with `upload all`, so it cannot silently bypass a verification gate.
195
+
196
+ For a dedicated required debug-artifact job, after the application build:
11
197
 
12
198
  ```sh
13
- bun add -D @goliapkg/sentori-cli
14
- # or invoke transiently with bunx / npx
199
+ # SENTORI_TOKEN and SENTORI_RELEASE are supplied by CI; URL is in the config.
200
+ npx --no-install sentori doctor --config sentori.ios.config.json
201
+ npx --no-install sentori config validate --config sentori.ios.config.json
202
+ npx --no-install sentori upload all --config sentori.ios.config.json --strict
203
+ npx --no-install sentori verify --config sentori.ios.config.json
15
204
  ```
16
205
 
17
- ## Source-map upload
206
+ Keep this job separate from compilation if upload outages must not block app
207
+ builds. For nonblocking upload hooks omit `--strict`; run `verify` separately to
208
+ notice missing files. `artifacts check --expect ...` is the older kind-presence
209
+ check and cannot replace exact-file `verify`. A receipt with unknown validation
210
+ is not a passing gate. After a partial failure, correct the cause and rerun the
211
+ same configuration and release.
212
+
213
+ ## Source ZIPs without Git
214
+
215
+ Preserve project-relative paths so modules with identical basenames remain
216
+ distinct. Supported source types include Swift, Kotlin, Java, C/C++, Objective-C,
217
+ JS/TS, Vue/Svelte, Rust, Go, Python, Ruby, PHP, C# and Dart.
18
218
 
19
219
  ```sh
20
- sentori-cli upload sourcemap \
21
- --release "my-app@1.2.3" \
22
- --token "$SENTORI_TOKEN" \
23
- ./dist
220
+ npx --no-install sentori sources pack --root . --release 'my-app@1.2.3+42' \
221
+ --exclude shared/tests --output sources.zip src shared
222
+ npx --no-install sentori upload srcbundle --api-url https://sentori.your-domain.com \
223
+ --release 'my-app@1.2.3+42' sources.zip --strict
24
224
  ```
25
225
 
26
- React Native (Android, raw maps still on disk):
226
+ The pack command writes a standard ZIP plus `sentori-source-manifest.json`
227
+ containing the release, file hashes, sizes and source provenance. It refuses to
228
+ overwrite an existing output. Limits: 10,000 files, 512 KiB per file, 64 MiB total
229
+ source text. Oversized/unreadable selected files cause an error, not truncation.
230
+ A plain ZIP of UTF-8 sources is also accepted; without a manifest, the uploader
231
+ is responsible for selecting the correct release. Include generated sources by
232
+ explicit file paths when needed; do not archive the whole output directory.
233
+
234
+ ## Pin the source revision
235
+
236
+ Git is optional. In Git mode `sources.root` must be the repository root, and the
237
+ selected tag must already be available locally. Both lightweight and annotated
238
+ tags are supported:
27
239
 
28
240
  ```sh
29
- sentori-cli react-native upload \
30
- --release "$APPLICATION_ID@$VERSION+$BUILD" \
31
- --metro-map android/app/build/intermediates/sourcemaps/react/release/index.android.bundle.packager.map \
32
- --hermes-map android/app/build/intermediates/sourcemaps/react/release/index.android.bundle.compiler.map \
33
- --bundle android/app/build/generated/assets/react/release/index.android.bundle
241
+ npx --no-install sentori upload all --git-tag v1.2.3 --release 'my-app@1.2.3+42'
242
+ npx --no-install sentori verify --git-tag v1.2.3 --release 'my-app@1.2.3+42'
34
243
  ```
35
244
 
36
- ## Issue triage (MCP)
245
+ This reads `refs/tags/v1.2.3` at its resolved commit and records tag and commit in
246
+ the manifest. It does not check out that commit, fetch tags, include dirty files
247
+ or guess the latest tag. The app build, debug files and chosen tag must come from
248
+ the same revision. Build-generated sources require directory mode from that build
249
+ workspace. An existing `sourceArchive` cannot be combined with `--git-tag`.
250
+
251
+ ## Individual uploads and Hermes
252
+
253
+ These commands remain available alongside the unified workflow:
37
254
 
38
255
  ```sh
39
- sentori-cli mcp serve --project "$PROJECT_UUID" --token "$SK_TOKEN"
256
+ npx --no-install sentori upload sourcemap --api-url https://sentori.your-domain.com \
257
+ --release "$SENTORI_RELEASE" ./dist
258
+ npx --no-install sentori upload dsym --api-url https://sentori.your-domain.com \
259
+ --release "$SENTORI_RELEASE" ./MyApp.app.dSYM
260
+ npx --no-install sentori upload mapping --api-url https://sentori.your-domain.com \
261
+ --release "$SENTORI_RELEASE" ./mapping.txt
262
+ npx --no-install sentori react-native upload --api-url https://sentori.your-domain.com \
263
+ --release "$SENTORI_RELEASE" --metro-map metro.map --hermes-map hermes.map
40
264
  ```
41
265
 
42
- Then point a MCP-compatible agent (Claude Code config / Cursor / etc.)
43
- at the spawned stdio server.
266
+ A source-map directory preserves relative filenames; individual files retain their
267
+ basename. Legacy individual dSYM upload uses macOS `dwarfdump`, or explicit
268
+ `--debug-id` and `--arch`; unified `upload all` passes the Mach-O binaries from the
269
+ `.dSYM` bundle and lets the server identify slices.
270
+
271
+ For Hermes, use the final composed map emitted by the build in
272
+ `artifacts.sourcemap`. If the build emits separate Metro and Hermes maps, use
273
+ `react-native upload` to compose them with the application's installed React
274
+ Native tooling. A Metro map alone does not resolve Hermes bytecode offsets.
275
+
276
+ ## Troubleshooting
277
+
278
+ | Result | Action |
279
+ | --- | --- |
280
+ | No `sentori` binary / unknown `init` | Check the installed package version; use 1.6.0 or its supplied tarball, with `npx --no-install` |
281
+ | `doctor` rejects ZIP capability | Upgrade/deploy the compatible server; CLI installation cannot enable server support |
282
+ | 401 / `admin_token_required` | Use an API-scope token for the correct project and instance |
283
+ | Local file missing | Build first; resolve paths relative to the configuration file |
284
+ | ZIP manifest release mismatch | Repack from the intended build with exactly the SDK release string |
285
+ | Missing exact bytes / format not verified | Rerun the same upload, inspect individual failures, and verify again |
286
+ | Files verified, frame still unresolved | Check actual release, dSYM/debug identity, composed map, source path and line |
44
287
 
45
- → Full reference: `sentori-cli --help`
46
- → Recipes: [sentori.golia.jp/docs/recipes/sourcemap-upload](https://sentori.golia.jp/docs/recipes/sourcemap-upload)
288
+ Use `npx --no-install sentori --help` for issue, probe, MCP and push commands.
47
289
 
48
290
  ## License
49
291
 
@@ -12,6 +12,13 @@ export type ArtifactRow = {
12
12
  usable?: boolean | null;
13
13
  };
14
14
  export type ArtifactsResponse = {
15
+ /** Additive capability advertisement. Absent on older servers. */
16
+ compatibility?: {
17
+ serverVersion: string;
18
+ artifactProtocol: number;
19
+ sourceBundleFormats: string[];
20
+ verification: string[];
21
+ };
15
22
  release: string;
16
23
  /** False when the server has never heard this release name at all
17
24
  * — a typo in `--release` and an un-uploaded release look the
@@ -1 +1 @@
1
- {"version":3,"file":"artifacts.d.ts","sourceRoot":"","sources":["../src/artifacts.ts"],"names":[],"mappings":"AAqBA,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,kEAAkE;IAClE,OAAO,EAAE,IAAI,GAAG,MAAM,CAAA;IACtB,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB;;uEAEmE;IACnE,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,CAAA;CACxB,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,MAAM,CAAA;IACf;;sEAEkE;IAClE,KAAK,EAAE,OAAO,CAAA;IACd,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC7B,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,SAAS,EAAE,WAAW,EAAE,CAAA;CACzB,CAAA;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE;IACzC,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;CACd,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAc7B"}
1
+ {"version":3,"file":"artifacts.d.ts","sourceRoot":"","sources":["../src/artifacts.ts"],"names":[],"mappings":"AAqBA,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,kEAAkE;IAClE,OAAO,EAAE,IAAI,GAAG,MAAM,CAAA;IACtB,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB;;uEAEmE;IACnE,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,CAAA;CACxB,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,kEAAkE;IAClE,aAAa,CAAC,EAAE;QACd,aAAa,EAAE,MAAM,CAAA;QACrB,gBAAgB,EAAE,MAAM,CAAA;QACxB,mBAAmB,EAAE,MAAM,EAAE,CAAA;QAC7B,YAAY,EAAE,MAAM,EAAE,CAAA;KACvB,CAAA;IACD,OAAO,EAAE,MAAM,CAAA;IACf;;sEAEkE;IAClE,KAAK,EAAE,OAAO,CAAA;IACd,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC7B,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,SAAS,EAAE,WAAW,EAAE,CAAA;CACzB,CAAA;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE;IACzC,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;CACd,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAkB7B"}
package/lib/artifacts.js CHANGED
@@ -28,6 +28,10 @@ export async function fetchArtifacts(opts) {
28
28
  const detail = await resp.text().catch(() => '');
29
29
  throw new Error(`${resp.status} ${resp.statusText}${detail ? ` — ${detail.slice(0, 200)}` : ''}`);
30
30
  }
31
- return (await resp.json());
31
+ const result = await resp.json();
32
+ if (!result || result.release !== opts.release || typeof result.known !== 'boolean' || !Array.isArray(result.artifacts)) {
33
+ throw new Error('Unsupported artifact inventory response; check --api-url and upgrade the Sentori server or CLI together.');
34
+ }
35
+ return result;
32
36
  }
33
37
  //# sourceMappingURL=artifacts.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"artifacts.js","sourceRoot":"","sources":["../src/artifacts.ts"],"names":[],"mappings":"AAAA,iDAAiD;AACjD,EAAE;AACF,qEAAqE;AACrE,iEAAiE;AACjE,iEAAiE;AACjE,+DAA+D;AAC/D,sEAAsE;AACtE,oEAAoE;AACpE,kCAAkC;AAClC,EAAE;AACF,iEAAiE;AACjE,kEAAkE;AAClE,mEAAmE;AACnE,mEAAmE;AACnE,6CAA6C;AAC7C,EAAE;AACF,gEAAgE;AAChE,+DAA+D;AAC/D,4DAA4D;AAC5D,uCAAuC;AA2BvC,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAIpC;IACC,MAAM,GAAG,GACP,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,eAAe;QACjD,GAAG,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,CAAA;IACjD,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;QAC5B,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,IAAI,CAAC,KAAK,EAAE,EAAE;KACnD,CAAC,CAAA;IACF,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;QACb,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAA;QAChD,MAAM,IAAI,KAAK,CACb,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CACjF,CAAA;IACH,CAAC;IACD,OAAO,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,CAAsB,CAAA;AACjD,CAAC"}
1
+ {"version":3,"file":"artifacts.js","sourceRoot":"","sources":["../src/artifacts.ts"],"names":[],"mappings":"AAAA,iDAAiD;AACjD,EAAE;AACF,qEAAqE;AACrE,iEAAiE;AACjE,iEAAiE;AACjE,+DAA+D;AAC/D,sEAAsE;AACtE,oEAAoE;AACpE,kCAAkC;AAClC,EAAE;AACF,iEAAiE;AACjE,kEAAkE;AAClE,mEAAmE;AACnE,mEAAmE;AACnE,6CAA6C;AAC7C,EAAE;AACF,gEAAgE;AAChE,+DAA+D;AAC/D,4DAA4D;AAC5D,uCAAuC;AAkCvC,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAIpC;IACC,MAAM,GAAG,GACP,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,eAAe;QACjD,GAAG,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,CAAA;IACjD,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;QAC5B,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,IAAI,CAAC,KAAK,EAAE,EAAE;KACnD,CAAC,CAAA;IACF,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;QACb,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAA;QAChD,MAAM,IAAI,KAAK,CACb,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CACjF,CAAA;IACH,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,EAAuB,CAAA;IACrD,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,OAAO,KAAK,IAAI,CAAC,OAAO,IAAI,OAAO,MAAM,CAAC,KAAK,KAAK,SAAS,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;QACxH,MAAM,IAAI,KAAK,CAAC,0GAA0G,CAAC,CAAA;IAC7H,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { ArtifactsResponse } from './artifacts.js';
2
+ /** Read the installed package, so a release bump cannot leave a stale constant. */
3
+ export declare const CLI_VERSION: string;
4
+ /** Capabilities, not a server version comparison: backports may share versions. */
5
+ export declare function checkCompatibility(result: ArtifactsResponse, sourceZip: boolean): string;
6
+ //# sourceMappingURL=compatibility.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compatibility.d.ts","sourceRoot":"","sources":["../src/compatibility.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAEvD,mFAAmF;AACnF,eAAO,MAAM,WAAW,EAAE,MAA8F,CAAA;AAExH,mFAAmF;AACnF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,iBAAiB,EAAE,SAAS,EAAE,OAAO,GAAG,MAAM,CAaxF"}
@@ -0,0 +1,21 @@
1
+ import { readFileSync } from 'node:fs';
2
+ /** Read the installed package, so a release bump cannot leave a stale constant. */
3
+ export const CLI_VERSION = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version;
4
+ /** Capabilities, not a server version comparison: backports may share versions. */
5
+ export function checkCompatibility(result, sourceZip) {
6
+ const info = result.compatibility;
7
+ if (info === undefined) {
8
+ if (sourceZip)
9
+ throw new Error('This server does not advertise source ZIP support. No files were uploaded. Upgrade the Sentori server to a build advertising artifact protocol 1 and sourceBundleFormats: ["zip"], then run sentori doctor. Existing CLI 1.5 pipelines may continue using legacy JSON sources.');
10
+ return 'Legacy artifact API: server capabilities are not advertised. Source maps, dSYM and mapping uploads remain available; verification still requires matching SHA-256 bytes and usable: true.';
11
+ }
12
+ if (!info || typeof info.serverVersion !== 'string' || info.artifactProtocol !== 1
13
+ || !Array.isArray(info.sourceBundleFormats) || !Array.isArray(info.verification)
14
+ || !info.verification.includes('sha256') || !info.verification.includes('format')) {
15
+ throw new Error('Unsupported server artifact protocol or verification capabilities. No files were uploaded. Install a compatible CLI/server pair; use sentori --version and sentori doctor to inspect it.');
16
+ }
17
+ if (sourceZip && !info.sourceBundleFormats.includes('zip'))
18
+ throw new Error(`Sentori server ${info.serverVersion} does not support source ZIPs. No files were uploaded. Upgrade the server before uploading sources.`);
19
+ return `Sentori CLI ${CLI_VERSION}; server ${info.serverVersion}; artifact protocol 1; source ZIP ${info.sourceBundleFormats.includes('zip') ? 'supported' : 'unavailable'}.`;
20
+ }
21
+ //# sourceMappingURL=compatibility.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compatibility.js","sourceRoot":"","sources":["../src/compatibility.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAGtC,mFAAmF;AACnF,MAAM,CAAC,MAAM,WAAW,GAAW,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,GAAG,CAAC,iBAAiB,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAA;AAExH,mFAAmF;AACnF,MAAM,UAAU,kBAAkB,CAAC,MAAyB,EAAE,SAAkB;IAC9E,MAAM,IAAI,GAAG,MAAM,CAAC,aAAa,CAAA;IACjC,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,IAAI,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,gRAAgR,CAAC,CAAA;QAChT,OAAO,2LAA2L,CAAA;IACpM,CAAC;IACD,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,CAAC,aAAa,KAAK,QAAQ,IAAI,IAAI,CAAC,gBAAgB,KAAK,CAAC;WAC7E,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC;WAC7E,CAAC,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QACpF,MAAM,IAAI,KAAK,CAAC,0LAA0L,CAAC,CAAA;IAC7M,CAAC;IACD,IAAI,SAAS,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,IAAI,CAAC,aAAa,qGAAqG,CAAC,CAAA;IACtN,OAAO,eAAe,WAAW,YAAY,IAAI,CAAC,aAAa,qCAAqC,IAAI,CAAC,mBAAmB,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,aAAa,GAAG,CAAA;AAC/K,CAAC"}