mcp-scraper 0.2.19 → 0.2.20

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.
Files changed (71) hide show
  1. package/README.md +3 -3
  2. package/dist/bin/api-server.cjs +1020 -348
  3. package/dist/bin/api-server.cjs.map +1 -1
  4. package/dist/bin/api-server.js +3 -3
  5. package/dist/bin/browser-agent-stdio-server.cjs +314 -55
  6. package/dist/bin/browser-agent-stdio-server.cjs.map +1 -1
  7. package/dist/bin/browser-agent-stdio-server.js +2 -2
  8. package/dist/bin/mcp-scraper-cli.cjs +321 -167
  9. package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
  10. package/dist/bin/mcp-scraper-cli.js +3 -2
  11. package/dist/bin/mcp-scraper-cli.js.map +1 -1
  12. package/dist/bin/mcp-scraper-combined-stdio-server.cjs +1128 -530
  13. package/dist/bin/mcp-scraper-combined-stdio-server.cjs.map +1 -1
  14. package/dist/bin/mcp-scraper-combined-stdio-server.js +5 -4
  15. package/dist/bin/mcp-scraper-combined-stdio-server.js.map +1 -1
  16. package/dist/bin/mcp-scraper-install.cjs +3 -3
  17. package/dist/bin/mcp-scraper-install.cjs.map +1 -1
  18. package/dist/bin/mcp-scraper-install.js +2 -2
  19. package/dist/bin/mcp-stdio-server.cjs +726 -387
  20. package/dist/bin/mcp-stdio-server.cjs.map +1 -1
  21. package/dist/bin/mcp-stdio-server.js +3 -2
  22. package/dist/bin/mcp-stdio-server.js.map +1 -1
  23. package/dist/bin/paa-harvest.cjs +5 -3
  24. package/dist/bin/paa-harvest.cjs.map +1 -1
  25. package/dist/bin/paa-harvest.js +2 -1
  26. package/dist/bin/paa-harvest.js.map +1 -1
  27. package/dist/{chunk-TL7YTFLH.js → chunk-DBQDG7EH.js} +99 -27
  28. package/dist/chunk-DBQDG7EH.js.map +1 -0
  29. package/dist/{chunk-F44RBOJ5.js → chunk-H2R232HK.js} +216 -108
  30. package/dist/chunk-H2R232HK.js.map +1 -0
  31. package/dist/{chunk-76P4DKWR.js → chunk-L4OWOUGR.js} +315 -56
  32. package/dist/chunk-L4OWOUGR.js.map +1 -0
  33. package/dist/chunk-LFATOGDF.js +111 -0
  34. package/dist/chunk-LFATOGDF.js.map +1 -0
  35. package/dist/{chunk-MCBOZ2JF.js → chunk-MLNCKQ2B.js} +3 -3
  36. package/dist/chunk-MLNCKQ2B.js.map +1 -0
  37. package/dist/{chunk-4UEVYYDW.js → chunk-O5J7E4VX.js} +394 -57
  38. package/dist/chunk-O5J7E4VX.js.map +1 -0
  39. package/dist/{chunk-BSYPATSM.js → chunk-SXTXMFEQ.js} +2 -2
  40. package/dist/{chunk-CQTAKXBN.js → chunk-XGUDTDZ2.js} +7 -104
  41. package/dist/chunk-XGUDTDZ2.js.map +1 -0
  42. package/dist/chunk-XKUDVN2E.js +7 -0
  43. package/dist/chunk-XKUDVN2E.js.map +1 -0
  44. package/dist/{db-P5X6UQ3E.js → db-BE4JVB3V.js} +8 -2
  45. package/dist/index.cjs +5 -3
  46. package/dist/index.cjs.map +1 -1
  47. package/dist/index.d.cts +2 -2
  48. package/dist/index.d.ts +2 -2
  49. package/dist/index.js +2 -1
  50. package/dist/index.js.map +1 -1
  51. package/dist/{server-CRUSZA2Q.js → server-7NE45K2A.js} +211 -37
  52. package/dist/server-7NE45K2A.js.map +1 -0
  53. package/dist/{worker-OZSWIS3F.js → worker-MIYG2B2I.js} +5 -4
  54. package/dist/{worker-OZSWIS3F.js.map → worker-MIYG2B2I.js.map} +1 -1
  55. package/docs/adr/0001-in-page-graphql-interception-for-anti-bot-scraping.md +2 -2
  56. package/docs/mcp-tool-craft-lint.generated.md +51 -0
  57. package/docs/mcp-tool-manifest.generated.json +1406 -0
  58. package/docs/specs/local-competitive-audit-spec.md +1 -2
  59. package/docs/specs/mcp-tool-definition-quality-audit-spec.md +1602 -0
  60. package/package.json +4 -1
  61. package/dist/chunk-4UEVYYDW.js.map +0 -1
  62. package/dist/chunk-76P4DKWR.js.map +0 -1
  63. package/dist/chunk-CQTAKXBN.js.map +0 -1
  64. package/dist/chunk-F44RBOJ5.js.map +0 -1
  65. package/dist/chunk-MCBOZ2JF.js.map +0 -1
  66. package/dist/chunk-RU2HZD55.js +0 -7
  67. package/dist/chunk-RU2HZD55.js.map +0 -1
  68. package/dist/chunk-TL7YTFLH.js.map +0 -1
  69. package/dist/server-CRUSZA2Q.js.map +0 -1
  70. /package/dist/{chunk-BSYPATSM.js.map → chunk-SXTXMFEQ.js.map} +0 -0
  71. /package/dist/{db-P5X6UQ3E.js.map → db-BE4JVB3V.js.map} +0 -0
@@ -1,3 +1,7 @@
1
+ import {
2
+ DEFAULT_MAPS_PROXY_MODE
3
+ } from "./chunk-LFATOGDF.js";
4
+
1
5
  // src/workflows/artifact-writer.ts
2
6
  import { mkdir, readFile, stat, writeFile } from "fs/promises";
3
7
  import { existsSync } from "fs";
@@ -206,6 +210,7 @@ async function openWorkflowReport(id, outputDir) {
206
210
  }
207
211
 
208
212
  // src/workflows/registry.ts
213
+ import { readFile as readFile2 } from "fs/promises";
209
214
  import { z as z5 } from "zod";
210
215
 
211
216
  // src/workflows/http-client.ts
@@ -387,102 +392,141 @@ var agentPacketWorkflowDefinition = {
387
392
  title: "Agent-Ready SEO Packet",
388
393
  description: "Create an evidence folder for AI agents from live SERP/PAA/AI search surfaces.",
389
394
  inputSchema: AgentPacketInputSchema,
390
- async run(input, ctx) {
391
- await ctx.artifacts.writeManifest("running", {}, [], []);
392
- const warnings = [];
393
- const errors = [];
394
- let serp = null;
395
- let paa = null;
396
- if (input.includeSerp) {
397
- try {
398
- serp = await ctx.client.post("/harvest/sync", {
399
- query: input.keyword,
400
- location: input.location,
401
- serpOnly: true,
402
- maxQuestions: 1,
403
- format: "json"
404
- }, 18e4);
405
- } catch (err) {
406
- warnings.push(`SERP evidence unavailable: ${err instanceof Error ? err.message : String(err)}`);
395
+ createState: () => ({ serp: null, paa: null, warnings: [] }),
396
+ steps: [
397
+ {
398
+ id: "harvest-serp",
399
+ title: "Harvest organic SERP + AI Overview",
400
+ async run({ input, state, ctx }) {
401
+ if (!input.includeSerp) {
402
+ return { state, output: { skipped: true, reason: "includeSerp is false" } };
403
+ }
404
+ try {
405
+ const serp = await ctx.client.post("/harvest/sync", {
406
+ query: input.keyword,
407
+ location: input.location,
408
+ serpOnly: true,
409
+ maxQuestions: 1,
410
+ format: "json"
411
+ }, 18e4);
412
+ await ctx.artifacts.writeJson("SERP raw JSON", "raw/serp.json", serp);
413
+ return {
414
+ state: { ...state, serp },
415
+ output: {
416
+ organicResults: serp.organicResults?.length ?? 0,
417
+ localPack: serp.localPack?.length ?? 0,
418
+ aiOverviewDetected: Boolean(serp.aiOverview?.detected),
419
+ aiOverviewCitations: serp.aiOverview?.citations?.length ?? 0
420
+ }
421
+ };
422
+ } catch (err) {
423
+ const message = `SERP evidence unavailable: ${err instanceof Error ? err.message : String(err)}`;
424
+ return { state: { ...state, warnings: [...state.warnings, message] }, output: { error: message }, warnings: [message] };
425
+ }
407
426
  }
408
- }
409
- if (input.includePaa) {
410
- try {
411
- paa = await ctx.client.post("/harvest/sync", {
412
- query: input.keyword,
413
- location: input.location,
414
- maxQuestions: input.maxQuestions,
415
- format: "json"
416
- }, 28e4);
417
- } catch (err) {
418
- warnings.push(`PAA evidence unavailable: ${err instanceof Error ? err.message : String(err)}`);
427
+ },
428
+ {
429
+ id: "harvest-paa",
430
+ title: "Harvest People Also Ask",
431
+ async run({ input, state, ctx }) {
432
+ if (!input.includePaa) {
433
+ return { state, output: { skipped: true, reason: "includePaa is false" } };
434
+ }
435
+ try {
436
+ const paa = await ctx.client.post("/harvest/sync", {
437
+ query: input.keyword,
438
+ location: input.location,
439
+ maxQuestions: input.maxQuestions,
440
+ format: "json"
441
+ }, 28e4);
442
+ await ctx.artifacts.writeJson("PAA raw JSON", "raw/paa.json", paa);
443
+ return {
444
+ state: { ...state, paa },
445
+ output: { paaQuestions: paa.flat?.length ?? 0 }
446
+ };
447
+ } catch (err) {
448
+ const message = `PAA evidence unavailable: ${err instanceof Error ? err.message : String(err)}`;
449
+ return { state: { ...state, warnings: [...state.warnings, message] }, output: { error: message }, warnings: [message] };
450
+ }
451
+ }
452
+ },
453
+ {
454
+ id: "assemble",
455
+ title: "Assemble evidence packet",
456
+ async run({ input, state, ctx }) {
457
+ const { serp, paa } = state;
458
+ const warnings = state.warnings;
459
+ if (!serp && !paa && !input.returnPartial) throw new Error("No SEO evidence was collected");
460
+ const rows = sourceRows(input, serp, paa);
461
+ const target = normalizeDomain(input.domain);
462
+ const competitors = competitorRows(rows, target);
463
+ const evidence = {
464
+ input,
465
+ serp: serp ?? { status: "skipped" },
466
+ paa: paa ?? { status: "skipped" },
467
+ target: {
468
+ domain: target,
469
+ organicPositions: rows.filter((r) => r.surface === "organic" && r.is_target).map((r) => Number(r.position)),
470
+ citedInAiOverview: rows.some((r) => r.surface === "ai_overview" && r.is_target),
471
+ citedInPaa: rows.some((r) => r.surface === "paa" && r.is_target)
472
+ },
473
+ competitors,
474
+ warnings
475
+ };
476
+ await ctx.artifacts.writeJson("Evidence JSON", "evidence.json", evidence);
477
+ await ctx.artifacts.writeCsv("Sources CSV", "sources.csv", ["surface", "position", "title", "url", "domain", "question", "answer_excerpt", "is_target", "is_competitor"], rows);
478
+ await ctx.artifacts.writeCsv("Competitors CSV", "competitors.csv", ["domain", "organic_best_position", "organic_count", "paa_mentions", "ai_overview_citations", "source_url_count"], competitors);
479
+ const brief = [
480
+ `# SEO Evidence Brief: ${input.keyword}`,
481
+ "",
482
+ `Location: ${input.location ?? "not specified"}`,
483
+ `Target domain: ${target ?? "not specified"}`,
484
+ "",
485
+ "## Evidence Summary",
486
+ `- Organic rows: ${rows.filter((r) => r.surface === "organic").length}`,
487
+ `- PAA rows: ${rows.filter((r) => r.surface === "paa").length}`,
488
+ `- AI Overview citations: ${rows.filter((r) => r.surface === "ai_overview").length}`,
489
+ `- Competitor domains: ${competitors.length}`,
490
+ "",
491
+ "## Recommended Use",
492
+ "Use the CSV files as source of truth. Tie content recommendations to evidence rows and mark unsupported ideas as assumptions."
493
+ ].join("\n");
494
+ await ctx.artifacts.writeText("Brief", "brief.md", brief);
495
+ await ctx.artifacts.writeText("Agent tasks", "tasks.md", [
496
+ "# Agent Tasks",
497
+ "",
498
+ "- [ ] Read `evidence.json` before writing recommendations.",
499
+ "- [ ] Compare the target domain against `competitors.csv`.",
500
+ "- [ ] Use `sources.csv` for citations and source-grounded page sections.",
501
+ "- [ ] Mark unsupported recommendations as assumptions."
502
+ ].join("\n"));
503
+ await ctx.artifacts.writeText("Agent instructions", "agent-instructions.md", [
504
+ "# Agent Instructions",
505
+ "",
506
+ "You are working from an MCP Scraper SEO evidence packet. Use `evidence.json` and CSV files as source of truth. Do not invent citations. If a recommendation is not supported by evidence, mark it as an assumption."
507
+ ].join("\n"));
508
+ const summary = `${rows.length} evidence rows and ${competitors.length} competitor domains collected.`;
509
+ const reportPath = await ctx.artifacts.writeHtml("HTML report", "report.html", renderWorkflowReport({
510
+ title: "Agent-Ready SEO Packet",
511
+ subtitle: `${input.keyword}${input.location ? ` \xB7 ${input.location}` : ""}`,
512
+ summary,
513
+ warnings,
514
+ tables: [
515
+ { title: "Competitor Domains", columns: ["domain", "organic_best_position", "organic_count", "paa_mentions", "ai_overview_citations", "source_url_count"], rows: competitors.slice(0, 50) },
516
+ { title: "Evidence Sources", columns: ["surface", "position", "title", "domain", "question", "is_target"], rows: rows.slice(0, 100) }
517
+ ]
518
+ }));
519
+ const status = warnings.length ? "partial" : "succeeded";
520
+ const counts = { sources: rows.length, competitors: competitors.length };
521
+ await ctx.artifacts.writeManifest(status, counts, warnings, []);
522
+ return {
523
+ state,
524
+ output: { sources: rows.length, competitors: competitors.length, status },
525
+ summary: { title: "Agent-Ready SEO Packet", summary, status, counts, warnings, errors: [], reportPath }
526
+ };
419
527
  }
420
528
  }
421
- if (!serp && !paa && !input.returnPartial) throw new Error("No SEO evidence was collected");
422
- const rows = sourceRows(input, serp, paa);
423
- const target = normalizeDomain(input.domain);
424
- const competitors = competitorRows(rows, target);
425
- const evidence = {
426
- input,
427
- serp: serp ?? { status: "skipped" },
428
- paa: paa ?? { status: "skipped" },
429
- target: {
430
- domain: target,
431
- organicPositions: rows.filter((r) => r.surface === "organic" && r.is_target).map((r) => Number(r.position)),
432
- citedInAiOverview: rows.some((r) => r.surface === "ai_overview" && r.is_target),
433
- citedInPaa: rows.some((r) => r.surface === "paa" && r.is_target)
434
- },
435
- competitors,
436
- warnings
437
- };
438
- await ctx.artifacts.writeJson("Evidence JSON", "evidence.json", evidence);
439
- await ctx.artifacts.writeCsv("Sources CSV", "sources.csv", ["surface", "position", "title", "url", "domain", "question", "answer_excerpt", "is_target", "is_competitor"], rows);
440
- await ctx.artifacts.writeCsv("Competitors CSV", "competitors.csv", ["domain", "organic_best_position", "organic_count", "paa_mentions", "ai_overview_citations", "source_url_count"], competitors);
441
- const brief = [
442
- `# SEO Evidence Brief: ${input.keyword}`,
443
- "",
444
- `Location: ${input.location ?? "not specified"}`,
445
- `Target domain: ${target ?? "not specified"}`,
446
- "",
447
- "## Evidence Summary",
448
- `- Organic rows: ${rows.filter((r) => r.surface === "organic").length}`,
449
- `- PAA rows: ${rows.filter((r) => r.surface === "paa").length}`,
450
- `- AI Overview citations: ${rows.filter((r) => r.surface === "ai_overview").length}`,
451
- `- Competitor domains: ${competitors.length}`,
452
- "",
453
- "## Recommended Use",
454
- "Use the CSV files as source of truth. Tie content recommendations to evidence rows and mark unsupported ideas as assumptions."
455
- ].join("\n");
456
- await ctx.artifacts.writeText("Brief", "brief.md", brief);
457
- await ctx.artifacts.writeText("Agent tasks", "tasks.md", [
458
- "# Agent Tasks",
459
- "",
460
- "- [ ] Read `evidence.json` before writing recommendations.",
461
- "- [ ] Compare the target domain against `competitors.csv`.",
462
- "- [ ] Use `sources.csv` for citations and source-grounded page sections.",
463
- "- [ ] Mark unsupported recommendations as assumptions."
464
- ].join("\n"));
465
- await ctx.artifacts.writeText("Agent instructions", "agent-instructions.md", [
466
- "# Agent Instructions",
467
- "",
468
- "You are working from an MCP Scraper SEO evidence packet. Use `evidence.json` and CSV files as source of truth. Do not invent citations. If a recommendation is not supported by evidence, mark it as an assumption."
469
- ].join("\n"));
470
- const summary = `${rows.length} evidence rows and ${competitors.length} competitor domains collected.`;
471
- const reportPath = await ctx.artifacts.writeHtml("HTML report", "report.html", renderWorkflowReport({
472
- title: "Agent-Ready SEO Packet",
473
- subtitle: `${input.keyword}${input.location ? ` \xB7 ${input.location}` : ""}`,
474
- summary,
475
- warnings,
476
- tables: [
477
- { title: "Competitor Domains", columns: ["domain", "organic_best_position", "organic_count", "paa_mentions", "ai_overview_citations", "source_url_count"], rows: competitors.slice(0, 50) },
478
- { title: "Evidence Sources", columns: ["surface", "position", "title", "domain", "question", "is_target"], rows: rows.slice(0, 100) }
479
- ]
480
- }));
481
- const status = warnings.length ? "partial" : "succeeded";
482
- const counts = { sources: rows.length, competitors: competitors.length };
483
- await ctx.artifacts.writeManifest(status, counts, warnings, errors);
484
- return { title: "Agent-Ready SEO Packet", summary, status, counts, warnings, errors, reportPath };
485
- }
529
+ ]
486
530
  };
487
531
 
488
532
  // src/workflows/workflows/directory.ts
@@ -494,7 +538,7 @@ var DirectoryWorkflowCliInputSchema = z2.object({
494
538
  maxCities: z2.number().int().min(1).max(100).default(25),
495
539
  maxResultsPerCity: z2.number().int().min(1).max(50).default(20),
496
540
  concurrency: z2.number().int().min(1).max(5).default(5),
497
- proxyMode: z2.enum(["location", "configured", "none"]).default("location"),
541
+ proxyMode: z2.enum(["location", "configured", "none"]).default(DEFAULT_MAPS_PROXY_MODE),
498
542
  saveCsv: z2.boolean().default(true)
499
543
  });
500
544
  function directoryRows(result) {
@@ -629,7 +673,7 @@ var LocalCompetitiveAuditInputSchema = z3.object({
629
673
  hydrateTop: z3.number().int().min(0).max(10).default(5),
630
674
  maxReviews: z3.number().int().min(0).max(500).default(50),
631
675
  concurrency: z3.number().int().min(1).max(5).default(5),
632
- proxyMode: z3.enum(["location", "configured", "none"]).default("location"),
676
+ proxyMode: z3.enum(["location", "configured", "none"]).default(DEFAULT_MAPS_PROXY_MODE),
633
677
  returnPartial: z3.boolean().default(true)
634
678
  });
635
679
  async function mapLimit(items, limit, fn) {
@@ -987,7 +1031,7 @@ var MapComparisonInputSchema = z4.object({
987
1031
  hydrateTop: z4.number().int().min(0).max(10).default(5),
988
1032
  maxReviews: z4.number().int().min(0).max(500).default(25),
989
1033
  concurrency: z4.number().int().min(1).max(5).default(5),
990
- proxyMode: ProxyModeSchema.default("location"),
1034
+ proxyMode: ProxyModeSchema.default(DEFAULT_MAPS_PROXY_MODE),
991
1035
  returnPartial: z4.boolean().default(true)
992
1036
  }).refine((input) => input.location || input.state, {
993
1037
  message: "Either location or state is required for map-comparison"
@@ -1534,22 +1578,44 @@ function workflowDefinition(id) {
1534
1578
  if (!definition) throw new Error(`Unknown workflow "${id}". Available: ${DEFINITIONS.map((def) => def.id).join(", ")}`);
1535
1579
  return definition;
1536
1580
  }
1537
- async function runWorkflow(id, rawInput, options = {}) {
1581
+ function workflowStepCount(id) {
1538
1582
  const definition = workflowDefinition(id);
1539
- const input = definition.inputSchema.parse(rawInput);
1583
+ return definition.steps?.length ?? 1;
1584
+ }
1585
+ function workflowSupportsSteps(id) {
1586
+ const definition = workflowDefinition(id);
1587
+ return Boolean(definition.steps && definition.steps.length > 0);
1588
+ }
1589
+ function resolveApiKey(options) {
1540
1590
  const apiKey = options.apiKey?.trim() || process.env.MCP_SCRAPER_API_KEY?.trim();
1541
1591
  if (!apiKey) throw new Error("MCP_SCRAPER_API_KEY is required for workflow runs. Pass --api-key or set the environment variable.");
1542
- const apiUrl = options.apiUrl?.trim() || process.env.MCP_SCRAPER_API_URL?.trim() || "https://mcpscraper.dev";
1592
+ return apiKey;
1593
+ }
1594
+ function resolveApiUrl(options) {
1595
+ return options.apiUrl?.trim() || process.env.MCP_SCRAPER_API_URL?.trim() || "https://mcpscraper.dev";
1596
+ }
1597
+ async function runWorkflow(id, rawInput, options = {}) {
1598
+ const definition = workflowDefinition(id);
1599
+ const input = definition.inputSchema.parse(rawInput);
1600
+ const apiKey = resolveApiKey(options);
1601
+ const apiUrl = resolveApiUrl(options);
1543
1602
  const artifacts = await ArtifactWriter.create(definition.id, definition.title, input, options.outputDir, options.runId);
1544
1603
  const client = new WorkflowHttpClient(apiUrl, apiKey, options.fetchImpl, options.headers);
1604
+ const ctx = { runId: artifacts.runId, startedAt: artifacts.startedAt, client, artifacts, signal: options.signal };
1545
1605
  try {
1546
- return await definition.run(input, {
1547
- runId: artifacts.runId,
1548
- startedAt: artifacts.startedAt,
1549
- client,
1550
- artifacts,
1551
- signal: options.signal
1552
- });
1606
+ if (definition.steps && definition.steps.length > 0) {
1607
+ let state = definition.createState ? definition.createState(input) : {};
1608
+ let summary = null;
1609
+ for (const step of definition.steps) {
1610
+ const outcome = await step.run({ input, state, ctx });
1611
+ state = outcome.state;
1612
+ if (outcome.summary) summary = outcome.summary;
1613
+ }
1614
+ if (!summary) throw new Error(`Workflow "${id}" produced no terminal step summary`);
1615
+ return summary;
1616
+ }
1617
+ if (definition.run) return await definition.run(input, ctx);
1618
+ throw new Error(`Workflow "${id}" has neither steps nor a run() implementation`);
1553
1619
  } catch (err) {
1554
1620
  const message = err instanceof Error ? err.message : String(err);
1555
1621
  await artifacts.writeText("Failure", "summary.md", `# ${definition.title}
@@ -1560,6 +1626,45 @@ ${message}
1560
1626
  throw err;
1561
1627
  }
1562
1628
  }
1629
+ async function runWorkflowStep(id, rawInput, opts) {
1630
+ const definition = workflowDefinition(id);
1631
+ const steps = definition.steps;
1632
+ if (!steps || steps.length === 0) throw new Error(`Workflow "${id}" does not support stepwise execution`);
1633
+ if (opts.stepIndex < 0 || opts.stepIndex >= steps.length) {
1634
+ throw new Error(`Step index ${opts.stepIndex} is out of range for "${id}" (${steps.length} steps)`);
1635
+ }
1636
+ const options = opts.options ?? {};
1637
+ const input = definition.inputSchema.parse(rawInput);
1638
+ const apiKey = resolveApiKey(options);
1639
+ const apiUrl = resolveApiUrl(options);
1640
+ const artifacts = await ArtifactWriter.create(definition.id, definition.title, input, options.outputDir, options.runId);
1641
+ const client = new WorkflowHttpClient(apiUrl, apiKey, options.fetchImpl, options.headers);
1642
+ const ctx = { runId: artifacts.runId, startedAt: artifacts.startedAt, client, artifacts, signal: options.signal };
1643
+ const state = opts.state ?? (definition.createState ? definition.createState(input) : {});
1644
+ const step = steps[opts.stepIndex];
1645
+ const outcome = await step.run({ input, state, ctx });
1646
+ const writtenArtifacts = await Promise.all(
1647
+ artifacts.artifacts.map(async (artifact) => ({
1648
+ ...artifact,
1649
+ content: await readFile2(artifact.path, "utf8").catch(() => "")
1650
+ }))
1651
+ );
1652
+ return {
1653
+ runId: artifacts.runId,
1654
+ workflowId: definition.id,
1655
+ title: definition.title,
1656
+ stepIndex: opts.stepIndex,
1657
+ stepId: step.id,
1658
+ stepTitle: step.title,
1659
+ totalSteps: steps.length,
1660
+ isLast: opts.stepIndex === steps.length - 1,
1661
+ output: outcome.output,
1662
+ state: outcome.state,
1663
+ warnings: outcome.warnings ?? [],
1664
+ summary: outcome.summary ?? null,
1665
+ artifacts: writtenArtifacts
1666
+ };
1667
+ }
1563
1668
 
1564
1669
  export {
1565
1670
  csvRecords,
@@ -1570,6 +1675,9 @@ export {
1570
1675
  openWorkflowReport,
1571
1676
  listWorkflowDefinitions,
1572
1677
  workflowDefinition,
1573
- runWorkflow
1678
+ workflowStepCount,
1679
+ workflowSupportsSteps,
1680
+ runWorkflow,
1681
+ runWorkflowStep
1574
1682
  };
1575
- //# sourceMappingURL=chunk-F44RBOJ5.js.map
1683
+ //# sourceMappingURL=chunk-H2R232HK.js.map