volute 0.1.0
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/LICENSE +21 -0
- package/README.md +227 -0
- package/dist/channel-Q642YUZE.js +90 -0
- package/dist/chunk-5YW4B7CG.js +181 -0
- package/dist/chunk-A5ZJEMHT.js +40 -0
- package/dist/chunk-D424ZQGI.js +31 -0
- package/dist/chunk-GSPKUPKU.js +120 -0
- package/dist/chunk-H5XQARAP.js +48 -0
- package/dist/chunk-KSMIWOCN.js +84 -0
- package/dist/chunk-N4QN44LC.js +74 -0
- package/dist/chunk-XZN4WPNC.js +34 -0
- package/dist/cli.js +95 -0
- package/dist/connect-LW6G23AV.js +48 -0
- package/dist/connectors/discord.js +213 -0
- package/dist/create-3K6O2SDC.js +62 -0
- package/dist/daemon-client-ZTHW7ROS.js +10 -0
- package/dist/daemon.js +1731 -0
- package/dist/delete-JNGY7ZFH.js +54 -0
- package/dist/disconnect-ACVTKTRE.js +30 -0
- package/dist/down-FYCUYC5H.js +71 -0
- package/dist/env-7SLRN3MG.js +159 -0
- package/dist/fork-BB3DZ426.js +112 -0
- package/dist/import-W2AMTEV5.js +410 -0
- package/dist/logs-BUHRIQ2L.js +35 -0
- package/dist/merge-446QTE7Q.js +219 -0
- package/dist/schedule-KKSOVUDF.js +113 -0
- package/dist/send-WQSVSRDD.js +50 -0
- package/dist/start-LKMWS6ZE.js +29 -0
- package/dist/status-CIEKUI3V.js +50 -0
- package/dist/stop-YTOAGYE4.js +29 -0
- package/dist/up-AJJ4GCXY.js +111 -0
- package/dist/upgrade-JACA6YMO.js +211 -0
- package/dist/variants-HPY4DEWU.js +60 -0
- package/dist/web-assets/assets/index-DNNPoxMn.js +158 -0
- package/dist/web-assets/index.html +15 -0
- package/package.json +76 -0
- package/templates/_base/.init/MEMORY.md +2 -0
- package/templates/_base/.init/SOUL.md +2 -0
- package/templates/_base/.init/memory/.gitkeep +0 -0
- package/templates/_base/_skills/memory/SKILL.md +30 -0
- package/templates/_base/_skills/volute-agent/SKILL.md +53 -0
- package/templates/_base/biome.json.tmpl +21 -0
- package/templates/_base/home/VOLUTE.md +19 -0
- package/templates/_base/src/lib/auto-commit.ts +46 -0
- package/templates/_base/src/lib/logger.ts +47 -0
- package/templates/_base/src/lib/types.ts +24 -0
- package/templates/_base/src/lib/volute-server.ts +98 -0
- package/templates/_base/tsconfig.json +13 -0
- package/templates/_base/volute.json.tmpl +3 -0
- package/templates/agent-sdk/.init/CLAUDE.md +36 -0
- package/templates/agent-sdk/package.json.tmpl +20 -0
- package/templates/agent-sdk/src/lib/agent.ts +199 -0
- package/templates/agent-sdk/src/lib/hooks/auto-commit.ts +14 -0
- package/templates/agent-sdk/src/lib/hooks/identity-reload.ts +26 -0
- package/templates/agent-sdk/src/lib/hooks/pre-compact.ts +20 -0
- package/templates/agent-sdk/src/lib/message-channel.ts +37 -0
- package/templates/agent-sdk/src/server.ts +158 -0
- package/templates/agent-sdk/volute-template.json +9 -0
- package/templates/pi/.init/AGENTS.md +26 -0
- package/templates/pi/package.json.tmpl +20 -0
- package/templates/pi/src/lib/agent.ts +205 -0
- package/templates/pi/src/server.ts +121 -0
- package/templates/pi/volute-template.json +9 -0
- package/templates/pi/volute.json.tmpl +3 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
parseArgs
|
|
4
|
+
} from "./chunk-D424ZQGI.js";
|
|
5
|
+
import {
|
|
6
|
+
agentDir,
|
|
7
|
+
findAgent,
|
|
8
|
+
removeAgent,
|
|
9
|
+
removeAllVariants
|
|
10
|
+
} from "./chunk-5YW4B7CG.js";
|
|
11
|
+
|
|
12
|
+
// src/commands/delete.ts
|
|
13
|
+
import { existsSync, rmSync } from "fs";
|
|
14
|
+
async function run(args) {
|
|
15
|
+
const { positional, flags } = parseArgs(args, {
|
|
16
|
+
force: { type: "boolean" }
|
|
17
|
+
});
|
|
18
|
+
const name = positional[0];
|
|
19
|
+
if (!name) {
|
|
20
|
+
console.error("Usage: volute delete <name> [--force]");
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
const entry = findAgent(name);
|
|
24
|
+
if (!entry) {
|
|
25
|
+
console.error(`Unknown agent: ${name}`);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
const { daemonFetch } = await import("./daemon-client-ZTHW7ROS.js");
|
|
30
|
+
const res = await daemonFetch(`/api/agents/${encodeURIComponent(name)}/stop`, {
|
|
31
|
+
method: "POST"
|
|
32
|
+
});
|
|
33
|
+
if (res.ok) {
|
|
34
|
+
console.log(`Stopped ${name}.`);
|
|
35
|
+
}
|
|
36
|
+
} catch {
|
|
37
|
+
}
|
|
38
|
+
const dir = agentDir(name);
|
|
39
|
+
removeAllVariants(name);
|
|
40
|
+
removeAgent(name);
|
|
41
|
+
console.log(`Removed ${name} from registry.`);
|
|
42
|
+
if (existsSync(dir)) {
|
|
43
|
+
if (!flags.force) {
|
|
44
|
+
console.log(`Directory: ${dir}`);
|
|
45
|
+
console.log("Use --force to also delete the agent directory.");
|
|
46
|
+
} else {
|
|
47
|
+
rmSync(dir, { recursive: true, force: true });
|
|
48
|
+
console.log(`Deleted ${dir}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export {
|
|
53
|
+
run
|
|
54
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
daemonFetch
|
|
4
|
+
} from "./chunk-H5XQARAP.js";
|
|
5
|
+
import "./chunk-5YW4B7CG.js";
|
|
6
|
+
|
|
7
|
+
// src/commands/disconnect.ts
|
|
8
|
+
async function run(args) {
|
|
9
|
+
const type = args[0];
|
|
10
|
+
const name = args[1];
|
|
11
|
+
if (!type || !name) {
|
|
12
|
+
console.error("Usage: volute disconnect <type> <agent>");
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
const res = await daemonFetch(
|
|
16
|
+
`/api/agents/${encodeURIComponent(name)}/connectors/${encodeURIComponent(type)}`,
|
|
17
|
+
{
|
|
18
|
+
method: "DELETE"
|
|
19
|
+
}
|
|
20
|
+
);
|
|
21
|
+
if (!res.ok) {
|
|
22
|
+
const body = await res.json().catch(() => ({ error: "Unknown error" }));
|
|
23
|
+
console.error(`Failed to stop ${type} connector: ${body.error}`);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
console.log(`${type} connector for ${name} stopped.`);
|
|
27
|
+
}
|
|
28
|
+
export {
|
|
29
|
+
run
|
|
30
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
VOLUTE_HOME
|
|
4
|
+
} from "./chunk-5YW4B7CG.js";
|
|
5
|
+
|
|
6
|
+
// src/commands/down.ts
|
|
7
|
+
import { existsSync, readFileSync, unlinkSync } from "fs";
|
|
8
|
+
import { resolve } from "path";
|
|
9
|
+
async function run(_args) {
|
|
10
|
+
const pidPath = resolve(VOLUTE_HOME, "daemon.pid");
|
|
11
|
+
if (!existsSync(pidPath)) {
|
|
12
|
+
const configPath = resolve(VOLUTE_HOME, "daemon.json");
|
|
13
|
+
let port = 4200;
|
|
14
|
+
if (existsSync(configPath)) {
|
|
15
|
+
try {
|
|
16
|
+
port = JSON.parse(readFileSync(configPath, "utf-8")).port ?? 4200;
|
|
17
|
+
} catch {
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
const res = await fetch(`http://localhost:${port}/api/health`);
|
|
22
|
+
if (res.ok) {
|
|
23
|
+
console.error(`Daemon appears to be running on port ${port} but PID file is missing.`);
|
|
24
|
+
console.error(`Kill the process manually: lsof -ti :${port} | xargs kill`);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
} catch {
|
|
28
|
+
}
|
|
29
|
+
console.error("Daemon is not running (no PID file found).");
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
const pid = parseInt(readFileSync(pidPath, "utf-8").trim(), 10);
|
|
33
|
+
try {
|
|
34
|
+
process.kill(pid, 0);
|
|
35
|
+
} catch {
|
|
36
|
+
try {
|
|
37
|
+
unlinkSync(pidPath);
|
|
38
|
+
} catch {
|
|
39
|
+
}
|
|
40
|
+
console.log("Daemon was not running (cleaned up stale PID file).");
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
process.kill(-pid, "SIGTERM");
|
|
45
|
+
console.log(`Sent SIGTERM to daemon group (pid ${pid})`);
|
|
46
|
+
} catch {
|
|
47
|
+
process.kill(pid, "SIGTERM");
|
|
48
|
+
console.log(`Sent SIGTERM to daemon (pid ${pid})`);
|
|
49
|
+
}
|
|
50
|
+
const maxWait = 1e4;
|
|
51
|
+
const start = Date.now();
|
|
52
|
+
while (Date.now() - start < maxWait) {
|
|
53
|
+
if (!existsSync(pidPath)) {
|
|
54
|
+
console.log("Daemon stopped.");
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
process.kill(-pid, "SIGKILL");
|
|
61
|
+
} catch {
|
|
62
|
+
try {
|
|
63
|
+
process.kill(pid, "SIGKILL");
|
|
64
|
+
} catch {
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
console.error("Daemon did not exit cleanly, sent SIGKILL.");
|
|
68
|
+
}
|
|
69
|
+
export {
|
|
70
|
+
run
|
|
71
|
+
};
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
agentEnvPath,
|
|
4
|
+
loadMergedEnv,
|
|
5
|
+
readEnv,
|
|
6
|
+
sharedEnvPath,
|
|
7
|
+
writeEnv
|
|
8
|
+
} from "./chunk-A5ZJEMHT.js";
|
|
9
|
+
import {
|
|
10
|
+
parseArgs
|
|
11
|
+
} from "./chunk-D424ZQGI.js";
|
|
12
|
+
import {
|
|
13
|
+
resolveAgent
|
|
14
|
+
} from "./chunk-5YW4B7CG.js";
|
|
15
|
+
|
|
16
|
+
// src/commands/env.ts
|
|
17
|
+
function getEnvPath(agentName) {
|
|
18
|
+
if (agentName) {
|
|
19
|
+
const { dir } = resolveAgent(agentName);
|
|
20
|
+
return agentEnvPath(dir);
|
|
21
|
+
}
|
|
22
|
+
return sharedEnvPath();
|
|
23
|
+
}
|
|
24
|
+
async function promptValue(key) {
|
|
25
|
+
process.stderr.write(`Enter value for ${key}: `);
|
|
26
|
+
if (process.stdin.isTTY) process.stdin.setRawMode(true);
|
|
27
|
+
return new Promise((resolve) => {
|
|
28
|
+
let value = "";
|
|
29
|
+
const onData = (buf) => {
|
|
30
|
+
for (const byte of buf) {
|
|
31
|
+
if (byte === 3) {
|
|
32
|
+
process.stderr.write("\n");
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
if (byte === 13 || byte === 10) {
|
|
36
|
+
process.stderr.write("\n");
|
|
37
|
+
if (process.stdin.isTTY) process.stdin.setRawMode(false);
|
|
38
|
+
process.stdin.removeListener("data", onData);
|
|
39
|
+
process.stdin.pause();
|
|
40
|
+
resolve(value);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (byte === 127 || byte === 8) {
|
|
44
|
+
value = value.slice(0, -1);
|
|
45
|
+
} else {
|
|
46
|
+
value += String.fromCharCode(byte);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
process.stdin.resume();
|
|
51
|
+
process.stdin.on("data", onData);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
async function run(args) {
|
|
55
|
+
const { positional, flags } = parseArgs(args, {
|
|
56
|
+
agent: { type: "string" }
|
|
57
|
+
});
|
|
58
|
+
const subcommand = positional[0];
|
|
59
|
+
switch (subcommand) {
|
|
60
|
+
case "set": {
|
|
61
|
+
const key = positional[1];
|
|
62
|
+
if (!key) {
|
|
63
|
+
console.error("Usage: volute env set <KEY> [<VALUE>] [--agent <name>]");
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
const value = positional[2] ?? await promptValue(key);
|
|
67
|
+
const path = getEnvPath(flags.agent);
|
|
68
|
+
const env = readEnv(path);
|
|
69
|
+
env[key] = value;
|
|
70
|
+
writeEnv(path, env);
|
|
71
|
+
const scope = flags.agent ? `agent:${flags.agent}` : "shared";
|
|
72
|
+
console.log(`Set ${key} [${scope}]`);
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
case "get": {
|
|
76
|
+
const key = positional[1];
|
|
77
|
+
if (!key) {
|
|
78
|
+
console.error("Usage: volute env get <KEY> [--agent <name>]");
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
if (flags.agent) {
|
|
82
|
+
const { dir } = resolveAgent(flags.agent);
|
|
83
|
+
const merged = loadMergedEnv(dir);
|
|
84
|
+
if (key in merged) {
|
|
85
|
+
console.log(merged[key]);
|
|
86
|
+
} else {
|
|
87
|
+
console.error(`${key} not set`);
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
91
|
+
const env = readEnv(sharedEnvPath());
|
|
92
|
+
if (key in env) {
|
|
93
|
+
console.log(env[key]);
|
|
94
|
+
} else {
|
|
95
|
+
console.error(`${key} not set`);
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
case "list": {
|
|
102
|
+
if (flags.agent) {
|
|
103
|
+
const { dir } = resolveAgent(flags.agent);
|
|
104
|
+
const shared = readEnv(sharedEnvPath());
|
|
105
|
+
const agent = readEnv(agentEnvPath(dir));
|
|
106
|
+
const allKeys = /* @__PURE__ */ new Set([...Object.keys(shared), ...Object.keys(agent)]);
|
|
107
|
+
if (allKeys.size === 0) {
|
|
108
|
+
console.log("No environment variables set.");
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
for (const key of [...allKeys].sort()) {
|
|
112
|
+
const scope = key in agent ? "agent" : "shared";
|
|
113
|
+
const value = key in agent ? agent[key] : shared[key];
|
|
114
|
+
console.log(`${key}=${value} [${scope}]`);
|
|
115
|
+
}
|
|
116
|
+
} else {
|
|
117
|
+
const env = readEnv(sharedEnvPath());
|
|
118
|
+
const keys = Object.keys(env);
|
|
119
|
+
if (keys.length === 0) {
|
|
120
|
+
console.log("No shared environment variables set.");
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
for (const key of keys.sort()) {
|
|
124
|
+
console.log(`${key}=${env[key]} [shared]`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
case "remove": {
|
|
130
|
+
const key = positional[1];
|
|
131
|
+
if (!key) {
|
|
132
|
+
console.error("Usage: volute env remove <KEY> [--agent <name>]");
|
|
133
|
+
process.exit(1);
|
|
134
|
+
}
|
|
135
|
+
const path = getEnvPath(flags.agent);
|
|
136
|
+
const env = readEnv(path);
|
|
137
|
+
if (!(key in env)) {
|
|
138
|
+
const scope2 = flags.agent ? `agent:${flags.agent}` : "shared";
|
|
139
|
+
console.error(`${key} not set in ${scope2} scope`);
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
delete env[key];
|
|
143
|
+
writeEnv(path, env);
|
|
144
|
+
const scope = flags.agent ? `agent:${flags.agent}` : "shared";
|
|
145
|
+
console.log(`Removed ${key} [${scope}]`);
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
default:
|
|
149
|
+
console.error(`Usage: volute env <set|get|list|remove> [--agent <name>]`);
|
|
150
|
+
if (subcommand) {
|
|
151
|
+
console.error(`
|
|
152
|
+
Unknown subcommand: ${subcommand}`);
|
|
153
|
+
}
|
|
154
|
+
process.exit(1);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
export {
|
|
158
|
+
run
|
|
159
|
+
};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
spawnServer
|
|
4
|
+
} from "./chunk-N4QN44LC.js";
|
|
5
|
+
import {
|
|
6
|
+
exec,
|
|
7
|
+
execInherit
|
|
8
|
+
} from "./chunk-XZN4WPNC.js";
|
|
9
|
+
import {
|
|
10
|
+
parseArgs
|
|
11
|
+
} from "./chunk-D424ZQGI.js";
|
|
12
|
+
import {
|
|
13
|
+
addVariant,
|
|
14
|
+
resolveAgent,
|
|
15
|
+
validateBranchName
|
|
16
|
+
} from "./chunk-5YW4B7CG.js";
|
|
17
|
+
|
|
18
|
+
// src/commands/fork.ts
|
|
19
|
+
import { existsSync, mkdirSync, writeFileSync } from "fs";
|
|
20
|
+
import { resolve } from "path";
|
|
21
|
+
async function run(args) {
|
|
22
|
+
const { positional, flags } = parseArgs(args, {
|
|
23
|
+
soul: { type: "string" },
|
|
24
|
+
port: { type: "number" },
|
|
25
|
+
"no-start": { type: "boolean" },
|
|
26
|
+
json: { type: "boolean" }
|
|
27
|
+
});
|
|
28
|
+
const agentName = positional[0];
|
|
29
|
+
const variantName = positional[1];
|
|
30
|
+
const { soul, port, json } = flags;
|
|
31
|
+
const noStart = flags["no-start"];
|
|
32
|
+
if (!agentName || !variantName) {
|
|
33
|
+
console.error(
|
|
34
|
+
'Usage: volute fork <agent> <variant> [--soul "..."] [--port N] [--no-start] [--json]'
|
|
35
|
+
);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
const err = validateBranchName(variantName);
|
|
39
|
+
if (err) {
|
|
40
|
+
console.error(err);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
const { dir: projectRoot } = resolveAgent(agentName);
|
|
44
|
+
const worktreeDir = resolve(projectRoot, ".worktrees", variantName);
|
|
45
|
+
if (existsSync(worktreeDir)) {
|
|
46
|
+
console.error(`Worktree already exists: ${worktreeDir}`);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
const parentDir = resolve(projectRoot, ".worktrees");
|
|
50
|
+
if (!existsSync(parentDir)) {
|
|
51
|
+
mkdirSync(parentDir, { recursive: true });
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
await exec("git", ["worktree", "add", "-b", variantName, worktreeDir], { cwd: projectRoot });
|
|
55
|
+
} catch (e) {
|
|
56
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
57
|
+
console.error(`Failed to create worktree: ${msg}`);
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
if (!json) console.log("Installing dependencies...");
|
|
61
|
+
try {
|
|
62
|
+
if (json) {
|
|
63
|
+
await exec("npm", ["install"], { cwd: worktreeDir });
|
|
64
|
+
} else {
|
|
65
|
+
await execInherit("npm", ["install"], { cwd: worktreeDir });
|
|
66
|
+
}
|
|
67
|
+
} catch (e) {
|
|
68
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
69
|
+
console.error(`npm install failed: ${msg}`);
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
if (soul) {
|
|
73
|
+
writeFileSync(resolve(worktreeDir, "home/SOUL.md"), soul);
|
|
74
|
+
}
|
|
75
|
+
let actualPort = null;
|
|
76
|
+
let pid = null;
|
|
77
|
+
if (!noStart) {
|
|
78
|
+
const requestedPort = port ?? 0;
|
|
79
|
+
if (!json) console.log("Starting server...");
|
|
80
|
+
const result = await spawnServer(worktreeDir, requestedPort, { detached: true });
|
|
81
|
+
if (!result) {
|
|
82
|
+
console.error("Server failed to start within timeout");
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
actualPort = result.actualPort;
|
|
86
|
+
pid = result.child.pid ?? null;
|
|
87
|
+
}
|
|
88
|
+
const variant = {
|
|
89
|
+
name: variantName,
|
|
90
|
+
branch: variantName,
|
|
91
|
+
path: worktreeDir,
|
|
92
|
+
port: actualPort ?? port ?? 0,
|
|
93
|
+
pid,
|
|
94
|
+
created: (/* @__PURE__ */ new Date()).toISOString()
|
|
95
|
+
};
|
|
96
|
+
addVariant(agentName, variant);
|
|
97
|
+
if (json) {
|
|
98
|
+
console.log(JSON.stringify(variant, null, 2));
|
|
99
|
+
} else {
|
|
100
|
+
console.log(`
|
|
101
|
+
Variant created: ${variantName}`);
|
|
102
|
+
console.log(` Branch: ${variant.branch}`);
|
|
103
|
+
console.log(` Path: ${variant.path}`);
|
|
104
|
+
if (actualPort) {
|
|
105
|
+
console.log(` Port: ${actualPort}`);
|
|
106
|
+
console.log(` PID: ${pid}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
export {
|
|
111
|
+
run
|
|
112
|
+
};
|