@tenonhq/dovetail-servicenow 0.0.21 → 0.0.23
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/dist/cli.js +117 -0
- package/dist/client.d.ts +35 -0
- package/dist/client.js +31 -0
- package/dist/hostAssets.d.ts +44 -0
- package/dist/hostAssets.js +579 -0
- package/dist/index.d.ts +5 -4
- package/dist/index.js +8 -1
- package/dist/mcp/registry.d.ts +1 -1
- package/dist/mcp/registry.js +51 -1
- package/dist/mcp/schemas.d.ts +83 -0
- package/dist/mcp/schemas.js +20 -1
- package/dist/table/addColumn.d.ts +98 -0
- package/dist/table/addColumn.js +384 -0
- package/dist/table/formSession.d.ts +18 -6
- package/dist/table/formSession.js +98 -37
- package/dist/table/index.d.ts +3 -1
- package/dist/table/index.js +6 -1
- package/dist/types.d.ts +81 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -79,6 +79,7 @@ const editFlow_1 = require("./flowDesigner/editFlow");
|
|
|
79
79
|
const editActionType_1 = require("./flowDesigner/editActionType");
|
|
80
80
|
const testFlow_1 = require("./flowDesigner/testFlow");
|
|
81
81
|
const table_1 = require("./table");
|
|
82
|
+
const hostAssets_1 = require("./hostAssets");
|
|
82
83
|
const flowDesigner_formatter_2 = require("./flowDesigner-formatter");
|
|
83
84
|
function parseArgs(argv) {
|
|
84
85
|
var command = argv[0] || "";
|
|
@@ -706,6 +707,13 @@ function printHelp() {
|
|
|
706
707
|
" --columns \"Label:type:max, ...\" OR --from-json <spec.json>\n" +
|
|
707
708
|
" [--extends <t>] [--number-prefix <p>] [--user-role <r>]\n" +
|
|
708
709
|
" [--no-acls] [--no-menu] [--update-set <sys_id>] [--dry-run] [--json])\n" +
|
|
710
|
+
" add-column Add ONE column to an EXISTING table via the Studio form save, then verify\n" +
|
|
711
|
+
" (--table <name|sys_id> --label <l> --type <t>\n" +
|
|
712
|
+
" [--name <element>] [--max-length <n>] [--reference <table>]\n" +
|
|
713
|
+
" [--scope <s>] [--update-set <sys_id>] [--dry-run] [--json])\n" +
|
|
714
|
+
" host-assets Deploy a built dist/ to ServiceNow (carrier sys_ui_script + attachment + m2m)\n" +
|
|
715
|
+
" (--dir <dist> --app <sys_id> --scope <namespace>\n" +
|
|
716
|
+
" [--update-set <sys_id>] [--max-bytes <n>] [--allow-oversize] [--dry-run] [--json])\n" +
|
|
709
717
|
" test-flow Validate (default) or run a flow/subflow\n" +
|
|
710
718
|
" (--sys-id <sys_id> [--execute --confirm] [--inputs <json>] [--json])\n" +
|
|
711
719
|
" edit-flow Patch a flow/subflow (rename, description, step inputs)\n" +
|
|
@@ -806,6 +814,109 @@ async function runCreateTable(flags) {
|
|
|
806
814
|
return 2;
|
|
807
815
|
return 0;
|
|
808
816
|
}
|
|
817
|
+
/**
|
|
818
|
+
* dove-sn add-column:
|
|
819
|
+
* --table x_cadso_journey --label URL --type url
|
|
820
|
+
* [--name url] [--max-length 1024] [--reference <table>]
|
|
821
|
+
* [--scope x_cadso_journey] [--update-set <sys_id>] [--save-action <sys_id>]
|
|
822
|
+
* [--columns-rel-id <sys_id>] [--from-json <spec.json>] [--dry-run] [--debug] [--json]
|
|
823
|
+
*/
|
|
824
|
+
async function runAddColumn(flags) {
|
|
825
|
+
var spec = {};
|
|
826
|
+
if (flags["from-json"]) {
|
|
827
|
+
spec = JSON.parse(fs.readFileSync(path.resolve(flags["from-json"]), "utf8"));
|
|
828
|
+
}
|
|
829
|
+
var table = flags.table || spec.table;
|
|
830
|
+
var column = spec.column;
|
|
831
|
+
if (flags.label || flags.type) {
|
|
832
|
+
column = { label: flags.label || "", type: flags.type || "string" };
|
|
833
|
+
if (flags.name)
|
|
834
|
+
column.name = flags.name;
|
|
835
|
+
if (flags["max-length"])
|
|
836
|
+
column.max_length = flags["max-length"];
|
|
837
|
+
if (flags.reference)
|
|
838
|
+
column.reference = flags.reference;
|
|
839
|
+
}
|
|
840
|
+
if (!table || !column || !column.label) {
|
|
841
|
+
process.stderr.write("add-column: --table and --label (with --type) are required (or --from-json)\n");
|
|
842
|
+
return 1;
|
|
843
|
+
}
|
|
844
|
+
var params = {
|
|
845
|
+
client: (0, client_1.createClient)({}),
|
|
846
|
+
table: table,
|
|
847
|
+
column: column
|
|
848
|
+
};
|
|
849
|
+
var scope = flags.scope || spec.scope;
|
|
850
|
+
if (scope)
|
|
851
|
+
params.scope = scope;
|
|
852
|
+
var us = flags["update-set"] || spec.updateSetSysId;
|
|
853
|
+
if (us)
|
|
854
|
+
params.updateSetSysId = us;
|
|
855
|
+
var sa = flags["save-action"] || spec.saveActionSysId;
|
|
856
|
+
if (sa)
|
|
857
|
+
params.saveActionSysId = sa;
|
|
858
|
+
var relId = flags["columns-rel-id"] || spec.columnsRelId;
|
|
859
|
+
if (relId)
|
|
860
|
+
params.columnsRelId = relId;
|
|
861
|
+
if (flags["dry-run"] === "true" || spec.dryRun === true)
|
|
862
|
+
params.dryRun = true;
|
|
863
|
+
if (flags.debug === "true" || spec.debug === true)
|
|
864
|
+
params.debug = true;
|
|
865
|
+
var result = await (0, table_1.addColumn)(params);
|
|
866
|
+
if (flags.json === "true") {
|
|
867
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
868
|
+
}
|
|
869
|
+
else {
|
|
870
|
+
process.stdout.write("[" + result.status + "] " + result.table + "." + result.element + " (" + result.internalType + ")"
|
|
871
|
+
+ (result.verified ? " — verified" : "")
|
|
872
|
+
+ "\n" + result.note + "\n");
|
|
873
|
+
}
|
|
874
|
+
if (result.status === "failed")
|
|
875
|
+
return 2;
|
|
876
|
+
return 0;
|
|
877
|
+
}
|
|
878
|
+
/**
|
|
879
|
+
* dove-sn host-assets:
|
|
880
|
+
* --dir <dist> Required. Path to the pre-built dist/ directory.
|
|
881
|
+
* --app <sys_id> Required. Application record sys_id (m2m `application`).
|
|
882
|
+
* --scope <namespace> Required. Carrier scope, e.g. x_cadso_app_shell.
|
|
883
|
+
* --update-set <sys_id> Optional. Defaults to the scope's current update set.
|
|
884
|
+
* --max-bytes <n> Optional. Per-chunk serve cap (default ~5 MB).
|
|
885
|
+
* --allow-oversize Optional. Warn instead of failing on an oversize chunk.
|
|
886
|
+
* --dry-run Optional. Plan only; no writes/uploads/prunes.
|
|
887
|
+
* --json Optional. Emit the structured HostAssetsResult.
|
|
888
|
+
*
|
|
889
|
+
* Exit codes: 0 done/dry-run, 1 bad args, 2 a write landed but read-back is unverified.
|
|
890
|
+
*/
|
|
891
|
+
async function runHostAssets(flags) {
|
|
892
|
+
var dir = flags.dir;
|
|
893
|
+
var app = flags.app;
|
|
894
|
+
var scope = flags.scope;
|
|
895
|
+
if (!dir || !app || !scope) {
|
|
896
|
+
process.stderr.write("host-assets: --dir, --app and --scope are required\n");
|
|
897
|
+
return 1;
|
|
898
|
+
}
|
|
899
|
+
var params = { dir: path.resolve(dir), app: app, scope: scope };
|
|
900
|
+
var us = flags["update-set"] || flags.updateSetSysId;
|
|
901
|
+
if (us)
|
|
902
|
+
params.updateSetSysId = us;
|
|
903
|
+
if (flags["max-bytes"])
|
|
904
|
+
params.maxBytes = Number(flags["max-bytes"]);
|
|
905
|
+
if (flags["allow-oversize"] === "true")
|
|
906
|
+
params.allowOversize = true;
|
|
907
|
+
if (flags["dry-run"] === "true")
|
|
908
|
+
params.dryRun = true;
|
|
909
|
+
var client = (0, client_1.createClient)({});
|
|
910
|
+
var result = await (0, hostAssets_1.hostAssets)(client, params);
|
|
911
|
+
if (flags.json === "true") {
|
|
912
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
913
|
+
}
|
|
914
|
+
else {
|
|
915
|
+
process.stdout.write((0, hostAssets_1.formatHostAssetsResult)(result) + "\n");
|
|
916
|
+
}
|
|
917
|
+
var unverified = !result.dryRun && result.chunks.some(function (c) { return !c.verified; });
|
|
918
|
+
return unverified ? 2 : 0;
|
|
919
|
+
}
|
|
809
920
|
async function main() {
|
|
810
921
|
var parsed = parseArgs(process.argv.slice(2));
|
|
811
922
|
// Load credentials before any command runs. `--env`/`--env-file` (or the
|
|
@@ -837,6 +948,12 @@ async function main() {
|
|
|
837
948
|
if (parsed.command === "create-table") {
|
|
838
949
|
return await runCreateTable(parsed.flags);
|
|
839
950
|
}
|
|
951
|
+
if (parsed.command === "add-column") {
|
|
952
|
+
return await runAddColumn(parsed.flags);
|
|
953
|
+
}
|
|
954
|
+
if (parsed.command === "host-assets") {
|
|
955
|
+
return await runHostAssets(parsed.flags);
|
|
956
|
+
}
|
|
840
957
|
if (parsed.command === "test-flow") {
|
|
841
958
|
return await runTestFlow(parsed.flags);
|
|
842
959
|
}
|
package/dist/client.d.ts
CHANGED
|
@@ -34,6 +34,15 @@ export interface TableSchema {
|
|
|
34
34
|
fields: Array<TableSchemaField>;
|
|
35
35
|
primary_key: string;
|
|
36
36
|
}
|
|
37
|
+
/** A sys_attachment row, as returned by the native Attachment API. */
|
|
38
|
+
export interface AttachmentMeta {
|
|
39
|
+
sys_id: string;
|
|
40
|
+
file_name: string;
|
|
41
|
+
content_type: string;
|
|
42
|
+
/** SHA-256 hex of the file content, computed by ServiceNow. Absent on older instances. */
|
|
43
|
+
hash?: string;
|
|
44
|
+
size_bytes?: string;
|
|
45
|
+
}
|
|
37
46
|
export interface ServiceNowClient {
|
|
38
47
|
table: {
|
|
39
48
|
/** GET /api/now/table/<t>?sysparm_query=...&sysparm_limit=N — returns result array. */
|
|
@@ -113,5 +122,31 @@ export interface ServiceNowClient {
|
|
|
113
122
|
/** POST an arbitrary native ServiceNow REST path with a JSON body. See `get`. */
|
|
114
123
|
post: <T = any>(path: string, body: any) => Promise<T>;
|
|
115
124
|
};
|
|
125
|
+
attachment: {
|
|
126
|
+
/**
|
|
127
|
+
* GET /api/now/attachment?sysparm_query=table_name=<t>^table_sys_id=<id> —
|
|
128
|
+
* list the sys_attachment rows on a record. Read-only.
|
|
129
|
+
*/
|
|
130
|
+
listFor: (params: {
|
|
131
|
+
table: string;
|
|
132
|
+
sysId: string;
|
|
133
|
+
}) => Promise<Array<AttachmentMeta>>;
|
|
134
|
+
/**
|
|
135
|
+
* POST /api/now/attachment/file — upload raw bytes as a sys_attachment on a record.
|
|
136
|
+
* The Buffer is sent verbatim with `contentType` as the request Content-Type (binary,
|
|
137
|
+
* not JSON). Reuses the shared auth/retry/throttle transport — not a bespoke HTTP path.
|
|
138
|
+
*/
|
|
139
|
+
upload: (params: {
|
|
140
|
+
table: string;
|
|
141
|
+
sysId: string;
|
|
142
|
+
fileName: string;
|
|
143
|
+
contentType: string;
|
|
144
|
+
data: Buffer;
|
|
145
|
+
}) => Promise<AttachmentMeta>;
|
|
146
|
+
/** DELETE /api/now/attachment/<sysId> — remove a single attachment. */
|
|
147
|
+
remove: (params: {
|
|
148
|
+
sysId: string;
|
|
149
|
+
}) => Promise<void>;
|
|
150
|
+
};
|
|
116
151
|
}
|
|
117
152
|
export declare function createClient(config?: ServiceNowClientConfig): ServiceNowClient;
|
package/dist/client.js
CHANGED
|
@@ -316,6 +316,37 @@ function createClient(config = {}) {
|
|
|
316
316
|
post: function (path, body) {
|
|
317
317
|
return request({ method: "POST", url: path, data: body }, "now.post(" + path + ")");
|
|
318
318
|
}
|
|
319
|
+
},
|
|
320
|
+
attachment: {
|
|
321
|
+
listFor: async function (params) {
|
|
322
|
+
var data = await request({
|
|
323
|
+
method: "GET",
|
|
324
|
+
url: "/api/now/attachment",
|
|
325
|
+
params: {
|
|
326
|
+
sysparm_query: "table_name=" + params.table + "^table_sys_id=" + params.sysId,
|
|
327
|
+
sysparm_fields: "sys_id,file_name,content_type,hash,size_bytes"
|
|
328
|
+
}
|
|
329
|
+
}, "attachment.listFor(" + params.table + ")");
|
|
330
|
+
return (data && data.result) || [];
|
|
331
|
+
},
|
|
332
|
+
upload: async function (params) {
|
|
333
|
+
var data = await request({
|
|
334
|
+
method: "POST",
|
|
335
|
+
url: "/api/now/attachment/file",
|
|
336
|
+
params: {
|
|
337
|
+
table_name: params.table,
|
|
338
|
+
table_sys_id: params.sysId,
|
|
339
|
+
file_name: params.fileName
|
|
340
|
+
},
|
|
341
|
+
data: params.data,
|
|
342
|
+
// Binary upload: override the client's default application/json.
|
|
343
|
+
headers: { "content-type": params.contentType }
|
|
344
|
+
}, "attachment.upload(" + params.fileName + ")");
|
|
345
|
+
return (data && data.result) || data;
|
|
346
|
+
},
|
|
347
|
+
remove: async function (params) {
|
|
348
|
+
await request({ method: "DELETE", url: "/api/now/attachment/" + params.sysId }, "attachment.remove(" + params.sysId + ")");
|
|
349
|
+
}
|
|
319
350
|
}
|
|
320
351
|
};
|
|
321
352
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hostAssets — deploy a pre-built front-end bundle to ServiceNow.
|
|
3
|
+
*
|
|
4
|
+
* For each chunk in a built dist/ (index.html + assets/*.{js,css}) this:
|
|
5
|
+
* 1. Upserts a carrier sys_ui_script named `app_shell_asset:<vite-relative-path>`.
|
|
6
|
+
* The name carries the rotating hash on purpose — the Scripted REST serving
|
|
7
|
+
* resource resolves an asset request to its carrier by this exact name, so the
|
|
8
|
+
* verb's naming MUST match the path the built index.html references, or the
|
|
9
|
+
* asset 404s on serve.
|
|
10
|
+
* 2. Stores the chunk's bytes as a sys_attachment on that record (the script field
|
|
11
|
+
* caps at 65 KB; real chunks are far larger). Identical bytes are detected by
|
|
12
|
+
* SHA-256 and left in place.
|
|
13
|
+
* 3. Wires an x_cadso_app_shell_m2m_app_script row (application, script, chunk_role,
|
|
14
|
+
* order) so the app shell loads the chunk.
|
|
15
|
+
* 4. Prunes carriers + m2m rows for chunks no longer in the build — hashes rotate
|
|
16
|
+
* every build, so last build's records would otherwise pile up.
|
|
17
|
+
*
|
|
18
|
+
* The sys_ui_script + m2m writes route through the Dovetail Scripted REST API and are
|
|
19
|
+
* captured in the target update set. Attachments are data, not customizations, so they
|
|
20
|
+
* are not part of the update set. Re-runnable per build, per instance; idempotent for
|
|
21
|
+
* an identical dist/.
|
|
22
|
+
*
|
|
23
|
+
* The serving layer streams each chunk via GlideSysAttachment.getContentStream(), capped
|
|
24
|
+
* at ~5 MB (glide.scriptable.excel.max_file_size). A chunk at/over the cap truncates on
|
|
25
|
+
* serve, so the verb fails fast on an oversize chunk unless allowOversize is set.
|
|
26
|
+
*/
|
|
27
|
+
import type { ServiceNowClient } from "./client";
|
|
28
|
+
import type { ChunkInfo, HostAssetsParams, HostAssetsResult } from "./types";
|
|
29
|
+
/**
|
|
30
|
+
* Classify build files into ordered ChunkInfo. Pure — no filesystem access — so the
|
|
31
|
+
* naming + role + order logic is unit-testable in isolation.
|
|
32
|
+
*/
|
|
33
|
+
export declare function classifyChunks(files: Array<{
|
|
34
|
+
viteRelPath: string;
|
|
35
|
+
ext: string;
|
|
36
|
+
isIndexHtml: boolean;
|
|
37
|
+
}>): Array<ChunkInfo>;
|
|
38
|
+
/**
|
|
39
|
+
* Deploy a built dist/ to ServiceNow as app-shell carrier scripts + attachments + m2m
|
|
40
|
+
* wiring. Idempotent; re-running an identical dist/ reports every chunk unchanged.
|
|
41
|
+
*/
|
|
42
|
+
export declare function hostAssets(client: ServiceNowClient, params: HostAssetsParams): Promise<HostAssetsResult>;
|
|
43
|
+
/** Human-readable one-line-per-chunk summary for the CLI. */
|
|
44
|
+
export declare function formatHostAssetsResult(r: HostAssetsResult): string;
|