@tenonhq/dovetail-servicenow 0.0.8 → 0.0.10

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
@@ -162,6 +162,9 @@ npx dove-sn view-flow --sys-id <sys_id> --json --raw # structured + full model
162
162
  # View a Custom Action Type's model (inputs/outputs)
163
163
  npx dove-sn view-action --sys-id <action_type_sys_id> --scope <scope_sys_id>
164
164
 
165
+ # Copy a flow / subflow (creates an INACTIVE DRAFT) via the Designer's Copy endpoint
166
+ npx dove-sn copy-flow --sys-id <sys_id> --name "My Copy" # scope defaults to source's
167
+
165
168
  # Publish (compile the snapshot of) a flow / subflow after editing in the Designer
166
169
  npx dove-sn publish-flow --sys-id <sys_id> # scope defaults to the flow's
167
170
 
@@ -169,17 +172,27 @@ npx dove-sn publish-flow --sys-id <sys_id> # scope defaults to the f
169
172
  npx dove-sn test-flow --sys-id <sys_id> --inputs '{"phone":"+1555..."}'
170
173
  npx dove-sn test-flow --sys-id <sys_id> --execute --confirm --inputs '{...}' # runs it
171
174
 
172
- # Edit a flow in place (rename / description / step inputs), then publish
175
+ # Edit a flow in place (rename / description / step inputs)
173
176
  echo '{"rename":{"name":"New Name"},"patchStepInputs":[{"step":"Calculate SMS Send At","input":"send_rate","value":"5"}]}' > ops.json
174
- npx dove-sn edit-flow --sys-id <sys_id> --from-json ops.json # dry-run (diff)
175
- npx dove-sn edit-flow --sys-id <sys_id> --from-json ops.json --apply # publish the edit
177
+ npx dove-sn edit-flow --sys-id <sys_id> --from-json ops.json # dry-run (diff)
178
+ npx dove-sn edit-flow --sys-id <sys_id> --from-json ops.json --apply --update-set <id> # persist
176
179
  ```
177
180
 
181
+ `copy-flow` calls the Designer's own `POST /processflow/flow/{id}/copy` — a
182
+ complete, faithful clone created as an **inactive draft**. (Don't publish +
183
+ activate a copy of a triggered production flow unless you intend it to fire.)
178
184
  `test-flow` defaults to **validate** — a safe pre-flight (published? inputs match
179
- declared variables?) that never runs the flow. `--execute --confirm` runs it via
180
- the server-side FlowAPI runner (deploy `resources/runFlow.md` first); running a
181
- flow can cause real side effects. `edit-flow` defaults to a **dry-run** diff;
182
- `--apply` re-publishes the patched model via the snapshot endpoint.
185
+ declared variables?) that never runs the flow; `--execute --confirm` runs it via
186
+ the server-side FlowAPI runner (deploy `resources/runFlow.md` first).
187
+
188
+ `edit-flow` defaults to a **dry-run** diff. With `--apply`: rename/description are
189
+ written to `sys_hub_flow` through the update-set-aware API (so `--update-set` is
190
+ **required** for those), while `patchStepInputs` ride a snapshot recompile (the
191
+ `/snapshot` POST persists step input values but NOT top-level flow fields).
192
+ Step-input persistence via the snapshot POST is verified for action types but is
193
+ **best-effort for flows** — after the recompile, `edit-flow` reads the model back
194
+ and **warns** if a value didn't actually persist, so a no-op never reports as a
195
+ silent success.
183
196
 
184
197
  `view-flow` reads `GET /api/now/processflow/flow/{id}` — the Designer's own model
185
198
  endpoint — and prints the ordered, nesting-aware action + flow-logic step graph
@@ -229,9 +242,10 @@ console.log(formatLayoutResult("form layout", result));
229
242
  Claude Code and agents: `create_view`, `set_list_layout`, `set_form_layout`,
230
243
  `set_related_lists`, `add_choices_to_field`, plus the Flow Designer tools
231
244
  `flow_view` (read a flow/subflow's step graph), `action_view` (read an action
232
- type's model), `flow_publish` (compile a flow/subflow snapshot), `flow_test`
233
- (validate or run a flow), and `flow_edit` (patch a flow + publish). It reads
234
- ServiceNow credentials from the same env vars as the CLI.
245
+ type's model), `flow_publish` (compile a flow/subflow snapshot), `flow_copy`
246
+ (copy a flow as an inactive draft), `flow_test` (validate or run a flow), and
247
+ `flow_edit` (patch a flow). It reads ServiceNow credentials from the same env
248
+ vars as the CLI.
235
249
 
236
250
  ```bash
237
251
  npx dove-sn mcp --smoke # list the registered tools and exit
package/dist/cli.js CHANGED
@@ -71,6 +71,7 @@ const flowDesigner_formatter_1 = require("./flowDesigner-formatter");
71
71
  const readFlow_1 = require("./flowDesigner/readFlow");
72
72
  const readActionType_1 = require("./flowDesigner/readActionType");
73
73
  const publishFlow_1 = require("./flowDesigner/publishFlow");
74
+ const copyFlow_1 = require("./flowDesigner/copyFlow");
74
75
  const editFlow_1 = require("./flowDesigner/editFlow");
75
76
  const testFlow_1 = require("./flowDesigner/testFlow");
76
77
  const flowDesigner_formatter_2 = require("./flowDesigner-formatter");
@@ -390,6 +391,38 @@ async function runPublishFlow(flags) {
390
391
  + (result.snapshotSysId ? " — snapshot " + result.snapshotSysId : "") + "\n");
391
392
  return 0;
392
393
  }
394
+ /**
395
+ * dove-sn copy-flow:
396
+ * --sys-id <sys_id> Required. Source sys_hub_flow sys_id (flow or subflow).
397
+ * --name <name> Required. Name for the copy.
398
+ * --scope <sys_id> Optional. Target scope (defaults to the source's scope).
399
+ * --json Optional. Emit the structured CopyFlowResult.
400
+ *
401
+ * Copies the flow via the Designer's own Copy endpoint — a complete, faithful
402
+ * clone created as an INACTIVE DRAFT. Publish it with publish-flow when ready.
403
+ * (Do NOT publish + activate a copy of a triggered production flow unless you
404
+ * intend it to fire.)
405
+ */
406
+ async function runCopyFlow(flags) {
407
+ var sysId = flags["sys-id"] || flags.sysId;
408
+ var name = flags.name;
409
+ if (!sysId || !name) {
410
+ process.stderr.write("copy-flow: --sys-id <sys_id> and --name <name> are required\n");
411
+ return 1;
412
+ }
413
+ var params = { client: (0, client_1.createClient)({}), sourceSysId: sysId, newName: name };
414
+ if (flags.scope || flags.scopeSysId) {
415
+ params.scopeSysId = flags.scope || flags.scopeSysId;
416
+ }
417
+ var result = await (0, copyFlow_1.copyFlow)(params);
418
+ if (flags.json === "true") {
419
+ process.stdout.write(JSON.stringify(result, null, 2) + "\n");
420
+ return 0;
421
+ }
422
+ process.stdout.write("Copied to '" + result.name + "' (sys_id " + result.sysId + ", scope " + result.scopeSysId
423
+ + ") — inactive draft. Publish with: dove-sn publish-flow --sys-id " + result.sysId + "\n");
424
+ return 0;
425
+ }
393
426
  /**
394
427
  * dove-sn test-flow:
395
428
  * --sys-id <sys_id> Required. sys_hub_flow sys_id (flow or subflow).
@@ -440,12 +473,14 @@ async function runTestFlow(flags) {
440
473
  * dove-sn edit-flow:
441
474
  * --sys-id <sys_id> Required. sys_hub_flow sys_id (flow or subflow).
442
475
  * --from-json <path> Required. JSON EditFlowOps { rename?, description?, patchStepInputs? }.
443
- * --apply Optional. Publish the edit (default is a dry-run diff).
476
+ * --apply Optional. Persist the edit (default is a dry-run diff).
444
477
  * --scope <sys_id> Optional. sysparm_transaction_scope for the publish.
478
+ * --update-set <sys_id> Required with --apply when ops include rename/description.
445
479
  * --json Optional. Emit the structured EditFlowResult.
446
480
  *
447
- * Reads the model, applies the declarative edits, and (with --apply) re-publishes
448
- * via the snapshot endpoint. Without --apply it prints the would-be changes.
481
+ * Reads the model, applies the declarative edits, and (with --apply) persists them:
482
+ * rename/description via the update-set-aware record write, step inputs via a
483
+ * snapshot recompile. Without --apply it prints the would-be changes.
449
484
  */
450
485
  async function runEditFlow(flags) {
451
486
  var sysId = flags["sys-id"] || flags.sysId;
@@ -467,6 +502,9 @@ async function runEditFlow(flags) {
467
502
  if (flags.scope || flags.scopeSysId) {
468
503
  params.scopeSysId = flags.scope || flags.scopeSysId;
469
504
  }
505
+ if (flags["update-set"] || flags.updateSetSysId) {
506
+ params.updateSetSysId = flags["update-set"] || flags.updateSetSysId;
507
+ }
470
508
  var result = await (0, editFlow_1.editFlow)(params);
471
509
  if (flags.json === "true") {
472
510
  process.stdout.write(JSON.stringify(result, null, 2) + "\n");
@@ -513,10 +551,12 @@ function printHelp() {
513
551
  " (--sys-id <sys_id> --scope <sys_id> [--json] [--raw])\n" +
514
552
  " publish-flow Compile a flow/subflow snapshot (write)\n" +
515
553
  " (--sys-id <sys_id> [--scope <sys_id>] [--json])\n" +
554
+ " copy-flow Copy a flow/subflow (inactive draft) via the Designer Copy API\n" +
555
+ " (--sys-id <sys_id> --name <name> [--scope <sys_id>] [--json])\n" +
516
556
  " test-flow Validate (default) or run a flow/subflow\n" +
517
557
  " (--sys-id <sys_id> [--execute --confirm] [--inputs <json>] [--json])\n" +
518
- " edit-flow Patch a flow/subflow (rename, description, step inputs) + publish\n" +
519
- " (--sys-id <sys_id> --from-json <ops.json> [--apply] [--scope <sys_id>] [--json])\n" +
558
+ " edit-flow Patch a flow/subflow (rename, description, step inputs)\n" +
559
+ " (--sys-id <sys_id> --from-json <ops.json> [--apply] [--update-set <sys_id>] [--scope <sys_id>] [--json])\n" +
520
560
  " mcp Run the MCP stdio server (--smoke lists tools and exits)\n");
521
561
  }
522
562
  async function main() {
@@ -537,6 +577,9 @@ async function main() {
537
577
  if (parsed.command === "publish-flow") {
538
578
  return await runPublishFlow(parsed.flags);
539
579
  }
580
+ if (parsed.command === "copy-flow") {
581
+ return await runCopyFlow(parsed.flags);
582
+ }
540
583
  if (parsed.command === "test-flow") {
541
584
  return await runTestFlow(parsed.flags);
542
585
  }
package/dist/client.d.ts CHANGED
@@ -6,15 +6,15 @@
6
6
  * - `buildAgent.*` — reads via the sn_build_agent API, with graceful
7
7
  * fallback to Table API / sys_dictionary when the
8
8
  * Build Agent plugin is unavailable
9
- * - `claude.*` — writes via the Dovetail Scripted REST API
10
- * (/api/cadso/dovetail/*), which handles update-set + scope
11
- * switching atomically so every write lands in the right
12
- * update set without touching sys_user_preference.
13
- * Falls back to the legacy /api/cadso/claude/* path on
14
- * instances where the API has not yet been re-imported.
15
- * The namespace name `claude` is preserved for API
16
- * compatibility; the underlying server-side API is now
17
- * named "Dovetail".
9
+ * - `claude.*` — writes via the Dovetail core Scripted REST API
10
+ * (/api/cadso/dovetail_core/*), which handles update-set +
11
+ * scope switching atomically so every write lands in the
12
+ * right update set without touching sys_user_preference.
13
+ * Falls back to the legacy global-scope /api/cadso/dovetail/*
14
+ * path on instances without the Dovetail app installed.
15
+ * The `claude.*` property name is preserved for API
16
+ * compatibility; the server-side API now lives in the
17
+ * Dovetail scoped application as "Dovetail Core".
18
18
  *
19
19
  * Env fallbacks mirror prior dashboard-fetch helpers so dev setups that already
20
20
  * have SN_INSTANCE/SN_USER/SN_PASSWORD work without reconfiguration.
@@ -62,7 +62,7 @@ export interface ServiceNowClient {
62
62
  getTableSchema: (table: string) => Promise<TableSchema>;
63
63
  };
64
64
  claude: {
65
- /** POST /api/cadso/dovetail/createRecord (legacy path: /api/cadso/claude/createRecord). */
65
+ /** POST /api/cadso/dovetail_core/createRecord (legacy: /api/cadso/dovetail/createRecord). */
66
66
  createRecord: (params: {
67
67
  table: string;
68
68
  fields: Record<string, any>;
@@ -73,7 +73,7 @@ export interface ServiceNowClient {
73
73
  sys_id: string;
74
74
  [k: string]: any;
75
75
  }>;
76
- /** POST /api/cadso/dovetail/pushWithUpdateSet (legacy: /api/cadso/claude/pushWithUpdateSet). */
76
+ /** POST /api/cadso/dovetail_core/pushWithUpdateSet (legacy: /api/cadso/dovetail/pushWithUpdateSet). */
77
77
  pushWithUpdateSet: (params: {
78
78
  update_set_sys_id: string;
79
79
  table: string;
@@ -83,18 +83,18 @@ export interface ServiceNowClient {
83
83
  sys_id: string;
84
84
  [k: string]: any;
85
85
  }>;
86
- /** GET /api/cadso/dovetail/currentUpdateSet?scope=... (legacy: /api/cadso/claude/currentUpdateSet). */
86
+ /** GET /api/cadso/dovetail_core/currentUpdateSet?scope=... (legacy: /api/cadso/dovetail/currentUpdateSet). */
87
87
  currentUpdateSet: (scope?: string) => Promise<{
88
88
  sys_id: string;
89
89
  name: string;
90
90
  }>;
91
- /** GET /api/cadso/dovetail/changeUpdateSet?sysId=... — pins the REST session's active update set. */
91
+ /** GET /api/cadso/dovetail_core/changeUpdateSet?sysId=... — pins the REST session's active update set. */
92
92
  changeUpdateSet: (params: {
93
93
  sysId: string;
94
94
  }) => Promise<{
95
95
  [k: string]: any;
96
96
  }>;
97
- /** POST /api/cadso/dovetail/deleteRecord — body { table, sys_id }. Returns the deleted record. */
97
+ /** POST /api/cadso/dovetail_core/deleteRecord — body { table, sys_id }. Returns the deleted record. */
98
98
  deleteRecord: (params: {
99
99
  table: string;
100
100
  sys_id: string;
package/dist/client.js CHANGED
@@ -7,15 +7,15 @@
7
7
  * - `buildAgent.*` — reads via the sn_build_agent API, with graceful
8
8
  * fallback to Table API / sys_dictionary when the
9
9
  * Build Agent plugin is unavailable
10
- * - `claude.*` — writes via the Dovetail Scripted REST API
11
- * (/api/cadso/dovetail/*), which handles update-set + scope
12
- * switching atomically so every write lands in the right
13
- * update set without touching sys_user_preference.
14
- * Falls back to the legacy /api/cadso/claude/* path on
15
- * instances where the API has not yet been re-imported.
16
- * The namespace name `claude` is preserved for API
17
- * compatibility; the underlying server-side API is now
18
- * named "Dovetail".
10
+ * - `claude.*` — writes via the Dovetail core Scripted REST API
11
+ * (/api/cadso/dovetail_core/*), which handles update-set +
12
+ * scope switching atomically so every write lands in the
13
+ * right update set without touching sys_user_preference.
14
+ * Falls back to the legacy global-scope /api/cadso/dovetail/*
15
+ * path on instances without the Dovetail app installed.
16
+ * The `claude.*` property name is preserved for API
17
+ * compatibility; the server-side API now lives in the
18
+ * Dovetail scoped application as "Dovetail Core".
19
19
  *
20
20
  * Env fallbacks mirror prior dashboard-fetch helpers so dev setups that already
21
21
  * have SN_INSTANCE/SN_USER/SN_PASSWORD work without reconfiguration.
@@ -104,11 +104,12 @@ function createClient(config = {}) {
104
104
  validateStatus: function () { return true; }
105
105
  });
106
106
  var lastAt = 0;
107
- // Dovetail Scripted REST API rebrand: prefer /api/cadso/dovetail/* and fall back
108
- // to the legacy /api/cadso/claude/* path on instances where the rename hasn't
109
- // been imported yet. Latch the legacy flag after the first 404 to avoid paying
110
- // the round-trip cost on every subsequent call.
111
- var useDovetailLegacyClaudePath = false;
107
+ // Dovetail core Scripted REST API: prefer the Dovetail-app path
108
+ // /api/cadso/dovetail_core/* and fall back to the legacy global-scope path
109
+ // /api/cadso/dovetail/* on instances that don't have the Dovetail app yet.
110
+ // Latch the legacy flag after the first 404 to avoid paying the round-trip
111
+ // cost on every subsequent call.
112
+ var useDovetailLegacyPath = false;
112
113
  async function request(cfg, ctx) {
113
114
  var attempt429 = 0;
114
115
  var attempt5xx = 0;
@@ -160,24 +161,24 @@ function createClient(config = {}) {
160
161
  return res.data;
161
162
  }
162
163
  }
163
- // Dovetail Scripted REST API request: try /api/cadso/dovetail/<op>, fall back to
164
- // /api/cadso/claude/<op> on 404 (with a one-time deprecation warning).
164
+ // Dovetail core Scripted REST API request: try /api/cadso/dovetail_core/<op>,
165
+ // fall back to the legacy /api/cadso/dovetail/<op> on 404 (one-time warning).
165
166
  async function dovetailRequest(method, op, body, params, ctx) {
166
- var url = useDovetailLegacyClaudePath
167
- ? "/api/cadso/claude/" + op
168
- : "/api/cadso/dovetail/" + op;
167
+ var url = useDovetailLegacyPath
168
+ ? "/api/cadso/dovetail/" + op
169
+ : "/api/cadso/dovetail_core/" + op;
169
170
  try {
170
171
  return await request({ method: method, url: url, data: body, params: params }, ctx);
171
172
  }
172
173
  catch (e) {
173
174
  var msg = e && e.message ? String(e.message) : "";
174
- if (!useDovetailLegacyClaudePath && msg.indexOf("SN 404 on") === 0) {
175
+ if (!useDovetailLegacyPath && msg.indexOf("SN 404 on") === 0) {
175
176
  // eslint-disable-next-line no-console
176
- console.warn("[deprecation] /api/cadso/dovetail/" + op +
177
- " returned 404. Falling back to legacy /api/cadso/claude/" + op +
178
- ". Re-import the Dovetail Scripted REST API XML on your ServiceNow instance to silence this warning.");
179
- useDovetailLegacyClaudePath = true;
180
- var legacyUrl = "/api/cadso/claude/" + op;
177
+ console.warn("[deprecation] /api/cadso/dovetail_core/" + op +
178
+ " returned 404. Falling back to legacy /api/cadso/dovetail/" + op +
179
+ ". Install the Dovetail application's Scripted REST APIs to silence this warning.");
180
+ useDovetailLegacyPath = true;
181
+ var legacyUrl = "/api/cadso/dovetail/" + op;
181
182
  return await request({ method: method, url: legacyUrl, data: body, params: params }, ctx);
182
183
  }
183
184
  throw e;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Copy a Flow Designer flow or subflow — the Designer's own "Copy flow" action,
3
+ * headless.
4
+ *
5
+ * The Workflow Studio "Copy flow" button calls a clean REST endpoint (captured
6
+ * from a HAR, same family as the publish/snapshot endpoint), which works for the
7
+ * integration user with basic auth:
8
+ *
9
+ * POST /api/now/processflow/flow/{sourceSysId}/copy?sysparm_transaction_scope={scope}
10
+ * body: { name: "<new flow name>", scope: "<target scope sys_id>" }
11
+ * -> 200, { result: { data: "<new flow sys_id>" } } — the copy is a complete,
12
+ * faithful clone (trigger + all actions + variables + properties), created
13
+ * as an INACTIVE DRAFT.
14
+ *
15
+ * This supersedes the record-graph clone (cloneSubflow), which can't work for
16
+ * the integration user: the design tables (sys_hub_flow_input/action_instance)
17
+ * ignore sysparm_query on the plain Table API, and the build-agent endpoint that
18
+ * honors the filter returns 401 without the Build Agent role — so a record-graph
19
+ * read either 401s or scans the whole table. The platform copy endpoint sidesteps
20
+ * all of that: ServiceNow assembles the copy server-side.
21
+ *
22
+ * The copy lands in `scopeSysId` (sysparm_transaction_scope) — which may differ
23
+ * from the source's scope — as an inactive draft. Publish it with publishFlow
24
+ * when ready. Do NOT publish + activate a copy of a triggered production flow
25
+ * unless you intend it to fire (e.g. a Send-SMS flow would duplicate sends).
26
+ */
27
+ import type { ServiceNowClient } from "../client";
28
+ export interface CopyFlowParams {
29
+ client: ServiceNowClient;
30
+ /** sys_id of the sys_hub_flow (flow or subflow) to copy. */
31
+ sourceSysId: string;
32
+ /** Name for the new copy. */
33
+ newName: string;
34
+ /**
35
+ * Target application scope sys_id (sysparm_transaction_scope) the copy is
36
+ * created in. Optional — defaults to the source flow's own scope (read first).
37
+ */
38
+ scopeSysId?: string;
39
+ }
40
+ export interface CopyFlowResult {
41
+ /** sys_id of the newly created copy. */
42
+ sysId: string;
43
+ /** Name of the copy (echoes newName). */
44
+ name: string;
45
+ /** The scope the copy was created in. */
46
+ scopeSysId: string;
47
+ }
48
+ export declare function copyFlow(params: CopyFlowParams): Promise<CopyFlowResult>;
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ /**
3
+ * Copy a Flow Designer flow or subflow — the Designer's own "Copy flow" action,
4
+ * headless.
5
+ *
6
+ * The Workflow Studio "Copy flow" button calls a clean REST endpoint (captured
7
+ * from a HAR, same family as the publish/snapshot endpoint), which works for the
8
+ * integration user with basic auth:
9
+ *
10
+ * POST /api/now/processflow/flow/{sourceSysId}/copy?sysparm_transaction_scope={scope}
11
+ * body: { name: "<new flow name>", scope: "<target scope sys_id>" }
12
+ * -> 200, { result: { data: "<new flow sys_id>" } } — the copy is a complete,
13
+ * faithful clone (trigger + all actions + variables + properties), created
14
+ * as an INACTIVE DRAFT.
15
+ *
16
+ * This supersedes the record-graph clone (cloneSubflow), which can't work for
17
+ * the integration user: the design tables (sys_hub_flow_input/action_instance)
18
+ * ignore sysparm_query on the plain Table API, and the build-agent endpoint that
19
+ * honors the filter returns 401 without the Build Agent role — so a record-graph
20
+ * read either 401s or scans the whole table. The platform copy endpoint sidesteps
21
+ * all of that: ServiceNow assembles the copy server-side.
22
+ *
23
+ * The copy lands in `scopeSysId` (sysparm_transaction_scope) — which may differ
24
+ * from the source's scope — as an inactive draft. Publish it with publishFlow
25
+ * when ready. Do NOT publish + activate a copy of a triggered production flow
26
+ * unless you intend it to fire (e.g. a Send-SMS flow would duplicate sends).
27
+ */
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.copyFlow = copyFlow;
30
+ /**
31
+ * Pull the new flow's sys_id from the copy response. The endpoint returns the
32
+ * new sys_id as a bare string under `result.data`; older/alternate shapes may
33
+ * nest it under a model object's `id`/`sys_id`.
34
+ */
35
+ function extractCopiedSysId(resp) {
36
+ if (resp && typeof resp === "object" && resp.result && typeof resp.result === "object") {
37
+ var d = resp.result.data;
38
+ if (typeof d === "string" && d.length >= 32) {
39
+ return d;
40
+ }
41
+ if (d && typeof d === "object") {
42
+ if (typeof d.id === "string")
43
+ return d.id;
44
+ if (typeof d.sys_id === "string")
45
+ return d.sys_id;
46
+ }
47
+ }
48
+ if (resp && typeof resp === "object") {
49
+ if (typeof resp.id === "string")
50
+ return resp.id;
51
+ if (typeof resp.sys_id === "string")
52
+ return resp.sys_id;
53
+ }
54
+ return "";
55
+ }
56
+ function flowGetPath(sysId) {
57
+ return "/api/now/processflow/flow/" + encodeURIComponent(sysId);
58
+ }
59
+ function copyPath(sourceSysId, scopeSysId) {
60
+ return "/api/now/processflow/flow/" + encodeURIComponent(sourceSysId) + "/copy"
61
+ + "?sysparm_transaction_scope=" + encodeURIComponent(scopeSysId);
62
+ }
63
+ function unwrapModel(data) {
64
+ if (data && typeof data === "object" && data.result && typeof data.result === "object") {
65
+ if (data.result.data && typeof data.result.data === "object") {
66
+ return data.result.data;
67
+ }
68
+ return data.result;
69
+ }
70
+ if (data && typeof data === "object" && data.data && typeof data.data === "object") {
71
+ return data.data;
72
+ }
73
+ return data;
74
+ }
75
+ async function copyFlow(params) {
76
+ var client = params.client;
77
+ var sourceSysId = params.sourceSysId;
78
+ if (!sourceSysId) {
79
+ throw new Error("copyFlow: sourceSysId is required.");
80
+ }
81
+ if (typeof params.newName !== "string" || params.newName.trim().length === 0) {
82
+ throw new Error("copyFlow: newName is required.");
83
+ }
84
+ // Resolve the target scope: explicit param, else the source flow's own scope.
85
+ var scopeSysId = params.scopeSysId;
86
+ if (!scopeSysId) {
87
+ var src = unwrapModel(await client.now.get(flowGetPath(sourceSysId)));
88
+ if (src && typeof src.scope === "string") {
89
+ scopeSysId = src.scope;
90
+ }
91
+ }
92
+ if (!scopeSysId) {
93
+ throw new Error("copyFlow: scopeSysId is required and the source flow carried no `scope` to default from.");
94
+ }
95
+ var resp = await client.now.post(copyPath(sourceSysId, scopeSysId), { name: params.newName, scope: scopeSysId });
96
+ var newSysId = extractCopiedSysId(resp);
97
+ if (!newSysId) {
98
+ throw new Error("copyFlow: the copy succeeded but no new flow sys_id was found in the response: "
99
+ + JSON.stringify(resp).substring(0, 300));
100
+ }
101
+ return {
102
+ sysId: newSysId,
103
+ name: params.newName,
104
+ scopeSysId: scopeSysId
105
+ };
106
+ }
@@ -41,14 +41,26 @@ export interface EditFlowParams {
41
41
  /** sys_id of the sys_hub_flow (flow or subflow) to edit. */
42
42
  sysId: string;
43
43
  ops: EditFlowOps;
44
- /** When true, publish the edited model. When false/omitted, dry-run (no write). */
44
+ /** When true, persist the edits. When false/omitted, dry-run (no write). */
45
45
  apply?: boolean;
46
46
  /** Override sysparm_transaction_scope for the publish; defaults to the model's scope. */
47
47
  scopeSysId?: string;
48
+ /**
49
+ * Update set sys_id that captures top-level field writes (rename/description).
50
+ * REQUIRED when apply=true and the ops include rename or description — those
51
+ * fields are written to sys_hub_flow through the update-set-aware Dovetail API
52
+ * (the snapshot POST does NOT persist them). Not needed for patchStepInputs-only
53
+ * edits (those ride the snapshot POST).
54
+ */
55
+ updateSetSysId?: string;
48
56
  }
49
57
  export interface EditFlowResult {
50
- /** "dry-run" (computed, not published) or "published". */
51
- status: "dry-run" | "published";
58
+ /**
59
+ * "dry-run" computed, nothing written;
60
+ * "applied" — edits persisted (record fields written; snapshot recompiled iff
61
+ * a step input changed).
62
+ */
63
+ status: "dry-run" | "applied";
52
64
  /** Human-readable list of changes that were applied to the model. */
53
65
  changes: Array<string>;
54
66
  /** Requested edits that could not be matched (e.g. unknown step or input). */
@@ -73,31 +73,42 @@ async function editFlow(params) {
73
73
  if (!sysId) {
74
74
  throw new Error("editFlow: sysId is required.");
75
75
  }
76
- // Read the full model — we need the raw instance graph to patch it.
76
+ // Read the full model — we need the raw instance graph to patch step inputs
77
+ // and to resolve the flow's scope for the update-set-aware record write.
77
78
  var read = await (0, readFlow_1.readFlow)({ client: client, sysId: sysId, raw: true });
78
79
  var model = read.raw;
79
80
  if (!model || typeof model !== "object") {
80
- // Defensive — readFlow already throws on a bad response, but the raw model
81
- // could in theory be absent.
82
81
  var resp = await client.now.get("/api/now/processflow/flow/" + encodeURIComponent(sysId));
83
82
  model = unwrapModel(resp);
84
83
  }
84
+ var scopeSysId = params.scopeSysId || (typeof model.scope === "string" ? model.scope : "");
85
85
  var changes = [];
86
86
  var warnings = [];
87
+ // Top-level sys_hub_flow columns. These are NOT persisted by the snapshot POST
88
+ // (that only writes step input values), so they go through a direct,
89
+ // update-set-aware record write — see recordFields below.
90
+ var recordFields = {};
87
91
  if (ops.rename) {
88
92
  if (ops.rename.name) {
89
- model.name = ops.rename.name;
93
+ recordFields.name = ops.rename.name;
90
94
  changes.push("name -> " + ops.rename.name);
91
95
  }
92
96
  if (ops.rename.internalName) {
93
- model.internalName = ops.rename.internalName;
97
+ recordFields.internal_name = ops.rename.internalName;
94
98
  changes.push("internalName -> " + ops.rename.internalName);
95
99
  }
96
100
  }
97
101
  if (typeof ops.description === "string") {
98
- model.description = ops.description;
102
+ recordFields.description = ops.description;
99
103
  changes.push("description updated");
100
104
  }
105
+ // Step input values ride the snapshot POST — the publish is documented to
106
+ // persist step input values back to the design records (verified for action
107
+ // types). For FLOWS this is best-effort: we apply, republish, then read back
108
+ // and confirm each value actually took (see the verify pass after publish), so
109
+ // a non-persist surfaces as a warning instead of a silent false success.
110
+ var stepInputsChanged = false;
111
+ var verifyTargets = [];
101
112
  if (ops.patchStepInputs && ops.patchStepInputs.length > 0) {
102
113
  for (var i = 0; i < ops.patchStepInputs.length; i += 1) {
103
114
  var patch = ops.patchStepInputs[i];
@@ -108,6 +119,8 @@ async function editFlow(params) {
108
119
  }
109
120
  var ok = setInputValue(step, patch.input, patch.value);
110
121
  if (ok) {
122
+ stepInputsChanged = true;
123
+ verifyTargets.push({ step: patch.step, input: patch.input, value: patch.value });
111
124
  changes.push("step '" + (step.name || patch.step) + "' input '" + patch.input + "' updated");
112
125
  }
113
126
  else {
@@ -115,18 +128,59 @@ async function editFlow(params) {
115
128
  }
116
129
  }
117
130
  }
118
- if (!params.apply) {
131
+ if (!params.apply || changes.length === 0) {
132
+ // Dry-run, or nothing matched — never write/publish a no-op.
119
133
  return { status: "dry-run", changes: changes, warnings: warnings };
120
134
  }
121
- if (changes.length === 0) {
122
- // Nothing matched don't publish a no-op (which would still recompile).
123
- return { status: "dry-run", changes: changes, warnings: warnings };
135
+ // 1. Persist top-level field edits via the Dovetail write API, pinned to the
136
+ // caller-supplied update set so the change is captured for promotion.
137
+ if (Object.keys(recordFields).length > 0) {
138
+ if (!params.updateSetSysId) {
139
+ throw new Error("editFlow: updateSetSysId is required to apply rename/description edits "
140
+ + "(they write sys_hub_flow through the update-set-aware API). Pass an "
141
+ + "in-progress update set sys_id, or limit ops to patchStepInputs.");
142
+ }
143
+ await client.claude.pushWithUpdateSet({
144
+ update_set_sys_id: params.updateSetSysId,
145
+ table: "sys_hub_flow",
146
+ record_sys_id: sysId,
147
+ fields: recordFields
148
+ });
149
+ }
150
+ // 2. Republish only when step inputs changed (recompiles the snapshot AND
151
+ // persists the step values). Metadata-only edits need no recompile.
152
+ var snapshotSysId;
153
+ if (stepInputsChanged) {
154
+ var pub = await (0, publishFlow_1.publishFlow)({ client: client, sysId: sysId, model: model, scopeSysId: scopeSysId || undefined });
155
+ snapshotSysId = pub.snapshotSysId;
156
+ // 3. Verify the step-input changes actually persisted. The snapshot POST is
157
+ // only documented to persist step values for action types; for flows it
158
+ // can no-op. Read the model back and confirm each value took — a miss is
159
+ // surfaced as a warning rather than a silent false "applied".
160
+ var afterResp = await client.now.get("/api/now/processflow/flow/" + encodeURIComponent(sysId));
161
+ var afterModel = unwrapModel(afterResp);
162
+ for (var v = 0; v < verifyTargets.length; v += 1) {
163
+ var t = verifyTargets[v];
164
+ var liveStep = afterModel ? findStep(afterModel, t.step) : null;
165
+ var persisted = false;
166
+ if (liveStep && Array.isArray(liveStep.inputs)) {
167
+ for (var k = 0; k < liveStep.inputs.length; k += 1) {
168
+ if (liveStep.inputs[k] && liveStep.inputs[k].name === t.input) {
169
+ persisted = String(liveStep.inputs[k].value) === String(t.value);
170
+ break;
171
+ }
172
+ }
173
+ }
174
+ if (!persisted) {
175
+ warnings.push("step input '" + t.input + "' on '" + t.step + "' did not persist via the snapshot POST "
176
+ + "— verify in the Designer (flow step-input persistence is best-effort).");
177
+ }
178
+ }
124
179
  }
125
- var pub = await (0, publishFlow_1.publishFlow)({ client: client, sysId: sysId, model: model, scopeSysId: params.scopeSysId });
126
180
  return {
127
- status: "published",
181
+ status: "applied",
128
182
  changes: changes,
129
183
  warnings: warnings,
130
- snapshotSysId: pub.snapshotSysId
184
+ snapshotSysId: snapshotSysId
131
185
  };
132
186
  }
@@ -22,6 +22,8 @@ export { readActionType } from "./readActionType";
22
22
  export type { ReadActionTypeParams, ReadActionTypeResult, ActionIo } from "./readActionType";
23
23
  export { publishFlow } from "./publishFlow";
24
24
  export type { PublishFlowParams, PublishFlowResult } from "./publishFlow";
25
+ export { copyFlow } from "./copyFlow";
26
+ export type { CopyFlowParams, CopyFlowResult } from "./copyFlow";
25
27
  export { editFlow } from "./editFlow";
26
28
  export type { EditFlowParams, EditFlowResult, EditFlowOps, StepInputPatch } from "./editFlow";
27
29
  export { testFlow, DEFAULT_RUN_FLOW_PATH } from "./testFlow";
@@ -6,7 +6,7 @@
6
6
  * functions land in Phase 1.C/D.
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.WriteOrderError = exports.executeWritePlan = exports.topoSort = exports.SYSTEM_FIELDS_TO_STRIP = exports.assertSysId = exports.applyScope = exports.stripSystemFields = exports.generateSysId = exports.DEFAULT_RUN_FLOW_PATH = exports.testFlow = exports.editFlow = exports.publishFlow = exports.readActionType = exports.readFlow = exports.publishActionType = exports.triggerPublication = exports.cloneActionType = exports.cloneSubflow = exports.verifyArtifact = exports.listTemplates = void 0;
9
+ exports.WriteOrderError = exports.executeWritePlan = exports.topoSort = exports.SYSTEM_FIELDS_TO_STRIP = exports.assertSysId = exports.applyScope = exports.stripSystemFields = exports.generateSysId = exports.DEFAULT_RUN_FLOW_PATH = exports.testFlow = exports.editFlow = exports.copyFlow = exports.publishFlow = exports.readActionType = exports.readFlow = exports.publishActionType = exports.triggerPublication = exports.cloneActionType = exports.cloneSubflow = exports.verifyArtifact = exports.listTemplates = void 0;
10
10
  var listTemplates_1 = require("./listTemplates");
11
11
  Object.defineProperty(exports, "listTemplates", { enumerable: true, get: function () { return listTemplates_1.listTemplates; } });
12
12
  var verifyArtifact_1 = require("./verifyArtifact");
@@ -25,6 +25,8 @@ var readActionType_1 = require("./readActionType");
25
25
  Object.defineProperty(exports, "readActionType", { enumerable: true, get: function () { return readActionType_1.readActionType; } });
26
26
  var publishFlow_1 = require("./publishFlow");
27
27
  Object.defineProperty(exports, "publishFlow", { enumerable: true, get: function () { return publishFlow_1.publishFlow; } });
28
+ var copyFlow_1 = require("./copyFlow");
29
+ Object.defineProperty(exports, "copyFlow", { enumerable: true, get: function () { return copyFlow_1.copyFlow; } });
28
30
  var editFlow_1 = require("./editFlow");
29
31
  Object.defineProperty(exports, "editFlow", { enumerable: true, get: function () { return editFlow_1.editFlow; } });
30
32
  var testFlow_1 = require("./testFlow");
package/dist/index.d.ts CHANGED
@@ -14,6 +14,6 @@ export { setFormLayout } from "./layout/formLayout";
14
14
  export { setRelatedLists } from "./layout/relatedLists";
15
15
  export { formatLayoutResult, formatCreateViewResult } from "./layout/formatter";
16
16
  export { sincPlugin } from "./plugin";
17
- export { listTemplates, verifyArtifact, cloneSubflow, cloneActionType, triggerPublication, publishActionType, readFlow, readActionType, publishFlow, editFlow, testFlow, DEFAULT_RUN_FLOW_PATH, generateSysId, topoSort, executeWritePlan, WriteOrderError } from "./flowDesigner";
18
- export type { TemplateRef, ListTemplatesParams, FlowKind, VerifyExpect, VerifyFound, VerifyFailure, VerifyReport, VerifyArtifactParams, CloneSubflowParams, CloneSubflowResult, CloneActionTypeParams, CloneActionTypeResult, TriggerPublicationParams, TriggerPublicationResult, PublishActionTypeParams, PublishActionTypeResult, ReadFlowParams, ReadFlowResult, FlowStep, FlowVariable, ReadActionTypeParams, ReadActionTypeResult, ActionIo, PublishFlowParams, PublishFlowResult, EditFlowParams, EditFlowResult, EditFlowOps, StepInputPatch, TestFlowParams, TestFlowResult, WriteOp, WriteOpResult } from "./flowDesigner";
17
+ export { listTemplates, verifyArtifact, cloneSubflow, cloneActionType, triggerPublication, publishActionType, readFlow, readActionType, publishFlow, copyFlow, editFlow, testFlow, DEFAULT_RUN_FLOW_PATH, generateSysId, topoSort, executeWritePlan, WriteOrderError } from "./flowDesigner";
18
+ export type { TemplateRef, ListTemplatesParams, FlowKind, VerifyExpect, VerifyFound, VerifyFailure, VerifyReport, VerifyArtifactParams, CloneSubflowParams, CloneSubflowResult, CloneActionTypeParams, CloneActionTypeResult, TriggerPublicationParams, TriggerPublicationResult, PublishActionTypeParams, PublishActionTypeResult, ReadFlowParams, ReadFlowResult, FlowStep, FlowVariable, ReadActionTypeParams, ReadActionTypeResult, ActionIo, PublishFlowParams, PublishFlowResult, CopyFlowParams, CopyFlowResult, EditFlowParams, EditFlowResult, EditFlowOps, StepInputPatch, TestFlowParams, TestFlowResult, WriteOp, WriteOpResult } from "./flowDesigner";
19
19
  export type { ServiceNowClientConfig, ChoiceValue, ChoiceType, AddChoicesParams, AddChoicesResult, ChoiceActionResult, DictionaryRecord, UpdateSetRecord, LayoutAction, LayoutRecordResult, LayoutResult, CreateViewParams, CreateViewResult, FormSectionSpec, SetFormLayoutParams, SetListLayoutParams, SetRelatedListsParams } from "./types";
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@
6
6
  * REST API so every change lands in the target update set and scope.
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.WriteOrderError = exports.executeWritePlan = exports.topoSort = exports.generateSysId = exports.DEFAULT_RUN_FLOW_PATH = exports.testFlow = exports.editFlow = exports.publishFlow = exports.readActionType = exports.readFlow = exports.publishActionType = exports.triggerPublication = exports.cloneActionType = exports.cloneSubflow = exports.verifyArtifact = exports.listTemplates = exports.sincPlugin = exports.formatCreateViewResult = exports.formatLayoutResult = exports.setRelatedLists = exports.setFormLayout = exports.setListLayout = exports.createView = exports.formatAddChoicesResult = exports.addChoicesToField = exports.createClient = void 0;
9
+ exports.WriteOrderError = exports.executeWritePlan = exports.topoSort = exports.generateSysId = exports.DEFAULT_RUN_FLOW_PATH = exports.testFlow = exports.editFlow = exports.copyFlow = exports.publishFlow = exports.readActionType = exports.readFlow = exports.publishActionType = exports.triggerPublication = exports.cloneActionType = exports.cloneSubflow = exports.verifyArtifact = exports.listTemplates = exports.sincPlugin = exports.formatCreateViewResult = exports.formatLayoutResult = exports.setRelatedLists = exports.setFormLayout = exports.setListLayout = exports.createView = exports.formatAddChoicesResult = exports.addChoicesToField = exports.createClient = void 0;
10
10
  var client_1 = require("./client");
11
11
  Object.defineProperty(exports, "createClient", { enumerable: true, get: function () { return client_1.createClient; } });
12
12
  var choices_1 = require("./choices");
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "publishActionType", { enumerable: true, get: fun
36
36
  Object.defineProperty(exports, "readFlow", { enumerable: true, get: function () { return flowDesigner_1.readFlow; } });
37
37
  Object.defineProperty(exports, "readActionType", { enumerable: true, get: function () { return flowDesigner_1.readActionType; } });
38
38
  Object.defineProperty(exports, "publishFlow", { enumerable: true, get: function () { return flowDesigner_1.publishFlow; } });
39
+ Object.defineProperty(exports, "copyFlow", { enumerable: true, get: function () { return flowDesigner_1.copyFlow; } });
39
40
  Object.defineProperty(exports, "editFlow", { enumerable: true, get: function () { return flowDesigner_1.editFlow; } });
40
41
  Object.defineProperty(exports, "testFlow", { enumerable: true, get: function () { return flowDesigner_1.testFlow; } });
41
42
  Object.defineProperty(exports, "DEFAULT_RUN_FLOW_PATH", { enumerable: true, get: function () { return flowDesigner_1.DEFAULT_RUN_FLOW_PATH; } });
@@ -9,7 +9,7 @@
9
9
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
10
10
  import { z } from "zod";
11
11
  import type { ServiceNowClient } from "../client";
12
- export declare var TOOL_NAMES: readonly ["create_view", "set_list_layout", "set_form_layout", "set_related_lists", "add_choices_to_field", "flow_view", "action_view", "flow_publish", "flow_test", "flow_edit"];
12
+ export declare var TOOL_NAMES: readonly ["create_view", "set_list_layout", "set_form_layout", "set_related_lists", "add_choices_to_field", "flow_view", "action_view", "flow_publish", "flow_copy", "flow_test", "flow_edit"];
13
13
  export type ToolName = typeof TOOL_NAMES[number];
14
14
  export interface RegistryDeps {
15
15
  /** Optional client injection for tests; defaults to createClient({}). */
@@ -20,6 +20,7 @@ const choices_1 = require("../choices");
20
20
  const readFlow_1 = require("../flowDesigner/readFlow");
21
21
  const readActionType_1 = require("../flowDesigner/readActionType");
22
22
  const publishFlow_1 = require("../flowDesigner/publishFlow");
23
+ const copyFlow_1 = require("../flowDesigner/copyFlow");
23
24
  const editFlow_1 = require("../flowDesigner/editFlow");
24
25
  const testFlow_1 = require("../flowDesigner/testFlow");
25
26
  const schemas_1 = require("./schemas");
@@ -32,6 +33,7 @@ exports.TOOL_NAMES = [
32
33
  "flow_view",
33
34
  "action_view",
34
35
  "flow_publish",
36
+ "flow_copy",
35
37
  "flow_test",
36
38
  "flow_edit"
37
39
  ];
@@ -126,6 +128,19 @@ function buildDescriptors(deps = {}) {
126
128
  return (0, publishFlow_1.publishFlow)({ client: client(), sysId: p.sysId, scopeSysId: p.scopeSysId });
127
129
  }
128
130
  },
131
+ {
132
+ name: "flow_copy",
133
+ description: "Copy a ServiceNow flow/subflow via the Designer's Copy endpoint — a complete, faithful "
134
+ + "clone created as an INACTIVE DRAFT in the target scope. sourceSysId is the sys_hub_flow "
135
+ + "to copy; newName is the copy's name; scopeSysId defaults to the source's scope. Publish "
136
+ + "with flow_publish when ready. Do NOT publish + activate a copy of a triggered production "
137
+ + "flow unless you intend it to fire.",
138
+ shape: schemas_1.copyFlowSchema.shape,
139
+ handler: async function (args) {
140
+ var p = schemas_1.copyFlowSchema.parse(args);
141
+ return (0, copyFlow_1.copyFlow)({ client: client(), sourceSysId: p.sourceSysId, newName: p.newName, scopeSysId: p.scopeSysId });
142
+ }
143
+ },
129
144
  {
130
145
  name: "flow_test",
131
146
  description: "Test or run a ServiceNow flow/subflow. mode='validate' (default) is a safe, read-only "
@@ -148,10 +163,11 @@ function buildDescriptors(deps = {}) {
148
163
  },
149
164
  {
150
165
  name: "flow_edit",
151
- description: "Edit a ServiceNow flow/subflow in place and re-publish. Supports rename "
152
- + "(name/internalName), description, and patchStepInputs (set named input values on steps "
153
- + "by uiId or label). apply=false (default) is a dry-run that returns the diff; apply=true "
154
- + "publishes the edit (a write). sysId is the sys_hub_flow sys_id.",
166
+ description: "Edit a ServiceNow flow/subflow in place. Supports rename (name/internalName), description, "
167
+ + "and patchStepInputs (set named input values on steps by uiId or label). apply=false "
168
+ + "(default) is a dry-run that returns the diff; apply=true persists the edit (a write). "
169
+ + "Rename/description require updateSetSysId (they write sys_hub_flow via the update-set-aware "
170
+ + "API); patchStepInputs ride a snapshot recompile. sysId is the sys_hub_flow sys_id.",
155
171
  shape: schemas_1.editFlowSchema.shape,
156
172
  handler: async function (args) {
157
173
  var p = schemas_1.editFlowSchema.parse(args);
@@ -160,7 +176,8 @@ function buildDescriptors(deps = {}) {
160
176
  sysId: p.sysId,
161
177
  ops: p.ops,
162
178
  apply: p.apply,
163
- scopeSysId: p.scopeSysId
179
+ scopeSysId: p.scopeSysId,
180
+ updateSetSysId: p.updateSetSysId
164
181
  });
165
182
  }
166
183
  }
@@ -218,6 +218,19 @@ export declare var publishFlowSchema: z.ZodObject<{
218
218
  sysId: string;
219
219
  scopeSysId?: string | undefined;
220
220
  }>;
221
+ export declare var copyFlowSchema: z.ZodObject<{
222
+ sourceSysId: z.ZodString;
223
+ newName: z.ZodString;
224
+ scopeSysId: z.ZodOptional<z.ZodString>;
225
+ }, "strip", z.ZodTypeAny, {
226
+ sourceSysId: string;
227
+ newName: string;
228
+ scopeSysId?: string | undefined;
229
+ }, {
230
+ sourceSysId: string;
231
+ newName: string;
232
+ scopeSysId?: string | undefined;
233
+ }>;
221
234
  export declare var testFlowSchema: z.ZodObject<{
222
235
  sysId: z.ZodString;
223
236
  mode: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"validate">, z.ZodLiteral<"execute">]>>;
@@ -302,6 +315,7 @@ export declare var editFlowSchema: z.ZodObject<{
302
315
  }>;
303
316
  apply: z.ZodOptional<z.ZodBoolean>;
304
317
  scopeSysId: z.ZodOptional<z.ZodString>;
318
+ updateSetSysId: z.ZodOptional<z.ZodString>;
305
319
  }, "strip", z.ZodTypeAny, {
306
320
  sysId: string;
307
321
  ops: {
@@ -317,6 +331,7 @@ export declare var editFlowSchema: z.ZodObject<{
317
331
  }[] | undefined;
318
332
  };
319
333
  scopeSysId?: string | undefined;
334
+ updateSetSysId?: string | undefined;
320
335
  apply?: boolean | undefined;
321
336
  }, {
322
337
  sysId: string;
@@ -333,5 +348,6 @@ export declare var editFlowSchema: z.ZodObject<{
333
348
  }[] | undefined;
334
349
  };
335
350
  scopeSysId?: string | undefined;
351
+ updateSetSysId?: string | undefined;
336
352
  apply?: boolean | undefined;
337
353
  }>;
@@ -4,7 +4,7 @@
4
4
  * own file so registry.ts stays focused on wiring.
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.editFlowSchema = exports.stepInputPatchSchema = exports.testFlowSchema = exports.publishFlowSchema = exports.viewActionSchema = exports.viewFlowSchema = exports.addChoicesToFieldSchema = exports.choiceValueSchema = exports.setRelatedListsSchema = exports.setFormLayoutSchema = exports.formSectionSchema = exports.setListLayoutSchema = exports.createViewSchema = void 0;
7
+ exports.editFlowSchema = exports.stepInputPatchSchema = exports.testFlowSchema = exports.copyFlowSchema = exports.publishFlowSchema = exports.viewActionSchema = exports.viewFlowSchema = exports.addChoicesToFieldSchema = exports.choiceValueSchema = exports.setRelatedListsSchema = exports.setFormLayoutSchema = exports.formSectionSchema = exports.setListLayoutSchema = exports.createViewSchema = void 0;
8
8
  const zod_1 = require("zod");
9
9
  exports.createViewSchema = zod_1.z.object({
10
10
  name: zod_1.z.string().min(1),
@@ -71,6 +71,11 @@ exports.publishFlowSchema = zod_1.z.object({
71
71
  sysId: zod_1.z.string().min(1),
72
72
  scopeSysId: zod_1.z.string().optional()
73
73
  });
74
+ exports.copyFlowSchema = zod_1.z.object({
75
+ sourceSysId: zod_1.z.string().min(1),
76
+ newName: zod_1.z.string().min(1),
77
+ scopeSysId: zod_1.z.string().optional()
78
+ });
74
79
  exports.testFlowSchema = zod_1.z.object({
75
80
  sysId: zod_1.z.string().min(1),
76
81
  mode: zod_1.z.union([zod_1.z.literal("validate"), zod_1.z.literal("execute")]).optional(),
@@ -94,5 +99,6 @@ exports.editFlowSchema = zod_1.z.object({
94
99
  patchStepInputs: zod_1.z.array(exports.stepInputPatchSchema).optional()
95
100
  }),
96
101
  apply: zod_1.z.boolean().optional(),
97
- scopeSysId: zod_1.z.string().optional()
102
+ scopeSysId: zod_1.z.string().optional(),
103
+ updateSetSysId: zod_1.z.string().optional()
98
104
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tenonhq/dovetail-servicenow",
3
- "version": "0.0.8",
3
+ "version": "0.0.10",
4
4
  "engines": {
5
5
  "node": ">=22"
6
6
  },