@jacebenson/jsn 0.1.0 → 0.1.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 CHANGED
@@ -32,7 +32,7 @@ jsn version --check
32
32
 
33
33
  ## What's New in v0.1.0
34
34
 
35
- Full feature parity with the original Go implementation — 128 tests, lint clean:
35
+ Full feature parity — 128 tests, lint clean:
36
36
 
37
37
  | Feature | Status |
38
38
  |---------|--------|
@@ -44,7 +44,7 @@ Full feature parity with the original Go implementation — 128 tests, lint clea
44
44
  | Group management (CRUD) | ✅ |
45
45
  | Group memberships and roles | ✅ |
46
46
  | Generic Table API (`jsn records`) | ✅ |
47
- | OAuth PKCE with keychain (Go-compatible auth store) | ✅ |
47
+ | OAuth PKCE with keychain (shared auth store) | ✅ |
48
48
  | Bot/CI/CD auth flags (`--code`, `--print-url`) | ✅ |
49
49
  | Dev commands: flows, actions, includes, rules, ACLs, etc. | ✅ |
50
50
  | Script execution (`jsn dev eval`) via OAuth session flow | ✅ |
@@ -211,7 +211,7 @@ jsn incidents -q | jq '.[].number'
211
211
 
212
212
  ## Authentication
213
213
 
214
- OAuth 2.0 with PKCE. Credentials stored in `~/.config/servicenow/credentials/` — shared with the legacy Go version.
214
+ OAuth 2.0 with PKCE. Credentials stored in `~/.config/servicenow/credentials/`.
215
215
 
216
216
  ```bash
217
217
  jsn auth login https://dev12345.service-now.com
@@ -335,10 +335,6 @@ jsn version --check
335
335
  npm install -g @jacebenson/jsn
336
336
  ```
337
337
 
338
- ## Historical Note
339
-
340
- JSN was originally implemented in Go (on the `main` branch) and migrated to Node.js for broader cross-platform support and simpler installation. As of v0.1.0, the Node.js version has full feature parity. The Go source remains available on the `main` branch for reference.
341
-
342
338
  ## License
343
339
 
344
340
  MIT License — see LICENSE file for details
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jacebenson/jsn",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
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": {
@@ -30,6 +30,7 @@ export function recordsCmd(wrap) {
30
30
  describe: 'List records from a table',
31
31
  builder: (y) => y
32
32
  .option('table', { type: 'string', demandOption: true, describe: 'Table name' })
33
+ .option('sys-id', { type: 'string', describe: 'Record sys_id (filters to a single record)' })
33
34
  .option('query', { type: 'string', describe: 'Encoded query (e.g. "nameLIKEincident" or "active=true")' })
34
35
  .option('columns', { alias: 'c', type: 'string', describe: 'Comma-separated columns (e.g. "number,short_description")' })
35
36
  .option('limit', { type: 'number', default: 20, describe: 'Max records' })
@@ -37,28 +38,36 @@ export function recordsCmd(wrap) {
37
38
  handler: wrap(async (argv, app) => {
38
39
  const table = argv.table;
39
40
  const columns = argv.columns ? argv.columns.split(',') : getDefaultColumns(table);
40
- const query = argv.query || '';
41
+ let query = argv.query || '';
41
42
 
42
- // Interactive picker
43
- const picked = await interactiveList({
44
- app, table, singular: 'record', columns, limit: argv.limit, query, labelField: 'sys_id',
45
- formatLabel: r => {
46
- const cols = getDefaultColumns(table);
47
- return cols.map(c => `${c}: ${getStringField(r, c) || '-'}`).join(' | ');
48
- },
49
- });
50
- if (picked) {
51
- picked._context = { instance_url: app.getEffectiveInstance(), table };
52
- return app.ok(picked, { summary: `Record from ${table}` });
43
+ // If --sys-id is provided, append it to the query
44
+ if (argv['sys-id']) {
45
+ if (query) query += '^';
46
+ query += `sys_id=${argv['sys-id']}`;
53
47
  }
54
48
 
55
- // Text/table fallback
49
+ // Interactive picker (only when no --sys-id filter, otherwise go straight to results)
50
+ if (!argv['sys-id']) {
51
+ const picked = await interactiveList({
52
+ app, table, singular: 'record', columns, limit: argv.limit, query, labelField: 'sys_id',
53
+ formatLabel: r => {
54
+ const cols = getDefaultColumns(table);
55
+ return cols.map(c => `${c}: ${getStringField(r, c) || '-'}`).join(' | ');
56
+ },
57
+ });
58
+ if (picked) {
59
+ picked._context = { instance_url: app.getEffectiveInstance(), table };
60
+ return app.ok(picked, { summary: `Record from ${table}` });
61
+ }
62
+ }
63
+
64
+ // Text/table fallback (or sys-id direct lookup)
56
65
  const params = new URLSearchParams();
57
- params.set('sysparm_limit', String(argv.limit));
66
+ params.set('sysparm_limit', argv['sys-id'] ? '1' : String(argv.limit));
58
67
  params.set('sysparm_offset', String(argv.offset));
59
68
  params.set('sysparm_display_value', 'all');
60
69
  params.set('sysparm_fields', ['sys_id', ...columns].join(','));
61
- if (argv.query) params.set('sysparm_query', argv.query);
70
+ if (query) params.set('sysparm_query', query);
62
71
  const records = await app.sdk.list(table, params);
63
72
  const displayRecords = records.map(r => formatRecordForDisplay(r, columns));
64
73
  const breadcrumbs = [
@@ -66,6 +75,13 @@ export function recordsCmd(wrap) {
66
75
  { action: 'filter', cmd: `jsn records list --table ${table} --query "priority=1"`, description: 'Filter: priority 1 only' },
67
76
  { action: 'columns', cmd: `jsn dev columns --table ${table}`, description: 'View available columns' },
68
77
  ];
78
+ if (argv['sys-id']) {
79
+ breadcrumbs.unshift({
80
+ action: 'get',
81
+ cmd: `jsn records get --table ${table} --sys-id ${argv['sys-id']}`,
82
+ description: 'Get full record details',
83
+ });
84
+ }
69
85
  if (records.length === argv.limit) {
70
86
  breadcrumbs.push({
71
87
  action: 'next',