powerautomate-mcp 0.7.9 → 0.9.0
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/index.js +530 -54
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1134,6 +1134,28 @@ async function createAuthProviderFromConfig(clientId, tenantId, cachePath, silen
|
|
|
1134
1134
|
};
|
|
1135
1135
|
return createWamAuthProvider(config, silentOnly);
|
|
1136
1136
|
}
|
|
1137
|
+
|
|
1138
|
+
// src/utils/jwt-claims.ts
|
|
1139
|
+
function decodeJwtClaims(token) {
|
|
1140
|
+
try {
|
|
1141
|
+
const parts = token.split(".");
|
|
1142
|
+
if (parts.length < 2 || !parts[1]) return {};
|
|
1143
|
+
const padded = parts[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
1144
|
+
const padding = padded.length % 4 === 0 ? "" : "=".repeat(4 - padded.length % 4);
|
|
1145
|
+
const json = Buffer.from(padded + padding, "base64").toString("utf-8");
|
|
1146
|
+
const claims = JSON.parse(json);
|
|
1147
|
+
return claims && typeof claims === "object" ? claims : {};
|
|
1148
|
+
} catch {
|
|
1149
|
+
return {};
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
function getOidFromToken(token) {
|
|
1153
|
+
const claims = decodeJwtClaims(token);
|
|
1154
|
+
const oid = claims.oid;
|
|
1155
|
+
return typeof oid === "string" && oid.length > 0 ? oid : void 0;
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
// src/api/flow-management.ts
|
|
1137
1159
|
var apiLogger = createChildLogger({ module: "flow-api" });
|
|
1138
1160
|
var FlowManagementApi = class {
|
|
1139
1161
|
authProvider;
|
|
@@ -1142,6 +1164,18 @@ var FlowManagementApi = class {
|
|
|
1142
1164
|
this.authProvider = config.authProvider;
|
|
1143
1165
|
this.defaultEnvironmentId = config.defaultEnvironmentId;
|
|
1144
1166
|
}
|
|
1167
|
+
/**
|
|
1168
|
+
* Return the current user's Azure AD object ID derived from the access token.
|
|
1169
|
+
* Used for client-side ownership filtering. Returns undefined if extraction fails.
|
|
1170
|
+
*/
|
|
1171
|
+
async getCurrentUserOid() {
|
|
1172
|
+
try {
|
|
1173
|
+
const token = await this.authProvider.getToken();
|
|
1174
|
+
return getOidFromToken(token);
|
|
1175
|
+
} catch {
|
|
1176
|
+
return void 0;
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1145
1179
|
/**
|
|
1146
1180
|
* Get authorization header with bearer token
|
|
1147
1181
|
*/
|
|
@@ -1484,6 +1518,30 @@ var FlowManagementApi = class {
|
|
|
1484
1518
|
return data;
|
|
1485
1519
|
}, "Fetch resource link");
|
|
1486
1520
|
}
|
|
1521
|
+
async getFlowRunActionRepetitions(flowId, runId, actionName, environmentId) {
|
|
1522
|
+
const envId = environmentId ?? this.defaultEnvironmentId;
|
|
1523
|
+
const safeFlowId = validateRowId(flowId);
|
|
1524
|
+
const safeRunId = validateFlowRunId(runId);
|
|
1525
|
+
const safeActionName = encodeURIComponent(actionName);
|
|
1526
|
+
apiLogger.debug({ flowId: safeFlowId, runId: safeRunId, actionName }, "Getting action repetitions");
|
|
1527
|
+
const url = this.buildUrl(
|
|
1528
|
+
envId,
|
|
1529
|
+
`/flows/${safeFlowId}/runs/${safeRunId}/actions/${safeActionName}/repetitions`
|
|
1530
|
+
);
|
|
1531
|
+
const headers = await this.getAuthHeaders();
|
|
1532
|
+
return wrapApiCall(async () => {
|
|
1533
|
+
const response = await request(url, { method: "GET", headers });
|
|
1534
|
+
const data = await this.handleResponse(
|
|
1535
|
+
response,
|
|
1536
|
+
`Get repetitions for action ${actionName}`
|
|
1537
|
+
);
|
|
1538
|
+
apiLogger.info(
|
|
1539
|
+
{ flowId, runId, actionName, count: data.value.length },
|
|
1540
|
+
"Retrieved action repetitions"
|
|
1541
|
+
);
|
|
1542
|
+
return data.value;
|
|
1543
|
+
}, `Get repetitions for action ${actionName}`);
|
|
1544
|
+
}
|
|
1487
1545
|
// ==========================================================================
|
|
1488
1546
|
// Flow Sharing & Permissions
|
|
1489
1547
|
// ==========================================================================
|
|
@@ -4212,11 +4270,15 @@ var listFlowsInputSchema = z.object({
|
|
|
4212
4270
|
environment: z.string().optional().describe("Environment name or GUID. Uses default if omitted."),
|
|
4213
4271
|
search: z.string().optional().describe("Search flows by display name (case-insensitive substring match). Applied client-side after fetching."),
|
|
4214
4272
|
filter: z.string().optional().describe(`OData filter expression (e.g., "properties/state eq 'Started'")`),
|
|
4215
|
-
top: z.number().min(1).max(250).optional().describe("Maximum number of results to return (default 50, max 250)")
|
|
4273
|
+
top: z.number().min(1).max(250).optional().describe("Maximum number of results to return (default 50, max 250)"),
|
|
4274
|
+
scope: z.enum(["owned", "shared", "all"]).optional().default("all").describe(
|
|
4275
|
+
"Filter by ownership: 'owned' = only flows you created, 'shared' = only flows shared with you, 'all' = both (default)."
|
|
4276
|
+
),
|
|
4277
|
+
includeOwner: z.boolean().optional().default(true).describe("Include owner/creator name in the output (default true).")
|
|
4216
4278
|
});
|
|
4217
4279
|
var listFlowsTool = {
|
|
4218
4280
|
name: "list_flows",
|
|
4219
|
-
description: "List Power Automate flows in an environment. Returns flow names, IDs, state (Started/Stopped),
|
|
4281
|
+
description: "List Power Automate flows in an environment. Returns flow names, IDs, state (Started/Stopped), last modified dates, and owners. Use scope='shared' to find flows shared with you, scope='owned' for your flows only. Use this to discover what flows exist before getting details or making changes.",
|
|
4220
4282
|
inputSchema: {
|
|
4221
4283
|
type: "object",
|
|
4222
4284
|
properties: {
|
|
@@ -4237,6 +4299,17 @@ var listFlowsTool = {
|
|
|
4237
4299
|
description: "Maximum number of results to return (default 50, max 250)",
|
|
4238
4300
|
minimum: 1,
|
|
4239
4301
|
maximum: 250
|
|
4302
|
+
},
|
|
4303
|
+
scope: {
|
|
4304
|
+
type: "string",
|
|
4305
|
+
enum: ["owned", "shared", "all"],
|
|
4306
|
+
description: "Filter by ownership: 'owned' = only flows you created, 'shared' = only flows shared with you, 'all' = both (default).",
|
|
4307
|
+
default: "all"
|
|
4308
|
+
},
|
|
4309
|
+
includeOwner: {
|
|
4310
|
+
type: "boolean",
|
|
4311
|
+
description: "Include owner/creator name in the output (default true).",
|
|
4312
|
+
default: true
|
|
4240
4313
|
}
|
|
4241
4314
|
}
|
|
4242
4315
|
}
|
|
@@ -4262,14 +4335,39 @@ async function handleListFlows(api, input) {
|
|
|
4262
4335
|
const needle = parsed.search.toLowerCase();
|
|
4263
4336
|
flows = flows.filter((f) => f.properties.displayName.toLowerCase().includes(needle));
|
|
4264
4337
|
}
|
|
4338
|
+
let currentUserOid;
|
|
4339
|
+
if (parsed.scope !== "all" || parsed.includeOwner) {
|
|
4340
|
+
currentUserOid = await api.getCurrentUserOid();
|
|
4341
|
+
if (parsed.scope !== "all" && !currentUserOid) {
|
|
4342
|
+
logger.warn("list_flows: could not derive current user OID; scope filter will not apply");
|
|
4343
|
+
}
|
|
4344
|
+
}
|
|
4345
|
+
if (parsed.scope !== "all" && currentUserOid) {
|
|
4346
|
+
flows = flows.filter((f) => {
|
|
4347
|
+
const creatorId = f.properties.creator?.id;
|
|
4348
|
+
if (!creatorId) return false;
|
|
4349
|
+
const isOwned = creatorId === currentUserOid;
|
|
4350
|
+
return parsed.scope === "owned" ? isOwned : !isOwned;
|
|
4351
|
+
});
|
|
4352
|
+
}
|
|
4265
4353
|
if (flows.length === 0) {
|
|
4266
|
-
return "No flows found";
|
|
4354
|
+
return parsed.scope === "shared" ? "No flows shared with you in this environment." : parsed.scope === "owned" ? "No flows you own in this environment." : "No flows found";
|
|
4267
4355
|
}
|
|
4268
|
-
const
|
|
4356
|
+
const scopeLabel = parsed.scope === "owned" ? "Owned" : parsed.scope === "shared" ? "Shared with you" : "Flows";
|
|
4357
|
+
const lines = [`${scopeLabel} (${flows.length}):`];
|
|
4269
4358
|
for (const flow of flows) {
|
|
4270
4359
|
const state = flow.properties.state === "Started" ? "\u25B6" : "\u23F9";
|
|
4271
4360
|
const modified = new Date(flow.properties.lastModifiedTime).toLocaleDateString();
|
|
4272
|
-
|
|
4361
|
+
let line = ` ${state} ${flow.properties.displayName} (${flow.name}) - ${modified}`;
|
|
4362
|
+
if (parsed.includeOwner) {
|
|
4363
|
+
const creator = flow.properties.creator;
|
|
4364
|
+
const ownerName = creator?.displayName ?? creator?.email;
|
|
4365
|
+
const isOwnedTag = currentUserOid && creator?.id === currentUserOid ? " [owned]" : creator ? " [shared]" : "";
|
|
4366
|
+
if (ownerName) {
|
|
4367
|
+
line += ` \u2014 ${ownerName}${isOwnedTag}`;
|
|
4368
|
+
}
|
|
4369
|
+
}
|
|
4370
|
+
lines.push(line);
|
|
4273
4371
|
}
|
|
4274
4372
|
if (result.nextLink) {
|
|
4275
4373
|
lines.push(`
|
|
@@ -4280,11 +4378,14 @@ async function handleListFlows(api, input) {
|
|
|
4280
4378
|
var getFlowInputSchema = z.object({
|
|
4281
4379
|
flowId: z.string().describe("The flow ID (GUID) to retrieve"),
|
|
4282
4380
|
environment: z.string().optional().describe("Environment name or GUID. Uses default if omitted."),
|
|
4283
|
-
includeDefinition: z.boolean().optional().default(true).describe("Include the full flow definition JSON (default true)")
|
|
4381
|
+
includeDefinition: z.boolean().optional().default(true).describe("Include the full flow definition JSON (default true)"),
|
|
4382
|
+
format: z.enum(["summary", "json", "both"]).optional().default("summary").describe(
|
|
4383
|
+
"Output format: 'summary' = human-readable top-level overview (default), 'json' = full raw flow.properties.definition with all nested actions, 'both' = summary followed by full JSON. Use 'json' or 'both' to capture nested actions inside Switch/If/Foreach/Scope."
|
|
4384
|
+
)
|
|
4284
4385
|
});
|
|
4285
4386
|
var getFlowTool = {
|
|
4286
4387
|
name: "get_flow",
|
|
4287
|
-
description: "Get the complete definition of a Power Automate flow including triggers, actions,
|
|
4388
|
+
description: "Get the complete definition of a Power Automate flow including triggers, actions, connection references, and description. Use list_flows first to find flow IDs. Set format='json' or format='both' to capture the FULL definition including nested actions inside Switch/If/Foreach/Scope (the default 'summary' format only shows top-level actions).",
|
|
4288
4389
|
inputSchema: {
|
|
4289
4390
|
type: "object",
|
|
4290
4391
|
properties: {
|
|
@@ -4300,6 +4401,12 @@ var getFlowTool = {
|
|
|
4300
4401
|
type: "boolean",
|
|
4301
4402
|
description: "Include the full flow definition JSON (default true)",
|
|
4302
4403
|
default: true
|
|
4404
|
+
},
|
|
4405
|
+
format: {
|
|
4406
|
+
type: "string",
|
|
4407
|
+
enum: ["summary", "json", "both"],
|
|
4408
|
+
description: "Output format: 'summary' = top-level overview (default), 'json' = full raw definition with all nested actions, 'both' = summary plus full JSON.",
|
|
4409
|
+
default: "summary"
|
|
4303
4410
|
}
|
|
4304
4411
|
},
|
|
4305
4412
|
required: ["flowId"]
|
|
@@ -4307,7 +4414,7 @@ var getFlowTool = {
|
|
|
4307
4414
|
};
|
|
4308
4415
|
async function handleGetFlow(api, input) {
|
|
4309
4416
|
const parsed = getFlowInputSchema.parse(input);
|
|
4310
|
-
logger.info({ flowId: parsed.flowId }, "Executing get_flow");
|
|
4417
|
+
logger.info({ flowId: parsed.flowId, format: parsed.format }, "Executing get_flow");
|
|
4311
4418
|
const flow = await api.getFlow(parsed.flowId, parsed.environment);
|
|
4312
4419
|
const triggers = flow.properties.definition.triggers;
|
|
4313
4420
|
const actions = flow.properties.definition.actions;
|
|
@@ -4320,6 +4427,22 @@ async function handleGetFlow(api, input) {
|
|
|
4320
4427
|
tier: ref.tier
|
|
4321
4428
|
})
|
|
4322
4429
|
);
|
|
4430
|
+
const description = flow.properties.description;
|
|
4431
|
+
const creator = flow.properties.creator;
|
|
4432
|
+
if (parsed.format === "json") {
|
|
4433
|
+
const payload = {
|
|
4434
|
+
id: flow.name,
|
|
4435
|
+
displayName: flow.properties.displayName,
|
|
4436
|
+
description,
|
|
4437
|
+
state: flow.properties.state,
|
|
4438
|
+
createdTime: flow.properties.createdTime,
|
|
4439
|
+
lastModifiedTime: flow.properties.lastModifiedTime,
|
|
4440
|
+
creator,
|
|
4441
|
+
connectionReferences: flow.properties.connectionReferences,
|
|
4442
|
+
definition: flow.properties.definition
|
|
4443
|
+
};
|
|
4444
|
+
return JSON.stringify(payload, null, 2);
|
|
4445
|
+
}
|
|
4323
4446
|
const lines = [
|
|
4324
4447
|
`## ${flow.properties.displayName}`,
|
|
4325
4448
|
``,
|
|
@@ -4329,6 +4452,12 @@ async function handleGetFlow(api, input) {
|
|
|
4329
4452
|
`**Actions:** ${Object.keys(actions).length}`,
|
|
4330
4453
|
`**Modified:** ${new Date(flow.properties.lastModifiedTime).toLocaleDateString()}`
|
|
4331
4454
|
];
|
|
4455
|
+
if (description) {
|
|
4456
|
+
lines.push(`**Description:** ${description}`);
|
|
4457
|
+
}
|
|
4458
|
+
if (creator?.displayName || creator?.email) {
|
|
4459
|
+
lines.push(`**Owner:** ${creator.displayName ?? creator.email}${creator.id ? ` (${creator.id})` : ""}`);
|
|
4460
|
+
}
|
|
4332
4461
|
if (connectionRefs.length > 0) {
|
|
4333
4462
|
lines.push(``, `### Connections`);
|
|
4334
4463
|
for (const conn of connectionRefs) {
|
|
@@ -4337,7 +4466,7 @@ async function handleGetFlow(api, input) {
|
|
|
4337
4466
|
}
|
|
4338
4467
|
}
|
|
4339
4468
|
if (parsed.includeDefinition !== false) {
|
|
4340
|
-
lines.push(``, `### Actions`);
|
|
4469
|
+
lines.push(``, `### Actions (top-level)`);
|
|
4341
4470
|
for (const [name, action] of Object.entries(actions)) {
|
|
4342
4471
|
const act = action;
|
|
4343
4472
|
const deps = act.runAfter ? Object.keys(act.runAfter).join(", ") : "trigger";
|
|
@@ -4346,6 +4475,11 @@ async function handleGetFlow(api, input) {
|
|
|
4346
4475
|
lines.push(` inputs: ${JSON.stringify(act.inputs, null, 2).split("\n").join("\n ")}`);
|
|
4347
4476
|
}
|
|
4348
4477
|
}
|
|
4478
|
+
lines.push(``);
|
|
4479
|
+
lines.push(`_Note: Nested actions inside Switch/If/Foreach/Scope are not expanded in this view. Call get_flow with format="json" or format="both" to capture them._`);
|
|
4480
|
+
}
|
|
4481
|
+
if (parsed.format === "both") {
|
|
4482
|
+
lines.push(``, `### Full Definition (JSON)`, "```json", JSON.stringify(flow.properties.definition, null, 2), "```");
|
|
4349
4483
|
}
|
|
4350
4484
|
return lines.join("\n");
|
|
4351
4485
|
}
|
|
@@ -5802,6 +5936,7 @@ var connectionRefSchema = z.object({
|
|
|
5802
5936
|
});
|
|
5803
5937
|
var createFlowInputSchema = z.object({
|
|
5804
5938
|
displayName: z.string().describe("The display name for the new flow"),
|
|
5939
|
+
description: z.string().optional().describe("Optional description for the flow"),
|
|
5805
5940
|
trigger: triggerSchema.describe("The trigger definition"),
|
|
5806
5941
|
triggerName: z.string().optional().default("manual").describe("Name for the trigger (default: 'manual')"),
|
|
5807
5942
|
actions: z.record(actionSchema).describe("Actions in the flow, keyed by action name"),
|
|
@@ -5819,6 +5954,10 @@ var createFlowTool = {
|
|
|
5819
5954
|
type: "string",
|
|
5820
5955
|
description: "The display name for the new flow"
|
|
5821
5956
|
},
|
|
5957
|
+
description: {
|
|
5958
|
+
type: "string",
|
|
5959
|
+
description: "Optional description shown in the Power Automate UI"
|
|
5960
|
+
},
|
|
5822
5961
|
trigger: {
|
|
5823
5962
|
type: "object",
|
|
5824
5963
|
description: "The trigger definition with type, kind, inputs, and optional recurrence",
|
|
@@ -5946,7 +6085,8 @@ async function handleCreateFlow(api, input) {
|
|
|
5946
6085
|
properties: {
|
|
5947
6086
|
displayName: parsed.displayName,
|
|
5948
6087
|
definition,
|
|
5949
|
-
connectionReferences: Object.keys(connectionReferences).length > 0 ? connectionReferences : void 0
|
|
6088
|
+
connectionReferences: Object.keys(connectionReferences).length > 0 ? connectionReferences : void 0,
|
|
6089
|
+
...parsed.description !== void 0 ? { description: parsed.description } : {}
|
|
5950
6090
|
}
|
|
5951
6091
|
};
|
|
5952
6092
|
try {
|
|
@@ -6008,16 +6148,25 @@ var connectionRefSchema2 = z.object({
|
|
|
6008
6148
|
var updateFlowInputSchema = z.object({
|
|
6009
6149
|
flowId: z.string().describe("The flow ID (GUID) to update"),
|
|
6010
6150
|
displayName: z.string().optional().describe("New display name for the flow"),
|
|
6151
|
+
description: z.string().optional().describe("New description (set to empty string to clear)"),
|
|
6011
6152
|
trigger: triggerSchema2.optional().describe("New trigger definition (replaces existing)"),
|
|
6012
6153
|
triggerName: z.string().optional().describe("Name for the trigger"),
|
|
6013
|
-
actions: z.record(actionSchema2).optional().describe(
|
|
6154
|
+
actions: z.record(actionSchema2).optional().describe(
|
|
6155
|
+
"Actions. By default REPLACES all existing actions (you must include every action you want kept). When mergeActions=true, only the named actions are added/updated; everything else stays. Use patchActions for surgical edits to a single nested branch (smallest payload)."
|
|
6156
|
+
),
|
|
6157
|
+
mergeActions: z.boolean().optional().default(false).describe(
|
|
6158
|
+
"When true, deep-merge the provided 'actions' onto the current flow's actions instead of replacing wholesale. Recurses into nested actions/cases/else/foreach. Lets you patch a sub-tree without resending the entire flow."
|
|
6159
|
+
),
|
|
6160
|
+
patchActions: z.record(actionSchema2.nullable()).optional().describe(
|
|
6161
|
+
"Path-based patch map. Keys are slash-separated paths into the action tree, e.g. 'If_Recognized_Form/cases/Default/actions/Compose'. Set value to null to delete a node. Smallest payload \u2014 only the changed branches travel. Applied AFTER mergeActions."
|
|
6162
|
+
),
|
|
6014
6163
|
connectionReferences: z.record(connectionRefSchema2).optional().describe("New connection references (replaces existing)"),
|
|
6015
6164
|
environment: z.string().optional().describe("Environment name or GUID"),
|
|
6016
6165
|
validateBeforeUpdate: z.boolean().optional().default(true).describe("Validate before updating")
|
|
6017
6166
|
});
|
|
6018
6167
|
var updateFlowTool = {
|
|
6019
6168
|
name: "update_flow",
|
|
6020
|
-
description: "Update an existing Power Automate flow.
|
|
6169
|
+
description: "Update an existing Power Automate flow. Three update modes: (1) full replace \u2014 pass 'actions' to replace all top-level actions (default; you must include nested actions you want kept), (2) merge \u2014 set mergeActions=true to deep-merge only the actions you provide, leaving the rest intact, (3) patch \u2014 use patchActions with path keys (e.g. 'If/cases/Default/actions/Compose') for surgical edits with the smallest payload. Use get_flow with format='json' first to see the full nested action tree.",
|
|
6021
6170
|
inputSchema: {
|
|
6022
6171
|
type: "object",
|
|
6023
6172
|
properties: {
|
|
@@ -6029,6 +6178,10 @@ var updateFlowTool = {
|
|
|
6029
6178
|
type: "string",
|
|
6030
6179
|
description: "New display name for the flow"
|
|
6031
6180
|
},
|
|
6181
|
+
description: {
|
|
6182
|
+
type: "string",
|
|
6183
|
+
description: "New description for the flow (empty string clears)"
|
|
6184
|
+
},
|
|
6032
6185
|
trigger: {
|
|
6033
6186
|
type: "object",
|
|
6034
6187
|
description: "New trigger definition (replaces existing)"
|
|
@@ -6039,7 +6192,16 @@ var updateFlowTool = {
|
|
|
6039
6192
|
},
|
|
6040
6193
|
actions: {
|
|
6041
6194
|
type: "object",
|
|
6042
|
-
description: "
|
|
6195
|
+
description: "Actions. By default replaces all existing top-level actions. With mergeActions=true, deep-merges instead."
|
|
6196
|
+
},
|
|
6197
|
+
mergeActions: {
|
|
6198
|
+
type: "boolean",
|
|
6199
|
+
description: "When true, deep-merge 'actions' onto current flow instead of replacing. Recurses into nested actions/cases/else/foreach.",
|
|
6200
|
+
default: false
|
|
6201
|
+
},
|
|
6202
|
+
patchActions: {
|
|
6203
|
+
type: "object",
|
|
6204
|
+
description: "Path-based patch map. Keys: slash-separated paths like 'If/cases/Default/actions/Compose'. Value=null deletes the node. Smallest payload."
|
|
6043
6205
|
},
|
|
6044
6206
|
connectionReferences: {
|
|
6045
6207
|
type: "object",
|
|
@@ -6057,17 +6219,80 @@ var updateFlowTool = {
|
|
|
6057
6219
|
required: ["flowId"]
|
|
6058
6220
|
}
|
|
6059
6221
|
};
|
|
6222
|
+
function deepMerge(base, patch) {
|
|
6223
|
+
const out = { ...base };
|
|
6224
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
6225
|
+
if (value === void 0) continue;
|
|
6226
|
+
const baseVal = out[key];
|
|
6227
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value) && baseVal !== null && typeof baseVal === "object" && !Array.isArray(baseVal)) {
|
|
6228
|
+
out[key] = deepMerge(
|
|
6229
|
+
baseVal,
|
|
6230
|
+
value
|
|
6231
|
+
);
|
|
6232
|
+
} else {
|
|
6233
|
+
out[key] = value;
|
|
6234
|
+
}
|
|
6235
|
+
}
|
|
6236
|
+
return out;
|
|
6237
|
+
}
|
|
6238
|
+
function applyPathPatch(actions, path, value) {
|
|
6239
|
+
const segments = path.split("/").filter(Boolean);
|
|
6240
|
+
if (segments.length === 0) return actions;
|
|
6241
|
+
const result = JSON.parse(JSON.stringify(actions));
|
|
6242
|
+
let cursor = result;
|
|
6243
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
6244
|
+
const seg = segments[i];
|
|
6245
|
+
const next = cursor[seg];
|
|
6246
|
+
if (next === null || typeof next !== "object" || Array.isArray(next)) {
|
|
6247
|
+
cursor[seg] = {};
|
|
6248
|
+
}
|
|
6249
|
+
cursor = cursor[seg];
|
|
6250
|
+
}
|
|
6251
|
+
const lastSeg = segments[segments.length - 1];
|
|
6252
|
+
if (value === null) {
|
|
6253
|
+
delete cursor[lastSeg];
|
|
6254
|
+
} else {
|
|
6255
|
+
cursor[lastSeg] = value;
|
|
6256
|
+
}
|
|
6257
|
+
return result;
|
|
6258
|
+
}
|
|
6060
6259
|
async function handleUpdateFlow(api, input) {
|
|
6061
6260
|
const parsed = updateFlowInputSchema.parse(input);
|
|
6062
|
-
logger.info(
|
|
6261
|
+
logger.info(
|
|
6262
|
+
{
|
|
6263
|
+
flowId: parsed.flowId,
|
|
6264
|
+
mergeActions: parsed.mergeActions,
|
|
6265
|
+
hasPatch: !!parsed.patchActions
|
|
6266
|
+
},
|
|
6267
|
+
"Executing update_flow"
|
|
6268
|
+
);
|
|
6063
6269
|
const currentFlow = await api.getFlow(parsed.flowId, parsed.environment);
|
|
6064
6270
|
const currentDef = currentFlow.properties.definition;
|
|
6065
6271
|
const currentConnRefs = currentFlow.properties.connectionReferences;
|
|
6066
6272
|
const newDisplayName = parsed.displayName ?? currentFlow.properties.displayName;
|
|
6273
|
+
const newDescription = parsed.description !== void 0 ? parsed.description : currentFlow.properties.description;
|
|
6067
6274
|
let newDefinition;
|
|
6068
|
-
|
|
6275
|
+
let resolvedActions;
|
|
6276
|
+
if (parsed.actions || parsed.patchActions) {
|
|
6277
|
+
if (parsed.mergeActions && parsed.actions) {
|
|
6278
|
+
resolvedActions = deepMerge(
|
|
6279
|
+
currentDef.actions,
|
|
6280
|
+
parsed.actions
|
|
6281
|
+
);
|
|
6282
|
+
} else if (parsed.actions) {
|
|
6283
|
+
resolvedActions = parsed.actions;
|
|
6284
|
+
} else {
|
|
6285
|
+
resolvedActions = JSON.parse(JSON.stringify(currentDef.actions));
|
|
6286
|
+
}
|
|
6287
|
+
if (parsed.patchActions) {
|
|
6288
|
+
for (const [path, value] of Object.entries(parsed.patchActions)) {
|
|
6289
|
+
resolvedActions = applyPathPatch(resolvedActions, path, value);
|
|
6290
|
+
}
|
|
6291
|
+
}
|
|
6292
|
+
}
|
|
6293
|
+
if (parsed.trigger || resolvedActions) {
|
|
6069
6294
|
const triggerName = parsed.triggerName ?? Object.keys(currentDef.triggers)[0] ?? "manual";
|
|
6070
|
-
const processedActions =
|
|
6295
|
+
const processedActions = resolvedActions ? preprocessFlowActions(resolvedActions) : currentDef.actions;
|
|
6071
6296
|
newDefinition = {
|
|
6072
6297
|
$schema: currentDef.$schema,
|
|
6073
6298
|
contentVersion: currentDef.contentVersion,
|
|
@@ -6121,7 +6346,8 @@ async function handleUpdateFlow(api, input) {
|
|
|
6121
6346
|
properties: {
|
|
6122
6347
|
displayName: newDisplayName,
|
|
6123
6348
|
definition: newDefinition,
|
|
6124
|
-
connectionReferences: newConnRefs
|
|
6349
|
+
connectionReferences: newConnRefs,
|
|
6350
|
+
...newDescription !== void 0 ? { description: newDescription } : {}
|
|
6125
6351
|
}
|
|
6126
6352
|
};
|
|
6127
6353
|
try {
|
|
@@ -6670,11 +6896,14 @@ var getRunActionsInputSchema = z.object({
|
|
|
6670
6896
|
flowId: z.string().describe("The flow ID (GUID)"),
|
|
6671
6897
|
runId: z.string().describe("The run ID (GUID)"),
|
|
6672
6898
|
environment: z.string().optional().describe("Environment name or GUID. Uses default if omitted."),
|
|
6673
|
-
failedOnly: z.boolean().optional().describe("If true, only return failed actions (default: false)")
|
|
6899
|
+
failedOnly: z.boolean().optional().describe("If true, only return failed actions (default: false)"),
|
|
6900
|
+
includeInputs: z.boolean().optional().describe("If true, fetch and display input data for each action (default: false for succeeded, true for failed)"),
|
|
6901
|
+
includeOutputs: z.boolean().optional().describe("If true, fetch and display output data for each action (default: false for succeeded, true for failed)"),
|
|
6902
|
+
actionName: z.string().optional().describe("Filter to a specific action by name (partial match). Use to drill into one action's details.")
|
|
6674
6903
|
});
|
|
6675
6904
|
var getRunActionsTool = {
|
|
6676
6905
|
name: "get_run_actions",
|
|
6677
|
-
description: "Get detailed action-level information for a flow run. Shows each action's status, timing, and
|
|
6906
|
+
description: "Get detailed action-level information for a flow run. Shows each action's status, timing, inputs/outputs, and error details. Use 'failedOnly: true' to focus on failures, 'actionName' to drill into a specific action, 'includeInputs'/'includeOutputs' to see full data payloads.",
|
|
6678
6907
|
inputSchema: {
|
|
6679
6908
|
type: "object",
|
|
6680
6909
|
properties: {
|
|
@@ -6693,11 +6922,34 @@ var getRunActionsTool = {
|
|
|
6693
6922
|
failedOnly: {
|
|
6694
6923
|
type: "boolean",
|
|
6695
6924
|
description: "If true, only return failed actions (default: false)"
|
|
6925
|
+
},
|
|
6926
|
+
includeInputs: {
|
|
6927
|
+
type: "boolean",
|
|
6928
|
+
description: "If true, fetch and display input data for each action (default: false for succeeded, true for failed)"
|
|
6929
|
+
},
|
|
6930
|
+
includeOutputs: {
|
|
6931
|
+
type: "boolean",
|
|
6932
|
+
description: "If true, fetch and display output data for each action (default: false for succeeded, true for failed)"
|
|
6933
|
+
},
|
|
6934
|
+
actionName: {
|
|
6935
|
+
type: "string",
|
|
6936
|
+
description: "Filter to a specific action by name (partial match). Use to drill into one action's details."
|
|
6696
6937
|
}
|
|
6697
6938
|
},
|
|
6698
6939
|
required: ["flowId", "runId"]
|
|
6699
6940
|
}
|
|
6700
6941
|
};
|
|
6942
|
+
var TRUNCATE_LIMIT = 1e3;
|
|
6943
|
+
var ERROR_TRUNCATE_LIMIT = 500;
|
|
6944
|
+
function formatResourceData(data) {
|
|
6945
|
+
if (data && typeof data === "object" && "body" in data) {
|
|
6946
|
+
const body = data.body;
|
|
6947
|
+
const str2 = typeof body === "object" ? JSON.stringify(body, null, 2) : String(body);
|
|
6948
|
+
return str2.length > TRUNCATE_LIMIT ? str2.slice(0, TRUNCATE_LIMIT) + "\u2026" : str2;
|
|
6949
|
+
}
|
|
6950
|
+
const str = JSON.stringify(data, null, 2);
|
|
6951
|
+
return str.length > TRUNCATE_LIMIT ? str.slice(0, TRUNCATE_LIMIT) + "\u2026" : str;
|
|
6952
|
+
}
|
|
6701
6953
|
async function handleGetRunActions(api, input) {
|
|
6702
6954
|
const parsed = getRunActionsInputSchema.parse(input);
|
|
6703
6955
|
logger.info({ input: parsed }, "Executing get_run_actions");
|
|
@@ -6707,38 +6959,70 @@ async function handleGetRunActions(api, input) {
|
|
|
6707
6959
|
parsed.environment
|
|
6708
6960
|
);
|
|
6709
6961
|
let filteredActions = actions;
|
|
6962
|
+
if (parsed.actionName) {
|
|
6963
|
+
const needle = parsed.actionName.toLowerCase();
|
|
6964
|
+
filteredActions = filteredActions.filter(
|
|
6965
|
+
(a) => a.name.toLowerCase().includes(needle)
|
|
6966
|
+
);
|
|
6967
|
+
}
|
|
6710
6968
|
if (parsed.failedOnly) {
|
|
6711
|
-
filteredActions =
|
|
6969
|
+
filteredActions = filteredActions.filter(
|
|
6712
6970
|
(a) => a.properties.status === "Failed" || a.properties.status === "TimedOut"
|
|
6713
6971
|
);
|
|
6714
6972
|
}
|
|
6715
|
-
const
|
|
6716
|
-
|
|
6973
|
+
const shouldFetchInputs = (status) => {
|
|
6974
|
+
if (parsed.includeInputs === true) return true;
|
|
6975
|
+
if (parsed.includeInputs === false) return false;
|
|
6976
|
+
return status === "Failed" || status === "TimedOut";
|
|
6977
|
+
};
|
|
6978
|
+
const shouldFetchOutputs = (status) => {
|
|
6979
|
+
if (parsed.includeOutputs === true) return true;
|
|
6980
|
+
if (parsed.includeOutputs === false) return false;
|
|
6981
|
+
return status === "Failed" || status === "TimedOut";
|
|
6982
|
+
};
|
|
6983
|
+
const actionIO = {};
|
|
6984
|
+
const fetchPromises = [];
|
|
6717
6985
|
for (const action of filteredActions) {
|
|
6718
|
-
const
|
|
6719
|
-
|
|
6720
|
-
|
|
6986
|
+
const status = action.properties.status;
|
|
6987
|
+
const needInputs = shouldFetchInputs(status) && action.properties.inputsLink?.uri;
|
|
6988
|
+
const needOutputs = shouldFetchOutputs(status) && action.properties.outputsLink?.uri;
|
|
6989
|
+
if (needInputs) {
|
|
6990
|
+
fetchPromises.push(
|
|
6991
|
+
api.fetchResourceLink(action.properties.inputsLink.uri).then(
|
|
6992
|
+
(data) => {
|
|
6993
|
+
if (!actionIO[action.name]) actionIO[action.name] = {};
|
|
6994
|
+
actionIO[action.name].inputs = formatResourceData(data);
|
|
6995
|
+
},
|
|
6996
|
+
(err) => {
|
|
6997
|
+
if (!actionIO[action.name]) actionIO[action.name] = {};
|
|
6998
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
6999
|
+
actionIO[action.name].inputs = `(fetch error: ${msg.slice(0, 100)})`;
|
|
7000
|
+
}
|
|
7001
|
+
)
|
|
7002
|
+
);
|
|
6721
7003
|
}
|
|
6722
|
-
|
|
6723
|
-
|
|
6724
|
-
|
|
6725
|
-
|
|
6726
|
-
|
|
6727
|
-
|
|
6728
|
-
|
|
6729
|
-
|
|
6730
|
-
|
|
6731
|
-
|
|
6732
|
-
|
|
6733
|
-
|
|
6734
|
-
|
|
6735
|
-
|
|
7004
|
+
if (needOutputs) {
|
|
7005
|
+
fetchPromises.push(
|
|
7006
|
+
api.fetchResourceLink(action.properties.outputsLink.uri).then(
|
|
7007
|
+
(data) => {
|
|
7008
|
+
if (!actionIO[action.name]) actionIO[action.name] = {};
|
|
7009
|
+
actionIO[action.name].outputs = formatResourceData(data);
|
|
7010
|
+
},
|
|
7011
|
+
(err) => {
|
|
7012
|
+
if (!actionIO[action.name]) actionIO[action.name] = {};
|
|
7013
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
7014
|
+
actionIO[action.name].outputs = `(fetch error: ${msg.slice(0, 100)})`;
|
|
7015
|
+
}
|
|
7016
|
+
)
|
|
7017
|
+
);
|
|
6736
7018
|
}
|
|
6737
7019
|
}
|
|
6738
|
-
|
|
7020
|
+
await Promise.all(fetchPromises);
|
|
7021
|
+
const results = filteredActions.map((action) => {
|
|
6739
7022
|
const props = action.properties;
|
|
6740
7023
|
const startTime = new Date(props.startTime);
|
|
6741
7024
|
const endTime = props.endTime ? new Date(props.endTime) : void 0;
|
|
7025
|
+
const io = actionIO[action.name];
|
|
6742
7026
|
return {
|
|
6743
7027
|
name: action.name,
|
|
6744
7028
|
status: props.status,
|
|
@@ -6746,25 +7030,213 @@ async function handleGetRunActions(api, input) {
|
|
|
6746
7030
|
endTime: props.endTime,
|
|
6747
7031
|
durationMs: endTime ? endTime.getTime() - startTime.getTime() : void 0,
|
|
6748
7032
|
error: props.error,
|
|
6749
|
-
|
|
7033
|
+
inputs: io?.inputs,
|
|
7034
|
+
outputs: io?.outputs,
|
|
7035
|
+
retryCount: props.retryHistory?.length,
|
|
7036
|
+
trackedProperties: props.trackedProperties
|
|
6750
7037
|
};
|
|
6751
7038
|
});
|
|
6752
|
-
const failedCount =
|
|
6753
|
-
|
|
6754
|
-
|
|
6755
|
-
|
|
6756
|
-
|
|
6757
|
-
|
|
6758
|
-
|
|
6759
|
-
}
|
|
6760
|
-
|
|
7039
|
+
const failedCount = results.filter(
|
|
7040
|
+
(r) => r.status === "Failed" || r.status === "TimedOut"
|
|
7041
|
+
).length;
|
|
7042
|
+
const succeededCount = results.filter(
|
|
7043
|
+
(r) => r.status === "Succeeded"
|
|
7044
|
+
).length;
|
|
7045
|
+
const lines = [
|
|
7046
|
+
`Actions: ${results.length} total (${succeededCount} succeeded, ${failedCount} failed)`
|
|
7047
|
+
];
|
|
7048
|
+
if (parsed.actionName) {
|
|
7049
|
+
lines[0] += ` [filtered by "${parsed.actionName}"]`;
|
|
6761
7050
|
}
|
|
6762
|
-
for (const action of
|
|
7051
|
+
for (const action of results) {
|
|
6763
7052
|
const duration = action.durationMs ? `${(action.durationMs / 1e3).toFixed(1)}s` : "";
|
|
6764
|
-
const status = action.status === "Succeeded" ? "\u2713" : action.status === "Failed" ? "\u2717" : "\u25CB";
|
|
6765
|
-
let line = `
|
|
7053
|
+
const status = action.status === "Succeeded" ? "\u2713" : action.status === "Failed" ? "\u2717" : action.status === "TimedOut" ? "\u23F1" : "\u25CB";
|
|
7054
|
+
let line = `
|
|
7055
|
+
${status} ${action.name} ${duration}`;
|
|
6766
7056
|
if (action.error) {
|
|
6767
|
-
line += `
|
|
7057
|
+
line += `
|
|
7058
|
+
Error: [${action.error.code}] ${action.error.message.slice(0, ERROR_TRUNCATE_LIMIT)}`;
|
|
7059
|
+
}
|
|
7060
|
+
if (action.inputs) {
|
|
7061
|
+
line += `
|
|
7062
|
+
Inputs: ${action.inputs}`;
|
|
7063
|
+
}
|
|
7064
|
+
if (action.outputs) {
|
|
7065
|
+
line += `
|
|
7066
|
+
Outputs: ${action.outputs}`;
|
|
7067
|
+
}
|
|
7068
|
+
if (action.trackedProperties && Object.keys(action.trackedProperties).length > 0) {
|
|
7069
|
+
line += `
|
|
7070
|
+
Tracked: ${JSON.stringify(action.trackedProperties)}`;
|
|
7071
|
+
}
|
|
7072
|
+
if (action.retryCount && action.retryCount > 0) {
|
|
7073
|
+
line += `
|
|
7074
|
+
Retries: ${action.retryCount}`;
|
|
7075
|
+
}
|
|
7076
|
+
lines.push(line);
|
|
7077
|
+
}
|
|
7078
|
+
return lines.join("\n");
|
|
7079
|
+
}
|
|
7080
|
+
var getRunActionRepetitionsInputSchema = z.object({
|
|
7081
|
+
flowId: z.string().describe("The flow ID (GUID)"),
|
|
7082
|
+
runId: z.string().describe("The run ID (GUID)"),
|
|
7083
|
+
actionName: z.string().describe("The exact action name inside the for_each/do_until loop"),
|
|
7084
|
+
environment: z.string().optional().describe("Environment name or GUID. Uses default if omitted."),
|
|
7085
|
+
failedOnly: z.boolean().optional().describe("If true, only return failed iterations (default: false)"),
|
|
7086
|
+
includeInputs: z.boolean().optional().describe("If true, fetch input data for each iteration (default: false)"),
|
|
7087
|
+
includeOutputs: z.boolean().optional().describe("If true, fetch output data for each iteration (default: true for failed)"),
|
|
7088
|
+
maxIterations: z.number().optional().describe("Maximum iterations to return (default: 50). Use to limit large loops.")
|
|
7089
|
+
});
|
|
7090
|
+
var getRunActionRepetitionsTool = {
|
|
7091
|
+
name: "get_run_action_repetitions",
|
|
7092
|
+
description: "Get iteration-level details for a for_each or do_until loop action in a flow run. Shows which iterations succeeded/failed, their inputs/outputs, and error details. Essential for debugging loops that partially fail.",
|
|
7093
|
+
inputSchema: {
|
|
7094
|
+
type: "object",
|
|
7095
|
+
properties: {
|
|
7096
|
+
flowId: {
|
|
7097
|
+
type: "string",
|
|
7098
|
+
description: "The flow ID (GUID)"
|
|
7099
|
+
},
|
|
7100
|
+
runId: {
|
|
7101
|
+
type: "string",
|
|
7102
|
+
description: "The run ID (GUID)"
|
|
7103
|
+
},
|
|
7104
|
+
actionName: {
|
|
7105
|
+
type: "string",
|
|
7106
|
+
description: "The exact action name inside the for_each/do_until loop"
|
|
7107
|
+
},
|
|
7108
|
+
environment: {
|
|
7109
|
+
type: "string",
|
|
7110
|
+
description: "Environment name or GUID. Uses default if omitted."
|
|
7111
|
+
},
|
|
7112
|
+
failedOnly: {
|
|
7113
|
+
type: "boolean",
|
|
7114
|
+
description: "If true, only return failed iterations (default: false)"
|
|
7115
|
+
},
|
|
7116
|
+
includeInputs: {
|
|
7117
|
+
type: "boolean",
|
|
7118
|
+
description: "If true, fetch input data for each iteration (default: false)"
|
|
7119
|
+
},
|
|
7120
|
+
includeOutputs: {
|
|
7121
|
+
type: "boolean",
|
|
7122
|
+
description: "If true, fetch output data for each iteration (default: true for failed)"
|
|
7123
|
+
},
|
|
7124
|
+
maxIterations: {
|
|
7125
|
+
type: "number",
|
|
7126
|
+
description: "Maximum iterations to return (default: 50). Use to limit large loops."
|
|
7127
|
+
}
|
|
7128
|
+
},
|
|
7129
|
+
required: ["flowId", "runId", "actionName"]
|
|
7130
|
+
}
|
|
7131
|
+
};
|
|
7132
|
+
var TRUNCATE_LIMIT2 = 1e3;
|
|
7133
|
+
function formatData(data) {
|
|
7134
|
+
if (data && typeof data === "object" && "body" in data) {
|
|
7135
|
+
const body = data.body;
|
|
7136
|
+
const str2 = typeof body === "object" ? JSON.stringify(body, null, 2) : String(body);
|
|
7137
|
+
return str2.length > TRUNCATE_LIMIT2 ? str2.slice(0, TRUNCATE_LIMIT2) + "\u2026" : str2;
|
|
7138
|
+
}
|
|
7139
|
+
const str = JSON.stringify(data, null, 2);
|
|
7140
|
+
return str.length > TRUNCATE_LIMIT2 ? str.slice(0, TRUNCATE_LIMIT2) + "\u2026" : str;
|
|
7141
|
+
}
|
|
7142
|
+
async function handleGetRunActionRepetitions(api, input) {
|
|
7143
|
+
const parsed = getRunActionRepetitionsInputSchema.parse(input);
|
|
7144
|
+
const maxIter = parsed.maxIterations ?? 50;
|
|
7145
|
+
logger.info({ input: parsed }, "Executing get_run_action_repetitions");
|
|
7146
|
+
const repetitions = await api.getFlowRunActionRepetitions(
|
|
7147
|
+
parsed.flowId,
|
|
7148
|
+
parsed.runId,
|
|
7149
|
+
parsed.actionName,
|
|
7150
|
+
parsed.environment
|
|
7151
|
+
);
|
|
7152
|
+
let filtered = repetitions;
|
|
7153
|
+
if (parsed.failedOnly) {
|
|
7154
|
+
filtered = filtered.filter(
|
|
7155
|
+
(r) => r.properties.status === "Failed" || r.properties.status === "TimedOut"
|
|
7156
|
+
);
|
|
7157
|
+
}
|
|
7158
|
+
const totalCount = filtered.length;
|
|
7159
|
+
filtered = filtered.slice(0, maxIter);
|
|
7160
|
+
const shouldFetchInputs = (status) => {
|
|
7161
|
+
if (parsed.includeInputs === true) return true;
|
|
7162
|
+
return false;
|
|
7163
|
+
};
|
|
7164
|
+
const shouldFetchOutputs = (status) => {
|
|
7165
|
+
if (parsed.includeOutputs === true) return true;
|
|
7166
|
+
if (parsed.includeOutputs === false) return false;
|
|
7167
|
+
return status === "Failed" || status === "TimedOut";
|
|
7168
|
+
};
|
|
7169
|
+
const iterIO = {};
|
|
7170
|
+
const fetchPromises = [];
|
|
7171
|
+
for (const rep of filtered) {
|
|
7172
|
+
const status = rep.properties.status;
|
|
7173
|
+
const key = rep.name;
|
|
7174
|
+
if (shouldFetchInputs() && rep.properties.inputsLink?.uri) {
|
|
7175
|
+
fetchPromises.push(
|
|
7176
|
+
api.fetchResourceLink(rep.properties.inputsLink.uri).then(
|
|
7177
|
+
(data) => {
|
|
7178
|
+
if (!iterIO[key]) iterIO[key] = {};
|
|
7179
|
+
iterIO[key].inputs = formatData(data);
|
|
7180
|
+
},
|
|
7181
|
+
(err) => {
|
|
7182
|
+
if (!iterIO[key]) iterIO[key] = {};
|
|
7183
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
7184
|
+
iterIO[key].inputs = `(fetch error: ${msg.slice(0, 100)})`;
|
|
7185
|
+
}
|
|
7186
|
+
)
|
|
7187
|
+
);
|
|
7188
|
+
}
|
|
7189
|
+
if (shouldFetchOutputs(status) && rep.properties.outputsLink?.uri) {
|
|
7190
|
+
fetchPromises.push(
|
|
7191
|
+
api.fetchResourceLink(rep.properties.outputsLink.uri).then(
|
|
7192
|
+
(data) => {
|
|
7193
|
+
if (!iterIO[key]) iterIO[key] = {};
|
|
7194
|
+
iterIO[key].outputs = formatData(data);
|
|
7195
|
+
},
|
|
7196
|
+
(err) => {
|
|
7197
|
+
if (!iterIO[key]) iterIO[key] = {};
|
|
7198
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
7199
|
+
iterIO[key].outputs = `(fetch error: ${msg.slice(0, 100)})`;
|
|
7200
|
+
}
|
|
7201
|
+
)
|
|
7202
|
+
);
|
|
7203
|
+
}
|
|
7204
|
+
}
|
|
7205
|
+
await Promise.all(fetchPromises);
|
|
7206
|
+
const failedCount = filtered.filter(
|
|
7207
|
+
(r) => r.properties.status === "Failed" || r.properties.status === "TimedOut"
|
|
7208
|
+
).length;
|
|
7209
|
+
const succeededCount = filtered.filter(
|
|
7210
|
+
(r) => r.properties.status === "Succeeded"
|
|
7211
|
+
).length;
|
|
7212
|
+
const lines = [
|
|
7213
|
+
`Repetitions for "${parsed.actionName}": ${totalCount} total (${succeededCount} succeeded, ${failedCount} failed)`
|
|
7214
|
+
];
|
|
7215
|
+
if (totalCount > maxIter) {
|
|
7216
|
+
lines.push(` (showing first ${maxIter} of ${totalCount})`);
|
|
7217
|
+
}
|
|
7218
|
+
for (const rep of filtered) {
|
|
7219
|
+
const props = rep.properties;
|
|
7220
|
+
const startTime = new Date(props.startTime);
|
|
7221
|
+
const endTime = props.endTime ? new Date(props.endTime) : void 0;
|
|
7222
|
+
const durationMs = endTime ? endTime.getTime() - startTime.getTime() : void 0;
|
|
7223
|
+
const duration = durationMs ? `${(durationMs / 1e3).toFixed(1)}s` : "";
|
|
7224
|
+
const indexes = props.repetitionIndexes?.map((ri) => `${ri.scopeName}[${ri.itemIndex}]`).join(" > ");
|
|
7225
|
+
const status = props.status === "Succeeded" ? "\u2713" : props.status === "Failed" ? "\u2717" : props.status === "TimedOut" ? "\u23F1" : "\u25CB";
|
|
7226
|
+
let line = `
|
|
7227
|
+
${status} ${indexes ?? rep.name} ${duration}`;
|
|
7228
|
+
if (props.error) {
|
|
7229
|
+
line += `
|
|
7230
|
+
Error: [${props.error.code}] ${props.error.message.slice(0, 500)}`;
|
|
7231
|
+
}
|
|
7232
|
+
const io = iterIO[rep.name];
|
|
7233
|
+
if (io?.inputs) {
|
|
7234
|
+
line += `
|
|
7235
|
+
Inputs: ${io.inputs}`;
|
|
7236
|
+
}
|
|
7237
|
+
if (io?.outputs) {
|
|
7238
|
+
line += `
|
|
7239
|
+
Outputs: ${io.outputs}`;
|
|
6768
7240
|
}
|
|
6769
7241
|
lines.push(line);
|
|
6770
7242
|
}
|
|
@@ -12721,6 +13193,7 @@ var tools = [
|
|
|
12721
13193
|
// Phase 4: Connections, Diagnostics, Sharing
|
|
12722
13194
|
listConnectionsTool,
|
|
12723
13195
|
getRunActionsTool,
|
|
13196
|
+
getRunActionRepetitionsTool,
|
|
12724
13197
|
getFlowPermissionsTool,
|
|
12725
13198
|
shareFlowTool,
|
|
12726
13199
|
// Phase 5: Run Control, Environment Management
|
|
@@ -12856,6 +13329,9 @@ async function executeTool(context, toolName, input) {
|
|
|
12856
13329
|
case "get_run_actions":
|
|
12857
13330
|
result = await handleGetRunActions(context.flowApi, input);
|
|
12858
13331
|
break;
|
|
13332
|
+
case "get_run_action_repetitions":
|
|
13333
|
+
result = await handleGetRunActionRepetitions(context.flowApi, input);
|
|
13334
|
+
break;
|
|
12859
13335
|
case "get_flow_permissions":
|
|
12860
13336
|
result = await handleGetFlowPermissions(context.flowApi, input);
|
|
12861
13337
|
break;
|