svamp-cli 0.2.247 → 0.2.248
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/{agentCommands-DOzMhE_G.mjs → agentCommands-B_ozIVKT.mjs} +28 -11
- package/dist/{auth-B_ZnoKX0.mjs → auth-BLDmT7XN.mjs} +2 -1
- package/dist/cli.mjs +62 -61
- package/dist/{commands-vCeWXvYJ.mjs → commands-C3TQOYUo.mjs} +2 -1
- package/dist/{commands-Bm5q9G35.mjs → commands-CL8YI8k5.mjs} +2 -1
- package/dist/{commands-_rMwFmFH.mjs → commands-CRMQUQZG.mjs} +2 -1
- package/dist/{commands-F6j9TF4Q.mjs → commands-Cs2fNd-_.mjs} +7 -7
- package/dist/{commands-CxOPcoWa.mjs → commands-DS4i8ohA.mjs} +10 -6
- package/dist/{commands-BENx03Oz.mjs → commands-Djvi1cZ2.mjs} +3 -2
- package/dist/{commands-C2u9U3y6.mjs → commands-DwhA-iV3.mjs} +3 -2
- package/dist/{fleet-pMXCRilx.mjs → fleet-CJZOVjOY.mjs} +2 -1
- package/dist/{frpc-Di-y4cWK.mjs → frpc-CsjEXBuS.mjs} +2 -1
- package/dist/{headlessCli-D6k9Pnum.mjs → headlessCli-qBontdlf.mjs} +3 -2
- package/dist/index.mjs +2 -1
- package/dist/{package-BJYmQjS2.mjs → package-B7z6GsNX.mjs} +2 -2
- package/dist/{rpc-DbY7NUhj.mjs → rpc-BGFgegj5.mjs} +1 -3
- package/dist/{rpc-BncvIQMO.mjs → rpc-C1DI7HfZ.mjs} +2 -1
- package/dist/{run-CYx_Rhiu.mjs → run-C61rsl7r.mjs} +2 -1
- package/dist/{run-1BBi0_ol.mjs → run-CHIppB2r.mjs} +655 -35
- package/dist/{scheduler-BkXOKjHn.mjs → scheduler-Csp21zzg.mjs} +1 -3
- package/dist/{serveCommands-CESRU6-1.mjs → serveCommands-BYHk72ZO.mjs} +5 -5
- package/dist/{serveManager-BZtpClpC.mjs → serveManager-Di3xrvG2.mjs} +3 -2
- package/dist/{sideband-BqD4-WWR.mjs → sideband-Cs-kNLye.mjs} +2 -1
- package/package.json +2 -2
- package/dist/runStore-C5IyfbON.mjs +0 -239
- package/dist/store-BTs0H_y0.mjs +0 -148
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { l as listWorkflows, i as isWorkflowEnabled, w as workflowCrons } from './store-BTs0H_y0.mjs';
|
|
3
|
-
import { r as runWorkflow } from './runStore-C5IyfbON.mjs';
|
|
1
|
+
import { n as resolveProjectRoot, H as listWorkflows, I as isWorkflowEnabled, J as workflowCrons, C as runWorkflow, K as cronMatches } from './run-CHIppB2r.mjs';
|
|
4
2
|
import 'os';
|
|
5
3
|
import 'fs/promises';
|
|
6
4
|
import 'fs';
|
|
@@ -54,7 +54,7 @@ async function handleServeCommand() {
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
async function serveAdd(args, machineId) {
|
|
57
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
57
|
+
const { connectAndGetMachine } = await import('./commands-C3TQOYUo.mjs');
|
|
58
58
|
const pos = positionalArgs(args);
|
|
59
59
|
const name = pos[0];
|
|
60
60
|
if (!name) {
|
|
@@ -93,7 +93,7 @@ async function serveAdd(args, machineId) {
|
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
95
|
async function serveApply(args, machineId) {
|
|
96
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
96
|
+
const { connectAndGetMachine } = await import('./commands-C3TQOYUo.mjs');
|
|
97
97
|
const fs = await import('fs');
|
|
98
98
|
const yaml = await import('yaml');
|
|
99
99
|
const file = positionalArgs(args)[0];
|
|
@@ -182,7 +182,7 @@ async function serveApply(args, machineId) {
|
|
|
182
182
|
}
|
|
183
183
|
}
|
|
184
184
|
async function serveRemove(args, machineId) {
|
|
185
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
185
|
+
const { connectAndGetMachine } = await import('./commands-C3TQOYUo.mjs');
|
|
186
186
|
const pos = positionalArgs(args);
|
|
187
187
|
const name = pos[0];
|
|
188
188
|
if (!name) {
|
|
@@ -202,7 +202,7 @@ async function serveRemove(args, machineId) {
|
|
|
202
202
|
}
|
|
203
203
|
}
|
|
204
204
|
async function serveList(args, machineId) {
|
|
205
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
205
|
+
const { connectAndGetMachine } = await import('./commands-C3TQOYUo.mjs');
|
|
206
206
|
const all = hasFlag(args, "--all", "-a");
|
|
207
207
|
const json = hasFlag(args, "--json");
|
|
208
208
|
const sessionId = getFlag(args, "--session");
|
|
@@ -235,7 +235,7 @@ async function serveList(args, machineId) {
|
|
|
235
235
|
}
|
|
236
236
|
}
|
|
237
237
|
async function serveInfo(machineId) {
|
|
238
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
238
|
+
const { connectAndGetMachine } = await import('./commands-C3TQOYUo.mjs');
|
|
239
239
|
const { machine, server } = await connectAndGetMachine(machineId);
|
|
240
240
|
try {
|
|
241
241
|
const info = await machine.serveInfo();
|
|
@@ -4,7 +4,7 @@ import * as fs from 'fs';
|
|
|
4
4
|
import * as http from 'http';
|
|
5
5
|
import * as net from 'net';
|
|
6
6
|
import * as path from 'path';
|
|
7
|
-
import {
|
|
7
|
+
import { l as getHyphaServerUrl, S as ServeAuth, m as hasCookieToken } from './run-CHIppB2r.mjs';
|
|
8
8
|
import 'os';
|
|
9
9
|
import 'fs/promises';
|
|
10
10
|
import 'url';
|
|
@@ -22,6 +22,7 @@ import '@modelcontextprotocol/sdk/types.js';
|
|
|
22
22
|
import 'zod';
|
|
23
23
|
import 'node:fs/promises';
|
|
24
24
|
import 'node:util';
|
|
25
|
+
import 'yaml';
|
|
25
26
|
|
|
26
27
|
function generateLinkToken() {
|
|
27
28
|
return crypto.randomBytes(11).toString("hex");
|
|
@@ -750,7 +751,7 @@ class ServeManager {
|
|
|
750
751
|
const mount = this.mounts.get(mountName);
|
|
751
752
|
const subdomainOverride = mount?.access === "link" && mount.linkToken ? /* @__PURE__ */ new Map([[this.port, buildLinkSubdomain(subdomainSafe, mount.linkToken)]]) : void 0;
|
|
752
753
|
try {
|
|
753
|
-
const { FrpcTunnel } = await import('./frpc-
|
|
754
|
+
const { FrpcTunnel } = await import('./frpc-CsjEXBuS.mjs');
|
|
754
755
|
let tunnel;
|
|
755
756
|
tunnel = new FrpcTunnel({
|
|
756
757
|
name: tunnelName,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { R as READ_ONLY_TOOLS,
|
|
1
|
+
import { R as READ_ONLY_TOOLS, N as loadMachineContext, O as buildMachineInstructions, P as machineToolsForRole, Q as buildMachineTools } from './run-CHIppB2r.mjs';
|
|
2
2
|
import 'node:child_process';
|
|
3
3
|
import 'os';
|
|
4
4
|
import 'fs/promises';
|
|
@@ -20,6 +20,7 @@ import '@modelcontextprotocol/sdk/types.js';
|
|
|
20
20
|
import 'zod';
|
|
21
21
|
import 'node:fs/promises';
|
|
22
22
|
import 'node:util';
|
|
23
|
+
import 'yaml';
|
|
23
24
|
|
|
24
25
|
function toolsForRole(role) {
|
|
25
26
|
if (role === "admin") return [...READ_ONLY_TOOLS, "run_bash", "send_to_session"];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svamp-cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.248",
|
|
4
4
|
"description": "Svamp CLI — AI workspace daemon on Hypha Cloud",
|
|
5
5
|
"author": "Amun AI AB",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"scripts": {
|
|
21
21
|
"build": "rm -rf dist bin/skills bin/commands && mkdir -p bin/skills && cp -r ../../skills/artifact bin/skills/artifact && cp -r ../../skills/crew bin/skills/crew && cp -r ../../commands bin/commands && tsc --noEmit && pkgroll",
|
|
22
22
|
"typecheck": "tsc --noEmit",
|
|
23
|
-
"test": "npx tsx test/test-context-window.mjs && npx tsx test/test-ratelimit-retry.mjs && npx tsx test/test-instance-config.mjs && npx tsx test/test-authorize.mjs && npx tsx test/test-normalize-allowed-user.mjs && npx tsx test/test-share-url.mjs && npx tsx test/test-update-sharing-normalization.mjs && npx tsx test/test-sharing-notify-sync.mjs && npx tsx test/test-staged-homes-sweep.mjs && npx tsx test/test-session-helpers.mjs && npx tsx test/test-cli-routing.mjs && npx tsx test/test-security-context.mjs && npx tsx test/test-isolation-decision.mjs && npx tsx test/test-message-helpers.mjs && npx tsx test/test-agent-config.mjs && npx tsx test/test-wrap-command.mjs && npx tsx test/test-credential-staging.mjs && npx tsx test/test-claude-auth.mjs && npx tsx test/test-btw-proxy-env.mjs && npx tsx test/test-output-formatters.mjs && npx tsx test/test-inbox-guard.mjs && npx tsx test/test-auto-topic.mjs && npx tsx test/test-project-info.mjs && npx tsx test/test-agent-types.mjs && npx tsx test/test-transport.mjs && npx tsx test/test-session-update-handlers.mjs && npx tsx test/test-session-scanner.mjs && npx tsx test/test-hypha-client.mjs && npx tsx test/test-hook-settings.mjs && npx tsx test/test-session-service-logic.mjs && npx tsx test/test-daemon-persistence.mjs && npx tsx test/test-detect-isolation.mjs && npx tsx test/test-machine-service-logic.mjs && npx tsx test/test-interactive-helpers.mjs && npx tsx test/test-codex-backend.mjs && npx tsx test/test-acp-backend.mjs && npx tsx test/test-acp-bridge.mjs && npx tsx test/test-hook-server.mjs && npx tsx test/test-session-commands.mjs && npx tsx test/test-interactive-console.mjs && npx tsx test/test-session-messages.mjs && npx tsx test/test-session-send-query.mjs && npx tsx test/test-skills.mjs && npx tsx test/test-agent-grouping.mjs && npx tsx test/test-machine-list-directory.mjs && npx tsx test/test-service-commands.mjs && npx tsx test/test-supervisor.mjs && npx tsx test/test-supervisor-lock.mjs && node test/test-supervisor-restart.mjs && npx tsx test/test-clear-detection.mjs && npx tsx test/test-session-consolidation.mjs && npx tsx test/test-inbox-store.mjs && npx tsx test/test-inbox.mjs && npx tsx test/test-inbox-cross-machine.mjs && npx tsx test/test-issue-store.mjs && npx tsx test/test-issue-close-gate.mjs && npx tsx test/test-loop-verify.mjs && npx tsx test/test-issue-rpc.mjs && npx tsx test/test-issue-pause.mjs && npx tsx test/test-workflow-store.mjs && npx tsx test/test-workflow-rpc.mjs && npx tsx test/test-workflow-scheduler.mjs && npx tsx test/test-cron.mjs && npx tsx test/test-workflow-runs.mjs && npx tsx test/test-serve-link-subdomain.mjs && npx tsx test/test-short-id.mjs && npx tsx test/test-transcript-edit.mjs && npx tsx test/test-edit-history.mjs && npx tsx test/test-friendly-name.mjs && npx tsx test/test-session-rpc-dispatch.mjs && npx tsx test/test-sandbox-cli.mjs && npx tsx test/test-serve-manager.mjs && npx tsx test/test-serve-stability.mjs && npx tsx test/test-frpc-e2e.mjs --unit-only && npx tsx test/test-frpc-status.mjs && node test/pinnedClaudeCode.test.mjs && node test/fleet.test.mjs && npx tsx test/test-session-file.mjs && npx tsx test/test-channel-rpc.mjs && npx tsx test/test-wise-agent.mjs && npx tsx test/test-channel-agent.mjs && npx tsx test/test-channels-service.mjs && npx tsx test/test-hotreload-seam.mjs && npx tsx test/test-hot-reload.mjs && npx tsx test/test-hot-reload-live.mjs && npx tsx test/test-session-core.mjs && npx tsx test/test-channel-async-reply.mjs && npx tsx test/test-channel-binding.mjs && npx tsx test/test-channel-identity.mjs && npx tsx test/test-shared-session-identity.mjs && npx tsx test/test-wise-agent-auth.mjs && npx tsx test/test-channel-http.mjs && npx tsx test/test-channel-upload.mjs && npx tsx test/test-wise-voice.mjs && npx tsx test/test-wise-headless.mjs && npx tsx test/test-wise-machine.mjs && npx tsx test/test-crew-merge.mjs && npx tsx test/test-crew-verdict-routing.mjs && npx tsx test/test-crew-standalone.mjs && npx tsx test/test-session-links.mjs && npx tsx test/test-issue-loop-pause-guard.mjs && npx tsx test/test-artifact-guard.mjs && npx tsx test/test-graceful-restart.mjs",
|
|
23
|
+
"test": "npx tsx test/test-context-window.mjs && npx tsx test/test-ratelimit-retry.mjs && npx tsx test/test-instance-config.mjs && npx tsx test/test-authorize.mjs && npx tsx test/test-normalize-allowed-user.mjs && npx tsx test/test-share-url.mjs && npx tsx test/test-update-sharing-normalization.mjs && npx tsx test/test-sharing-notify-sync.mjs && npx tsx test/test-staged-homes-sweep.mjs && npx tsx test/test-session-helpers.mjs && npx tsx test/test-cli-routing.mjs && npx tsx test/test-security-context.mjs && npx tsx test/test-isolation-decision.mjs && npx tsx test/test-message-helpers.mjs && npx tsx test/test-agent-config.mjs && npx tsx test/test-wrap-command.mjs && npx tsx test/test-credential-staging.mjs && npx tsx test/test-claude-auth.mjs && npx tsx test/test-btw-proxy-env.mjs && npx tsx test/test-output-formatters.mjs && npx tsx test/test-inbox-guard.mjs && npx tsx test/test-auto-topic.mjs && npx tsx test/test-project-info.mjs && npx tsx test/test-agent-types.mjs && npx tsx test/test-transport.mjs && npx tsx test/test-session-update-handlers.mjs && npx tsx test/test-session-scanner.mjs && npx tsx test/test-hypha-client.mjs && npx tsx test/test-hook-settings.mjs && npx tsx test/test-session-service-logic.mjs && npx tsx test/test-daemon-persistence.mjs && npx tsx test/test-detect-isolation.mjs && npx tsx test/test-machine-service-logic.mjs && npx tsx test/test-interactive-helpers.mjs && npx tsx test/test-codex-backend.mjs && npx tsx test/test-acp-backend.mjs && npx tsx test/test-acp-bridge.mjs && npx tsx test/test-hook-server.mjs && npx tsx test/test-session-commands.mjs && npx tsx test/test-interactive-console.mjs && npx tsx test/test-session-messages.mjs && npx tsx test/test-session-send-query.mjs && npx tsx test/test-skills.mjs && npx tsx test/test-agent-grouping.mjs && npx tsx test/test-machine-list-directory.mjs && npx tsx test/test-service-commands.mjs && npx tsx test/test-supervisor.mjs && npx tsx test/test-supervisor-lock.mjs && node test/test-supervisor-restart.mjs && npx tsx test/test-clear-detection.mjs && npx tsx test/test-session-consolidation.mjs && npx tsx test/test-inbox-store.mjs && npx tsx test/test-inbox.mjs && npx tsx test/test-inbox-cross-machine.mjs && npx tsx test/test-issue-store.mjs && npx tsx test/test-issue-close-gate.mjs && npx tsx test/test-loop-verify.mjs && npx tsx test/test-issue-rpc.mjs && npx tsx test/test-issue-pause.mjs && npx tsx test/test-workflow-store.mjs && npx tsx test/test-workflow-rpc.mjs && npx tsx test/test-workflow-scheduler.mjs && npx tsx test/test-cron.mjs && npx tsx test/test-workflow-runs.mjs && npx tsx test/test-workflow-idle.mjs && npx tsx test/test-serve-link-subdomain.mjs && npx tsx test/test-short-id.mjs && npx tsx test/test-transcript-edit.mjs && npx tsx test/test-edit-history.mjs && npx tsx test/test-friendly-name.mjs && npx tsx test/test-session-rpc-dispatch.mjs && npx tsx test/test-sandbox-cli.mjs && npx tsx test/test-serve-manager.mjs && npx tsx test/test-serve-stability.mjs && npx tsx test/test-frpc-e2e.mjs --unit-only && npx tsx test/test-frpc-status.mjs && node test/pinnedClaudeCode.test.mjs && node test/fleet.test.mjs && npx tsx test/test-session-file.mjs && npx tsx test/test-channel-rpc.mjs && npx tsx test/test-wise-agent.mjs && npx tsx test/test-channel-agent.mjs && npx tsx test/test-channels-service.mjs && npx tsx test/test-hotreload-seam.mjs && npx tsx test/test-hot-reload.mjs && npx tsx test/test-hot-reload-live.mjs && npx tsx test/test-session-core.mjs && npx tsx test/test-channel-async-reply.mjs && npx tsx test/test-channel-binding.mjs && npx tsx test/test-channel-identity.mjs && npx tsx test/test-shared-session-identity.mjs && npx tsx test/test-wise-agent-auth.mjs && npx tsx test/test-channel-http.mjs && npx tsx test/test-channel-upload.mjs && npx tsx test/test-wise-voice.mjs && npx tsx test/test-wise-headless.mjs && npx tsx test/test-wise-machine.mjs && npx tsx test/test-crew-merge.mjs && npx tsx test/test-crew-verdict-routing.mjs && npx tsx test/test-crew-standalone.mjs && npx tsx test/test-session-links.mjs && npx tsx test/test-issue-loop-pause-guard.mjs && npx tsx test/test-artifact-guard.mjs && npx tsx test/test-graceful-restart.mjs",
|
|
24
24
|
"test:hypha": "node --no-warnings test/test-hypha-service.mjs",
|
|
25
25
|
"dev": "tsx src/cli.ts",
|
|
26
26
|
"dev:daemon": "tsx src/cli.ts daemon start-sync",
|
|
@@ -1,239 +0,0 @@
|
|
|
1
|
-
import { spawn } from 'node:child_process';
|
|
2
|
-
import { existsSync, readFileSync, mkdirSync, writeFileSync, renameSync } from 'node:fs';
|
|
3
|
-
import { randomBytes } from 'node:crypto';
|
|
4
|
-
import { join } from 'node:path';
|
|
5
|
-
import { c as workflowSteps, d as workflowsDir } from './store-BTs0H_y0.mjs';
|
|
6
|
-
import { v as listIssues, t as addIssue } from './run-1BBi0_ol.mjs';
|
|
7
|
-
|
|
8
|
-
const MAX_RUNS_PER_WORKFLOW = 50;
|
|
9
|
-
function maxRunsPerWorkflow() {
|
|
10
|
-
const raw = Number(process.env.SVAMP_MAX_RUNS_PER_WORKFLOW);
|
|
11
|
-
if (Number.isFinite(raw) && raw > 0) return Math.min(Math.floor(raw), 5e3);
|
|
12
|
-
return MAX_RUNS_PER_WORKFLOW;
|
|
13
|
-
}
|
|
14
|
-
const MAX_STREAM_CHARS = 8e3;
|
|
15
|
-
const STEP_TIMEOUT_MS = 12e4;
|
|
16
|
-
function capStream(s, max = MAX_STREAM_CHARS) {
|
|
17
|
-
if (s.length <= max) return s;
|
|
18
|
-
const head = Math.floor(max * 0.6);
|
|
19
|
-
const tail = max - head;
|
|
20
|
-
const omitted = s.length - max;
|
|
21
|
-
return `${s.slice(0, head)}
|
|
22
|
-
\u2026 [${omitted} chars omitted] \u2026
|
|
23
|
-
${s.slice(s.length - tail)}`;
|
|
24
|
-
}
|
|
25
|
-
function runsDir(projectRoot) {
|
|
26
|
-
return join(workflowsDir(projectRoot), ".runs");
|
|
27
|
-
}
|
|
28
|
-
function runsFile(projectRoot, workflow) {
|
|
29
|
-
const safe = workflow.replace(/[^\w.-]+/g, "_");
|
|
30
|
-
return join(runsDir(projectRoot), `${safe}.jsonl`);
|
|
31
|
-
}
|
|
32
|
-
function shortRunId(seed) {
|
|
33
|
-
let h = 2166136261;
|
|
34
|
-
for (let i = 0; i < seed.length; i++) {
|
|
35
|
-
h ^= seed.charCodeAt(i);
|
|
36
|
-
h = Math.imul(h, 16777619);
|
|
37
|
-
}
|
|
38
|
-
return (h >>> 0).toString(36).padStart(7, "0").slice(0, 7);
|
|
39
|
-
}
|
|
40
|
-
function recordRun(projectRoot, run) {
|
|
41
|
-
try {
|
|
42
|
-
const dir = runsDir(projectRoot);
|
|
43
|
-
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
44
|
-
const file = runsFile(projectRoot, run.workflow);
|
|
45
|
-
const existing = loadRuns(projectRoot, run.workflow);
|
|
46
|
-
const idx = existing.findIndex((r) => r.id === run.id);
|
|
47
|
-
if (idx >= 0) existing[idx] = run;
|
|
48
|
-
else existing.push(run);
|
|
49
|
-
const kept = existing.slice(-maxRunsPerWorkflow());
|
|
50
|
-
const tmp = `${file}.tmp-${process.pid}-${randomBytes(4).toString("hex")}`;
|
|
51
|
-
writeFileSync(tmp, kept.map((r) => JSON.stringify(r)).join("\n") + "\n");
|
|
52
|
-
renameSync(tmp, file);
|
|
53
|
-
} catch {
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
function loadRuns(projectRoot, workflow) {
|
|
57
|
-
try {
|
|
58
|
-
const file = runsFile(projectRoot, workflow);
|
|
59
|
-
if (!existsSync(file)) return [];
|
|
60
|
-
return readFileSync(file, "utf-8").split("\n").filter(Boolean).map((l) => {
|
|
61
|
-
try {
|
|
62
|
-
return JSON.parse(l);
|
|
63
|
-
} catch {
|
|
64
|
-
return null;
|
|
65
|
-
}
|
|
66
|
-
}).filter((r) => !!r);
|
|
67
|
-
} catch {
|
|
68
|
-
return [];
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
function listRuns(projectRoot, workflow, opts = {}) {
|
|
72
|
-
const o = typeof opts === "number" ? { limit: opts } : opts;
|
|
73
|
-
const all = loadRuns(projectRoot, workflow);
|
|
74
|
-
const total = all.length;
|
|
75
|
-
const newestFirst = all.slice().reverse();
|
|
76
|
-
const offset = Math.max(0, Math.floor(o.offset ?? 0));
|
|
77
|
-
const limit = o.limit != null && o.limit > 0 ? Math.floor(o.limit) : total;
|
|
78
|
-
const page = newestFirst.slice(offset, offset + limit).map((r) => {
|
|
79
|
-
const failedStep = r.steps.findIndex((s) => s.exitCode !== 0 && s.exitCode !== null || s.timedOut);
|
|
80
|
-
const { steps, ...rest } = r;
|
|
81
|
-
return { ...rest, stepCount: steps.length, failedStep: failedStep >= 0 ? failedStep : void 0 };
|
|
82
|
-
});
|
|
83
|
-
return { runs: page, total, offset, hasMore: offset + page.length < total };
|
|
84
|
-
}
|
|
85
|
-
function getRun(projectRoot, workflow, runId) {
|
|
86
|
-
return loadRuns(projectRoot, workflow).find((r) => r.id === runId) || null;
|
|
87
|
-
}
|
|
88
|
-
function defaultExecStep(root, cmd, env) {
|
|
89
|
-
return new Promise((resolve) => {
|
|
90
|
-
let stdout = "", stderr = "", timedOut = false, settled = false;
|
|
91
|
-
const finish = (exitCode) => {
|
|
92
|
-
if (settled) return;
|
|
93
|
-
settled = true;
|
|
94
|
-
resolve({ exitCode, stdout, stderr, timedOut });
|
|
95
|
-
};
|
|
96
|
-
try {
|
|
97
|
-
const child = spawn("sh", ["-c", cmd], { cwd: root, env, stdio: ["ignore", "pipe", "pipe"] });
|
|
98
|
-
const timer = setTimeout(() => {
|
|
99
|
-
timedOut = true;
|
|
100
|
-
try {
|
|
101
|
-
child.kill("SIGKILL");
|
|
102
|
-
} catch {
|
|
103
|
-
}
|
|
104
|
-
}, STEP_TIMEOUT_MS);
|
|
105
|
-
child.stdout?.on("data", (d) => {
|
|
106
|
-
stdout += d.toString();
|
|
107
|
-
if (stdout.length > MAX_STREAM_CHARS * 4) stdout = capStream(stdout);
|
|
108
|
-
});
|
|
109
|
-
child.stderr?.on("data", (d) => {
|
|
110
|
-
stderr += d.toString();
|
|
111
|
-
if (stderr.length > MAX_STREAM_CHARS * 4) stderr = capStream(stderr);
|
|
112
|
-
});
|
|
113
|
-
child.on("error", (e) => {
|
|
114
|
-
clearTimeout(timer);
|
|
115
|
-
stderr += `
|
|
116
|
-
[spawn error] ${e.message}`;
|
|
117
|
-
finish(null);
|
|
118
|
-
});
|
|
119
|
-
child.on("close", (code) => {
|
|
120
|
-
clearTimeout(timer);
|
|
121
|
-
finish(code);
|
|
122
|
-
});
|
|
123
|
-
} catch (e) {
|
|
124
|
-
stderr += `[spawn threw] ${e?.message || e}`;
|
|
125
|
-
finish(null);
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
function escalateWorkflowFailure(projectRoot, wf, run) {
|
|
130
|
-
try {
|
|
131
|
-
if (process.env.SVAMP_WORKFLOW_ESCALATE === "0") return void 0;
|
|
132
|
-
const failLabel = `wf-fail:${wf.name}`;
|
|
133
|
-
const open = listIssues(projectRoot, { label: failLabel }).filter((i) => i.status !== "archived");
|
|
134
|
-
if (open.length > 0) return void 0;
|
|
135
|
-
const failed = run.steps.find((s) => s.timedOut || s.exitCode !== 0 && s.exitCode !== null) || run.steps[run.steps.length - 1];
|
|
136
|
-
const failCause = run.error ? `runner error: ${run.error}` : failed?.timedOut ? `step timed out after ${STEP_TIMEOUT_MS / 1e3}s` : `step exited with code ${failed?.exitCode ?? "null (killed/signal)"}`;
|
|
137
|
-
const defLines = workflowSteps(wf).map((s, i) => ` ${i}. ${s.run}`).join("\n") || " (no steps)";
|
|
138
|
-
const stderrTail = failed?.stderr ? capStream(failed.stderr, 3e3) : "(none)";
|
|
139
|
-
const stdoutTail = failed?.stdout ? capStream(failed.stdout, 1500) : "(none)";
|
|
140
|
-
const body = [
|
|
141
|
-
`Workflow **${wf.name}** failed on its last run (${run.trigger} trigger${run.triggerContext ? ` \xB7 ${run.triggerContext}` : ""}). Diagnose the failure and **correct the workflow definition** so future runs succeed \u2014 edit it with \`svamp workflow\` (see \`svamp workflow --help\`), or pause/remove it if it's obsolete.`,
|
|
142
|
-
"",
|
|
143
|
-
"## Failure",
|
|
144
|
-
`- Cause: ${failCause}`,
|
|
145
|
-
`- Failed step: ${failed ? `#${failed.index} \u2014 \`${failed.cmd}\`` : "(unknown)"}`,
|
|
146
|
-
`- Run id: ${run.id} \xB7 finished: ${run.finishedAt || "?"}`,
|
|
147
|
-
"",
|
|
148
|
-
"## Workflow definition",
|
|
149
|
-
"```",
|
|
150
|
-
`name: ${wf.name}`,
|
|
151
|
-
`steps:`,
|
|
152
|
-
defLines,
|
|
153
|
-
"```",
|
|
154
|
-
"",
|
|
155
|
-
"## Failed step stderr (tail)",
|
|
156
|
-
"```",
|
|
157
|
-
stderrTail,
|
|
158
|
-
"```",
|
|
159
|
-
"",
|
|
160
|
-
"## Failed step stdout (tail)",
|
|
161
|
-
"```",
|
|
162
|
-
stdoutTail,
|
|
163
|
-
"```"
|
|
164
|
-
].join("\n");
|
|
165
|
-
const issue = addIssue(projectRoot, {
|
|
166
|
-
title: `Fix failing workflow "${wf.name}" (${failCause})`,
|
|
167
|
-
labels: ["workflow", "automation", "auto-escalated", failLabel],
|
|
168
|
-
verify: { type: "agent", text: `The workflow "${wf.name}" is corrected (or removed/paused) so it no longer fails with: ${failCause}` },
|
|
169
|
-
scope: wf.session ? "session" : "project",
|
|
170
|
-
session: wf.session ?? null,
|
|
171
|
-
triaged: true,
|
|
172
|
-
body
|
|
173
|
-
});
|
|
174
|
-
return issue.id;
|
|
175
|
-
} catch {
|
|
176
|
-
return void 0;
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
async function runWorkflow(projectRoot, wf, opts) {
|
|
180
|
-
const now = opts.now || (() => {
|
|
181
|
-
const d = /* @__PURE__ */ new Date();
|
|
182
|
-
return { iso: d.toISOString(), ms: d.getTime() };
|
|
183
|
-
});
|
|
184
|
-
const exec = opts.execStep || defaultExecStep;
|
|
185
|
-
const start = now();
|
|
186
|
-
const id = shortRunId(`${wf.name}:${start.iso}:${start.ms}`);
|
|
187
|
-
const env = wf.session ? { ...process.env, SVAMP_SESSION_ID: wf.session } : process.env;
|
|
188
|
-
const run = {
|
|
189
|
-
id,
|
|
190
|
-
workflow: wf.name,
|
|
191
|
-
trigger: opts.trigger,
|
|
192
|
-
triggerContext: opts.triggerContext,
|
|
193
|
-
actor: opts.actor ?? wf.session ?? null,
|
|
194
|
-
startedAt: start.iso,
|
|
195
|
-
status: "running",
|
|
196
|
-
steps: []
|
|
197
|
-
};
|
|
198
|
-
recordRun(projectRoot, run);
|
|
199
|
-
const steps = workflowSteps(wf);
|
|
200
|
-
try {
|
|
201
|
-
for (let i = 0; i < steps.length; i++) {
|
|
202
|
-
const s0 = now();
|
|
203
|
-
const r = await exec(projectRoot, steps[i].run, env);
|
|
204
|
-
const s1 = now();
|
|
205
|
-
run.steps.push({
|
|
206
|
-
index: i,
|
|
207
|
-
cmd: steps[i].run,
|
|
208
|
-
exitCode: r.exitCode,
|
|
209
|
-
stdout: capStream(r.stdout),
|
|
210
|
-
stderr: capStream(r.stderr),
|
|
211
|
-
durationMs: s1.ms - s0.ms,
|
|
212
|
-
timedOut: r.timedOut || void 0
|
|
213
|
-
});
|
|
214
|
-
if (r.timedOut || r.exitCode !== 0) {
|
|
215
|
-
run.status = "failed";
|
|
216
|
-
break;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
if (run.status === "running") run.status = "success";
|
|
220
|
-
} catch (e) {
|
|
221
|
-
run.status = "failed";
|
|
222
|
-
run.error = e?.message || String(e);
|
|
223
|
-
}
|
|
224
|
-
const end = now();
|
|
225
|
-
run.finishedAt = end.iso;
|
|
226
|
-
run.durationMs = end.ms - start.ms;
|
|
227
|
-
if (run.status === "failed") {
|
|
228
|
-
const escalated = escalateWorkflowFailure(projectRoot, wf, run);
|
|
229
|
-
if (escalated) {
|
|
230
|
-
run.escalatedIssue = escalated;
|
|
231
|
-
opts.log?.(`[workflow] run ${id} "${wf.name}" failed \u2192 escalated to issue #${escalated}`);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
recordRun(projectRoot, run);
|
|
235
|
-
opts.log?.(`[workflow] run ${id} "${wf.name}" \u2192 ${run.status} (${run.steps.length} step${run.steps.length === 1 ? "" : "s"})`);
|
|
236
|
-
return run;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
export { getRun as g, listRuns as l, runWorkflow as r };
|
package/dist/store-BTs0H_y0.mjs
DELETED
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
import { existsSync, unlinkSync, readFileSync, readdirSync, statSync, mkdirSync, writeFileSync, renameSync } from 'node:fs';
|
|
2
|
-
import { join } from 'node:path';
|
|
3
|
-
import { parse, stringify } from 'yaml';
|
|
4
|
-
|
|
5
|
-
function isWorkflowEnabled(wf) {
|
|
6
|
-
return wf.enabled !== false;
|
|
7
|
-
}
|
|
8
|
-
function workflowSteps(wf) {
|
|
9
|
-
return Object.values(wf.jobs || {}).flatMap((j) => j?.steps || []);
|
|
10
|
-
}
|
|
11
|
-
function workflowCrons(wf) {
|
|
12
|
-
return (wf.on?.schedule || []).map((s) => s?.cron).filter((c) => !!c);
|
|
13
|
-
}
|
|
14
|
-
function workflowsDir(projectRoot) {
|
|
15
|
-
return join(projectRoot, ".svamp", "workflows");
|
|
16
|
-
}
|
|
17
|
-
function workflowPath(projectRoot, name) {
|
|
18
|
-
return join(workflowsDir(projectRoot), `${name}.yaml`);
|
|
19
|
-
}
|
|
20
|
-
function normalizeOn(on) {
|
|
21
|
-
if (!on || typeof on !== "object") {
|
|
22
|
-
return void 0;
|
|
23
|
-
}
|
|
24
|
-
const out = {};
|
|
25
|
-
if (Array.isArray(on.schedule)) {
|
|
26
|
-
const sched = on.schedule.map((s) => ({ cron: String(s?.cron ?? s ?? "") })).filter((s) => s.cron);
|
|
27
|
-
if (sched.length) out.schedule = sched;
|
|
28
|
-
} else if (typeof on.schedule === "string" && on.schedule.trim()) {
|
|
29
|
-
out.schedule = [{ cron: on.schedule.trim() }];
|
|
30
|
-
}
|
|
31
|
-
if ("workflow_dispatch" in on || on.dispatch === true) out.workflow_dispatch = true;
|
|
32
|
-
if (typeof on.channel === "string" && on.channel) out.channel = on.channel;
|
|
33
|
-
if (Array.isArray(on.issue) && on.issue.length) out.issue = on.issue.map(String);
|
|
34
|
-
return Object.keys(out).length ? out : void 0;
|
|
35
|
-
}
|
|
36
|
-
function normalizeJobs(jobs) {
|
|
37
|
-
if (Array.isArray(jobs)) {
|
|
38
|
-
const steps = jobs.map((j) => ({ run: String(j?.run ?? "") })).filter((s) => s.run);
|
|
39
|
-
return steps.length ? { run: { steps } } : {};
|
|
40
|
-
}
|
|
41
|
-
if (jobs && typeof jobs === "object") {
|
|
42
|
-
const out = {};
|
|
43
|
-
for (const [id, job] of Object.entries(jobs)) {
|
|
44
|
-
const rawSteps = Array.isArray(job?.steps) ? job.steps : job?.run ? [{ run: job.run }] : [];
|
|
45
|
-
const steps = rawSteps.map((s) => ({ run: String(s?.run ?? ""), ...s?.name ? { name: String(s.name) } : {} })).filter((s) => s.run);
|
|
46
|
-
if (steps.length) out[String(id)] = { steps };
|
|
47
|
-
}
|
|
48
|
-
return out;
|
|
49
|
-
}
|
|
50
|
-
return {};
|
|
51
|
-
}
|
|
52
|
-
function parseWorkflow(content) {
|
|
53
|
-
try {
|
|
54
|
-
const o = parse(content);
|
|
55
|
-
if (!o || typeof o !== "object" || !o.name) return null;
|
|
56
|
-
return {
|
|
57
|
-
name: String(o.name),
|
|
58
|
-
on: normalizeOn(o.on),
|
|
59
|
-
jobs: normalizeJobs(o.jobs),
|
|
60
|
-
session: o.session ?? null,
|
|
61
|
-
// Only `enabled: false` disables; anything else (absent/true) is active.
|
|
62
|
-
...o.enabled === false ? { enabled: false } : {}
|
|
63
|
-
};
|
|
64
|
-
} catch {
|
|
65
|
-
return null;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
function serializeWorkflow(wf) {
|
|
69
|
-
const clean = { name: wf.name };
|
|
70
|
-
if (wf.session) clean.session = wf.session;
|
|
71
|
-
if (wf.enabled === false) clean.enabled = false;
|
|
72
|
-
const on = {};
|
|
73
|
-
if (wf.on?.schedule?.length) on.schedule = wf.on.schedule.map((s) => ({ cron: s.cron }));
|
|
74
|
-
if (wf.on?.workflow_dispatch) on.workflow_dispatch = {};
|
|
75
|
-
if (wf.on?.channel) on.channel = wf.on.channel;
|
|
76
|
-
if (wf.on?.issue?.length) on.issue = wf.on.issue;
|
|
77
|
-
if (Object.keys(on).length) clean.on = on;
|
|
78
|
-
const jobs = {};
|
|
79
|
-
for (const [id, job] of Object.entries(wf.jobs || {})) {
|
|
80
|
-
jobs[id] = { steps: (job.steps || []).map((s) => s.name ? { name: s.name, run: s.run } : { run: s.run }) };
|
|
81
|
-
}
|
|
82
|
-
clean.jobs = Object.keys(jobs).length ? jobs : { run: { steps: [] } };
|
|
83
|
-
return stringify(clean);
|
|
84
|
-
}
|
|
85
|
-
function listWorkflows(projectRoot) {
|
|
86
|
-
const dir = workflowsDir(projectRoot);
|
|
87
|
-
if (!existsSync(dir)) return [];
|
|
88
|
-
const out = [];
|
|
89
|
-
for (const name of readdirSync(dir)) {
|
|
90
|
-
if (!name.endsWith(".yaml") && !name.endsWith(".yml")) continue;
|
|
91
|
-
const p = join(dir, name);
|
|
92
|
-
try {
|
|
93
|
-
const wf = parseWorkflow(readFileSync(p, "utf-8"));
|
|
94
|
-
if (wf) {
|
|
95
|
-
try {
|
|
96
|
-
const st = statSync(p);
|
|
97
|
-
wf.created = new Date(st.birthtimeMs || st.mtimeMs).toISOString();
|
|
98
|
-
} catch {
|
|
99
|
-
}
|
|
100
|
-
out.push(wf);
|
|
101
|
-
}
|
|
102
|
-
} catch {
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
return out.sort((a, b) => a.name.localeCompare(b.name));
|
|
106
|
-
}
|
|
107
|
-
function getWorkflow(projectRoot, name) {
|
|
108
|
-
const p = workflowPath(projectRoot, name);
|
|
109
|
-
if (!existsSync(p)) return null;
|
|
110
|
-
try {
|
|
111
|
-
return parseWorkflow(readFileSync(p, "utf-8"));
|
|
112
|
-
} catch {
|
|
113
|
-
return null;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
function rawWorkflow(projectRoot, name) {
|
|
117
|
-
const p = workflowPath(projectRoot, name);
|
|
118
|
-
return existsSync(p) ? readFileSync(p, "utf-8") : null;
|
|
119
|
-
}
|
|
120
|
-
function saveWorkflow(projectRoot, wf) {
|
|
121
|
-
const dir = workflowsDir(projectRoot);
|
|
122
|
-
mkdirSync(dir, { recursive: true });
|
|
123
|
-
const path = workflowPath(projectRoot, wf.name);
|
|
124
|
-
const tmp = `${path}.tmp-${process.pid}`;
|
|
125
|
-
writeFileSync(tmp, serializeWorkflow(wf));
|
|
126
|
-
renameSync(tmp, path);
|
|
127
|
-
}
|
|
128
|
-
function setWorkflowEnabled(projectRoot, name, enabled) {
|
|
129
|
-
const wf = getWorkflow(projectRoot, name);
|
|
130
|
-
if (!wf) return null;
|
|
131
|
-
const next = { ...wf };
|
|
132
|
-
if (enabled) delete next.enabled;
|
|
133
|
-
else next.enabled = false;
|
|
134
|
-
saveWorkflow(projectRoot, next);
|
|
135
|
-
return next;
|
|
136
|
-
}
|
|
137
|
-
function removeWorkflow(projectRoot, name) {
|
|
138
|
-
const p = workflowPath(projectRoot, name);
|
|
139
|
-
if (!existsSync(p)) return false;
|
|
140
|
-
try {
|
|
141
|
-
unlinkSync(p);
|
|
142
|
-
return true;
|
|
143
|
-
} catch {
|
|
144
|
-
return false;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
export { saveWorkflow as a, rawWorkflow as b, workflowSteps as c, workflowsDir as d, getWorkflow as g, isWorkflowEnabled as i, listWorkflows as l, removeWorkflow as r, setWorkflowEnabled as s, workflowCrons as w };
|