@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.
@@ -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: env.SWENY_CLOUD_TOKEN || f("cloud-token") || "",
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
- * Local files + inline text are resolved now; URLs are passed to the prepare node.
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, rulesUrls, contextUrls } = await resolveRulesAndContext(config);
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, rulesUrls, contextUrls } = await resolveRulesAndContext(config);
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
@@ -713,6 +713,7 @@ export declare const workflowJsonSchema: {
713
713
  readonly properties: {
714
714
  readonly inline: {
715
715
  readonly type: "string";
716
+ readonly minLength: 1;
716
717
  };
717
718
  };
718
719
  readonly required: readonly ["inline"];
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(), "sweny-sources-validation-test");
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 });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sweny-ai/core",
3
- "version": "0.1.55",
3
+ "version": "0.1.57",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "sweny": "./dist/cli/main.js"