@sweny-ai/core 0.1.55 → 0.1.57
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/cli/config.js +8 -1
- package/dist/cli/main.js +3 -16
- package/dist/schema.d.ts +1 -0
- package/dist/schema.js +1 -1
- package/dist/sources.validation.test.js +20 -1
- package/package.json +1 -1
package/dist/cli/config.js
CHANGED
|
@@ -127,7 +127,14 @@ export function parseCliInputs(options, fileConfig = {}) {
|
|
|
127
127
|
serviceMapPath: options.serviceMapPath || f("service-map-path") || ".github/service-map.yml",
|
|
128
128
|
githubToken: env.GITHUB_TOKEN || "",
|
|
129
129
|
botToken: env.BOT_TOKEN || "",
|
|
130
|
-
cloudToken:
|
|
130
|
+
cloudToken: (() => {
|
|
131
|
+
const fromEnv = env.SWENY_CLOUD_TOKEN || "";
|
|
132
|
+
const fromFile = f("cloud-token") || "";
|
|
133
|
+
if (!fromEnv && fromFile) {
|
|
134
|
+
console.warn("\x1b[33m⚠ cloud-token is set in .sweny.yml — consider using SWENY_CLOUD_TOKEN env var instead to avoid accidental commits.\x1b[0m");
|
|
135
|
+
}
|
|
136
|
+
return fromEnv || fromFile;
|
|
137
|
+
})(),
|
|
131
138
|
sourceControlProvider: options.sourceControlProvider || f("source-control-provider") || "github",
|
|
132
139
|
// Secrets: env only
|
|
133
140
|
jiraBaseUrl: env.JIRA_BASE_URL || "",
|
package/dist/cli/main.js
CHANGED
|
@@ -134,7 +134,7 @@ function buildProviderCtx(config, mcpServers) {
|
|
|
134
134
|
}
|
|
135
135
|
/**
|
|
136
136
|
* Resolve rules and context from config into structured workflow input fields.
|
|
137
|
-
*
|
|
137
|
+
* All source kinds (inline, file, URL) are resolved eagerly.
|
|
138
138
|
*/
|
|
139
139
|
async function resolveRulesAndContext(config) {
|
|
140
140
|
const [rulesResult, contextResult] = await Promise.all([
|
|
@@ -144,8 +144,6 @@ async function resolveRulesAndContext(config) {
|
|
|
144
144
|
return {
|
|
145
145
|
rules: rulesResult.resolved,
|
|
146
146
|
context: contextResult.resolved,
|
|
147
|
-
rulesUrls: rulesResult.urls,
|
|
148
|
-
contextUrls: contextResult.urls,
|
|
149
147
|
};
|
|
150
148
|
}
|
|
151
149
|
// ── sweny triage ──────────────────────────────────────────────────────
|
|
@@ -298,7 +296,7 @@ triageCmd.action(async (options) => {
|
|
|
298
296
|
const observer = composeObservers(progressObserver, config.stream ? createStreamObserver() : undefined);
|
|
299
297
|
// ── Build workflow input from config ──────────────────────
|
|
300
298
|
const providerCtx = buildProviderCtx(config, mcpServers);
|
|
301
|
-
const { rules, context
|
|
299
|
+
const { rules, context } = await resolveRulesAndContext(config);
|
|
302
300
|
// Combine provider context + additional instructions into the context field
|
|
303
301
|
const contextParts = [providerCtx];
|
|
304
302
|
if (config.additionalInstructions)
|
|
@@ -328,8 +326,6 @@ triageCmd.action(async (options) => {
|
|
|
328
326
|
// Structured rules/context for executor (URLs resolved eagerly by loadAdditionalContext)
|
|
329
327
|
rules,
|
|
330
328
|
context: fullContext,
|
|
331
|
-
rulesUrls,
|
|
332
|
-
contextUrls,
|
|
333
329
|
};
|
|
334
330
|
try {
|
|
335
331
|
const { results, trace } = await execute(triageWorkflow, workflowInput, {
|
|
@@ -463,7 +459,7 @@ implementCmd.action(async (issueId, options) => {
|
|
|
463
459
|
const observer = composeObservers(implProgressObserver, Boolean(options.stream) ? createStreamObserver() : undefined);
|
|
464
460
|
// Resolve rules/context from .sweny.yml (same as triage path)
|
|
465
461
|
const providerCtx = buildProviderCtx(config, mcpServers);
|
|
466
|
-
const { rules, context
|
|
462
|
+
const { rules, context } = await resolveRulesAndContext(config);
|
|
467
463
|
const implContextParts = [providerCtx];
|
|
468
464
|
if (config.additionalInstructions)
|
|
469
465
|
implContextParts.push(config.additionalInstructions);
|
|
@@ -480,8 +476,6 @@ implementCmd.action(async (issueId, options) => {
|
|
|
480
476
|
// Structured rules/context for executor
|
|
481
477
|
rules,
|
|
482
478
|
context: fullImplContext,
|
|
483
|
-
rulesUrls,
|
|
484
|
-
contextUrls,
|
|
485
479
|
};
|
|
486
480
|
try {
|
|
487
481
|
const { results } = await execute(implementWorkflow, workflowInput, {
|
|
@@ -710,13 +704,6 @@ export async function workflowRunAction(file, options) {
|
|
|
710
704
|
// silent
|
|
711
705
|
}
|
|
712
706
|
const hasFailed = [...results.values()].some((r) => r.status === "failed");
|
|
713
|
-
// Report to SWEny Cloud (best-effort)
|
|
714
|
-
try {
|
|
715
|
-
await reportToCloud(results, 0, config, workflow.id);
|
|
716
|
-
}
|
|
717
|
-
catch {
|
|
718
|
-
// silent
|
|
719
|
-
}
|
|
720
707
|
if (hasFailed) {
|
|
721
708
|
console.error(chalk.red(` Workflow failed\n`));
|
|
722
709
|
process.exit(1);
|
package/dist/schema.d.ts
CHANGED
package/dist/schema.js
CHANGED
|
@@ -239,7 +239,7 @@ const sourceJsonSchema = {
|
|
|
239
239
|
{ type: "string", minLength: 1 },
|
|
240
240
|
{
|
|
241
241
|
type: "object",
|
|
242
|
-
properties: { inline: { type: "string" } },
|
|
242
|
+
properties: { inline: { type: "string", minLength: 1 } },
|
|
243
243
|
required: ["inline"],
|
|
244
244
|
additionalProperties: false,
|
|
245
245
|
},
|
|
@@ -10,11 +10,12 @@
|
|
|
10
10
|
* - Security: no auth token leakage, URL validation
|
|
11
11
|
*/
|
|
12
12
|
import { mkdirSync, writeFileSync, rmSync } from "node:fs";
|
|
13
|
+
import { randomBytes } from "node:crypto";
|
|
13
14
|
import { tmpdir } from "node:os";
|
|
14
15
|
import * as path from "node:path";
|
|
15
16
|
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
16
17
|
import { classifySource, sourceZ, hashContent, resolveSource, resolveSources, } from "./sources.js";
|
|
17
|
-
const fileTmp = path.join(tmpdir(),
|
|
18
|
+
const fileTmp = path.join(tmpdir(), `sweny-sources-validation-test-${randomBytes(4).toString("hex")}`);
|
|
18
19
|
const baseCtx = () => ({
|
|
19
20
|
cwd: "/tmp",
|
|
20
21
|
env: {},
|
|
@@ -253,6 +254,24 @@ describe("error messages include actionable information", () => {
|
|
|
253
254
|
expect(e.message).toContain("--offline");
|
|
254
255
|
}
|
|
255
256
|
});
|
|
257
|
+
it("SOURCE_FILE_READ_FAILED on permission denied", async () => {
|
|
258
|
+
const unreadable = path.join(fileTmp, "unreadable.md");
|
|
259
|
+
writeFileSync(unreadable, "secret");
|
|
260
|
+
const { chmodSync } = await import("node:fs");
|
|
261
|
+
chmodSync(unreadable, 0o000);
|
|
262
|
+
try {
|
|
263
|
+
await resolveSource("./unreadable.md", "context[0]", { ...baseCtx(), cwd: fileTmp });
|
|
264
|
+
expect.unreachable("should have thrown");
|
|
265
|
+
}
|
|
266
|
+
catch (e) {
|
|
267
|
+
expect(e.message).toContain("SOURCE_FILE_READ_FAILED");
|
|
268
|
+
expect(e.message).toContain("unreadable.md");
|
|
269
|
+
expect(e.message).toContain("context[0]");
|
|
270
|
+
}
|
|
271
|
+
finally {
|
|
272
|
+
chmodSync(unreadable, 0o644);
|
|
273
|
+
}
|
|
274
|
+
});
|
|
256
275
|
it("SOURCE_OFFLINE_REQUIRES_FETCH includes the URL and field path", async () => {
|
|
257
276
|
try {
|
|
258
277
|
await resolveSource("https://example.com/rules.md", "rules[1]", { ...baseCtx(), offline: true });
|