@jacebenson/jsn 4.3.8 → 4.3.9-dev.62

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 CHANGED
@@ -1,445 +1,39 @@
1
- # JSN — ServiceNow CLI
1
+ # JSN: ServiceNow CLI
2
2
 
3
- A command-line interface for ServiceNow. Type `jsn` and start working — no web UI needed.
4
-
5
- Works standalone or with AI agents (Claude Code, OpenCode, Hermes, Cursor, Copilot).
3
+ A command-line interface for ServiceNow. Read and update records, manage flows, inspect platform data, and automate routine work from your terminal or AI agent.
6
4
 
7
5
  ## Install
8
6
 
9
- ```bash
10
- npm install -g @jacebenson/jsn
11
- ```
12
-
13
- Node.js 22.5.0 or newer. macOS, Linux, Windows.
14
-
15
- The install also copies an AI agent skill file to `~/.agents/skills/servicenow/SKILL.md`.
16
-
17
- ### Install from GitHub (test unreleased builds)
18
-
19
- To test a branch or the freshly merged `main` before it's published to npm:
20
-
21
- ```bash
22
- npm install -g github:jacebenson/jsn#main # merged but not yet tagged
23
- npm install -g github:jacebenson/jsn#<branch> # any PR/feature branch
24
- ```
25
-
26
- Go back to the released version with:
7
+ Requires Node.js 22.5.0 or newer. Supports macOS, Linux, and Windows.
27
8
 
28
9
  ```bash
29
10
  npm install -g @jacebenson/jsn
11
+ jsn setup
30
12
  ```
31
13
 
32
- **Note:** git installs build `better-sqlite3` (docs search) from source, so the machine needs build tools (Python, make, a C++ compiler).
33
-
34
- ## Direct MID installation
35
-
36
- ```sh
37
- jsn midservers add --install-method direct --name worker1
38
- ```
39
-
40
- Official x86-64 ZIPs for Linux and Windows, selected from the instance's
41
- `mid.version`. Credentials are prompted and validated before installation.
42
- Linux supports a user-owned background runtime. Windows currently prepares
43
- private files and offers a foreground console, not native service registration.
44
- See [direct MID installation](docs/mid-direct-install.md) for security boundaries,
45
- lifecycle commands and the untested Windows host phases. Docker remains available.
46
-
47
- ## Quick Start
48
-
49
- ```bash
50
- jsn setup # Interactive: add, switch, remove, modify instances
51
- jsn incidents list --query "priority=1" # Critical incidents
52
- ```
53
-
54
- ## What It Does
14
+ See the [setup guide](docs/docs-setup.md) to connect an instance safely.
55
15
 
56
- jsn talks to the ServiceNow REST API. Read, create, update, delete — tickets, users, groups, records on any table. Inspect flows and business rules. Export update sets. Run background scripts.
57
-
58
- Everything outputs JSON when piped, styled tables in a terminal.
16
+ ## Try it
59
17
 
60
18
  ```bash
61
- # Core ticket work
62
19
  jsn incidents list --query "active=true^priority=1"
63
- jsn incidents INC0010001
64
- jsn incidents create --description "Server down" --priority 1
65
-
66
- # Admin tasks
67
- jsn flows list # Interactive picker with pagination
68
- jsn flows show "Assign Task" # Full flow detail
69
- jsn flows executions # All states, newest first
70
- jsn flows executions --active # Only waiting/running/queued
71
- jsn flows executions --since "2026-08-25 00:00:00" --until "2026-08-26 00:00:00"
72
- jsn flows executions --summary # Server totals + sampled duration metrics by flow
73
- jsn flows executions --limit 100 # Inspect more rows from the matching population
74
- jsn flows executions --record <sys_id> # Executions for one source record
75
- jsn flows create --data-file flow.manifest.json # Create a draft record-triggered flow
76
- jsn rules list --query "collection=incident"
77
- jsn updatesets set "My Feature"
78
-
79
- # Generic table access
80
- jsn records list --table incident --limit 50 --json | jq '.data.records[].number'
81
- jsn records create --table incident --data '{"short_description":"test"}'
82
- jsn records list --table incident --limit 50 --get "data.records.0.number" # no jq needed
83
- jsn records list --table incident --query "active=true" # totals included by default
84
- jsn records list --table incident --limit 10 --no-count # opt out of the total query
85
- jsn records bulk --table incident --query "priority=1" --set '{"state":"3"}' # dry-run by default
86
- jsn records get --table incident --sys-id 8a1234abcd5678 --attachments # record + its files
87
- jsn records list --table incident --limit 10 --csv # output CSV
88
-
89
- # Read-only cross-profile record diff
90
- jsn diff record --table incident --sys-id <sys_id> --profile-a dev --profile-b prod
91
- jsn diff record --table incident --sys-id <sys_id> --profile-a dev --profile-b prod --ignore state
92
- jsn diff record --table incident --sys-id <sys_id> --profile-a dev --profile-b prod --no-ignore
93
-
94
- # Script execution
95
- jsn eval "gs.info('Hello World')"
96
- cat script.js | jsn eval --stdin # pipe a script in (no shell escaping)
97
-
98
- # Now GraphQL (documents may contain mutations; read-only profiles are blocked)
99
- jsn graphql --query '{ GlideRecord_Query { incident(pagination: {limit: 2}) { _results { number { value } } } } }'
100
- cat incident.graphql | jsn graphql --stdin --variables '{"limit":2}'
101
-
102
- # Saved query snippets (stored locally, run against the active profile)
103
- jsn snippets save open-inc --table incident --query "active=true"
104
- jsn snippets run open-inc
105
-
106
- # Live log tailing (Ctrl+C to stop)
107
- jsn logs follow --level error --tail 10
108
- ```
109
-
110
- ### Flow Designer actions
111
-
112
- `actions` and `action` are the same command. `list`, `show`, and explicit
113
- `delete` remain available. In a human terminal, omit the target to open a
114
- searchable picker. Create and edit guide you through provider schemas and save
115
- full Process Flow definitions, not ordinary table-field updates.
116
-
117
- ```bash
118
- jsn actions create # Guided scope, name, schemas, inputs and script
119
- jsn actions edit # Pick an action, edit its saved definition
120
- jsn actions definition # Pick and inspect a complete definition
121
- jsn actions test --wait # Pick an action, supply saved-schema inputs, confirm
122
- jsn actions step-types # Pick the exact transaction scope
123
- ```
124
-
125
- Script inputs offer a file path or your `$VISUAL`/`$EDITOR`. New script steps can
126
- declare outputs and map them to action outputs. Variable declarations reuse
127
- provider schema templates and receive fresh UI identities. Unknown nested
128
- schema fields are retained. Unsupported input types stop with instructions to
129
- use `--data-file`; JSN does not silently omit them or invent schema defaults.
130
- Creation waits for an explicit final confirmation before allocating a parent.
131
- It saves a draft only and never publishes or runs it automatically.
132
-
133
- For automation, keep using explicit identifiers and full JSON:
134
-
135
- ```bash
136
- jsn actions step-types --scope <scope_sys_id> --get data > step-types.json
137
- jsn actions create --scope <scope_sys_id> --data-file new-action.json --json
138
- jsn actions definition <action_sys_id> --get data > action.json
139
- # Edit action.json, retaining its ID, scope, lifecycle fields and all arrays.
140
- jsn actions update <action_sys_id> --data-file action.json --json
141
- jsn action edit <action_sys_id> --data-file action.json --json # same operation
142
- jsn actions test <action_sys_id> --force --output-map '{"response":"hello"}' --wait --timeout 60 --json
143
- jsn actions test <action_sys_id> --force --data-file action.json --output-map '{"response":"hello"}' --wait --get data.outputs.response.value
144
- jsn actions delete <action_sys_id> --force # explicit cleanup, never automatic
20
+ jsn records list --table incident --limit 10 --json
21
+ jsn flows list
145
22
  ```
146
23
 
147
- Existing targets infer their exact transaction scope from the action record.
148
- An explicit `--scope` must match that record byte-for-byte and exist in
149
- `sys_scope`. For Global, use the literal sys_id `global`.
150
-
151
- This **illustrative create document** shows an input/script/output mapping.
152
- Replace `<SCRIPT_STEP_TYPE_SYS_ID>` with the target's `step-types` result.
153
- Start from that step type's full input/output schemas, preserving runtime
154
- settings, status outputs and any other required fields. The shortened example
155
- is not a captured or instance-verified template. New steps/variables need their
156
- own identifiers; do not clone another action's record IDs. Leave new steps'
157
- `step_id` and `action` blank or omitted. Create binds their `action` to the newly
158
- allocated parent; persisted step IDs or parent bindings are rejected before creation.
159
-
160
- ```json
161
- {
162
- "name": "Echo response",
163
- "description": "Return the supplied response",
164
- "internal_name": "echo_response",
165
- "inputs": [{"name": "response", "label": "Response", "type": "string", "mandatory": true}],
166
- "outputs": [{"name": "response", "label": "Response", "type": "string", "value": "{{step[11111111-2222-4333-8444-555555555555].response}}"}],
167
- "steps": [{
168
- "cid": "11111111-2222-4333-8444-555555555555",
169
- "step_type": "SCRIPT",
170
- "step_type_id": "<SCRIPT_STEP_TYPE_SYS_ID>",
171
- "label": "Echo script",
172
- "order": 1,
173
- "inputs": [
174
- {"name": "required_run_time", "type": "choice", "value": "instance"},
175
- {"name": "script", "type": "script", "value": "(function execute(inputs, outputs) { outputs.response = inputs.response; })(inputs, outputs);"}
176
- ],
177
- "outputs": [],
178
- "extended_inputs": [{"name": "response", "type": "string", "value": "{{inputs.response}}", "extended": true}],
179
- "extended_outputs": [{"name": "response", "type": "string", "extended": true}]
180
- }]
181
- }
182
- ```
24
+ Run `jsn --help` for the command list. See the [CLI guide](docs/cli-guide.md) for authentication, output formats, development, and examples. The [docs site](https://jacebenson.github.io/jsn/) has feature guides.
183
25
 
184
- Create first inserts the parent in `sys_hub_action_type_definition`, reads that
185
- parent's own Process Flow defaults, then saves the inputs, outputs and nonempty
186
- steps with the internal `/api/now/processflow/action/action_types/{id}` PUT.
187
- It retains fresh lifecycle/status metadata instead of copying source snapshot
188
- or status IDs. A subsequent definition GET plus `/step_instances` GET verifies
189
- scripts, mappings and schemas. Update requires a complete document from
190
- `definition`, including target-owned lifecycle fields. Omitted fields are
191
- rejected before mutation. A PUT timeout triggers readback, not another PUT;
192
- `persisted_after_timeout` means persistence was verified, not that HTTP success
193
- was received. Failed creation reports the parent ID and leaves cleanup to you.
194
-
195
- Test sends `action`, `outputMap`, `runOnThread` and `tracingEnabled` to `/test`.
196
- Despite its wire name, `--output-map` supplies **action inputs**. Without
197
- `--wait`, success means dispatch only, with `status: dispatched` and no outputs.
198
- With `--wait`, JSN requires a `COMPLETE` context, blank `error_message`, and each
199
- declared user output's actual value from `sys_flow_runtime_value`. Output entries
200
- have `value`, `displayValue` and `hasValue`; false, zero and empty strings count.
201
- Failures, denied/missing outputs and timeouts exit nonzero. The wait budget
202
- starts after dispatch, defaults to 60 seconds, and is capped at 3600 seconds.
203
- Timeout does not cancel the server execution. Draft actions can be tested without
204
- publication. Testing runs their scripts and can have side effects. It requires
205
- interactive confirmation or `--force`, unless the profile explicitly skips
206
- confirmations. Read-only profiles block create/update/delete/test. No publish
207
- or snapshot command is added.
208
- These internal endpoints and runtime-table permissions can vary by instance.
209
-
210
- ### Flow execution fields
211
-
212
- ### Flow Designer terminal workflows
213
-
214
- ```bash
215
- jsn flows create # Guided record trigger and published action inputs
216
- jsn flows edit # Pick an inactive Draft, edit condition or existing inputs
217
- jsn flows show # Pick a flow to inspect
218
- jsn flows status # Pick a flow to diagnose
219
- jsn flows test # Pick flow and a record from its actual trigger table, confirm
220
- jsn flows publish # Pick a flow, explicitly confirm activation
221
- # Automation must explicitly confirm publication too:
222
- jsn flows publish <flow_sys_id> --force --json
223
- ```
224
-
225
- Wizards and omitted-target pickers require both a prompt-capable terminal and
226
- `auto` output. `--json`, explicit formats, `--get`, `--data`, `--data-file`,
227
- `--output-map`, pipes, `CI`, `NONINTERACTIVE` and `JSN_NO_PROMPTS=1` never open a
228
- wizard. In those modes provide identifiers and required scope/record/data
229
- explicitly. Tests, publish and deletion require `--force` or the profile's
230
- existing explicit skip-confirmations setting when prompting is unavailable.
231
- Read-only profiles still block mutations before authentication or prompting.
232
- Press Escape in record pickers or Ctrl+C in any wizard to cancel. Refusing a
233
- final confirmation leaves the instance untouched.
234
-
235
- Existing action/flow scopes come from saved raw identifiers. New scopes offer
236
- the active application preference if available, then a searchable scope picker.
237
- No scope name is guessed. Input pills are passed through unchanged.
238
-
239
- Flow edits are limited to the existing manifest contract. They do not rename
240
- flows, add/remove steps, implement Flow Logic, subflows, variables or error
241
- handling. Flow deletion remains unsupported. Action deletion retains its
242
- existing explicit parent-record cleanup and absence readback, not a new native
243
- builder deletion lifecycle. See [command coverage and local proof](docs/flow-designer-interactive.md).
244
-
245
- ### Flow creation manifest
246
-
247
- `jsn flows create` accepts `--data-file` or inline `--data`, or guides human
248
- terminal users without those options. The supported slice
249
- supports one `record_create_or_update` trigger and published Action types whose
250
- input schemas are available from the instance. JSN resolves action metadata,
251
- defaults, and typed inputs from ServiceNow; callers do not provide GraphQL or
252
- editor-generated identifiers.
253
-
254
- ```json
255
- {
256
- "name": "JSN-STUDY-ticket-log",
257
- "description": "Log JSN study tickets",
258
- "scope": "<SCOPE_SYS_ID>",
259
- "trigger": {
260
- "type": "record_create_or_update",
261
- "table": "ticket",
262
- "condition": "short_descriptionSTARTSWITHJSN-STUDY-"
263
- },
264
- "actions": [
265
- { "type": "Log", "inputs": { "log_level": "info", "log_message": "Ticket received" } }
266
- ]
267
- }
268
- ```
26
+ ## AI agents
269
27
 
270
- `scope` is required; JSN never defaults it to a tenant-specific or captured
271
- scope. The record-trigger condition currently accepts one simple encoded-query
272
- predicate (`fieldSTARTSWITHvalue`, `field=value`, `fieldLIKEvalue`, and the
273
- equivalent single-predicate operators). Compound `^`/`^OR` expressions are
274
- rejected until their predicate AST mapping is proven against the instance.
275
-
276
- Creation leaves the flow in Draft and inactive, then creates and reads back an
277
- Autosave version. `flows publish` remains a separate lifecycle operation: it
278
- reads the saved ProcessFlow definition, POSTs `/snapshot`, creates an
279
- `Activate/Publish` version, reads version history, and verifies `active=true`.
280
- Do not use it in a dry-run: publishing activates the flow.
281
-
282
- `jsn flows test <identifier> --record <sys_id>` is also a mutating operation.
283
- It requires confirmation (or `--force`), reads the saved definition, verifies
284
- that the supplied record exists in the record-trigger table, and POSTs the full
285
- definition to the ProcessFlow test endpoint. A response with only a context ID
286
- is reported as accepted, not completed. Test runs execute actions and can have
287
- side effects; this slice does not execute test flows during development.
288
-
289
- Flow Logic, subflows, flow-variable lifecycle, and error handling are deferred
290
- and rejected by this slice rather than silently ignored.
291
-
292
- `jsn flows update <identifier> --data-file update.json` updates supported fields
293
- of existing inactive Draft flows. The manifest may contain only `trigger` (with
294
- `table` and/or `condition`) and `actions`. Each action selects one saved instance
295
- by its one-based `order` and reusable action `type` (name or sys_id). Its `inputs`
296
- object may change only inputs already present in that saved Action. The CLI reads
297
- the saved component identities itself; manifests never carry UI UUIDs. Omitted
298
- trigger fields, Action inputs, metadata, and other flow components are preserved.
299
- Names must resolve to exactly one flow, and the protected `jace-test-flow` is
300
- rejected. Update sends the captured GraphQL `triggerInstances.update` and/or
301
- `actions.update` shapes, reads the full ProcessFlow definition back, creates an
302
- Autosave version, and reports update-set captures. It never publishes or
303
- activates the flow. Unsupported Flow Logic, subflows, variables, error handling,
304
- new Action inputs, ambiguous selectors, active flows, and non-Draft flows are
305
- rejected before mutation.
306
-
307
- `jsn flows executions` reads `sys_flow_context` and returns both the raw row and a normalized `execution` object. JSN discovers the runtime columns from `sys_dictionary` first, then uses these mappings:
308
-
309
- - `started`: `started`, `start_time`, `started_at`, then `sys_created_on`
310
- - `ended`: `ended`, `end_time`, `ended_at`, then `completed_on`
311
- - `duration_seconds`: stored `duration`, `run_time`, or `execution_duration`, otherwise `ended - started`
312
- - `waiting_age_seconds`: current time minus `started` while the status is waiting, queued, paused, or pending
313
- - `execution_age_seconds`: current time minus `started` while the status is running, executing, or processing
314
- - `status`: `state`, then `status`, then `execution_state`
315
- - `error`: `error`, `error_message`, `exception`, then `message`
316
-
317
- The timestamp fields are instance-dependent. `sys_created_on` is a fallback for when the context row was created, not a claim that it is the true runtime start time.
318
-
319
- ## Commands
320
-
321
- Run `jsn` for the full grouped list. Commands are organized by ServiceNow domain:
322
-
323
- **Core** — `incidents`, `changes`, `requests`, `tasks`, `tickets`
324
-
325
- **Automation** — `flows`, `actions`, `rules`, `scrapi`, `updatesets`, `eval`, `rest`
326
-
327
- **Access** — `acls`, `roles`, `scopes`, `properties`, `privileges`
328
-
329
- **User Experience** — `forms`, `lists`, `clientscripts`, `uipolicies`, `uiactions`
330
-
331
- **Data** — `records` (list/get/create/update/delete/count/bulk/attachments), `diff record` (cross-profile read-only comparison), `tables`, `columns`, `includes`, `import`, `logs` (list/show/follow), `snippets` (save/run), `users`, `groups`
332
-
333
- **Platform** — `platform health` (cluster records) and `platform stats` (bounded, read-only `/xmlstats.do` summary; allowlisted metrics only)
334
-
335
- Every command supports `--json`, `--query`, and `--help`.
336
-
337
- For multiline script fields, `--data-file` parses the file as UTF-8 JSON and
338
- passes the resulting values through unchanged. On Windows PowerShell, generate
339
- the payload as JSON text and inspect it before invoking JSN, for example:
340
-
341
- ```powershell
342
- $script = [string](Get-Content -Path .\Example.script.js -Raw)
343
- $payload = @{ script = $script; description = 'Example' } |
344
- ConvertTo-Json -Depth 5 -Compress
345
- Set-Content -Path .\Example.update.json -Value $payload -Encoding UTF8
346
- Get-Content .\Example.update.json -Raw | ConvertFrom-Json | Select-Object -ExpandProperty script
347
- jsn includes update Example --data-file .\Example.update.json --json
348
- ```
349
-
350
- `JSON.parse` preserves a JSON string as a JavaScript string, and the SDK
351
- serializes update payloads with `JSON.stringify`. If the file already contains
352
- a `{value=...}` or FileSystem-provider representation, that text is external
353
- to JSN and must be corrected before the update. Use a read-back command or
354
- `records update --strict` where applicable to verify persisted fields.
355
-
356
- ## Instances
357
-
358
- Switch between instances without re-authenticating.
359
-
360
- ```bash
361
- jsn setup # Interactive: add, switch, remove, or modify instances
362
- jsn auth status # Dashboard — auth state, read-only 🔒, skip-confirmations ⚡
363
- ```
364
-
365
- `jsn setup` is the human front door — one command, interactive menu. For scripts and CI, use the atomic commands:
366
-
367
- ```bash
368
- jsn auth login https://dev12345.service-now.com # Add + authenticate (scripted)
369
- jsn auth refresh # Manually refresh the OAuth token
370
- jsn auth switch dev12345 # Flip the active profile (scripted)
371
- jsn auth modify dev12345 # Toggle read-only / skip confirmations
372
- ```
373
-
374
- ## Output Formats
375
-
376
- | Flag | When to use |
377
- |------|-------------|
378
- | (default) | Terminal — styled tables |
379
- | `--json` | Pipelines, scripts |
380
- | `--markdown` | Documentation |
381
- | `--quiet` / `-q` | Data only, no envelope |
382
- | `--csv` | Spreadsheets (opens straight into Excel) |
383
- | `--get <path>` | Pull one value out of the JSON envelope — no jq (e.g. `--get "data.records.0.number"`) |
384
-
385
- ## Authentication
386
-
387
- JSN supports OAuth 2.0 with PKCE, Basic Auth, and browser-session auth. Credentials are stored in the operating system's credential store, with a file fallback under `~/.config/servicenow/credentials/` when the keyring is unavailable.
388
-
389
- ```bash
390
- jsn auth login https://dev12345.service-now.com
391
- ```
392
-
393
- For Basic Auth, set `SN_USERNAME` and `SN_PASSWORD`, then run `jsn auth login --basic <instance>`.
394
- For browser-session auth, run `jsn auth login --gck <instance>` and paste the request headers when prompted.
395
-
396
- For CI/CD, set environment variables:
397
-
398
- ```bash
399
- export SERVICENOW_INSTANCE_URL="https://dev12345.service-now.com"
400
- export SERVICENOW_OAUTH_TOKEN="***"
401
- jsn incidents list
402
- ```
403
-
404
- For the credential identity, auth-method, diagnostics, migration, and concurrency contract, see [`docs/AUTH_LIFECYCLE.md`](docs/AUTH_LIFECYCLE.md).
405
-
406
- GraphQL uses ServiceNow's `/api/now/graphql` endpoint and sends `{query, variables}` through the authenticated SDK transport. The command preserves GraphQL `data` and `errors`; introspection and available namespaces depend on the target instance.
407
-
408
- `jsn platform stats` uses a fixed `GET /xmlstats.do` request. It never accepts a processor path, query parameters, or request body, and it does not emit raw XML, cookies, credentials, or full node identifiers. Oversized, HTML/login, permission, unsupported, timeout, and malformed responses are reported with bounded collector statuses; missing metrics remain unavailable rather than being reported as zero.
409
-
410
- ## Local data
411
-
412
- JSN stores durable local data under `~/.jsn/`. Documentation lives in
413
- `~/.jsn/docs/`, including `docs.db`, the ServiceNowDocs source checkout, and
414
- community markdown. Configuration stays under `~/.config/servicenow/` and
415
- credentials use the operating system's credential store, with a file fallback under `~/.config/servicenow/credentials/`.
416
-
417
- Existing installations migrate documentation from
418
- `~/.cache/servicenow-cli/docs/` automatically the first time the docs data is
419
- accessed. The old directory is removed only after the destination is verified.
420
-
421
-
422
- ## AI Agents
423
-
424
- jsn ships with a skill file that tells AI agents how to use it. Installed automatically to `~/.agents/skills/servicenow/SKILL.md`.
425
-
426
- ```bash
427
- jsn skill show # View the skill
428
- jsn skill install # Install to Hermes
429
- jsn skill install --target all # All supported agents
430
- ```
28
+ JSN includes a skill for Claude Code, OpenCode, Hermes, Cursor, Copilot, and other agents. Use `jsn skill install --target all` to install it or `jsn skill show` to inspect it.
431
29
 
432
30
  ## Development
433
31
 
434
32
  ```bash
435
- git clone https://github.com/jacebenson/jsn.git
436
- cd jsn
437
33
  npm install
438
34
  npm test
439
35
  ```
440
36
 
441
- Releases run from the GitHub Actions workflow. Dispatch `Release production` with `patch`, `minor`, or `major`. The workflow tests and lints the repo, bumps the version, pushes the commit and tag, dispatches the npm publish workflow, and creates the GitHub release.
442
-
443
37
  ## License
444
38
 
445
39
  MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jacebenson/jsn",
3
- "version": "4.3.8",
3
+ "version": "4.3.9-dev.62",
4
4
  "description": "A command-line interface for ServiceNow that follows the Unix philosophy: simple, composable, and scriptable.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -8,7 +8,8 @@
8
8
  },
9
9
  "main": "src/cli.js",
10
10
  "scripts": {
11
- "test": "node --experimental-test-module-mocks --test --test-concurrency=1 $(find test -name '*.test.js')",
11
+ "test": "node --experimental-test-module-mocks --test --test-concurrency=4 $(find test -name '*.test.js')",
12
+ "test:mutation": "stryker run",
12
13
  "lint": "npx eslint src/ bin/ test/",
13
14
  "start": "node bin/jsn.js",
14
15
  "docs": "npm --prefix docs run build",
@@ -35,6 +36,7 @@
35
36
  },
36
37
  "devDependencies": {
37
38
  "@eslint/js": "^10.0.1",
39
+ "@stryker-mutator/core": "^10.0.0",
38
40
  "globals": "^17.6.0"
39
41
  },
40
42
  "files": [
@@ -73,6 +73,60 @@ Pick the most specific tool for the job. **Never default to eval** — it's the
73
73
  | **Docs** | `docs sync`, `docs status`, `docs search`, `docs serve` |
74
74
  | **Developer** | `eval`, `rest`, `skill`, `version` |
75
75
 
76
+ ### Customization evidence
77
+
78
+ ```sh
79
+ jsn customizations capture --label review --customer-scope x_acme_app --json
80
+ jsn customizations list --json
81
+ jsn customizations show <run_id> --get data.categories
82
+ jsn customizations show <run_id> --get data.triage
83
+ jsn customizations html <run_id> [--out review.html]
84
+ jsn customizations compare <baseline_id> <new_id> --json
85
+ ```
86
+
87
+ Capture performs bounded GET-only table reads and saves local reports. It never
88
+ evals or creates, updates or deletes instance records. Standard authentication
89
+ may refresh OAuth tokens, store refreshed credentials locally and bootstrap a
90
+ session/GCK using authentication POSTs. List/show/compare are offline.
91
+ Declare customer ownership with repeatable `--customer-scope` values or sys_ids;
92
+ never infer customer ownership from a non-global scope. The declaration is
93
+ caller evidence, not independent publisher verification. OOTB findings reflect
94
+ historical upgrade skips, not a current verified baseline deviation. The upgrade
95
+ history is read server-side for the skip dispositions and every observed skipped
96
+ `file_name` is resolved against `sys_metadata` by chunked
97
+ `sys_update_nameIN<chunk>` lookups, so the OOTB inventory covers the observed
98
+ skipped artifacts; the broad `sys_metadata` read is an arbitrary `sys_id`-ordered
99
+ slice and is reported as a bounded sample, not an inventory, describing only the
100
+ unclassified and declared-customer categories. Unresolved skipped names and
101
+ failed lookup chunks stay explicit rather than becoming absent artifacts, and a
102
+ resolved artifact can lack a scope name when its scope record was outside the
103
+ bounded `sys_scope` read. Missing
104
+ ACL access, pagination limits and unmatched evidence remain explicit. All
105
+ reports are incomplete; no findings is not proof of no customizations.
106
+ Comparisons cover captured metadata/evidence, not source payloads. Removals mean
107
+ no longer observed, not proven deletion. Reversion impact and dependencies
108
+ remain unknown until separately investigated. Never recommend automatic reversion.
109
+
110
+ Upgrade history joins `file_name` to metadata `sys_update_name`. Raw disposition
111
+ `4` or `104` is skipped-customization evidence; resolution `reviewed_reverted`
112
+ excludes that history row. Use raw values, not display labels or other skip reasons.
113
+
114
+ ### Complete an update set
115
+
116
+ ```bash
117
+ jsn updatesets complete "My Feature" --notes "Fixed validation" --force --json
118
+ ```
119
+
120
+ `complete [name]` defaults to the current set when the name is omitted. Explicit
121
+ names must be unique. Supply nonempty `--notes` in automation; real newlines
122
+ separate bullets. A human terminal opens a notes editor when `--notes` is absent.
123
+ Existing Description text is preserved and appended after confirmation. Use
124
+ `--force` only after approval, or the profile's `skip_confirmations` policy.
125
+ Notes apply only to the selected set. ServiceNow's normal batch rules can complete
126
+ child sets when the parent completes; their descriptions are not replaced.
127
+ Both selected-set Description and state are read back before success. On
128
+ verification failure, inspect the set before retrying.
129
+
76
130
  ### Table and raw REST examples
77
131
 
78
132
  ```bash
package/src/cli.js CHANGED
@@ -9,6 +9,7 @@ import { App } from './app.js';
9
9
  import { resolveSession, applySession } from './session.js';
10
10
  import { renderHelp } from './help.js';
11
11
  import { isMutationCommand, refreshMutationCommands } from './mutations.js';
12
+ import { skippedUpdatesCmd } from './commands/skipped-updates.js';
12
13
  import { noInstanceCommands, dailyCheckSkipCommands } from './capabilities.js';
13
14
  import { guardExit, exitWithError, AppError, CodeUsage } from './errors.js';
14
15
  import { formatCommandList } from './helpers.js';
@@ -70,6 +71,7 @@ import { b4rulesCmd } from './commands/b4rules.js';
70
71
  import { atfCmd } from './commands/atf.js';
71
72
  import { approvalsCmd } from './commands/approvals.js';
72
73
  import { perfCmd } from './commands/perf.js';
74
+ import { customizationsCmd } from './commands/customizations.js';
73
75
  import { midserversCmd } from './commands/midservers.js';
74
76
  import { diffCmd } from './commands/diff.js';
75
77
  import { graphqlCmd } from './commands/graphql.js';
@@ -405,7 +407,9 @@ export function buildCLI() {
405
407
  // DEVELOPER
406
408
  .command(atfCmd(wrap))
407
409
  .command(approvalsCmd(wrap))
410
+ .command(skippedUpdatesCmd(wrap))
408
411
  .command(perfCmd(wrap))
412
+ .command(customizationsCmd(wrap))
409
413
  .command(midserversCmd(wrap))
410
414
  .command(diffCmd(wrap))
411
415
  .command(graphqlCmd(wrap))