@jacebenson/jsn 3.1.0 → 3.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/package.json +3 -2
- package/skills/servicenow/SKILL.md +41 -123
- package/src/cli.js +247 -200
- package/src/commands/_ticket.js +14 -1
- package/src/commands/auth.js +10 -1
- package/src/commands/catalog.js +179 -292
- package/src/commands/dev/_generic.js +39 -51
- package/src/commands/dev/_simple.js +27 -0
- package/src/commands/dev/b4rules.js +5 -0
- package/src/commands/dev/forms.js +123 -255
- package/src/commands/dev/logs.js +42 -9
- package/src/commands/groupmembers.js +10 -1
- package/src/commands/grouproles.js +10 -1
- package/src/commands/groups.js +12 -1
- package/src/commands/records.js +13 -1
- package/src/commands/skill.js +11 -1
- package/src/commands/tickets.js +12 -1
- package/src/commands/users.js +12 -1
- package/src/help.js +125 -78
- package/src/helpers.js +33 -20
- package/src/paginated-search.js +187 -155
- package/src/sdk.js +4 -0
package/src/commands/tickets.js
CHANGED
|
@@ -117,6 +117,17 @@ export function ticketsCmd(wrap) {
|
|
|
117
117
|
})
|
|
118
118
|
|
|
119
119
|
},
|
|
120
|
-
handler: () => {
|
|
120
|
+
handler: () => {
|
|
121
|
+
console.log('Query generic tickets from the ticket table.');
|
|
122
|
+
console.log('');
|
|
123
|
+
console.log('Available subcommands:');
|
|
124
|
+
console.log(' list List tickets');
|
|
125
|
+
console.log(' show <number or sys_id> Show a ticket');
|
|
126
|
+
console.log(' create Create a new ticket');
|
|
127
|
+
console.log(' update <identifier> Update a ticket');
|
|
128
|
+
console.log(' delete <identifier> Delete a ticket');
|
|
129
|
+
console.log('');
|
|
130
|
+
console.log('Run "jsn tickets <command> --help" for details.');
|
|
131
|
+
},
|
|
121
132
|
};
|
|
122
133
|
}
|
package/src/commands/users.js
CHANGED
|
@@ -140,6 +140,17 @@ export function usersCmd(wrap) {
|
|
|
140
140
|
}),
|
|
141
141
|
});
|
|
142
142
|
},
|
|
143
|
-
handler: () => {
|
|
143
|
+
handler: () => {
|
|
144
|
+
console.log('Manage ServiceNow users from the sys_user table.');
|
|
145
|
+
console.log('');
|
|
146
|
+
console.log('Available subcommands:');
|
|
147
|
+
console.log(' list List users');
|
|
148
|
+
console.log(' show <name or sys_id> Show a user by user_name or sys_id');
|
|
149
|
+
console.log(' create Create a new user');
|
|
150
|
+
console.log(' update <identifier> Update a user');
|
|
151
|
+
console.log(' delete <identifier> Delete a user');
|
|
152
|
+
console.log('');
|
|
153
|
+
console.log('Run "jsn users <command> --help" for details.');
|
|
154
|
+
},
|
|
144
155
|
};
|
|
145
156
|
}
|
package/src/help.js
CHANGED
|
@@ -1,87 +1,140 @@
|
|
|
1
1
|
// Custom grouped help renderer
|
|
2
|
-
//
|
|
3
|
-
|
|
4
|
-
const COMMAND_GROUPS = {
|
|
5
|
-
'CORE COMMANDS': [
|
|
6
|
-
{ name: 'incidents', alias: 'inc', desc: 'Manage incidents' },
|
|
7
|
-
{ name: 'changes', alias: 'chg', desc: 'Manage change requests' },
|
|
8
|
-
{ name: 'requests', alias: 'req', desc: 'Manage service catalog requests' },
|
|
9
|
-
{ name: 'tasks', alias: 'task', desc: 'Manage catalog tasks' },
|
|
10
|
-
{ name: 'tickets', alias: 'ticket', desc: 'Query generic tickets' },
|
|
11
|
-
],
|
|
12
|
-
'AUTOMATION': [
|
|
13
|
-
{ name: 'flows', desc: 'Manage Flow Designer flows' },
|
|
14
|
-
{ name: 'actions', alias: 'action', desc: 'Manage flow actions (sys_hub_action_type_definition)' },
|
|
15
|
-
{ name: 'rules', alias: 'br', desc: 'Manage business rules' },
|
|
16
|
-
{ name: 'scrapi', desc: 'Manage Scripted REST APIs' },
|
|
17
|
-
{ name: 'updatesets', desc: 'Manage update sets (set, yolo, export, import)' },
|
|
18
|
-
{ name: 'eval', desc: 'Run server-side JavaScript' },
|
|
19
|
-
{ name: 'rest', desc: 'Make raw REST API calls' },
|
|
20
|
-
],
|
|
21
|
-
'ACCESS': [
|
|
22
|
-
{ name: 'acls', alias: 'acl', desc: 'Manage access controls' },
|
|
23
|
-
{ name: 'roles', alias: 'role', desc: 'Manage roles' },
|
|
24
|
-
{ name: 'scopes', desc: 'Manage application scopes' },
|
|
25
|
-
{ name: 'properties', alias: 'prop', desc: 'Manage system properties' },
|
|
26
|
-
{ name: 'privileges', alias: 'priv', desc: 'Manage scope privileges' },
|
|
27
|
-
{ name: 'securitytypes', alias: 'st', desc: 'Manage security types' },
|
|
28
|
-
{ name: 'aliases', alias: 'als', desc: 'Manage table aliases' },
|
|
29
|
-
],
|
|
30
|
-
'USER EXPERIENCE': [
|
|
31
|
-
{ name: 'forms', desc: 'Manage form layouts' },
|
|
32
|
-
{ name: 'lists', desc: 'Manage list layouts' },
|
|
33
|
-
{ name: 'clientscripts', alias: 'cs', desc: 'Manage client scripts' },
|
|
34
|
-
{ name: 'uipolicies', alias: 'up', desc: 'Manage UI policies' },
|
|
35
|
-
{ name: 'uiactions', alias: 'ua', desc: 'Manage UI actions' },
|
|
36
|
-
{ name: 'sppages', desc: 'Manage Service Portal pages' },
|
|
37
|
-
{ name: 'spwidgets', desc: 'Manage Service Portal widgets' },
|
|
38
|
-
{ name: 'uipages', desc: 'Manage UI pages' },
|
|
39
|
-
{ name: 'appmenu', desc: 'Manage application menus' },
|
|
40
|
-
{ name: 'listcontrols', alias: 'lc', desc: 'Manage list controls' },
|
|
41
|
-
{ name: 'views', alias: 'vw', desc: 'Manage views' },
|
|
42
|
-
{ name: 'uxscripts', alias: 'ux', desc: 'Manage UX source scripts' },
|
|
43
|
-
{ name: 'catalogscripts', desc: 'Manage catalog client scripts' },
|
|
44
|
-
{ name: 'cataloguipolicies', alias: 'cup', desc: 'Manage catalog UI policies' },
|
|
45
|
-
],
|
|
46
|
-
'DATA': [
|
|
47
|
-
{ name: 'records', desc: 'Generic Table API for any table' },
|
|
48
|
-
{ name: 'tables', alias: 't', desc: 'Manage table definitions (sys_db_object)' },
|
|
49
|
-
{ name: 'columns', alias: 'col', desc: 'Manage column definitions (sys_dictionary)' },
|
|
50
|
-
{ name: 'includes', alias: 'si', desc: 'Manage script includes' },
|
|
51
|
-
{ name: 'import', desc: 'Manage import sets' },
|
|
52
|
-
{ name: 'logs', desc: 'View system logs' },
|
|
53
|
-
{ name: 'users', alias: 'user', desc: 'Manage ServiceNow users' },
|
|
54
|
-
{ name: 'groups', alias: 'group', desc: 'Manage groups' },
|
|
55
|
-
{ name: 'groupmembers', alias: 'gm', desc: 'Manage group memberships' },
|
|
56
|
-
{ name: 'grouproles', alias: 'gr', desc: 'Manage group roles' },
|
|
57
|
-
{ name: 'relationships', alias: 'rel', desc: 'Manage table relationships' },
|
|
58
|
-
{ name: 'appmodules', alias: 'am', desc: 'Manage application modules' },
|
|
59
|
-
],
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
const CONFIGURATION_COMMANDS = [
|
|
63
|
-
{ name: 'setup', desc: 'Interactive first-time setup' },
|
|
64
|
-
{ name: 'auth', desc: 'Manage OAuth authentication' },
|
|
65
|
-
{ name: 'profiles', alias: 'profile', desc: 'Manage instance profiles' },
|
|
66
|
-
{ name: 'dev', desc: '[DEPRECATED] All dev subcommands are now top-level — use jsn flows, jsn acls, etc.' },
|
|
67
|
-
];
|
|
68
|
-
|
|
69
|
-
const UTILITY_COMMANDS = [
|
|
70
|
-
{ name: 'skill', desc: 'Manage the jsn AI agent skill file (show, fetch, install)' },
|
|
71
|
-
{ name: 'version', desc: 'Show version information (--check for npm updates)' },
|
|
72
|
-
];
|
|
2
|
+
// Organized by ServiceNow domain model with subgroup dividers
|
|
73
3
|
|
|
74
4
|
function renderGroup(name, commands) {
|
|
75
5
|
const lines = [`\n${name}`];
|
|
76
6
|
lines.push('─'.repeat(50));
|
|
77
7
|
for (const cmd of commands) {
|
|
8
|
+
if (cmd.divider) {
|
|
9
|
+
lines.push('');
|
|
10
|
+
lines.push(` ${cmd.divider}`);
|
|
11
|
+
continue;
|
|
12
|
+
}
|
|
78
13
|
const aliasPart = cmd.alias ? ` (${cmd.alias})` : '';
|
|
79
14
|
const padded = ` jsn ${cmd.name}`.padEnd(22);
|
|
80
|
-
|
|
15
|
+
const status = cmd.status ? ` ${cmd.status}` : '';
|
|
16
|
+
lines.push(`${padded}${cmd.desc}${aliasPart}${status}`);
|
|
81
17
|
}
|
|
82
18
|
return lines.join('\n');
|
|
83
19
|
}
|
|
84
20
|
|
|
21
|
+
const CORE_COMMANDS = [
|
|
22
|
+
{ name: 'incidents', alias: 'inc', desc: 'Manage incidents' },
|
|
23
|
+
{ name: 'changes', alias: 'chg', desc: 'Manage change requests' },
|
|
24
|
+
{ name: 'requests', alias: 'req', desc: 'Manage service catalog requests' },
|
|
25
|
+
{ name: 'tasks', alias: 'task', desc: 'Manage catalog tasks' },
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
const AUTOMATION_COMMANDS = [
|
|
29
|
+
{ divider: '── Async ──' },
|
|
30
|
+
{ name: 'flows', alias: 'flow', desc: 'Flow Designer flows' },
|
|
31
|
+
{ name: 'actions', alias: 'action', desc: 'Flow action definitions' },
|
|
32
|
+
{ name: 'scriptactions', desc: 'Script actions' },
|
|
33
|
+
{ name: 'scheduledjobs', desc: 'Scheduled jobs' },
|
|
34
|
+
{ name: 'triggers', desc: 'Event triggers' },
|
|
35
|
+
{ name: 'asyncrules', desc: 'Asynchronous business rules' },
|
|
36
|
+
|
|
37
|
+
{ divider: '── In Memory ──' },
|
|
38
|
+
{ name: 'rules', alias: 'br', desc: 'Business rules (create, update, delete)' },
|
|
39
|
+
{ name: 'workflows', desc: 'Workflow definitions' },
|
|
40
|
+
{ name: 'decisiontables', desc: 'Decision tables' },
|
|
41
|
+
{ name: 'assignments', desc: 'Assignment rules' },
|
|
42
|
+
|
|
43
|
+
{ divider: '── Inbound ──' },
|
|
44
|
+
{ name: 'scrapi', desc: 'Scripted REST APIs' },
|
|
45
|
+
{ name: 'email', desc: 'Inbound email actions' },
|
|
46
|
+
|
|
47
|
+
{ divider: '── Outbound ──' },
|
|
48
|
+
{ name: 'restmessage', alias: 'rm', desc: 'REST Messages (sys_rest_message)' },
|
|
49
|
+
{ name: 'restmethods', desc: 'REST Message methods (sys_rest_message_fn)' },
|
|
50
|
+
{ name: 'soapmessages', desc: 'SOAP Messages' },
|
|
51
|
+
{ name: 'soapfunctions', desc: 'SOAP Message functions' },
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
const ACCESS_COMMANDS = [
|
|
55
|
+
{ name: 'acls', alias: 'acl', desc: 'Access controls' },
|
|
56
|
+
{ name: 'b4rules', alias: 'b4r', desc: 'Before-query business rules (see also Automation)' },
|
|
57
|
+
{ name: 'roles', alias: 'role', desc: 'Roles' },
|
|
58
|
+
{ name: 'privileges', alias: 'priv', desc: 'Scope privileges' },
|
|
59
|
+
{ name: 'securitytypes', alias: 'st', desc: 'Custom security types' },
|
|
60
|
+
{ name: 'aliases', alias: 'als', desc: 'Connection & credential aliases' },
|
|
61
|
+
{ name: 'groups', alias: 'group', desc: 'Manage groups' },
|
|
62
|
+
{ name: 'groupmembers', alias: 'gm', desc: 'Group memberships' },
|
|
63
|
+
{ name: 'grouproles', alias: 'gr', desc: 'Group roles' },
|
|
64
|
+
{ name: 'users', alias: 'user', desc: 'Manage users' },
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
const UX_COMMANDS = [
|
|
68
|
+
{ divider: '── Shared (across all experiences) ──' },
|
|
69
|
+
{ name: 'forms', desc: 'Form layouts' },
|
|
70
|
+
{ name: 'lists', desc: 'List layouts' },
|
|
71
|
+
{ name: 'clientscripts', alias: 'cs', desc: 'Client scripts' },
|
|
72
|
+
{ name: 'uipolicies', alias: 'up', desc: 'UI policies' },
|
|
73
|
+
{ name: 'uiactions', alias: 'ua', desc: 'UI actions (buttons, links, context menus)' },
|
|
74
|
+
{ name: 'views', alias: 'vw', desc: 'Views' },
|
|
75
|
+
{ name: 'catalogitems', alias: 'ci', desc: 'Service Catalog items + variables' },
|
|
76
|
+
{ name: 'catalogscripts', desc: 'Catalog client scripts' },
|
|
77
|
+
{ name: 'cataloguipolicies', alias: 'cup', desc: 'Catalog UI policies' },
|
|
78
|
+
|
|
79
|
+
{ divider: '── Core UI ──' },
|
|
80
|
+
{ name: 'uipages', desc: 'UI pages' },
|
|
81
|
+
{ name: 'uimacros', desc: 'UI macros' },
|
|
82
|
+
{ name: 'listcontrols', alias: 'lc', desc: 'List controls' },
|
|
83
|
+
|
|
84
|
+
{ divider: '── Service Portal ──' },
|
|
85
|
+
{ name: 'sppages', desc: 'Service Portal pages' },
|
|
86
|
+
{ name: 'spwidgets', desc: 'Service Portal widgets' },
|
|
87
|
+
|
|
88
|
+
{ divider: '── Next Experience (Workspaces) ──' },
|
|
89
|
+
{ name: 'uxscripts', alias: 'ux', desc: 'UX source scripts' },
|
|
90
|
+
{ name: 'uxlists', desc: 'Workspace lists (sys_ux_list)' },
|
|
91
|
+
{ name: 'uxapplicability', desc: 'Workspace applicability (m2m)' },
|
|
92
|
+
|
|
93
|
+
{ divider: '── Navigation ──' },
|
|
94
|
+
{ name: 'appmenu', desc: 'Application menus' },
|
|
95
|
+
{ name: 'appmodules', alias: 'am', desc: 'Application modules' },
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
const DATA_COMMANDS = [
|
|
99
|
+
{ divider: '── DB Schema ──' },
|
|
100
|
+
{ name: 'tables', alias: 't', desc: 'Table definitions' },
|
|
101
|
+
{ name: 'columns', alias: 'col', desc: 'Column definitions' },
|
|
102
|
+
{ name: 'relationships', alias: 'rel', desc: 'Table relationships' },
|
|
103
|
+
|
|
104
|
+
{ divider: '── Shared Code, Transforms, Logs ──' },
|
|
105
|
+
{ name: 'includes', alias: 'si', desc: 'Script includes' },
|
|
106
|
+
{ name: 'import', desc: 'Import sets' },
|
|
107
|
+
{ name: 'logs', desc: 'System logs' },
|
|
108
|
+
{ name: 'properties', alias: 'prop', desc: 'System properties' },
|
|
109
|
+
|
|
110
|
+
{ name: 'records', desc: 'Generic Table API (any table)' },
|
|
111
|
+
];
|
|
112
|
+
|
|
113
|
+
const CONFIG_COMMANDS = [
|
|
114
|
+
{ name: 'setup', desc: 'Interactive first-time setup' },
|
|
115
|
+
{ name: 'auth', desc: 'OAuth authentication' },
|
|
116
|
+
{ name: 'profiles', alias: 'profile', desc: 'Instance profiles' },
|
|
117
|
+
{ name: 'updatesets', desc: 'Update sets (set, yolo, export, import)' },
|
|
118
|
+
{ name: 'scopes', desc: 'Application scopes' },
|
|
119
|
+
];
|
|
120
|
+
|
|
121
|
+
const DEVELOPER_COMMANDS = [
|
|
122
|
+
{ name: 'eval', desc: 'Run server-side JavaScript' },
|
|
123
|
+
{ name: 'rest', desc: 'Raw REST API calls' },
|
|
124
|
+
{ name: 'skill', desc: 'AI agent skill file (show, fetch, install)' },
|
|
125
|
+
{ name: 'version', desc: 'Version info (--check for npm updates)' },
|
|
126
|
+
];
|
|
127
|
+
|
|
128
|
+
const COMMAND_GROUPS = {
|
|
129
|
+
'CORE': CORE_COMMANDS,
|
|
130
|
+
'AUTOMATION': AUTOMATION_COMMANDS,
|
|
131
|
+
'ACCESS': ACCESS_COMMANDS,
|
|
132
|
+
'USER EXPERIENCE': UX_COMMANDS,
|
|
133
|
+
'DATA': DATA_COMMANDS,
|
|
134
|
+
'CONFIGURATION': CONFIG_COMMANDS,
|
|
135
|
+
'DEVELOPER': DEVELOPER_COMMANDS,
|
|
136
|
+
};
|
|
137
|
+
|
|
85
138
|
function renderFlags() {
|
|
86
139
|
return `
|
|
87
140
|
FLAGS
|
|
@@ -107,8 +160,6 @@ LEARN MORE
|
|
|
107
160
|
|
|
108
161
|
export function renderHelp() {
|
|
109
162
|
const sections = Object.entries(COMMAND_GROUPS).map(([name, cmds]) => renderGroup(name, cmds));
|
|
110
|
-
const configSection = renderGroup('CONFIGURATION', CONFIGURATION_COMMANDS);
|
|
111
|
-
const utilitySection = renderGroup('UTILITY', UTILITY_COMMANDS);
|
|
112
163
|
|
|
113
164
|
return [
|
|
114
165
|
'Usage: jsn <command> [options]',
|
|
@@ -117,10 +168,6 @@ export function renderHelp() {
|
|
|
117
168
|
'',
|
|
118
169
|
...sections,
|
|
119
170
|
'',
|
|
120
|
-
configSection,
|
|
121
|
-
'',
|
|
122
|
-
utilitySection,
|
|
123
|
-
'',
|
|
124
171
|
renderFlags(),
|
|
125
172
|
renderTips(),
|
|
126
173
|
'',
|
package/src/helpers.js
CHANGED
|
@@ -89,10 +89,15 @@ export function resolveFieldsParam(columns) {
|
|
|
89
89
|
* @param {string} opts.labelField — field used to match selection (default: 'name')
|
|
90
90
|
* @returns {Promise<void>|null} null if no selection made or non-interactive
|
|
91
91
|
*/
|
|
92
|
+
function vowelArticle(word) {
|
|
93
|
+
const first = word.charAt(0).toLowerCase();
|
|
94
|
+
return first === 'a' || first === 'e' || first === 'i' || first === 'o' || first === 'u' ? 'an' : 'a';
|
|
95
|
+
}
|
|
96
|
+
|
|
92
97
|
export async function interactiveList({ app, table, singular, columns, limit = 50, query = '', formatLabel, labelField = 'name' }) {
|
|
93
98
|
app.requireInstance();
|
|
94
99
|
const effectiveFormat = app.output.getFormat() === FormatAuto ? (isTTY(process.stdout) ? FormatAuto : FormatAuto) : app.output.getFormat();
|
|
95
|
-
if (effectiveFormat !== FormatAuto || !isTTY(process.stdout) || !isTTY(process.stdin)
|
|
100
|
+
if (effectiveFormat !== FormatAuto || !isTTY(process.stdout) || !isTTY(process.stdin)) {
|
|
96
101
|
return null; // not interactive — caller should fall back to text/table
|
|
97
102
|
}
|
|
98
103
|
|
|
@@ -108,23 +113,32 @@ export async function interactiveList({ app, table, singular, columns, limit = 5
|
|
|
108
113
|
}
|
|
109
114
|
if (totalCount === 0) return null;
|
|
110
115
|
|
|
111
|
-
//
|
|
112
|
-
async function serverSource(term, offset) {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
116
|
+
// Paginated source adapter: (term, offset, { signal }) => choices[]
|
|
117
|
+
async function serverSource(term, offset, { signal } = {}) {
|
|
118
|
+
try {
|
|
119
|
+
const params = new URLSearchParams();
|
|
120
|
+
params.set('sysparm_limit', String(limit));
|
|
121
|
+
params.set('sysparm_display_value', 'all');
|
|
122
|
+
if (pickerFields) params.set('sysparm_fields', pickerFields);
|
|
123
|
+
if (term) {
|
|
124
|
+
params.set('sysparm_query', `${labelField}LIKE${term}^ORDERBYDESCsys_updated_on`);
|
|
125
|
+
} else {
|
|
126
|
+
params.set('sysparm_query', 'ORDERBYDESCsys_updated_on');
|
|
127
|
+
params.set('sysparm_offset', String(offset));
|
|
128
|
+
}
|
|
129
|
+
const records = await app.sdk.list(table, params);
|
|
130
|
+
if (!Array.isArray(records)) {
|
|
131
|
+
console.error('SDK non-array:', typeof records);
|
|
132
|
+
return [];
|
|
133
|
+
}
|
|
134
|
+
return records.map(r => ({
|
|
135
|
+
name: formatLabel ? formatLabel(r) : (getStringField(r, labelField) || getStringField(r, 'sys_id')),
|
|
136
|
+
value: r,
|
|
137
|
+
}));
|
|
138
|
+
} catch (err) {
|
|
139
|
+
console.error('serverSource error:', err.message || err);
|
|
140
|
+
return [];
|
|
122
141
|
}
|
|
123
|
-
const records = await app.sdk.list(table, params);
|
|
124
|
-
return records.map(r => ({
|
|
125
|
-
name: formatLabel ? formatLabel(r) : (getStringField(r, labelField) || getStringField(r, 'sys_id')),
|
|
126
|
-
value: r,
|
|
127
|
-
}));
|
|
128
142
|
}
|
|
129
143
|
|
|
130
144
|
const selected = await paginatedSearch({
|
|
@@ -133,9 +147,8 @@ export async function interactiveList({ app, table, singular, columns, limit = 5
|
|
|
133
147
|
totalCount,
|
|
134
148
|
source: serverSource,
|
|
135
149
|
});
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
return selected?.value; // unwrap {name, value} → raw record
|
|
150
|
+
|
|
151
|
+
return selected?.value; // unwrap {name, value} → raw record, undefined on cancel
|
|
139
152
|
}
|
|
140
153
|
|
|
141
154
|
/**
|