@sweny-ai/core 0.1.54 → 0.1.56

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 (41) hide show
  1. package/dist/__tests__/executor.test.js +2 -1
  2. package/dist/__tests__/integration/sources.integration.test.d.ts +1 -0
  3. package/dist/__tests__/integration/sources.integration.test.js +123 -0
  4. package/dist/__tests__/workflow-yaml.test.js +12 -5
  5. package/dist/cli/__tests__/report-cloud.test.d.ts +1 -0
  6. package/dist/cli/__tests__/report-cloud.test.js +165 -0
  7. package/dist/cli/cloud-report.d.ts +12 -0
  8. package/dist/cli/cloud-report.js +79 -0
  9. package/dist/cli/config-file.d.ts +3 -3
  10. package/dist/cli/config-file.js +18 -0
  11. package/dist/cli/config.d.ts +3 -0
  12. package/dist/cli/config.js +25 -2
  13. package/dist/cli/config.test.js +65 -1
  14. package/dist/cli/e2e.js +4 -2
  15. package/dist/cli/e2e.test.js +9 -2
  16. package/dist/cli/main.js +44 -12
  17. package/dist/cli/output.test.js +1 -0
  18. package/dist/cli/publish.test.d.ts +1 -0
  19. package/dist/cli/publish.test.js +610 -0
  20. package/dist/executor.d.ts +8 -0
  21. package/dist/executor.js +21 -3
  22. package/dist/executor.test.js +56 -1
  23. package/dist/index.d.ts +2 -2
  24. package/dist/index.js +1 -1
  25. package/dist/mermaid.test.js +2 -0
  26. package/dist/schema.d.ts +175 -46
  27. package/dist/schema.js +31 -2
  28. package/dist/sources.d.ts +70 -0
  29. package/dist/sources.js +194 -0
  30. package/dist/sources.test.d.ts +1 -0
  31. package/dist/sources.test.js +264 -0
  32. package/dist/sources.validation.test.d.ts +12 -0
  33. package/dist/sources.validation.test.js +360 -0
  34. package/dist/templates.d.ts +6 -2
  35. package/dist/templates.js +36 -46
  36. package/dist/testing.js +4 -1
  37. package/dist/types.d.ts +8 -1
  38. package/dist/types.js +0 -5
  39. package/dist/workflows/browser.js +1 -1
  40. package/dist/workflows/triage.yml +1 -23
  41. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect } from "vitest";
2
- import { validateInputs } from "./config.js";
2
+ import { validateInputs, parseCliInputs } from "./config.js";
3
3
  /**
4
4
  * Focused tests for validateInputs() — specifically the notification-provider
5
5
  * switch. Regressions here break every `swenyai/triage` action run because
@@ -65,6 +65,9 @@ function baseConfig(overrides = {}) {
65
65
  workspaceTools: [],
66
66
  rules: [],
67
67
  context: [],
68
+ offline: false,
69
+ fetchAuth: {},
70
+ cloudToken: "",
68
71
  ...overrides,
69
72
  };
70
73
  }
@@ -109,3 +112,64 @@ describe("validateInputs — notification provider", () => {
109
112
  expect(err).toContain("console");
110
113
  });
111
114
  });
115
+ describe("fetch.auth + offline parsing", () => {
116
+ it("parses offline flag", () => {
117
+ const config = parseCliInputs({ offline: true }, {});
118
+ expect(config.offline).toBe(true);
119
+ });
120
+ it("defaults fetchAuth to {} and offline to false", () => {
121
+ const config = parseCliInputs({}, {});
122
+ expect(config.offline).toBe(false);
123
+ expect(config.fetchAuth).toEqual({});
124
+ });
125
+ it("parses fetch.auth from file config", () => {
126
+ const config = parseCliInputs({}, { "fetch.auth": { "api.example.com": "MY_TOKEN" } });
127
+ expect(config.fetchAuth).toEqual({ "api.example.com": "MY_TOKEN" });
128
+ });
129
+ });
130
+ describe("parseCliInputs — cloud token", () => {
131
+ it("reads SWENY_CLOUD_TOKEN from env", () => {
132
+ const original = process.env.SWENY_CLOUD_TOKEN;
133
+ process.env.SWENY_CLOUD_TOKEN = "sweny_pk_test123";
134
+ try {
135
+ const config = parseCliInputs({});
136
+ expect(config.cloudToken).toBe("sweny_pk_test123");
137
+ }
138
+ finally {
139
+ if (original === undefined)
140
+ delete process.env.SWENY_CLOUD_TOKEN;
141
+ else
142
+ process.env.SWENY_CLOUD_TOKEN = original;
143
+ }
144
+ });
145
+ it("reads cloud-token from config file", () => {
146
+ const config = parseCliInputs({}, { "cloud-token": "sweny_pk_fromfile" });
147
+ expect(config.cloudToken).toBe("sweny_pk_fromfile");
148
+ });
149
+ it("env var overrides config file", () => {
150
+ const original = process.env.SWENY_CLOUD_TOKEN;
151
+ process.env.SWENY_CLOUD_TOKEN = "sweny_pk_env";
152
+ try {
153
+ const config = parseCliInputs({}, { "cloud-token": "sweny_pk_file" });
154
+ expect(config.cloudToken).toBe("sweny_pk_env");
155
+ }
156
+ finally {
157
+ if (original === undefined)
158
+ delete process.env.SWENY_CLOUD_TOKEN;
159
+ else
160
+ process.env.SWENY_CLOUD_TOKEN = original;
161
+ }
162
+ });
163
+ it("defaults to empty string", () => {
164
+ const original = process.env.SWENY_CLOUD_TOKEN;
165
+ delete process.env.SWENY_CLOUD_TOKEN;
166
+ try {
167
+ const config = parseCliInputs({});
168
+ expect(config.cloudToken).toBe("");
169
+ }
170
+ finally {
171
+ if (original !== undefined)
172
+ process.env.SWENY_CLOUD_TOKEN = original;
173
+ }
174
+ });
175
+ });
package/dist/cli/e2e.js CHANGED
@@ -830,9 +830,11 @@ export async function runE2eRun(options) {
830
830
  runResults.push({ file: fileName, passed: false });
831
831
  continue;
832
832
  }
833
- // Replace template vars in node instructions
833
+ // Replace template vars in node instructions (e2e workflows always use string instructions)
834
834
  for (const node of Object.values(workflow.nodes)) {
835
- node.instruction = resolveTemplateVars(node.instruction, vars);
835
+ if (typeof node.instruction === "string") {
836
+ node.instruction = resolveTemplateVars(node.instruction, vars);
837
+ }
836
838
  }
837
839
  // Build skills + Claude client
838
840
  const skills = createSkillMap(configuredSkills(process.env, cwd));
@@ -247,7 +247,13 @@ describe("buildFlowNodes", () => {
247
247
  const { nodes } = buildFlowNodes(flow);
248
248
  for (const [id, node] of Object.entries(nodes)) {
249
249
  expect(node.name, `${id}.name`).toBeTruthy();
250
- expect(node.instruction.length, `${id}.instruction`).toBeGreaterThan(0);
250
+ const instr = node.instruction;
251
+ if (typeof instr === "string") {
252
+ expect(instr.length, `${id}.instruction`).toBeGreaterThan(0);
253
+ }
254
+ else {
255
+ expect(instr, `${id}.instruction`).toBeTruthy();
256
+ }
251
257
  expect(Array.isArray(node.skills), `${id}.skills`).toBe(true);
252
258
  }
253
259
  });
@@ -607,7 +613,8 @@ describe("template variable completeness — all {vars} in instructions are reso
607
613
  it(`${flow.type}: all template vars in instructions resolve to values`, () => {
608
614
  const wf = buildFlowWorkflow(flow, "http://localhost:3000");
609
615
  for (const [nodeId, node] of Object.entries(wf.nodes)) {
610
- const resolved = resolveTemplateVars(node.instruction, vars);
616
+ const instrText = typeof node.instruction === "string" ? node.instruction : "";
617
+ const resolved = resolveTemplateVars(instrText, vars);
611
618
  // After resolution, no {word} placeholders should remain
612
619
  // (except for agent-browser refs like @<ref> which are not {vars})
613
620
  const unresolvedVars = resolved.match(/\{(\w+)\}/g) || [];
package/dist/cli/main.js CHANGED
@@ -29,6 +29,7 @@ import { c, formatBanner, getStepDetails, formatStepLine, formatDagResultHuman,
29
29
  import { checkProviderConnectivity } from "./check.js";
30
30
  import { registerSetupCommand } from "./setup.js";
31
31
  import { registerPublishCommand } from "./publish.js";
32
+ import { reportToCloud } from "./cloud-report.js";
32
33
  // ── Stream observer (NDJSON) ────────────────────────────────────────
33
34
  /**
34
35
  * Create an observer that writes NDJSON ExecutionEvents to stdout.
@@ -133,7 +134,7 @@ function buildProviderCtx(config, mcpServers) {
133
134
  }
134
135
  /**
135
136
  * Resolve rules and context from config into structured workflow input fields.
136
- * Local files + inline text are resolved now; URLs are passed to the prepare node.
137
+ * All source kinds (inline, file, URL) are resolved eagerly.
137
138
  */
138
139
  async function resolveRulesAndContext(config) {
139
140
  const [rulesResult, contextResult] = await Promise.all([
@@ -143,8 +144,6 @@ async function resolveRulesAndContext(config) {
143
144
  return {
144
145
  rules: rulesResult.resolved,
145
146
  context: contextResult.resolved,
146
- rulesUrls: rulesResult.urls,
147
- contextUrls: contextResult.urls,
148
147
  };
149
148
  }
150
149
  // ── sweny triage ──────────────────────────────────────────────────────
@@ -297,7 +296,7 @@ triageCmd.action(async (options) => {
297
296
  const observer = composeObservers(progressObserver, config.stream ? createStreamObserver() : undefined);
298
297
  // ── Build workflow input from config ──────────────────────
299
298
  const providerCtx = buildProviderCtx(config, mcpServers);
300
- const { rules, context, rulesUrls, contextUrls } = await resolveRulesAndContext(config);
299
+ const { rules, context } = await resolveRulesAndContext(config);
301
300
  // Combine provider context + additional instructions into the context field
302
301
  const contextParts = [providerCtx];
303
302
  if (config.additionalInstructions)
@@ -324,12 +323,9 @@ triageCmd.action(async (options) => {
324
323
  ...(config.observabilityCredentials.tableName && {
325
324
  betterstackTableName: config.observabilityCredentials.tableName,
326
325
  }),
327
- // Structured rules/context for executor
326
+ // Structured rules/context for executor (URLs resolved eagerly by loadAdditionalContext)
328
327
  rules,
329
328
  context: fullContext,
330
- // URLs for the prepare node to fetch at runtime
331
- rulesUrls,
332
- contextUrls,
333
329
  };
334
330
  try {
335
331
  const { results, trace } = await execute(triageWorkflow, workflowInput, {
@@ -337,6 +333,10 @@ triageCmd.action(async (options) => {
337
333
  claude,
338
334
  observer,
339
335
  logger: consoleLogger,
336
+ cwd: process.cwd(),
337
+ env: process.env,
338
+ fetchAuth: config.fetchAuth,
339
+ offline: config.offline,
340
340
  });
341
341
  const durationMs = Date.now() - runStart;
342
342
  // Output
@@ -360,6 +360,13 @@ triageCmd.action(async (options) => {
360
360
  console.error(c.subtle(` ⚠ could not write GITHUB_STEP_SUMMARY: ${err instanceof Error ? err.message : err}`));
361
361
  }
362
362
  }
363
+ // Report to SWEny Cloud (best-effort, never block the run)
364
+ try {
365
+ await reportToCloud(results, durationMs, config, "triage");
366
+ }
367
+ catch {
368
+ // silent — cloud reporting is optional
369
+ }
363
370
  // Terminal bell
364
371
  if (config.bell)
365
372
  process.stderr.write("\x07");
@@ -420,6 +427,7 @@ implementCmd.action(async (issueId, options) => {
420
427
  mcpServers,
421
428
  });
422
429
  console.log(chalk.cyan(`\n sweny implement ${issueId}\n`));
430
+ const implRunStart = Date.now();
423
431
  const isTTY = process.stderr.isTTY ?? false;
424
432
  const implProgressObserver = (event) => {
425
433
  switch (event.type) {
@@ -451,7 +459,7 @@ implementCmd.action(async (issueId, options) => {
451
459
  const observer = composeObservers(implProgressObserver, Boolean(options.stream) ? createStreamObserver() : undefined);
452
460
  // Resolve rules/context from .sweny.yml (same as triage path)
453
461
  const providerCtx = buildProviderCtx(config, mcpServers);
454
- const { rules, context, rulesUrls, contextUrls } = await resolveRulesAndContext(config);
462
+ const { rules, context } = await resolveRulesAndContext(config);
455
463
  const implContextParts = [providerCtx];
456
464
  if (config.additionalInstructions)
457
465
  implContextParts.push(config.additionalInstructions);
@@ -468,8 +476,6 @@ implementCmd.action(async (issueId, options) => {
468
476
  // Structured rules/context for executor
469
477
  rules,
470
478
  context: fullImplContext,
471
- rulesUrls,
472
- contextUrls,
473
479
  };
474
480
  try {
475
481
  const { results } = await execute(implementWorkflow, workflowInput, {
@@ -477,6 +483,10 @@ implementCmd.action(async (issueId, options) => {
477
483
  claude,
478
484
  observer,
479
485
  logger: consoleLogger,
486
+ cwd: process.cwd(),
487
+ env: process.env,
488
+ fetchAuth: config.fetchAuth,
489
+ offline: config.offline,
480
490
  });
481
491
  const hasFailed = [...results.values()].some((r) => r.status === "failed");
482
492
  if (hasFailed) {
@@ -491,6 +501,13 @@ implementCmd.action(async (issueId, options) => {
491
501
  else {
492
502
  console.log(chalk.green(`\n Implement workflow completed\n`));
493
503
  }
504
+ // Report to SWEny Cloud (best-effort)
505
+ try {
506
+ await reportToCloud(results, Date.now() - implRunStart, config, "implement");
507
+ }
508
+ catch {
509
+ // silent
510
+ }
494
511
  process.exit(0);
495
512
  }
496
513
  catch (err) {
@@ -540,6 +557,7 @@ export async function workflowRunAction(file, options) {
540
557
  }
541
558
  process.exit(0);
542
559
  }
560
+ const runStart = Date.now();
543
561
  const fileConfig = loadConfigFile();
544
562
  const config = parseCliInputs(options, fileConfig);
545
563
  const isJson = Boolean(options.json);
@@ -659,6 +677,10 @@ export async function workflowRunAction(file, options) {
659
677
  claude,
660
678
  observer,
661
679
  logger: consoleLogger,
680
+ cwd: process.cwd(),
681
+ env: process.env,
682
+ fetchAuth: config.fetchAuth,
683
+ offline: config.offline,
662
684
  });
663
685
  if (isJson) {
664
686
  process.stdout.write(JSON.stringify(Object.fromEntries(results), null, 2) + "\n");
@@ -674,6 +696,13 @@ export async function workflowRunAction(file, options) {
674
696
  }
675
697
  process.stdout.write(toMermaidBlock(workflow, { state, trace, title: workflow.name }) + "\n");
676
698
  }
699
+ // Report to SWEny Cloud (best-effort)
700
+ try {
701
+ await reportToCloud(results, Date.now() - runStart, config, workflow.id);
702
+ }
703
+ catch {
704
+ // silent
705
+ }
677
706
  const hasFailed = [...results.values()].some((r) => r.status === "failed");
678
707
  if (hasFailed) {
679
708
  console.error(chalk.red(` Workflow failed\n`));
@@ -804,9 +833,12 @@ workflowCmd
804
833
  .action(workflowExportAction);
805
834
  workflowCmd
806
835
  .command("create <description>")
807
- .description("Generate a new workflow from a natural language description")
836
+ .description("[DEPRECATED] Use `sweny new` and pick 'Describe your own'")
808
837
  .option("--json", "Output workflow JSON to stdout (no interactive prompt)")
809
838
  .action(async (description, options) => {
839
+ if (!options.json) {
840
+ console.warn("\x1B[33m ⚠ `sweny workflow create` is deprecated. Use `sweny new` instead.\x1B[0m\n");
841
+ }
810
842
  const skills = configuredSkills();
811
843
  const claude = new ClaudeClient({
812
844
  maxTurns: 3,
@@ -44,6 +44,7 @@ function fakeTrace(steps) {
44
44
  iteration: s.iteration ?? 1,
45
45
  })),
46
46
  edges: [],
47
+ sources: {},
47
48
  };
48
49
  }
49
50
  describe("formatDagResultMarkdown", () => {
@@ -0,0 +1 @@
1
+ export {};