@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.
- package/dist/__tests__/executor.test.js +2 -1
- package/dist/__tests__/integration/sources.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/sources.integration.test.js +123 -0
- package/dist/__tests__/workflow-yaml.test.js +12 -5
- package/dist/cli/__tests__/report-cloud.test.d.ts +1 -0
- package/dist/cli/__tests__/report-cloud.test.js +165 -0
- package/dist/cli/cloud-report.d.ts +12 -0
- package/dist/cli/cloud-report.js +79 -0
- package/dist/cli/config-file.d.ts +3 -3
- package/dist/cli/config-file.js +18 -0
- package/dist/cli/config.d.ts +3 -0
- package/dist/cli/config.js +25 -2
- package/dist/cli/config.test.js +65 -1
- package/dist/cli/e2e.js +4 -2
- package/dist/cli/e2e.test.js +9 -2
- package/dist/cli/main.js +44 -12
- package/dist/cli/output.test.js +1 -0
- package/dist/cli/publish.test.d.ts +1 -0
- package/dist/cli/publish.test.js +610 -0
- package/dist/executor.d.ts +8 -0
- package/dist/executor.js +21 -3
- package/dist/executor.test.js +56 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/mermaid.test.js +2 -0
- package/dist/schema.d.ts +175 -46
- package/dist/schema.js +31 -2
- package/dist/sources.d.ts +70 -0
- package/dist/sources.js +194 -0
- package/dist/sources.test.d.ts +1 -0
- package/dist/sources.test.js +264 -0
- package/dist/sources.validation.test.d.ts +12 -0
- package/dist/sources.validation.test.js +360 -0
- package/dist/templates.d.ts +6 -2
- package/dist/templates.js +36 -46
- package/dist/testing.js +4 -1
- package/dist/types.d.ts +8 -1
- package/dist/types.js +0 -5
- package/dist/workflows/browser.js +1 -1
- package/dist/workflows/triage.yml +1 -23
- package/package.json +1 -1
package/dist/cli/config.test.js
CHANGED
|
@@ -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
|
-
|
|
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));
|
package/dist/cli/e2e.test.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
-
*
|
|
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
|
|
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
|
|
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("
|
|
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,
|
package/dist/cli/output.test.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|