@jacebenson/jsn 4.3.1 → 4.3.2
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 +1 -1
- package/scripts/pre-commit-check.sh +1 -1
- package/src/auth.js +21 -2
- package/src/capabilities.js +1 -7
- package/src/cli.js +13 -16
- package/src/commands/{dev/_generic.js → _generic.js} +6 -9
- package/src/commands/{dev/_simple.js → _simple.js} +13 -13
- package/src/commands/_ticket.js +1 -1
- package/src/commands/{dev/aliases.js → aliases.js} +1 -1
- package/src/commands/auth.js +5 -5
- package/src/commands/{dev/b4rules.js → b4rules.js} +1 -1
- package/src/commands/decisiontables.js +1 -1
- package/src/commands/{dev/domains.js → domains.js} +3 -3
- package/src/commands/{dev/eval.js → eval.js} +4 -4
- package/src/commands/{dev/flows.js → flows.js} +7 -7
- package/src/commands/{dev/forms.js → forms.js} +1 -1
- package/src/commands/{dev/import.js → import.js} +1 -1
- package/src/commands/{dev/lists.js → lists.js} +1 -1
- package/src/commands/{dev/logs.js → logs.js} +1 -1
- package/src/commands/{dev/rest.js → rest.js} +3 -3
- package/src/commands/{dev/restmessage.js → restmessage.js} +1 -2
- package/src/commands/{dev/scopes.js → scopes.js} +5 -5
- package/src/commands/{dev/scrapi.js → scrapi.js} +5 -5
- package/src/commands/{dev/soapmessages.js → soapmessages.js} +1 -2
- package/src/commands/{dev/uipolicies.js → uipolicies.js} +1 -1
- package/src/commands/{dev/updatesets.js → updatesets.js} +4 -5
- package/src/sdk.js +60 -9
- package/src/commands/dev.js +0 -82
- /package/src/commands/{dev/columns.js → columns.js} +0 -0
package/package.json
CHANGED
|
@@ -56,6 +56,6 @@ else
|
|
|
56
56
|
echo ""
|
|
57
57
|
echo "⚠️ Architecture violations found!"
|
|
58
58
|
echo " Remember: Commands should call app.SDK.List() directly with local types."
|
|
59
|
-
echo " See internal/commands/
|
|
59
|
+
echo " See internal/commands/forms.go for the correct pattern."
|
|
60
60
|
exit 1
|
|
61
61
|
fi
|
package/src/auth.js
CHANGED
|
@@ -812,8 +812,27 @@ export class AuthManager {
|
|
|
812
812
|
async loginWithGck(instanceURL, input, username = this._activeUsername()) {
|
|
813
813
|
instanceURL = normalizeInstanceURL(instanceURL);
|
|
814
814
|
const creds = parseBrowserSessionInput(input);
|
|
815
|
-
|
|
816
|
-
|
|
815
|
+
let response;
|
|
816
|
+
try {
|
|
817
|
+
response = await fetch(`${instanceURL}/api/now/table/sys_user?sysparm_limit=1`, {
|
|
818
|
+
method: 'GET',
|
|
819
|
+
headers: {
|
|
820
|
+
Accept: 'application/json',
|
|
821
|
+
'X-UserToken': creds.access_token,
|
|
822
|
+
Cookie: creds.cookies,
|
|
823
|
+
},
|
|
824
|
+
});
|
|
825
|
+
} catch (error) {
|
|
826
|
+
throw errAuth(`Could not test the browser session against ${instanceURL}. Nothing was saved. Retry with: jsn auth login ${instanceURL} --gck (${error.message})`);
|
|
827
|
+
}
|
|
828
|
+
if (!response.ok) {
|
|
829
|
+
throw errAuth(`Browser session rejected by ${instanceURL} (HTTP ${response.status}). Nothing was saved. Retry with: jsn auth login ${instanceURL} --gck`);
|
|
830
|
+
}
|
|
831
|
+
const body = await response.json();
|
|
832
|
+
const user = body?.result?.[0];
|
|
833
|
+
const verifiedUsername = typeof user?.user_name === 'string' ? user.user_name : username;
|
|
834
|
+
this.saveCredentials(instanceURL, { ...creds, username: verifiedUsername || undefined }, verifiedUsername || undefined);
|
|
835
|
+
return { ...creds, username: verifiedUsername || undefined };
|
|
817
836
|
}
|
|
818
837
|
|
|
819
838
|
async loginWithCode(instanceURL, code, username = this._activeUsername()) {
|
package/src/capabilities.js
CHANGED
|
@@ -8,8 +8,6 @@
|
|
|
8
8
|
// // Subcommand verbs that mutate instance state. The mutation guard
|
|
9
9
|
// // (src/mutations.js) derives its command-path list from these.
|
|
10
10
|
// mutationSubcommands: ['create', 'update', 'delete'],
|
|
11
|
-
// // Also registered under `jsn dev <name>` — derive both spellings.
|
|
12
|
-
// devAlias: true,
|
|
13
11
|
// // Command runs without a configured instance (joins the middleware
|
|
14
12
|
// // skip-list for the instance guard).
|
|
15
13
|
// noInstance: true,
|
|
@@ -42,7 +40,7 @@ const REGISTRY = new Map();
|
|
|
42
40
|
* @returns {object} the caps object (for convenient inline use)
|
|
43
41
|
*/
|
|
44
42
|
export function declareCapabilities(name, caps) {
|
|
45
|
-
REGISTRY.set(name, {
|
|
43
|
+
REGISTRY.set(name, { ...caps });
|
|
46
44
|
return caps;
|
|
47
45
|
}
|
|
48
46
|
|
|
@@ -99,9 +97,6 @@ export function dailyCheckSkipCommands(caps = collectCapabilities()) {
|
|
|
99
97
|
* begins with the pattern IS that mutation (trailing positionals like a
|
|
100
98
|
* sys_id don't change intent).
|
|
101
99
|
*
|
|
102
|
-
* Commands with `devAlias: true` are also registered under `jsn dev <name>`,
|
|
103
|
-
* so both spellings are emitted.
|
|
104
|
-
*
|
|
105
100
|
* A whole-command mutation surface (e.g. `jsn eval`, `jsn rest` — the
|
|
106
101
|
* command itself is the mutation, there is no mutating sub-verb) declares
|
|
107
102
|
* `mutationSubcommands: ['']`.
|
|
@@ -116,7 +111,6 @@ export function mutationPaths(caps = collectCapabilities()) {
|
|
|
116
111
|
for (const sub of c.mutationSubcommands) {
|
|
117
112
|
const parts = sub === '' ? [] : sub.split(' ');
|
|
118
113
|
paths.push([name, ...parts]);
|
|
119
|
-
if (c.devAlias) paths.push(['dev', name, ...parts]);
|
|
120
114
|
}
|
|
121
115
|
}
|
|
122
116
|
return paths;
|
package/src/cli.js
CHANGED
|
@@ -34,12 +34,11 @@ import { transactionsCmd } from './commands/transactions.js';
|
|
|
34
34
|
import { platformCmd } from './commands/platform.js';
|
|
35
35
|
import { codesearchCmd } from './commands/codesearch.js';
|
|
36
36
|
import { versionCmd } from './commands/version.js';
|
|
37
|
-
import { devCmd } from './commands/dev.js';
|
|
38
37
|
import { skillCmd, checkSkill } from './commands/skill.js';
|
|
39
38
|
import { getVersion, checkLatest } from './commands/version.js';
|
|
40
39
|
import { docsCmd } from './commands/docs/docs.js';
|
|
41
40
|
|
|
42
|
-
//
|
|
41
|
+
// Table and platform commands registered at root level for progressive disclosure
|
|
43
42
|
import {
|
|
44
43
|
actionsCmd, includesCmd, rulesCmd,
|
|
45
44
|
clientScriptsCmd, uiActionsCmd,
|
|
@@ -55,19 +54,19 @@ import {
|
|
|
55
54
|
emailCmd,
|
|
56
55
|
restmessageCmd, soapmessagesCmd,
|
|
57
56
|
uimacrosCmd, uxlistsCmd, uxapplicabilityCmd,
|
|
58
|
-
} from './commands/
|
|
57
|
+
} from './commands/_simple.js';
|
|
59
58
|
import { decisiontablesCmd } from './commands/decisiontables.js';
|
|
60
|
-
import { flowsCmd } from './commands/
|
|
61
|
-
import { formsCmd } from './commands/
|
|
62
|
-
import { listsCmd } from './commands/
|
|
63
|
-
import { updateSetsCmd } from './commands/
|
|
64
|
-
import { scopesCmd } from './commands/
|
|
65
|
-
import { domainsCmd } from './commands/
|
|
66
|
-
import { evalCmd } from './commands/
|
|
67
|
-
import { restCmd } from './commands/
|
|
68
|
-
import { logsCmd } from './commands/
|
|
69
|
-
import { scrapiCmd } from './commands/
|
|
70
|
-
import { b4rulesCmd } from './commands/
|
|
59
|
+
import { flowsCmd } from './commands/flows.js';
|
|
60
|
+
import { formsCmd } from './commands/forms.js';
|
|
61
|
+
import { listsCmd } from './commands/lists.js';
|
|
62
|
+
import { updateSetsCmd } from './commands/updatesets.js';
|
|
63
|
+
import { scopesCmd } from './commands/scopes.js';
|
|
64
|
+
import { domainsCmd } from './commands/domains.js';
|
|
65
|
+
import { evalCmd } from './commands/eval.js';
|
|
66
|
+
import { restCmd } from './commands/rest.js';
|
|
67
|
+
import { logsCmd } from './commands/logs.js';
|
|
68
|
+
import { scrapiCmd } from './commands/scrapi.js';
|
|
69
|
+
import { b4rulesCmd } from './commands/b4rules.js';
|
|
71
70
|
import { atfCmd } from './commands/atf.js';
|
|
72
71
|
import { approvalsCmd } from './commands/approvals.js';
|
|
73
72
|
import { perfCmd } from './commands/perf.js';
|
|
@@ -378,8 +377,6 @@ export function buildCLI() {
|
|
|
378
377
|
.command(skillCmd(wrap))
|
|
379
378
|
.command(docsCmd(wrap))
|
|
380
379
|
.command(versionCmd(wrap))
|
|
381
|
-
// Legacy
|
|
382
|
-
.command(devCmd(wrap))
|
|
383
380
|
.demandCommand(1, 'You must specify a command')
|
|
384
381
|
// Completion plumbing under a hidden command name, so the explicit
|
|
385
382
|
// 'completion' command below owns the UX. (#176)
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
// Generic
|
|
1
|
+
// Generic table-based CRUD command builder
|
|
2
2
|
|
|
3
|
-
import { formatRecordForDisplay, getStringField, parseDataArg, confirmDelete, interactiveList, canPrompt } from '
|
|
4
|
-
import { resolveRecord, unwrapSysId } from '
|
|
5
|
-
import { getCurrentUser, getCurrentApplication } from '
|
|
6
|
-
import { FormatAuto } from '
|
|
7
|
-
import { declareCapabilities } from '
|
|
3
|
+
import { formatRecordForDisplay, getStringField, parseDataArg, confirmDelete, interactiveList, canPrompt } from '../helpers.js';
|
|
4
|
+
import { resolveRecord, unwrapSysId } from '../resolve-record.js';
|
|
5
|
+
import { getCurrentUser, getCurrentApplication } from '../context.js';
|
|
6
|
+
import { FormatAuto } from '../output.js';
|
|
7
|
+
import { declareCapabilities } from '../capabilities.js';
|
|
8
8
|
|
|
9
9
|
function vowelArticle(word) {
|
|
10
10
|
const first = word.charAt(0).toLowerCase();
|
|
@@ -95,11 +95,8 @@ export function buildDevCmd(name, table, aliases, defaultColumns, wrap, opts = {
|
|
|
95
95
|
const singular = toSingular(name, opts.singular);
|
|
96
96
|
const readOnly = opts.readOnly || false;
|
|
97
97
|
// Capability declaration: the readOnly flag drives the mutation registry.
|
|
98
|
-
// buildDevCmd commands are registered both at root and under `jsn dev`
|
|
99
|
-
// unless opts.devAlias says otherwise (root-only commands pass false).
|
|
100
98
|
declareCapabilities(name, {
|
|
101
99
|
mutationSubcommands: readOnly ? [] : ['create', 'update', 'delete'],
|
|
102
|
-
devAlias: opts.devAlias !== false,
|
|
103
100
|
});
|
|
104
101
|
const scopeValidation = opts.scopeValidation || false;
|
|
105
102
|
const extraQuery = opts.extraQuery || '';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { buildDevCmd } from './_generic.js';
|
|
2
|
-
import { getStringField } from '
|
|
3
|
-
// Simple table-based
|
|
2
|
+
import { getStringField } from '../helpers.js';
|
|
3
|
+
// Simple table-based commands — singular names passed for grammar
|
|
4
4
|
// Default columns and aliases match the Go version exactly
|
|
5
5
|
|
|
6
6
|
async function fetchExtensionChain(sdk, record) {
|
|
@@ -81,7 +81,7 @@ export { columnsCmd } from './columns.js';
|
|
|
81
81
|
|
|
82
82
|
// Read-only commands (Go only has list/show)
|
|
83
83
|
export { importCmd } from './import.js';
|
|
84
|
-
export const spPagesCmd = (wrap) => buildDevCmd('sppages', 'sp_page', ['sp-pages'
|
|
84
|
+
export const spPagesCmd = (wrap) => buildDevCmd('sppages', 'sp_page', ['sp-pages'], ['id', 'title', 'sys_scope'], wrap, { singular: 'Service Portal page', readOnly: true });
|
|
85
85
|
export const spWidgetsCmd = (wrap) => buildDevCmd('spwidgets', 'sp_widget', ['sp-widget', 'widgets'], ['id', 'name', 'sys_scope'], wrap, { singular: 'Service Portal widget', readOnly: true });
|
|
86
86
|
export const uiPagesCmd = (wrap) => buildDevCmd('uipages', 'sys_ui_page', ['ui-page', 'pages'], ['name', 'sys_scope'], wrap, { singular: 'UI page', scopeValidation: true });
|
|
87
87
|
export const appMenuCmd = (wrap) => buildDevCmd('appmenu', 'sys_app_application', ['app-menu', 'menu'], ['name', 'active', 'sys_scope'], wrap, { singular: 'application menu', scopeValidation: true });
|
|
@@ -99,30 +99,30 @@ export const viewsCmd = (wrap) => buildDevCmd('views', 'sys_ui_view', ['view', '
|
|
|
99
99
|
export const privilegesCmd = (wrap) => buildDevCmd('privileges', 'sys_scope_privilege', ['privilege', 'priv'], ['source_scope', 'target_scope', 'target_name', 'operation', 'status'], wrap, { singular: 'privilege', scopeValidation: true });
|
|
100
100
|
export const uxscriptsCmd = (wrap) => buildDevCmd('uxscripts', 'sys_ux_lib_source_script', ['uxscript', 'ux'], ['name', 'active', 'sys_scope'], wrap, { singular: 'UX script', scopeValidation: true });
|
|
101
101
|
export { aliasesCmd } from './aliases.js';
|
|
102
|
-
export const catalogscriptsCmd = (wrap) => buildDevCmd('catalogscripts', 'catalog_script_client', ['catalogscript'
|
|
102
|
+
export const catalogscriptsCmd = (wrap) => buildDevCmd('catalogscripts', 'catalog_script_client', ['catalogscript'], ['name', 'table', 'active', 'sys_scope'], wrap, { singular: 'catalog script', scopeValidation: true });
|
|
103
103
|
|
|
104
104
|
// ── Automation: Async ──
|
|
105
|
-
export const scriptactionsCmd = (wrap) => buildDevCmd('scriptactions', 'sysevent_script_action', ['scriptaction'], ['name', 'active', 'sys_scope'], wrap, { singular: 'script action', scopeValidation: true
|
|
106
|
-
export const scheduledjobsCmd = (wrap) => buildDevCmd('scheduledjobs', 'sysauto_script', ['scheduledjob'], ['name', 'active', 'run_type', 'sys_scope'], wrap, { singular: 'scheduled job', scopeValidation: true
|
|
107
|
-
export const asyncrulesCmd = (wrap) => buildDevCmd('asyncrules', 'sys_script', ['asyncrule'], ['name', 'collection', 'active', 'order', 'sys_scope'], wrap, { singular: 'async business rule', scopeValidation: true, extraQuery: 'when=async'
|
|
108
|
-
export const triggersCmd = (wrap) => buildDevCmd('triggers', 'sysevent_register', ['trigger'], ['name', 'event_name', 'active', 'sys_scope'], wrap, { singular: 'event trigger', scopeValidation: true
|
|
105
|
+
export const scriptactionsCmd = (wrap) => buildDevCmd('scriptactions', 'sysevent_script_action', ['scriptaction'], ['name', 'active', 'sys_scope'], wrap, { singular: 'script action', scopeValidation: true });
|
|
106
|
+
export const scheduledjobsCmd = (wrap) => buildDevCmd('scheduledjobs', 'sysauto_script', ['scheduledjob'], ['name', 'active', 'run_type', 'sys_scope'], wrap, { singular: 'scheduled job', scopeValidation: true });
|
|
107
|
+
export const asyncrulesCmd = (wrap) => buildDevCmd('asyncrules', 'sys_script', ['asyncrule'], ['name', 'collection', 'active', 'order', 'sys_scope'], wrap, { singular: 'async business rule', scopeValidation: true, extraQuery: 'when=async' });
|
|
108
|
+
export const triggersCmd = (wrap) => buildDevCmd('triggers', 'sysevent_register', ['trigger'], ['name', 'event_name', 'active', 'sys_scope'], wrap, { singular: 'event trigger', scopeValidation: true });
|
|
109
109
|
|
|
110
110
|
// ── Automation: In Memory ──
|
|
111
111
|
|
|
112
|
-
export const assignmentsCmd = (wrap) => buildDevCmd('assignments', 'sysrule_assignment', ['assignment'], ['name', 'active', 'sys_scope'], wrap, { singular: 'assignment rule', scopeValidation: true, readOnly: true
|
|
112
|
+
export const assignmentsCmd = (wrap) => buildDevCmd('assignments', 'sysrule_assignment', ['assignment'], ['name', 'active', 'sys_scope'], wrap, { singular: 'assignment rule', scopeValidation: true, readOnly: true });
|
|
113
113
|
|
|
114
114
|
// ── Automation: Inbound ──
|
|
115
|
-
export const emailCmd = (wrap) => buildDevCmd('email', 'sysevent_in_email_action', ['emails'], ['name', 'active', 'type', 'sys_scope'], wrap, { singular: 'inbound email action', scopeValidation: true
|
|
115
|
+
export const emailCmd = (wrap) => buildDevCmd('email', 'sysevent_in_email_action', ['emails'], ['name', 'active', 'type', 'sys_scope'], wrap, { singular: 'inbound email action', scopeValidation: true });
|
|
116
116
|
|
|
117
117
|
// ── Automation: Outbound ──
|
|
118
118
|
export { restmessageCmd } from './restmessage.js';
|
|
119
119
|
export { soapmessagesCmd } from './soapmessages.js';
|
|
120
120
|
|
|
121
121
|
// ── UX: Core UI ──
|
|
122
|
-
export const uimacrosCmd = (wrap) => buildDevCmd('uimacros', 'sys_ui_macro', ['uimacro'], ['name', 'active', 'sys_scope'], wrap, { singular: 'UI macro', scopeValidation: true
|
|
122
|
+
export const uimacrosCmd = (wrap) => buildDevCmd('uimacros', 'sys_ui_macro', ['uimacro'], ['name', 'active', 'sys_scope'], wrap, { singular: 'UI macro', scopeValidation: true });
|
|
123
123
|
|
|
124
124
|
// ── UX: Workspaces ──
|
|
125
|
-
export const uxlistsCmd = (wrap) => buildDevCmd('uxlists', 'sys_ux_list', ['uxlist'], ['name', 'active', 'sys_scope'], wrap, { singular: 'workspace list', scopeValidation: true, readOnly: true
|
|
126
|
-
export const uxapplicabilityCmd = (wrap) => buildDevCmd('uxapplicability', 'sys_ux_applicability_m2m', ['uxapp'], ['name', 'active', 'sys_scope'], wrap, { singular: 'workspace applicability', scopeValidation: true, readOnly: true
|
|
125
|
+
export const uxlistsCmd = (wrap) => buildDevCmd('uxlists', 'sys_ux_list', ['uxlist'], ['name', 'active', 'sys_scope'], wrap, { singular: 'workspace list', scopeValidation: true, readOnly: true });
|
|
126
|
+
export const uxapplicabilityCmd = (wrap) => buildDevCmd('uxapplicability', 'sys_ux_applicability_m2m', ['uxapp'], ['name', 'active', 'sys_scope'], wrap, { singular: 'workspace applicability', scopeValidation: true, readOnly: true });
|
|
127
127
|
export { uipoliciesCmd } from './uipolicies.js';
|
|
128
128
|
export const cataloguipoliciesCmd = (wrap) => buildDevCmd('cataloguipolicies', 'catalog_ui_policy', ['cataloguipolicy', 'cup'], ['short_description', 'catalog_item', 'variable_set', 'active', 'sys_scope'], wrap, { singular: 'catalog UI policy', scopeValidation: true });
|
package/src/commands/_ticket.js
CHANGED
|
@@ -8,7 +8,7 @@ import { declareCapabilities } from '../capabilities.js';
|
|
|
8
8
|
|
|
9
9
|
export function buildTicketCommands(table, displayName, alias, defaultColumns, stateMap, iconFn, wrap) {
|
|
10
10
|
// Ticket-style CRUD: list/show are reads; create/update/delete mutate.
|
|
11
|
-
// Root-level only
|
|
11
|
+
// Root-level command only.
|
|
12
12
|
declareCapabilities(displayName, { mutationSubcommands: ['create', 'update', 'delete'] });
|
|
13
13
|
return {
|
|
14
14
|
command: `${displayName} [subcommand]`,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { buildDevCmd } from './_generic.js';
|
|
2
|
-
import { getStringField } from '
|
|
2
|
+
import { getStringField } from '../helpers.js';
|
|
3
3
|
|
|
4
4
|
export const aliasesCmd = (wrap) => buildDevCmd('aliases', 'sys_alias', ['alias', 'als'],
|
|
5
5
|
['name', 'table', 'sys_scope'], wrap, {
|
package/src/commands/auth.js
CHANGED
|
@@ -326,7 +326,7 @@ export async function loginWizard(app, argv = {}) {
|
|
|
326
326
|
// Probe domain separation capability once and stamp the profile, so
|
|
327
327
|
// `jsn domains` (and its help entry) only appear on instances with it.
|
|
328
328
|
try {
|
|
329
|
-
const domainsMod = await import('./
|
|
329
|
+
const domainsMod = await import('./domains.js');
|
|
330
330
|
const sdk = app.getSDKForProfile
|
|
331
331
|
? app.getSDKForProfile(instance, {
|
|
332
332
|
profile,
|
|
@@ -436,14 +436,14 @@ export async function modifyProfile(app, argv = {}) {
|
|
|
436
436
|
app.ok({ profile: name, domain: '' }, { summary: `Domain cleared for ${name}` });
|
|
437
437
|
return;
|
|
438
438
|
}
|
|
439
|
-
const { isDomainSeparationInstalled } = await import('./
|
|
439
|
+
const { isDomainSeparationInstalled } = await import('./domains.js');
|
|
440
440
|
const sdk = app.getSDKForProfile
|
|
441
441
|
? app.getSDKForProfile(profile.instance_url, { profile, authMethod: profile.auth_method, username: profile.username })
|
|
442
442
|
: app.sdk;
|
|
443
443
|
if (!(await isDomainSeparationInstalled({ sdk }))) {
|
|
444
444
|
throw new Error(`Domain separation is not installed on ${profile.instance_url}`);
|
|
445
445
|
}
|
|
446
|
-
const { resolveDomainSysId } = await import('./
|
|
446
|
+
const { resolveDomainSysId } = await import('./domains.js');
|
|
447
447
|
const sysID = await resolveDomainSysId({ sdk }, argv.domain);
|
|
448
448
|
profile.domain = sysID;
|
|
449
449
|
await saveConfig(app.config);
|
|
@@ -472,7 +472,7 @@ export async function modifyProfile(app, argv = {}) {
|
|
|
472
472
|
choices,
|
|
473
473
|
});
|
|
474
474
|
if (flag === 'domain') {
|
|
475
|
-
const domainsMod = await import('./
|
|
475
|
+
const domainsMod = await import('./domains.js');
|
|
476
476
|
const installed = await domainsMod.isDomainSeparationInstalled({ sdk });
|
|
477
477
|
if (!installed) {
|
|
478
478
|
throw new Error(`Domain separation is not installed on ${profile.instance_url}`);
|
|
@@ -948,7 +948,7 @@ Examples:
|
|
|
948
948
|
// Re-probe domain separation on refresh so the capability flag
|
|
949
949
|
// stays in sync if the plugin was installed/removed since setup.
|
|
950
950
|
try {
|
|
951
|
-
const { isDomainSeparationInstalled } = await import('./
|
|
951
|
+
const { isDomainSeparationInstalled } = await import('./domains.js');
|
|
952
952
|
const sdk = app.getSDKForProfile
|
|
953
953
|
? app.getSDKForProfile(instanceURL, {
|
|
954
954
|
...targetAuthOptions,
|
|
@@ -2,4 +2,4 @@ import { buildDevCmd } from './_generic.js';
|
|
|
2
2
|
|
|
3
3
|
// Before-query business rules — applies filters to GlideRecord queries before they execute.
|
|
4
4
|
// These are sys_script records with order < 100.
|
|
5
|
-
export const b4rulesCmd = (wrap) => buildDevCmd('b4rules', 'sys_script', ['b4rule'], ['name', 'collection', 'active', 'order', 'sys_scope'], wrap, { singular: 'before-query business rule', scopeValidation: true, extraQuery: 'when=before'
|
|
5
|
+
export const b4rulesCmd = (wrap) => buildDevCmd('b4rules', 'sys_script', ['b4rule'], ['name', 'collection', 'active', 'order', 'sys_scope'], wrap, { singular: 'before-query business rule', scopeValidation: true, extraQuery: 'when=before' });
|
|
@@ -8,7 +8,7 @@ import { declareCapabilities } from '../capabilities.js';
|
|
|
8
8
|
export const decisiontablesCmd = (wrap) => {
|
|
9
9
|
const name = 'decisiontables';
|
|
10
10
|
const singular = 'decision table';
|
|
11
|
-
declareCapabilities(name, { mutationSubcommands: ['create', 'update', 'delete']
|
|
11
|
+
declareCapabilities(name, { mutationSubcommands: ['create', 'update', 'delete'] });
|
|
12
12
|
|
|
13
13
|
const readData = (argv) => {
|
|
14
14
|
if (argv.dataFile) return fs.readFileSync(argv.dataFile, 'utf8');
|
|
@@ -10,11 +10,11 @@
|
|
|
10
10
|
// via the `X-Now-Domain` header — the same scoping the platform applies
|
|
11
11
|
// to its own picker sessions.
|
|
12
12
|
|
|
13
|
-
import { getStringField, interactiveList, assertSafeExactMatch } from '
|
|
14
|
-
import { declareCapabilities } from '
|
|
13
|
+
import { getStringField, interactiveList, assertSafeExactMatch } from '../helpers.js';
|
|
14
|
+
import { declareCapabilities } from '../capabilities.js';
|
|
15
15
|
|
|
16
16
|
// `domains set` writes the chosen domain into the profile config.
|
|
17
|
-
declareCapabilities('domains', { mutationSubcommands: ['set']
|
|
17
|
+
declareCapabilities('domains', { mutationSubcommands: ['set'] });
|
|
18
18
|
|
|
19
19
|
const DETAIL_FIELDS = 'sys_id,name,parent,sys_domain_path,description,active,sys_created_on,sys_updated_on';
|
|
20
20
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
|
-
import { getCurrentUser, getCurrentApplication } from '
|
|
3
|
-
import { declareCapabilities } from '
|
|
2
|
+
import { getCurrentUser, getCurrentApplication } from '../context.js';
|
|
3
|
+
import { declareCapabilities } from '../capabilities.js';
|
|
4
4
|
|
|
5
5
|
// The whole command is a mutation surface (runs arbitrary server-side JS) —
|
|
6
|
-
// declared as `['']` so
|
|
7
|
-
declareCapabilities('eval', { mutationSubcommands: ['']
|
|
6
|
+
// declared as `['']` so the root `jsn eval` command are gated.
|
|
7
|
+
declareCapabilities('eval', { mutationSubcommands: [''] });
|
|
8
8
|
|
|
9
9
|
export function evalCmd(wrap) {
|
|
10
10
|
return {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { formatRecordForDisplay, getStringField, interactiveList } from '
|
|
2
|
-
import { discoverFlowContextFields, normalizeFlowContext, summarizeFlowContexts, formatFlowContextSummary, aggregateFlowContextMappings, mergeFlowContextStats, buildFlowContextQuery } from '
|
|
3
|
-
import { declareCapabilities } from '
|
|
4
|
-
import { inspectFlow } from '
|
|
5
|
-
import { publishFlows, publishDoctor, flowStatus } from '
|
|
6
|
-
import { resolveRecord, unwrapSysId } from '
|
|
1
|
+
import { formatRecordForDisplay, getStringField, interactiveList } from '../helpers.js';
|
|
2
|
+
import { discoverFlowContextFields, normalizeFlowContext, summarizeFlowContexts, formatFlowContextSummary, aggregateFlowContextMappings, mergeFlowContextStats, buildFlowContextQuery } from '../flow-context.js';
|
|
3
|
+
import { declareCapabilities } from '../capabilities.js';
|
|
4
|
+
import { inspectFlow } from '../flow-inspection.js';
|
|
5
|
+
import { publishFlows, publishDoctor, flowStatus } from '../flow-publish.js';
|
|
6
|
+
import { resolveRecord, unwrapSysId } from '../resolve-record.js';
|
|
7
7
|
|
|
8
8
|
function flowInspectionAdapter(app) {
|
|
9
9
|
return {
|
|
@@ -12,7 +12,7 @@ function flowInspectionAdapter(app) {
|
|
|
12
12
|
};
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
declareCapabilities('flows', { mutationSubcommands: ['create', 'update', 'delete', 'publish']
|
|
15
|
+
declareCapabilities('flows', { mutationSubcommands: ['create', 'update', 'delete', 'publish'] });
|
|
16
16
|
|
|
17
17
|
function renderChecks(checks) {
|
|
18
18
|
return checks.map((c) => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { buildDevCmd } from './_generic.js';
|
|
2
|
-
import { getStringField } from '
|
|
2
|
+
import { getStringField } from '../helpers.js';
|
|
3
3
|
|
|
4
4
|
export const importCmd = (wrap) => buildDevCmd('import', 'sys_import_set', ['imports', 'imp'],
|
|
5
5
|
['name', 'sys_target_table', 'state', 'sys_created_on'], wrap, {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { declareCapabilities } from '
|
|
1
|
+
import { declareCapabilities } from '../capabilities.js';
|
|
2
2
|
|
|
3
3
|
// Raw REST passthrough — can hit any endpoint with any method, so the whole
|
|
4
|
-
// command is a mutation surface on read-only profiles
|
|
5
|
-
declareCapabilities('rest', { mutationSubcommands: ['']
|
|
4
|
+
// command is a mutation surface on read-only profiles.
|
|
5
|
+
declareCapabilities('rest', { mutationSubcommands: [''] });
|
|
6
6
|
|
|
7
7
|
export function restCmd(wrap) {
|
|
8
8
|
return {
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { buildDevCmd } from './_generic.js';
|
|
2
|
-
import { getStringField } from '
|
|
2
|
+
import { getStringField } from '../helpers.js';
|
|
3
3
|
|
|
4
4
|
export const restmessageCmd = (wrap) => buildDevCmd('restmessage', 'sys_rest_message', ['rm', 'restmsg'],
|
|
5
5
|
['name', 'endpoint', 'active', 'sys_scope'], wrap, {
|
|
6
6
|
singular: 'REST message',
|
|
7
7
|
scopeValidation: true,
|
|
8
|
-
devAlias: false,
|
|
9
8
|
onShow: async (record, app) => {
|
|
10
9
|
const sysID = getStringField(record, 'sys_id') || '';
|
|
11
10
|
if (!sysID) return;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { formatRecordForDisplay, getStringField, interactiveList, assertSafeExactMatch } from '
|
|
2
|
-
import { requireCurrentUserSysId, setCurrentApplication } from '
|
|
3
|
-
import { declareCapabilities } from '
|
|
4
|
-
import { unwrapSysId } from '
|
|
1
|
+
import { formatRecordForDisplay, getStringField, interactiveList, assertSafeExactMatch } from '../helpers.js';
|
|
2
|
+
import { requireCurrentUserSysId, setCurrentApplication } from '../context.js';
|
|
3
|
+
import { declareCapabilities } from '../capabilities.js';
|
|
4
|
+
import { unwrapSysId } from '../resolve-record.js';
|
|
5
5
|
|
|
6
|
-
declareCapabilities('scopes', { mutationSubcommands: ['create', 'set']
|
|
6
|
+
declareCapabilities('scopes', { mutationSubcommands: ['create', 'set'] });
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Resolve a scope identifier to a single sys_scope record.
|
|
@@ -8,13 +8,13 @@
|
|
|
8
8
|
|
|
9
9
|
import fs from 'node:fs';
|
|
10
10
|
import path from 'node:path';
|
|
11
|
-
import { formatRecordForDisplay, getStringField, isHexString } from '
|
|
12
|
-
import { errUsageHint } from '
|
|
13
|
-
import { declareCapabilities } from '
|
|
11
|
+
import { formatRecordForDisplay, getStringField, isHexString } from '../helpers.js';
|
|
12
|
+
import { errUsageHint } from '../errors.js';
|
|
13
|
+
import { declareCapabilities } from '../capabilities.js';
|
|
14
14
|
|
|
15
15
|
// `scrapi create` builds a full Scripted REST API in one call (list/show are
|
|
16
|
-
// reads). Registered at root
|
|
17
|
-
declareCapabilities('scrapi', { mutationSubcommands: ['create']
|
|
16
|
+
// reads). Registered at the root.
|
|
17
|
+
declareCapabilities('scrapi', { mutationSubcommands: ['create'] });
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* Parse a --resource flag value of the form:
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { buildDevCmd } from './_generic.js';
|
|
2
|
-
import { getStringField } from '
|
|
2
|
+
import { getStringField } from '../helpers.js';
|
|
3
3
|
|
|
4
4
|
export const soapmessagesCmd = (wrap) => buildDevCmd('soapmessages', 'sys_soap_message', ['soapmsg'],
|
|
5
5
|
['name', 'active', 'sys_scope'], wrap, {
|
|
6
6
|
singular: 'SOAP message',
|
|
7
7
|
scopeValidation: true,
|
|
8
|
-
devAlias: false,
|
|
9
8
|
onShow: async (record, app) => {
|
|
10
9
|
const sysID = getStringField(record, 'sys_id') || '';
|
|
11
10
|
if (!sysID) return;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { buildDevCmd } from './_generic.js';
|
|
2
|
-
import { getStringField } from '
|
|
2
|
+
import { getStringField } from '../helpers.js';
|
|
3
3
|
|
|
4
4
|
export const uipoliciesCmd = (wrap) => buildDevCmd('uipolicies', 'sys_ui_policy', ['uipolicy', 'up'],
|
|
5
5
|
['short_description', 'table', 'sys_class_name', 'active'], wrap, {
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
|
-
import { formatRecordForDisplay, getStringField, interactiveList, assertSafeExactMatch } from '
|
|
3
|
-
import { getCurrentApplication, getCurrentUpdateSet, requireCurrentUserSysId, setCurrentApplication, setCurrentUpdateSet } from '
|
|
4
|
-
import { errAPI, errUsage } from '
|
|
5
|
-
import { declareCapabilities } from '
|
|
2
|
+
import { formatRecordForDisplay, getStringField, interactiveList, assertSafeExactMatch } from '../helpers.js';
|
|
3
|
+
import { getCurrentApplication, getCurrentUpdateSet, requireCurrentUserSysId, setCurrentApplication, setCurrentUpdateSet } from '../context.js';
|
|
4
|
+
import { errAPI, errUsage } from '../errors.js';
|
|
5
|
+
import { declareCapabilities } from '../capabilities.js';
|
|
6
6
|
|
|
7
7
|
declareCapabilities('updatesets', {
|
|
8
8
|
mutationSubcommands: ['create', 'set', 'complete', 'ignore', 'parent'],
|
|
9
|
-
devAlias: true,
|
|
10
9
|
});
|
|
11
10
|
|
|
12
11
|
/**
|
package/src/sdk.js
CHANGED
|
@@ -29,9 +29,11 @@ export class SDKClient {
|
|
|
29
29
|
this.authProvider = authProvider;
|
|
30
30
|
this.timeout = opts.timeout || DEFAULT_TIMEOUT;
|
|
31
31
|
this.domain = opts.domain || '';
|
|
32
|
+
this.oauthCookies = null;
|
|
33
|
+
this.sessionUserToken = '';
|
|
32
34
|
}
|
|
33
35
|
|
|
34
|
-
async _setAuth(req) {
|
|
36
|
+
async _setAuth(req, { skipOAuthSession = false } = {}) {
|
|
35
37
|
if (!this.authProvider) {
|
|
36
38
|
throw errAuth('No authentication configured');
|
|
37
39
|
}
|
|
@@ -41,10 +43,22 @@ export class SDKClient {
|
|
|
41
43
|
}
|
|
42
44
|
switch (creds.auth_method) {
|
|
43
45
|
case 'basic':
|
|
46
|
+
if (!skipOAuthSession) {
|
|
47
|
+
const cookies = await this._warmSession();
|
|
48
|
+
if (cookies) {
|
|
49
|
+
req.headers.set('Cookie', cookies);
|
|
50
|
+
if (this.sessionUserToken) req.headers.set('X-UserToken', this.sessionUserToken);
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
44
54
|
req.headers.set('Authorization', 'Basic ' + Buffer.from(`${creds.username}:${creds.password}`).toString('base64'));
|
|
45
55
|
break;
|
|
46
56
|
case 'token':
|
|
47
57
|
case 'oauth':
|
|
58
|
+
if (!skipOAuthSession) {
|
|
59
|
+
const cookies = await this._warmSession();
|
|
60
|
+
if (cookies) req.headers.set('Cookie', cookies);
|
|
61
|
+
}
|
|
48
62
|
req.headers.set('Authorization', `Bearer ${creds.access_token}`);
|
|
49
63
|
break;
|
|
50
64
|
case 'gck':
|
|
@@ -113,7 +127,7 @@ export class SDKClient {
|
|
|
113
127
|
const timer = setTimeout(() => controller.abort(), this.timeout);
|
|
114
128
|
try {
|
|
115
129
|
const req = new Request(endpoint, { ...opts, signal: controller.signal });
|
|
116
|
-
await this._setAuth(req);
|
|
130
|
+
await this._setAuth(req, { skipOAuthSession: true });
|
|
117
131
|
return await fetch(req);
|
|
118
132
|
} finally {
|
|
119
133
|
clearTimeout(timer);
|
|
@@ -780,15 +794,52 @@ export class SDKClient {
|
|
|
780
794
|
}
|
|
781
795
|
|
|
782
796
|
async _warmSession() {
|
|
797
|
+
if (this.oauthCookies) return this.oauthCookies;
|
|
783
798
|
try {
|
|
784
|
-
const
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
799
|
+
const creds = await this.authProvider.getCredentials();
|
|
800
|
+
if (creds.auth_method === 'oauth') {
|
|
801
|
+
let cookies = '';
|
|
802
|
+
const collectCookies = (resp) => {
|
|
803
|
+
const values = resp.headers.getSetCookie?.() || [];
|
|
804
|
+
const raw = Array.isArray(values) ? values : [resp.headers.get('set-cookie')].filter(Boolean);
|
|
805
|
+
const next = raw.map(value => value.split(';', 1)[0]).filter(Boolean);
|
|
806
|
+
if (next.length) cookies = [...new Set(`${cookies}; ${next.join('; ')}`.split(';').map(v => v.trim()).filter(Boolean))].join('; ');
|
|
807
|
+
};
|
|
808
|
+
const login = `${this.baseURL}/angular.do?sysparm_type=get_user`;
|
|
809
|
+
collectCookies(await fetch(new Request(login, { method: 'POST' })));
|
|
810
|
+
const scopeEndpoint = `${this.baseURL}/api/now/table/sys_scope?sysparm_fields=sys_id%2Csys_class_name&sysparm_limit=1`;
|
|
811
|
+
collectCookies(await this._fetchWithAuth(scopeEndpoint, {
|
|
812
|
+
method: 'GET',
|
|
813
|
+
headers: cookies ? { Cookie: cookies } : {},
|
|
814
|
+
}));
|
|
815
|
+
collectCookies(await this._fetchWithAuth(login, {
|
|
816
|
+
method: 'POST',
|
|
817
|
+
headers: cookies ? { Cookie: cookies } : {},
|
|
818
|
+
}));
|
|
819
|
+
this.oauthCookies = cookies;
|
|
820
|
+
return cookies;
|
|
790
821
|
}
|
|
791
|
-
|
|
822
|
+
const endpoint = `${this.baseURL}/angular.do`;
|
|
823
|
+
const loginResp = await this._fetchWithAuth(`${endpoint}?sysparm_type=view_form.login`, {
|
|
824
|
+
method: 'POST',
|
|
825
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
826
|
+
body: new URLSearchParams({
|
|
827
|
+
sysparm_type: 'login',
|
|
828
|
+
'ni.nolog.user_password': 'true',
|
|
829
|
+
user_name: creds.username,
|
|
830
|
+
user_password: creds.password,
|
|
831
|
+
}).toString(),
|
|
832
|
+
});
|
|
833
|
+
const values = loginResp.headers.getSetCookie?.() || [];
|
|
834
|
+
const raw = Array.isArray(values) ? values : [loginResp.headers.get('set-cookie')].filter(Boolean);
|
|
835
|
+
const cookies = raw.map(value => value.split(';', 1)[0]).filter(Boolean).join('; ');
|
|
836
|
+
const refreshResp = await this._fetchWithAuth(`${endpoint}?sysparm_type=get_user`, {
|
|
837
|
+
method: 'POST',
|
|
838
|
+
headers: cookies ? { Cookie: cookies } : {},
|
|
839
|
+
});
|
|
840
|
+
this.sessionUserToken = refreshResp.headers.get('x-usertoken-response') || '';
|
|
841
|
+
this.oauthCookies = cookies;
|
|
842
|
+
return cookies;
|
|
792
843
|
} catch {
|
|
793
844
|
return '';
|
|
794
845
|
}
|
package/src/commands/dev.js
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
// Parent dev command
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
actionsCmd, includesCmd, rulesCmd,
|
|
5
|
-
clientScriptsCmd, uiActionsCmd,
|
|
6
|
-
tablesCmd, columnsCmd, importCmd,
|
|
7
|
-
spPagesCmd, spWidgetsCmd, uiPagesCmd, appMenuCmd,
|
|
8
|
-
aclsCmd, rolesCmd, propertiesCmd,
|
|
9
|
-
relationshipsCmd, appmodulesCmd, listcontrolsCmd, viewsCmd,
|
|
10
|
-
privilegesCmd, uxscriptsCmd, aliasesCmd,
|
|
11
|
-
catalogscriptsCmd, cataloguipoliciesCmd,
|
|
12
|
-
uipoliciesCmd,
|
|
13
|
-
} from './dev/_simple.js';
|
|
14
|
-
import { flowsCmd } from './dev/flows.js';
|
|
15
|
-
import { formsCmd } from './dev/forms.js';
|
|
16
|
-
import { listsCmd } from './dev/lists.js';
|
|
17
|
-
import { updateSetsCmd } from './dev/updatesets.js';
|
|
18
|
-
import { scopesCmd } from './dev/scopes.js';
|
|
19
|
-
import { domainsCmd } from './dev/domains.js';
|
|
20
|
-
import { evalCmd } from './dev/eval.js';
|
|
21
|
-
import { restCmd } from './dev/rest.js';
|
|
22
|
-
import { logsCmd } from './dev/logs.js';
|
|
23
|
-
import { scrapiCmd } from './dev/scrapi.js';
|
|
24
|
-
|
|
25
|
-
export function devCmd(wrap) {
|
|
26
|
-
return {
|
|
27
|
-
command: 'dev [subcommand]',
|
|
28
|
-
describe: 'Manage ServiceNow development artifacts',
|
|
29
|
-
builder: (yargs) => {
|
|
30
|
-
return yargs
|
|
31
|
-
.command(flowsCmd(wrap))
|
|
32
|
-
.command(actionsCmd(wrap))
|
|
33
|
-
.command(includesCmd(wrap))
|
|
34
|
-
.command(rulesCmd(wrap))
|
|
35
|
-
.command(clientScriptsCmd(wrap))
|
|
36
|
-
.command(uiActionsCmd(wrap))
|
|
37
|
-
.command(uipoliciesCmd(wrap))
|
|
38
|
-
.command(tablesCmd(wrap))
|
|
39
|
-
.command(columnsCmd(wrap))
|
|
40
|
-
.command(formsCmd(wrap))
|
|
41
|
-
.command(listsCmd(wrap))
|
|
42
|
-
.command(importCmd(wrap))
|
|
43
|
-
.command(spPagesCmd(wrap))
|
|
44
|
-
.command(spWidgetsCmd(wrap))
|
|
45
|
-
.command(uiPagesCmd(wrap))
|
|
46
|
-
.command(appMenuCmd(wrap))
|
|
47
|
-
.command(scrapiCmd(wrap))
|
|
48
|
-
.command(aclsCmd(wrap))
|
|
49
|
-
.command(rolesCmd(wrap))
|
|
50
|
-
.command(updateSetsCmd(wrap))
|
|
51
|
-
.command(scopesCmd(wrap))
|
|
52
|
-
.command(domainsCmd(wrap))
|
|
53
|
-
.command(propertiesCmd(wrap))
|
|
54
|
-
.command(relationshipsCmd(wrap))
|
|
55
|
-
.command(appmodulesCmd(wrap))
|
|
56
|
-
.command(listcontrolsCmd(wrap))
|
|
57
|
-
.command(viewsCmd(wrap))
|
|
58
|
-
.command(privilegesCmd(wrap))
|
|
59
|
-
.command(uxscriptsCmd(wrap))
|
|
60
|
-
.command(aliasesCmd(wrap))
|
|
61
|
-
.command(catalogscriptsCmd(wrap))
|
|
62
|
-
.command(cataloguipoliciesCmd(wrap))
|
|
63
|
-
.command(logsCmd(wrap))
|
|
64
|
-
.command(evalCmd(wrap))
|
|
65
|
-
.command(restCmd(wrap));
|
|
66
|
-
},
|
|
67
|
-
handler: (argv) => {
|
|
68
|
-
if (argv._[1]) return; // a subcommand ran — its own handler handled it
|
|
69
|
-
console.log('Manage ServiceNow development artifacts.');
|
|
70
|
-
console.log('');
|
|
71
|
-
console.log('Most dev commands are also available at the top level:');
|
|
72
|
-
console.log(' flows, actions, rules, clientscripts, uiactions, uipolicies,');
|
|
73
|
-
console.log(' tables, columns, forms, lists, import, sppages, spwidgets,');
|
|
74
|
-
console.log(' uipages, appmenu, scrapi, acls, roles, properties,');
|
|
75
|
-
console.log(' relationships, appmodules, listcontrols, views, privileges,');
|
|
76
|
-
console.log(' uxscripts, aliases, catalogscripts, cataloguipolicies, logs,');
|
|
77
|
-
console.log(' updatesets, scopes, eval, rest');
|
|
78
|
-
console.log('');
|
|
79
|
-
console.log('Run "jsn dev <command> --help" or "jsn <command> --help" for details.');
|
|
80
|
-
},
|
|
81
|
-
};
|
|
82
|
-
}
|
|
File without changes
|