@jacebenson/jsn 4.3.7-dev.32 → 4.3.7-dev.33

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jacebenson/jsn",
3
- "version": "4.3.7-dev.32",
3
+ "version": "4.3.7-dev.33",
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": {
@@ -1,7 +1,7 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { input, password as passwordPrompt, select } from '@inquirer/prompts';
4
- import { confirmDelete, formatRecordForDisplay, getStringField, interactiveList, canPrompt } from '../helpers.js';
4
+ import { confirmDelete, formatRecordForDisplay, getStringField, interactiveList, canPrompt, assertSafeExactMatch } from '../helpers.js';
5
5
  import { FormatAuto } from '../output.js';
6
6
  import { declareCapabilities } from '../capabilities.js';
7
7
  import { resolveRecord, unwrapSysId } from '../resolve-record.js';
@@ -91,6 +91,85 @@ async function resolveMidServer(sdk, identifier) {
91
91
  }
92
92
  }
93
93
 
94
+ async function showMidDiagnostics(app, argv, kind) {
95
+ app.requireInstance();
96
+ let mid;
97
+ if (argv.identifier) {
98
+ mid = await resolveMidServer(app.sdk, argv.identifier);
99
+ } else {
100
+ if (!canPrompt() || app.output.getFormat() !== FormatAuto) {
101
+ throw new Error('An identifier is required outside an interactive terminal');
102
+ }
103
+ mid = await interactiveList({
104
+ app, table: AGENT_TABLE, singular: 'MID Server', columns: LIST_FIELDS,
105
+ limit: 50, labelField: 'name', message: `Select a MID Server for ${kind}`,
106
+ formatLabel: record => `${getStringField(record, 'name')} [${getStringField(record, 'status')}]`,
107
+ });
108
+ if (!mid) return;
109
+ }
110
+ const name = getStringField(mid, 'name');
111
+ const sysId = unwrapSysId(mid);
112
+ assertSafeExactMatch(name);
113
+ assertSafeExactMatch(sysId);
114
+ const issues = kind === 'issues';
115
+ const table = issues ? 'ecc_agent_issue' : 'mid_server_event';
116
+ const columns = issues
117
+ ? ['sys_id', 'state', 'source', 'message', 'last_detected']
118
+ : ['sys_id', 'event', 'source', 'message', 'sys_created_on'];
119
+ const query = issues
120
+ ? `mid_server=${sysId}${argv.all ? '' : '^state!=resolved'}^ORDERBYDESClast_detected`
121
+ : `agent=${name}^ORDERBYDESCsys_created_on`;
122
+ const limit = argv.limit ?? 50;
123
+ const offset = argv.offset ?? 0;
124
+ if (!Number.isInteger(limit) || limit < 1 || limit > 1000 || !Number.isInteger(offset) || offset < 0) {
125
+ throw new Error('Use --limit between 1 and 1000 and a nonnegative --offset');
126
+ }
127
+ const records = await app.sdk.list(table, new URLSearchParams({
128
+ sysparm_query: query, sysparm_fields: columns.join(','), sysparm_display_value: 'all',
129
+ sysparm_limit: String(limit), sysparm_offset: String(offset),
130
+ }));
131
+ const instanceURL = app.getEffectiveInstance();
132
+ const url = new URL(`/${table}_list.do`, instanceURL);
133
+ url.searchParams.set('sysparm_query', query);
134
+ const rows = records.map(record => ({
135
+ ...record,
136
+ url: new URL(`/${table}.do?sys_id=${encodeURIComponent(unwrapSysId(record))}`, instanceURL).href,
137
+ }));
138
+ const label = issues ? (argv.all ? 'issues' : 'unresolved issues') : 'lifecycle events';
139
+ const summary = rows.length
140
+ ? `Showing ${rows.length} ${label} for MID Server '${name}' (offset ${offset})`
141
+ : `No ${label} returned for MID Server '${name}' (offset ${offset})`;
142
+ const lines = [summary, ''];
143
+ for (const record of rows) {
144
+ lines.push(`${getStringField(record, issues ? 'last_detected' : 'sys_created_on')} ${getStringField(record, issues ? 'state' : 'event')}`);
145
+ const source = getStringField(record, 'source');
146
+ const message = getStringField(record, 'message');
147
+ if (source) lines.push(` ${source}`);
148
+ if (message) lines.push(` ${message}`);
149
+ lines.push(` ${record.url}`, '');
150
+ }
151
+ lines.push(`View in ServiceNow: ${url.href}`);
152
+ app.ok({
153
+ mid_server: name, sys_id: sysId, table, count: rows.length, records: rows,
154
+ pagination: { limit, offset }, url: url.href, context: { instance_url: instanceURL },
155
+ _formatted: `${lines.join('\n')}\n`,
156
+ }, { summary });
157
+ }
158
+
159
+ function midDiagnosticsCommand(kind, wrap) {
160
+ return {
161
+ command: `${kind} [identifier]`,
162
+ describe: kind === 'issues' ? 'Show MID Server issues (unresolved by default)' : 'Show MID Server lifecycle history',
163
+ builder: b => {
164
+ b.option('limit', { alias: 'l', type: 'number', default: 50, describe: 'Max records (1-1000)' })
165
+ .option('offset', { type: 'number', default: 0, describe: 'Result offset' });
166
+ if (kind === 'issues') b.option('all', { type: 'boolean', default: false, describe: 'Include resolved issues' });
167
+ return b;
168
+ },
169
+ handler: wrap(async (argv, app) => showMidDiagnostics(app, argv, kind)),
170
+ };
171
+ }
172
+
94
173
  async function listAgents(app, argv) {
95
174
  const columns = argv.columns
96
175
  ? [...new Set(argv.columns.split(',').map(column => column.trim()).filter(Boolean))]
@@ -244,6 +323,8 @@ export function midserversCmd(wrap) {
244
323
  aliases: ['midserver', 'mid'],
245
324
  describe: 'Manage ServiceNow MID Servers and run commands through them',
246
325
  builder: (y) => y
326
+ .command(midDiagnosticsCommand('issues', wrap))
327
+ .command(midDiagnosticsCommand('events', wrap))
247
328
  .command({
248
329
  command: 'list',
249
330
  aliases: ['ls'],
@@ -479,6 +560,8 @@ export function midserversCmd(wrap) {
479
560
  console.log('');
480
561
  console.log('Available subcommands:');
481
562
  console.log(' list List MID Servers');
563
+ console.log(' issues [name_or_sys_id] Show unresolved issues (--all includes resolved)');
564
+ console.log(' events [name_or_sys_id] Show lifecycle history');
482
565
  console.log(' show <name_or_sys_id> Show a MID Server');
483
566
  console.log(' add --name <name> Create a MID Server record');
484
567
  console.log(' remove <name_or_sys_id> Remove a MID Server record');