@sweny-ai/core 0.1.170 → 0.1.172
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/agent-env.d.ts +2 -1
- package/dist/agent-env.js +9 -3
- package/dist/browser.d.ts +2 -0
- package/dist/browser.js +3 -0
- package/dist/budget.js +5 -1
- package/dist/cli/check.js +12 -12
- package/dist/cli/comment-output.d.ts +4 -2
- package/dist/cli/comment-output.js +16 -8
- package/dist/cli/config.js +24 -24
- package/dist/cli/e2e.js +17 -17
- package/dist/cli/main.js +42 -26
- package/dist/cli/marketplace.js +3 -3
- package/dist/cli/new.js +11 -8
- package/dist/cli/output.d.ts +3 -2
- package/dist/cli/output.js +39 -37
- package/dist/cli/progress.d.ts +33 -0
- package/dist/cli/progress.js +69 -0
- package/dist/cli/publish.js +2 -2
- package/dist/cli/record.d.ts +66 -0
- package/dist/cli/record.js +344 -0
- package/dist/cli/renderer.d.ts +4 -0
- package/dist/cli/renderer.js +20 -13
- package/dist/cli/run-output.d.ts +13 -6
- package/dist/cli/run-output.js +36 -26
- package/dist/cli/runs.js +17 -11
- package/dist/cli/setup.js +7 -7
- package/dist/cli/style.d.ts +26 -0
- package/dist/cli/style.js +48 -0
- package/dist/cli/terminal.d.ts +47 -0
- package/dist/cli/terminal.js +123 -0
- package/dist/cli/ticket.d.ts +89 -0
- package/dist/cli/ticket.js +218 -0
- package/dist/cli/try.d.ts +19 -8
- package/dist/cli/try.js +97 -65
- package/dist/cli/upgrade.js +1 -1
- package/dist/executor.d.ts +8 -1
- package/dist/executor.js +20 -2
- package/dist/git-credentials.d.ts +106 -0
- package/dist/git-credentials.js +322 -0
- package/dist/harness/acp.js +3 -0
- package/dist/harness/claude-code.d.ts +7 -0
- package/dist/harness/claude-code.js +44 -3
- package/dist/harness/codex.js +3 -0
- package/dist/harness/pi.js +3 -0
- package/dist/harness/policy.d.ts +5 -0
- package/dist/harness/policy.js +14 -0
- package/dist/harness/sandbox-wrapper.d.ts +9 -2
- package/dist/harness/sandbox-wrapper.js +14 -4
- package/dist/harness/types.d.ts +10 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/mermaid.d.ts +10 -5
- package/dist/mermaid.js +11 -13
- package/dist/safe-outputs.js +5 -0
- package/dist/skills/git-push.d.ts +62 -0
- package/dist/skills/git-push.js +132 -0
- package/dist/skills/github.d.ts +17 -0
- package/dist/skills/github.js +23 -0
- package/dist/theme.d.ts +185 -0
- package/dist/theme.js +183 -0
- package/dist/workflows/browser.js +2 -2
- package/dist/workflows/implement.yml +2 -1
- package/dist/workflows/triage.yml +2 -1
- package/package.json +6 -1
package/dist/agent-env.d.ts
CHANGED
|
@@ -232,7 +232,8 @@ export declare function noPushGitConfig(dir: string): Array<[string, string]>;
|
|
|
232
232
|
* them. A deliberate agent on an unsandboxed run that finds a credential on
|
|
233
233
|
* disk (an ssh key without a passphrase, a keychain entry, a token persisted
|
|
234
234
|
* in `.git/config` by a checkout) can still push. Run sandboxed (the sandbox
|
|
235
|
-
* hides credential files such as `~/.ssh
|
|
235
|
+
* hides credential files such as `~/.ssh`, and a checkout's persisted token,
|
|
236
|
+
* see git-credentials.ts) and without push credentials in CI
|
|
236
237
|
* for a guarantee; under a strict harness policy an unsandboxed staged write
|
|
237
238
|
* node is refused (`stagedWrite` in policy.ts). `enabled` false returns `env`
|
|
238
239
|
* unchanged. Pure apart from creating {@link noPushDir} once.
|
package/dist/agent-env.js
CHANGED
|
@@ -646,7 +646,8 @@ export function noPushGitConfig(dir) {
|
|
|
646
646
|
* them. A deliberate agent on an unsandboxed run that finds a credential on
|
|
647
647
|
* disk (an ssh key without a passphrase, a keychain entry, a token persisted
|
|
648
648
|
* in `.git/config` by a checkout) can still push. Run sandboxed (the sandbox
|
|
649
|
-
* hides credential files such as `~/.ssh
|
|
649
|
+
* hides credential files such as `~/.ssh`, and a checkout's persisted token,
|
|
650
|
+
* see git-credentials.ts) and without push credentials in CI
|
|
650
651
|
* for a guarantee; under a strict harness policy an unsandboxed staged write
|
|
651
652
|
* node is refused (`stagedWrite` in policy.ts). `enabled` false returns `env`
|
|
652
653
|
* unchanged. Pure apart from creating {@link noPushDir} once.
|
|
@@ -670,10 +671,15 @@ export function withPushBlocked(env, enabled) {
|
|
|
670
671
|
n++;
|
|
671
672
|
}
|
|
672
673
|
out.GIT_CONFIG_COUNT = String(n);
|
|
673
|
-
const
|
|
674
|
+
const wrapper = shQuote(path.join(dir, "ssh"));
|
|
675
|
+
// Applied twice, GIT_SSH_COMMAND is already our wrapper: keep the operator's command it recorded,
|
|
676
|
+
// or the wrapper would exec itself forever on a fetch.
|
|
677
|
+
const ownSsh = env.GIT_SSH_COMMAND === wrapper
|
|
678
|
+
? env.SWENY_NO_PUSH_SSH
|
|
679
|
+
: (env.GIT_SSH_COMMAND ?? (env.GIT_SSH ? shQuote(env.GIT_SSH) : undefined));
|
|
674
680
|
if (ownSsh)
|
|
675
681
|
out.SWENY_NO_PUSH_SSH = ownSsh;
|
|
676
|
-
out.GIT_SSH_COMMAND =
|
|
682
|
+
out.GIT_SSH_COMMAND = wrapper;
|
|
677
683
|
out.GIT_SSH_VARIANT = "ssh";
|
|
678
684
|
out.GIT_ASKPASS = path.join(dir, "askpass");
|
|
679
685
|
out.GIT_TERMINAL_PROMPT = "0";
|
package/dist/browser.d.ts
CHANGED
|
@@ -24,3 +24,5 @@ export { isWhenExpression, whenLabel, parseExpression, evaluateExpression, check
|
|
|
24
24
|
export type { ExprNode, ExpressionScope, ExpressionResult } from "./when.js";
|
|
25
25
|
export { workflowToFlow, flowToWorkflow, applyExecutionEvent, exportAsTypescript, getSkillCatalog } from "./studio.js";
|
|
26
26
|
export type { FlowNode, SkillNodeData, FlowEdge } from "./studio.js";
|
|
27
|
+
export { SWENY_TAGLINE, PALETTE, ROLE_COLORS, SURFACE_COLORS, GLYPHS, SPINNER_FRAMES, SPINNER_INTERVAL_MS, TICKET_BOX, MERMAID_CLASS_DEFS, MERMAID_EDGE_STYLES, } from "./theme.js";
|
|
28
|
+
export type { ColorRole, GlyphKey, GlyphSet, TicketBox, DiagramNodeStatus } from "./theme.js";
|
package/dist/browser.js
CHANGED
|
@@ -27,3 +27,6 @@ export { workflowZ, nodeZ, edgeZ, skillZ, parseWorkflow, validateWorkflow, workf
|
|
|
27
27
|
export { isWhenExpression, whenLabel, parseExpression, evaluateExpression, checkExpression, ExpressionSyntaxError, } from "./when.js";
|
|
28
28
|
// Studio adapter
|
|
29
29
|
export { workflowToFlow, flowToWorkflow, applyExecutionEvent, exportAsTypescript, getSkillCatalog } from "./studio.js";
|
|
30
|
+
// Visual tokens (#479): palette, color roles, glyphs, diagram classDefs. No
|
|
31
|
+
// imports, no chalk: Studio and the docs site share them with the CLI.
|
|
32
|
+
export { SWENY_TAGLINE, PALETTE, ROLE_COLORS, SURFACE_COLORS, GLYPHS, SPINNER_FRAMES, SPINNER_INTERVAL_MS, TICKET_BOX, MERMAID_CLASS_DEFS, MERMAID_EDGE_STYLES, } from "./theme.js";
|
package/dist/budget.js
CHANGED
|
@@ -38,7 +38,11 @@ export function hasLimits(l) {
|
|
|
38
38
|
export function spendOf(u) {
|
|
39
39
|
if (!u)
|
|
40
40
|
return {};
|
|
41
|
-
|
|
41
|
+
// Count only finite fields: a NaN beside a real count must not turn the sum into NaN,
|
|
42
|
+
// which no limit compares as crossed (the run would pass its budget without stopping).
|
|
43
|
+
const inTokens = isNum(u.inputTokens) ? u.inputTokens : undefined;
|
|
44
|
+
const outTokens = isNum(u.outputTokens) ? u.outputTokens : undefined;
|
|
45
|
+
const tokens = inTokens !== undefined || outTokens !== undefined ? (inTokens ?? 0) + (outTokens ?? 0) : undefined;
|
|
42
46
|
return { ...(tokens !== undefined ? { tokens } : {}), ...(isNum(u.costUsd) ? { costUsd: u.costUsd } : {}) };
|
|
43
47
|
}
|
|
44
48
|
function over(spent, limit, strictlyOver) {
|
package/dist/cli/check.js
CHANGED
|
@@ -63,7 +63,7 @@ export async function checkProviderConnectivity(config, opts = {}) {
|
|
|
63
63
|
results.push(await checkSentry(creds));
|
|
64
64
|
}
|
|
65
65
|
else if (provider === "file") {
|
|
66
|
-
results.push({ name: "Observability (file)", status: "skip", detail: "File provider
|
|
66
|
+
results.push({ name: "Observability (file)", status: "skip", detail: "File provider - no network check needed" });
|
|
67
67
|
}
|
|
68
68
|
else {
|
|
69
69
|
results.push({
|
|
@@ -89,7 +89,7 @@ export async function checkProviderConnectivity(config, opts = {}) {
|
|
|
89
89
|
results.push({
|
|
90
90
|
name: "Issue tracker (file)",
|
|
91
91
|
status: "skip",
|
|
92
|
-
detail: "File provider
|
|
92
|
+
detail: "File provider - no network check needed",
|
|
93
93
|
});
|
|
94
94
|
}
|
|
95
95
|
else {
|
|
@@ -118,7 +118,7 @@ export async function checkProviderConnectivity(config, opts = {}) {
|
|
|
118
118
|
results.push({
|
|
119
119
|
name: "Source control (file)",
|
|
120
120
|
status: "skip",
|
|
121
|
-
detail: "File provider
|
|
121
|
+
detail: "File provider - no network check needed",
|
|
122
122
|
});
|
|
123
123
|
}
|
|
124
124
|
else {
|
|
@@ -145,7 +145,7 @@ async function checkAnthropic(apiKey) {
|
|
|
145
145
|
return {
|
|
146
146
|
name,
|
|
147
147
|
status: "fail",
|
|
148
|
-
detail: "401 Unauthorized
|
|
148
|
+
detail: "401 Unauthorized - check ANTHROPIC_API_KEY at https://console.anthropic.com",
|
|
149
149
|
};
|
|
150
150
|
}
|
|
151
151
|
return { name, status: "fail", detail: `Unexpected HTTP ${res.status}` };
|
|
@@ -220,7 +220,7 @@ export async function checkDatadog(creds) {
|
|
|
220
220
|
return {
|
|
221
221
|
name,
|
|
222
222
|
status: "fail",
|
|
223
|
-
detail: `Invalid DD_SITE "${site}"
|
|
223
|
+
detail: `Invalid DD_SITE "${site}" - expected a hostname like datadoghq.com or datadoghq.eu`,
|
|
224
224
|
};
|
|
225
225
|
}
|
|
226
226
|
try {
|
|
@@ -235,7 +235,7 @@ export async function checkDatadog(creds) {
|
|
|
235
235
|
return {
|
|
236
236
|
name,
|
|
237
237
|
status: "fail",
|
|
238
|
-
detail: "403 Forbidden
|
|
238
|
+
detail: "403 Forbidden - check DD_API_KEY and DD_APP_KEY at https://app.datadoghq.com/organization-settings/api-keys",
|
|
239
239
|
};
|
|
240
240
|
}
|
|
241
241
|
return { name, status: "fail", detail: `Unexpected HTTP ${res.status}` };
|
|
@@ -262,7 +262,7 @@ async function checkSentry(creds) {
|
|
|
262
262
|
return {
|
|
263
263
|
name,
|
|
264
264
|
status: "fail",
|
|
265
|
-
detail: "401 Unauthorized
|
|
265
|
+
detail: "401 Unauthorized - check SENTRY_AUTH_TOKEN at https://sentry.io/settings/auth-tokens/",
|
|
266
266
|
};
|
|
267
267
|
}
|
|
268
268
|
return { name, status: "fail", detail: `Unexpected HTTP ${res.status}` };
|
|
@@ -286,7 +286,7 @@ async function checkLinear(apiKey) {
|
|
|
286
286
|
if (res.status === 200) {
|
|
287
287
|
const body = (await res.json());
|
|
288
288
|
if (body.errors?.length) {
|
|
289
|
-
return { name, status: "fail", detail: "GraphQL error
|
|
289
|
+
return { name, status: "fail", detail: "GraphQL error - check LINEAR_API_KEY" };
|
|
290
290
|
}
|
|
291
291
|
const displayName = body.data?.viewer?.name ?? "authenticated";
|
|
292
292
|
return { name, status: "ok", detail: `Authenticated as ${displayName}` };
|
|
@@ -295,7 +295,7 @@ async function checkLinear(apiKey) {
|
|
|
295
295
|
return {
|
|
296
296
|
name,
|
|
297
297
|
status: "fail",
|
|
298
|
-
detail: "401 Unauthorized
|
|
298
|
+
detail: "401 Unauthorized - check LINEAR_API_KEY at https://linear.app/settings/api",
|
|
299
299
|
};
|
|
300
300
|
}
|
|
301
301
|
return { name, status: "fail", detail: `Unexpected HTTP ${res.status}` };
|
|
@@ -321,7 +321,7 @@ async function checkGitHub(token, name) {
|
|
|
321
321
|
return {
|
|
322
322
|
name,
|
|
323
323
|
status: "fail",
|
|
324
|
-
detail: "401 Bad credentials
|
|
324
|
+
detail: "401 Bad credentials - check GITHUB_TOKEN at https://github.com/settings/tokens",
|
|
325
325
|
};
|
|
326
326
|
}
|
|
327
327
|
return { name, status: "fail", detail: `Unexpected HTTP ${res.status}` };
|
|
@@ -335,11 +335,11 @@ function networkErrorMessage(err) {
|
|
|
335
335
|
// explicit abort surfaces as "AbortError". Either means we gave up waiting.
|
|
336
336
|
const errName = err instanceof Error ? err.name : "";
|
|
337
337
|
if (errName === "TimeoutError" || errName === "AbortError") {
|
|
338
|
-
return `Connection timed out after ${CHECK_TIMEOUT_MS}ms
|
|
338
|
+
return `Connection timed out after ${CHECK_TIMEOUT_MS}ms - host unreachable or too slow`;
|
|
339
339
|
}
|
|
340
340
|
const msg = err instanceof Error ? err.message : String(err);
|
|
341
341
|
if (/ENOTFOUND|ETIMEDOUT|ECONNREFUSED/i.test(msg)) {
|
|
342
|
-
return `Network error
|
|
342
|
+
return `Network error - check your internet connection (${msg})`;
|
|
343
343
|
}
|
|
344
344
|
return msg;
|
|
345
345
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* PR billboard: the markdown a CI runner posts as one sticky comment on a
|
|
3
|
-
* pull request. Pure and runner-agnostic; the CLI writes it with
|
|
3
|
+
* pull request. Leads with the receipt ticket, then the run's DAG. Pure and runner-agnostic; the CLI writes it with
|
|
4
4
|
* `--comment-file`, and any runner (the GitHub Action, GitLab, a script) posts it.
|
|
5
5
|
*
|
|
6
6
|
* METADATA ONLY. Node names, statuses, counts, and durations. Never prompts,
|
|
@@ -13,6 +13,8 @@ export interface RunCommentOptions {
|
|
|
13
13
|
trace?: ExecutionTrace;
|
|
14
14
|
/** Per-node wall-clock duration in ms, keyed by node id. Missing entries render as "-". */
|
|
15
15
|
durationsMs?: Record<string, number>;
|
|
16
|
+
/** Run id; the ticket shows its short hash. */
|
|
17
|
+
runId?: string;
|
|
16
18
|
}
|
|
17
19
|
/** Hidden marker the poster searches for to update the comment in place. */
|
|
18
20
|
export declare function runCommentMarker(workflowId: string): string;
|
|
@@ -22,7 +24,7 @@ export declare function formatRunComment(workflow: Workflow, results: Map<string
|
|
|
22
24
|
* and no error text (thrown messages can embed model prose). Replaces any stale
|
|
23
25
|
* success comment for the same workflow.
|
|
24
26
|
*/
|
|
25
|
-
export declare function formatCrashComment(workflow: Workflow, summary: RunSummary): string;
|
|
27
|
+
export declare function formatCrashComment(workflow: Workflow, summary: RunSummary, runId?: string): string;
|
|
26
28
|
/** Write the comment markdown to `file`. Never throws; returns true only when written. */
|
|
27
29
|
export declare function writeRunComment(file: string, workflow: Workflow, results: Map<string, NodeResult>, summary: RunSummary, opts?: RunCommentOptions & {
|
|
28
30
|
crashed?: boolean;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* PR billboard: the markdown a CI runner posts as one sticky comment on a
|
|
3
|
-
* pull request. Pure and runner-agnostic; the CLI writes it with
|
|
3
|
+
* pull request. Leads with the receipt ticket, then the run's DAG. Pure and runner-agnostic; the CLI writes it with
|
|
4
4
|
* `--comment-file`, and any runner (the GitHub Action, GitLab, a script) posts it.
|
|
5
5
|
*
|
|
6
6
|
* METADATA ONLY. Node names, statuses, counts, and durations. Never prompts,
|
|
@@ -9,8 +9,14 @@
|
|
|
9
9
|
import fs from "node:fs";
|
|
10
10
|
import path from "node:path";
|
|
11
11
|
import { toMermaidBlock } from "../mermaid.js";
|
|
12
|
-
import {
|
|
12
|
+
import { formatReceiptDuration, nodeStates } from "./run-output.js";
|
|
13
|
+
import { formatTicketText } from "./ticket.js";
|
|
14
|
+
import { glyphs } from "./terminal.js";
|
|
13
15
|
export const RUN_COMMENT_FOOTER = "Run with [SWEny](https://github.com/swenyai/sweny): `npx @sweny-ai/core new`";
|
|
16
|
+
/** The receipt ticket as a fenced block, the same card a TTY run ends on. */
|
|
17
|
+
function ticketBlock(workflow, summary, runId, crashed) {
|
|
18
|
+
return ["```text", formatTicketText({ summary, workflow: workflow.id, runId, crashed }), "```"].join("\n");
|
|
19
|
+
}
|
|
14
20
|
/** Hidden marker the poster searches for to update the comment in place. */
|
|
15
21
|
export function runCommentMarker(workflowId) {
|
|
16
22
|
const safe = workflowId.replace(/[^A-Za-z0-9._-]+/g, "_").replace(/-{2,}/g, "-") || "workflow";
|
|
@@ -33,9 +39,9 @@ export function formatRunComment(workflow, results, summary, opts = {}) {
|
|
|
33
39
|
runCommentMarker(workflow.id),
|
|
34
40
|
`## ${summary.ok ? "✅" : "❌"} ${cell(workflow.name)}`,
|
|
35
41
|
"",
|
|
36
|
-
|
|
42
|
+
ticketBlock(workflow, summary, opts.runId),
|
|
37
43
|
"",
|
|
38
|
-
toMermaidBlock(workflow, { state, trace: opts.trace
|
|
44
|
+
toMermaidBlock(workflow, { state, trace: opts.trace }),
|
|
39
45
|
"",
|
|
40
46
|
"<details>",
|
|
41
47
|
"<summary>Nodes</summary>",
|
|
@@ -55,12 +61,12 @@ export function formatRunComment(workflow, results, summary, opts = {}) {
|
|
|
55
61
|
* and no error text (thrown messages can embed model prose). Replaces any stale
|
|
56
62
|
* success comment for the same workflow.
|
|
57
63
|
*/
|
|
58
|
-
export function formatCrashComment(workflow, summary) {
|
|
64
|
+
export function formatCrashComment(workflow, summary, runId) {
|
|
59
65
|
return [
|
|
60
66
|
runCommentMarker(workflow.id),
|
|
61
67
|
`## ❌ ${cell(workflow.name)}`,
|
|
62
68
|
"",
|
|
63
|
-
|
|
69
|
+
ticketBlock(workflow, summary, runId, true),
|
|
64
70
|
"",
|
|
65
71
|
"The run stopped before finishing. See the job log for details.",
|
|
66
72
|
"",
|
|
@@ -72,11 +78,13 @@ export function formatCrashComment(workflow, summary) {
|
|
|
72
78
|
export function writeRunComment(file, workflow, results, summary, opts = {}) {
|
|
73
79
|
try {
|
|
74
80
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
75
|
-
fs.writeFileSync(file, opts.crashed
|
|
81
|
+
fs.writeFileSync(file, opts.crashed
|
|
82
|
+
? formatCrashComment(workflow, summary, opts.runId)
|
|
83
|
+
: formatRunComment(workflow, results, summary, opts));
|
|
76
84
|
return true;
|
|
77
85
|
}
|
|
78
86
|
catch (err) {
|
|
79
|
-
process.stderr.write(`
|
|
87
|
+
process.stderr.write(` ${glyphs().warning} could not write comment file: ${err instanceof Error ? err.message : err}\n`);
|
|
80
88
|
return false;
|
|
81
89
|
}
|
|
82
90
|
}
|
package/dist/cli/config.js
CHANGED
|
@@ -149,7 +149,7 @@ export function parseCliInputs(options, fileConfig = {}) {
|
|
|
149
149
|
const fromEnv = env.SWENY_CLOUD_TOKEN || "";
|
|
150
150
|
const fromFile = f("cloud-token") || "";
|
|
151
151
|
if (!fromEnv && fromFile) {
|
|
152
|
-
console.warn("\x1b[33m⚠ cloud-token is set in .sweny.yml
|
|
152
|
+
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");
|
|
153
153
|
}
|
|
154
154
|
return fromEnv || fromFile;
|
|
155
155
|
})(),
|
|
@@ -229,7 +229,7 @@ export function validateInputs(config) {
|
|
|
229
229
|
switch (config.codingAgentProvider) {
|
|
230
230
|
case "claude":
|
|
231
231
|
if (!config.anthropicApiKey && !config.claudeOauthToken) {
|
|
232
|
-
errors.push("Missing: ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN
|
|
232
|
+
errors.push("Missing: ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN - get a key at https://console.anthropic.com");
|
|
233
233
|
}
|
|
234
234
|
break;
|
|
235
235
|
case "codex":
|
|
@@ -262,13 +262,13 @@ export function validateInputs(config) {
|
|
|
262
262
|
switch (provider) {
|
|
263
263
|
case "datadog":
|
|
264
264
|
if (!creds.apiKey)
|
|
265
|
-
errors.push("Missing: DD_API_KEY
|
|
265
|
+
errors.push("Missing: DD_API_KEY - find API keys at https://app.datadoghq.com/organization-settings/api-keys");
|
|
266
266
|
if (!creds.appKey)
|
|
267
|
-
errors.push("Missing: DD_APP_KEY
|
|
267
|
+
errors.push("Missing: DD_APP_KEY - find application keys at https://app.datadoghq.com/organization-settings/application-keys");
|
|
268
268
|
break;
|
|
269
269
|
case "sentry":
|
|
270
270
|
if (!creds.authToken)
|
|
271
|
-
errors.push("Missing: SENTRY_AUTH_TOKEN
|
|
271
|
+
errors.push("Missing: SENTRY_AUTH_TOKEN - create a token at https://sentry.io/settings/auth-tokens/");
|
|
272
272
|
if (!creds.organization)
|
|
273
273
|
errors.push("Missing: --sentry-org is required for sentry provider");
|
|
274
274
|
if (!creds.project)
|
|
@@ -342,33 +342,33 @@ export function validateInputs(config) {
|
|
|
342
342
|
break;
|
|
343
343
|
case "vercel":
|
|
344
344
|
if (!creds.token)
|
|
345
|
-
errors.push("Missing: VERCEL_TOKEN
|
|
345
|
+
errors.push("Missing: VERCEL_TOKEN - create a token at https://vercel.com/account/tokens");
|
|
346
346
|
if (!creds.projectId)
|
|
347
|
-
errors.push("Missing: VERCEL_PROJECT_ID
|
|
347
|
+
errors.push("Missing: VERCEL_PROJECT_ID - find it in your Vercel project settings");
|
|
348
348
|
break;
|
|
349
349
|
case "supabase":
|
|
350
350
|
if (!creds.managementApiKey)
|
|
351
|
-
errors.push("Missing: SUPABASE_MANAGEMENT_KEY
|
|
351
|
+
errors.push("Missing: SUPABASE_MANAGEMENT_KEY - create a token at https://supabase.com/dashboard/account/tokens");
|
|
352
352
|
if (!creds.projectRef)
|
|
353
|
-
errors.push("Missing: SUPABASE_PROJECT_REF
|
|
353
|
+
errors.push("Missing: SUPABASE_PROJECT_REF - find it in your Supabase project settings > General");
|
|
354
354
|
break;
|
|
355
355
|
case "netlify":
|
|
356
356
|
if (!creds.token)
|
|
357
|
-
errors.push("Missing: NETLIFY_TOKEN
|
|
357
|
+
errors.push("Missing: NETLIFY_TOKEN - create a token at https://app.netlify.com/user/applications#personal-access-tokens");
|
|
358
358
|
if (!creds.siteId)
|
|
359
|
-
errors.push("Missing: NETLIFY_SITE_ID
|
|
359
|
+
errors.push("Missing: NETLIFY_SITE_ID - find it in Site Settings > General > Site details");
|
|
360
360
|
break;
|
|
361
361
|
case "fly":
|
|
362
362
|
if (!creds.token)
|
|
363
|
-
errors.push("Missing: FLY_TOKEN
|
|
363
|
+
errors.push("Missing: FLY_TOKEN - create a token at https://fly.io/user/personal_access_tokens");
|
|
364
364
|
if (!creds.appName)
|
|
365
|
-
errors.push("Missing: FLY_APP_NAME
|
|
365
|
+
errors.push("Missing: FLY_APP_NAME - the name of your Fly.io application");
|
|
366
366
|
break;
|
|
367
367
|
case "render":
|
|
368
368
|
if (!creds.apiKey)
|
|
369
|
-
errors.push("Missing: RENDER_API_KEY
|
|
369
|
+
errors.push("Missing: RENDER_API_KEY - create an API key at https://dashboard.render.com/u/settings");
|
|
370
370
|
if (!creds.serviceId)
|
|
371
|
-
errors.push("Missing: RENDER_SERVICE_ID
|
|
371
|
+
errors.push("Missing: RENDER_SERVICE_ID - find it in your service's Settings page (srv-...)");
|
|
372
372
|
break;
|
|
373
373
|
default:
|
|
374
374
|
errors.push(`Unknown --observability-provider "${provider}". Valid values: none, datadog, sentry, cloudwatch, splunk, elastic, newrelic, loki, prometheus, pagerduty, honeycomb, heroku, opsgenie, axiom, betterstack, vercel, supabase, netlify, fly, render, file`);
|
|
@@ -378,17 +378,17 @@ export function validateInputs(config) {
|
|
|
378
378
|
switch (config.issueTrackerProvider) {
|
|
379
379
|
case "linear":
|
|
380
380
|
if (!config.linearApiKey)
|
|
381
|
-
errors.push("Missing: LINEAR_API_KEY
|
|
381
|
+
errors.push("Missing: LINEAR_API_KEY - find API keys at https://linear.app/settings/api");
|
|
382
382
|
if (!config.linearTeamId)
|
|
383
|
-
errors.push("Missing: LINEAR_TEAM_ID
|
|
383
|
+
errors.push("Missing: LINEAR_TEAM_ID - find it in Linear > Settings > Workspace > Teams > [your team] > copy the ID from the URL");
|
|
384
384
|
break;
|
|
385
385
|
case "jira":
|
|
386
386
|
if (!config.jiraBaseUrl)
|
|
387
|
-
errors.push("Missing: JIRA_BASE_URL
|
|
387
|
+
errors.push("Missing: JIRA_BASE_URL - set to your Atlassian domain, e.g. https://your-org.atlassian.net");
|
|
388
388
|
if (!config.jiraEmail)
|
|
389
|
-
errors.push("Missing: JIRA_EMAIL
|
|
389
|
+
errors.push("Missing: JIRA_EMAIL - your Atlassian account email address");
|
|
390
390
|
if (!config.jiraApiToken)
|
|
391
|
-
errors.push("Missing: JIRA_API_TOKEN
|
|
391
|
+
errors.push("Missing: JIRA_API_TOKEN - create a token at https://id.atlassian.com/manage-profile/security/api-tokens");
|
|
392
392
|
break;
|
|
393
393
|
case "github-issues":
|
|
394
394
|
case "file":
|
|
@@ -401,13 +401,13 @@ export function validateInputs(config) {
|
|
|
401
401
|
switch (config.sourceControlProvider) {
|
|
402
402
|
case "github":
|
|
403
403
|
if (!config.githubToken && !config.botToken)
|
|
404
|
-
errors.push("Missing: GITHUB_TOKEN
|
|
404
|
+
errors.push("Missing: GITHUB_TOKEN - create a Personal Access Token at https://github.com/settings/tokens");
|
|
405
405
|
break;
|
|
406
406
|
case "gitlab":
|
|
407
407
|
if (!config.gitlabToken)
|
|
408
|
-
errors.push("Missing: GITLAB_TOKEN
|
|
408
|
+
errors.push("Missing: GITLAB_TOKEN - create a Personal Access Token at https://gitlab.com/-/user_settings/personal_access_tokens (api + read_repository + write_repository scopes)");
|
|
409
409
|
if (!config.gitlabProjectId)
|
|
410
|
-
errors.push("Missing: GITLAB_PROJECT_ID
|
|
410
|
+
errors.push("Missing: GITLAB_PROJECT_ID - find it in GitLab > [your project] > Settings > General (numeric ID at the top)");
|
|
411
411
|
break;
|
|
412
412
|
case "file":
|
|
413
413
|
// No external credentials needed
|
|
@@ -670,7 +670,7 @@ export function registerImplementCommand(program) {
|
|
|
670
670
|
.option("--coding-agent-provider <provider>", "Coding agent provider (alias for --agent)")
|
|
671
671
|
.option("--issue-tracker-provider <provider>", "Issue tracker (linear|jira|github-issues|file)")
|
|
672
672
|
.option("--source-control-provider <provider>", "Source control (github|gitlab|file)")
|
|
673
|
-
.option("--dry-run", "Skip creating PR
|
|
673
|
+
.option("--dry-run", "Skip creating PR - report only", false)
|
|
674
674
|
.option("--stage", "Run normally, but preview the PR and issue comment instead of writing them", false)
|
|
675
675
|
.option("--max-implement-turns <n>", "Max coding agent turns (default: 40)")
|
|
676
676
|
.option("--base-branch <branch>", "Base branch for PRs (default: main)")
|
package/dist/cli/e2e.js
CHANGED
|
@@ -87,17 +87,17 @@ This returns element references like @e1, @e2, @e3.
|
|
|
87
87
|
Use those refs with: agent-browser click @e5, agent-browser fill @e7 "text", etc.
|
|
88
88
|
|
|
89
89
|
Available commands:
|
|
90
|
-
- agent-browser open <url>
|
|
91
|
-
- agent-browser snapshot
|
|
92
|
-
- agent-browser click <ref>
|
|
93
|
-
- agent-browser fill <ref> <text>
|
|
94
|
-
- agent-browser press <key>
|
|
95
|
-
- agent-browser get url
|
|
96
|
-
- agent-browser get text <ref>
|
|
97
|
-
- agent-browser screenshot <path>
|
|
98
|
-
- agent-browser scroll <direction> <pixels>
|
|
99
|
-
- agent-browser scrollintoview <ref>
|
|
100
|
-
- agent-browser select <ref> <value>
|
|
90
|
+
- agent-browser open <url> - navigate to URL
|
|
91
|
+
- agent-browser snapshot - get accessibility tree with @refs
|
|
92
|
+
- agent-browser click <ref> - click an element
|
|
93
|
+
- agent-browser fill <ref> <text> - clear input and fill with text
|
|
94
|
+
- agent-browser press <key> - press keyboard key (Enter, Tab, Escape)
|
|
95
|
+
- agent-browser get url - get current page URL
|
|
96
|
+
- agent-browser get text <ref> - get element text content
|
|
97
|
+
- agent-browser screenshot <path> - save screenshot to file
|
|
98
|
+
- agent-browser scroll <direction> <pixels> - scroll page
|
|
99
|
+
- agent-browser scrollintoview <ref> - scroll element into view
|
|
100
|
+
- agent-browser select <ref> <value> - select dropdown option`,
|
|
101
101
|
skills: [],
|
|
102
102
|
output: {
|
|
103
103
|
type: "object",
|
|
@@ -142,7 +142,7 @@ Use curl to call the Supabase Admin API:
|
|
|
142
142
|
2. Filter for emails starting with "e2e-" and ending with "@yourapp.test"
|
|
143
143
|
3. Delete each matching user: DELETE {SUPABASE_URL}/auth/v1/admin/users/{user_id}
|
|
144
144
|
|
|
145
|
-
If no test users are found, that's fine
|
|
145
|
+
If no test users are found, that's fine - skip gracefully.
|
|
146
146
|
If the service role key is not available, skip cleanup gracefully.`;
|
|
147
147
|
break;
|
|
148
148
|
case "firebase":
|
|
@@ -501,7 +501,7 @@ const CLEANUP_ENV_VARS = {
|
|
|
501
501
|
*/
|
|
502
502
|
export function buildE2eEnvTemplate(selections) {
|
|
503
503
|
const lines = [];
|
|
504
|
-
lines.push("# E2E Testing
|
|
504
|
+
lines.push("# E2E Testing - SWEny");
|
|
505
505
|
lines.push("# Fill in values, then run: sweny workflow run");
|
|
506
506
|
lines.push("");
|
|
507
507
|
lines.push(`E2E_BASE_URL=${selections.baseUrl}`);
|
|
@@ -640,7 +640,7 @@ export async function runE2eInit(options = {}) {
|
|
|
640
640
|
if (p.isCancel(overwrite))
|
|
641
641
|
cancel();
|
|
642
642
|
if (!overwrite) {
|
|
643
|
-
p.cancel("Setup cancelled
|
|
643
|
+
p.cancel("Setup cancelled - existing files preserved.");
|
|
644
644
|
process.exit(0);
|
|
645
645
|
}
|
|
646
646
|
}
|
|
@@ -741,7 +741,7 @@ export async function runE2eInit(options = {}) {
|
|
|
741
741
|
p.log.success(`Appended E2E vars to ${chalk.cyan(".env")}`);
|
|
742
742
|
}
|
|
743
743
|
else {
|
|
744
|
-
p.log.warn(".env already has E2E vars
|
|
744
|
+
p.log.warn(".env already has E2E vars - skipped");
|
|
745
745
|
}
|
|
746
746
|
}
|
|
747
747
|
else {
|
|
@@ -930,10 +930,10 @@ export async function runE2eRun(options) {
|
|
|
930
930
|
const elapsed = ((Date.now() - enterTime) / 1000).toFixed(1);
|
|
931
931
|
const status = event.result.data?.status || event.result.status;
|
|
932
932
|
if (isTTY) {
|
|
933
|
-
process.stderr.write(`\x1B[1A\x1B[2K ${icon} ${event.node}
|
|
933
|
+
process.stderr.write(`\x1B[1A\x1B[2K ${icon} ${event.node} - ${status} (${elapsed}s)\n`);
|
|
934
934
|
}
|
|
935
935
|
else {
|
|
936
|
-
process.stderr.write(` ${icon} ${event.node}
|
|
936
|
+
process.stderr.write(` ${icon} ${event.node} - ${status} (${elapsed}s)\n`);
|
|
937
937
|
}
|
|
938
938
|
break;
|
|
939
939
|
}
|