@vicvic-im/agent-bot 0.1.1 → 0.1.3
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/README.md +9 -3
- package/dist/agents/acp.d.ts +2 -1
- package/dist/agents/acp.d.ts.map +1 -1
- package/dist/agents/types.d.ts +16 -0
- package/dist/agents/types.d.ts.map +1 -1
- package/dist/bin-server.cjs +1422 -0
- package/dist/bin-server.cjs.map +1 -0
- package/dist/bin-server.d.ts +2 -0
- package/dist/bin-server.d.ts.map +1 -0
- package/dist/bin-server.js +19 -0
- package/dist/bin-server.js.map +1 -0
- package/dist/{chunk-QQ2NXJ3L.js → chunk-CDBYOHVL.js} +2 -2
- package/dist/{chunk-SCVC76UL.js → chunk-ELLWQDOY.js} +217 -105
- package/dist/chunk-ELLWQDOY.js.map +1 -0
- package/dist/cli.cjs +246 -119
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +32 -17
- package/dist/cli.js.map +1 -1
- package/dist/core/dispatcher.d.ts +3 -1
- package/dist/core/dispatcher.d.ts.map +1 -1
- package/dist/core/pm2.d.ts +7 -0
- package/dist/core/pm2.d.ts.map +1 -0
- package/dist/index.cjs +210 -98
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/server.cjs +210 -96
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +10 -10
- package/dist/chunk-SCVC76UL.js.map +0 -1
- /package/dist/{chunk-QQ2NXJ3L.js.map → chunk-CDBYOHVL.js.map} +0 -0
package/dist/cli.cjs
CHANGED
|
@@ -24,8 +24,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
));
|
|
25
25
|
|
|
26
26
|
// src/cli.ts
|
|
27
|
-
var
|
|
28
|
-
var
|
|
27
|
+
var import_node_child_process3 = require("child_process");
|
|
28
|
+
var import_node_path6 = require("path");
|
|
29
29
|
var import_commander = require("commander");
|
|
30
30
|
var import_picocolors2 = __toESM(require("picocolors"), 1);
|
|
31
31
|
|
|
@@ -136,11 +136,11 @@ function parseEnv(value) {
|
|
|
136
136
|
if (value === void 0 || value === null) return void 0;
|
|
137
137
|
const record = asRecord(value, "agent.env");
|
|
138
138
|
const env = {};
|
|
139
|
-
for (const [key,
|
|
140
|
-
if (typeof
|
|
139
|
+
for (const [key, entry] of Object.entries(record)) {
|
|
140
|
+
if (typeof entry !== "string") {
|
|
141
141
|
throw new ConfigError(`Invalid "agent.env.${key}": expected a string`);
|
|
142
142
|
}
|
|
143
|
-
env[key] =
|
|
143
|
+
env[key] = entry;
|
|
144
144
|
}
|
|
145
145
|
return env;
|
|
146
146
|
}
|
|
@@ -184,7 +184,7 @@ function parseAgent(root, configDir) {
|
|
|
184
184
|
return {
|
|
185
185
|
name: agentName,
|
|
186
186
|
command: optionalString(agentRaw.command, "agent.command"),
|
|
187
|
-
args: Array.isArray(argsRaw) ? argsRaw.map((
|
|
187
|
+
args: Array.isArray(argsRaw) ? argsRaw.map((entry) => String(entry)) : void 0,
|
|
188
188
|
cwd,
|
|
189
189
|
model: optionalString(agentRaw.model, "agent.model") ?? null,
|
|
190
190
|
env: parseEnv(agentRaw.env)
|
|
@@ -318,7 +318,7 @@ var import_node_fs3 = require("fs");
|
|
|
318
318
|
var import_node_path3 = require("path");
|
|
319
319
|
function resolveServerEntry(cliPath2) {
|
|
320
320
|
const real = (0, import_node_fs3.realpathSync)(cliPath2);
|
|
321
|
-
const candidate = (0, import_node_path3.join)((0, import_node_path3.dirname)(real), "server.js");
|
|
321
|
+
const candidate = (0, import_node_path3.join)((0, import_node_path3.dirname)(real), "bin-server.js");
|
|
322
322
|
if (!(0, import_node_fs3.existsSync)(candidate)) {
|
|
323
323
|
throw new Error(`Server bundle not found at ${candidate}. Reinstall @vicvic-im/agent-bot.`);
|
|
324
324
|
}
|
|
@@ -333,17 +333,38 @@ function readPackageVersion(cliPath2) {
|
|
|
333
333
|
}
|
|
334
334
|
}
|
|
335
335
|
|
|
336
|
-
// src/
|
|
337
|
-
var
|
|
336
|
+
// src/core/pm2.ts
|
|
337
|
+
var import_node_child_process = require("child_process");
|
|
338
|
+
var ENV_BIN = "/usr/bin/env";
|
|
339
|
+
function hasPm2(exec = import_node_child_process.execFileSync) {
|
|
340
|
+
try {
|
|
341
|
+
exec(ENV_BIN, ["pm2", "--version"], { stdio: "ignore" });
|
|
342
|
+
return true;
|
|
343
|
+
} catch {
|
|
344
|
+
return false;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
function ensurePm2(exec = import_node_child_process.execFileSync) {
|
|
348
|
+
if (hasPm2(exec)) return true;
|
|
349
|
+
try {
|
|
350
|
+
exec(ENV_BIN, ["npm", "i", "-g", "pm2"], { stdio: "inherit" });
|
|
351
|
+
return hasPm2(exec);
|
|
352
|
+
} catch {
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
338
356
|
|
|
339
357
|
// src/agents/acp.ts
|
|
340
|
-
var
|
|
358
|
+
var import_node_child_process2 = require("child_process");
|
|
341
359
|
var import_node_stream = require("stream");
|
|
342
360
|
var acp = __toESM(require("@agentclientprotocol/sdk"), 1);
|
|
343
361
|
|
|
344
362
|
// src/agents/types.ts
|
|
345
363
|
var EMPTY_USAGE = { used: 0, size: 0, cost: null };
|
|
346
364
|
|
|
365
|
+
// src/version.ts
|
|
366
|
+
var VBOT_VERSION = "0.1.3";
|
|
367
|
+
|
|
347
368
|
// src/agents/acp.ts
|
|
348
369
|
var AgentAbortError = class extends Error {
|
|
349
370
|
constructor() {
|
|
@@ -360,6 +381,14 @@ var AgentStartError = class extends Error {
|
|
|
360
381
|
stderr;
|
|
361
382
|
};
|
|
362
383
|
var STDERR_LIMIT = 4e3;
|
|
384
|
+
function flattenSelectOptions(options) {
|
|
385
|
+
return options.flatMap(
|
|
386
|
+
(entry) => "group" in entry ? entry.options.map((option) => ({ value: option.value, label: option.name })) : [{ value: entry.value, label: entry.name }]
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
function isModelOption(option) {
|
|
390
|
+
return option.type === "select" && (option.category === "model" || option.id === "model");
|
|
391
|
+
}
|
|
363
392
|
var AcpAgent = class {
|
|
364
393
|
constructor(name, config) {
|
|
365
394
|
this.name = name;
|
|
@@ -389,6 +418,14 @@ var AcpAgent = class {
|
|
|
389
418
|
sessionId() {
|
|
390
419
|
return this.session;
|
|
391
420
|
}
|
|
421
|
+
currentModel() {
|
|
422
|
+
const option = this.configOptions.find(isModelOption);
|
|
423
|
+
if (!option || option.type !== "select") return null;
|
|
424
|
+
const value = String(option.currentValue);
|
|
425
|
+
const options = flattenSelectOptions(option.options);
|
|
426
|
+
const label = options.find((entry) => entry.value === value)?.label ?? value;
|
|
427
|
+
return { value, label, options };
|
|
428
|
+
}
|
|
392
429
|
resetSession() {
|
|
393
430
|
this.session = null;
|
|
394
431
|
}
|
|
@@ -404,7 +441,7 @@ var AcpAgent = class {
|
|
|
404
441
|
const definition = definitionOf(this.name);
|
|
405
442
|
const command = this.config.command ?? definition.command.command;
|
|
406
443
|
const args = this.config.args ?? definition.command.args;
|
|
407
|
-
const child = (0,
|
|
444
|
+
const child = (0, import_node_child_process2.spawn)(command, args, {
|
|
408
445
|
cwd: this.config.cwd,
|
|
409
446
|
env: { ...process.env, ...this.config.env },
|
|
410
447
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -456,7 +493,7 @@ var AcpAgent = class {
|
|
|
456
493
|
const init = await this.ctx.request(acp.methods.agent.initialize, {
|
|
457
494
|
protocolVersion: 1,
|
|
458
495
|
clientCapabilities: {},
|
|
459
|
-
clientInfo: { name: "vbot", version:
|
|
496
|
+
clientInfo: { name: "vbot", version: VBOT_VERSION }
|
|
460
497
|
});
|
|
461
498
|
this.initCaps = init.agentCapabilities;
|
|
462
499
|
} catch (error) {
|
|
@@ -543,7 +580,7 @@ var AcpAgent = class {
|
|
|
543
580
|
);
|
|
544
581
|
pending.catch(() => void 0);
|
|
545
582
|
const response = await Promise.race([pending, aborted]);
|
|
546
|
-
await new Promise((
|
|
583
|
+
await new Promise((resolve4) => setImmediate(resolve4));
|
|
547
584
|
if (response.stopReason === "cancelled" || request.signal.aborted) {
|
|
548
585
|
throw new AgentAbortError();
|
|
549
586
|
}
|
|
@@ -575,15 +612,14 @@ var AcpAgent = class {
|
|
|
575
612
|
this.pendingModel = model;
|
|
576
613
|
return true;
|
|
577
614
|
}
|
|
578
|
-
const option = this.configOptions.find(
|
|
579
|
-
(entry2) => entry2.type === "select" && (entry2.id === "model" || entry2.category === "model")
|
|
580
|
-
);
|
|
615
|
+
const option = this.configOptions.find(isModelOption);
|
|
581
616
|
if (!option) return false;
|
|
582
|
-
await this.ctx.request(acp.methods.agent.session.setConfigOption, {
|
|
617
|
+
const response = await this.ctx.request(acp.methods.agent.session.setConfigOption, {
|
|
583
618
|
sessionId: this.session,
|
|
584
619
|
configId: option.id,
|
|
585
620
|
value: model
|
|
586
621
|
});
|
|
622
|
+
if (response?.configOptions?.length) this.configOptions = response.configOptions;
|
|
587
623
|
this.pendingModel = null;
|
|
588
624
|
return true;
|
|
589
625
|
}
|
|
@@ -602,8 +638,8 @@ var AcpAgent = class {
|
|
|
602
638
|
} catch {
|
|
603
639
|
}
|
|
604
640
|
if (!child || child.exitCode !== null) return;
|
|
605
|
-
const exited = new Promise((
|
|
606
|
-
child.once("exit", () =>
|
|
641
|
+
const exited = new Promise((resolve4) => {
|
|
642
|
+
child.once("exit", () => resolve4());
|
|
607
643
|
});
|
|
608
644
|
child.kill("SIGTERM");
|
|
609
645
|
const timer = setTimeout(() => child.kill("SIGKILL"), 3e3);
|
|
@@ -719,21 +755,29 @@ var BotQueue = class {
|
|
|
719
755
|
}
|
|
720
756
|
};
|
|
721
757
|
|
|
722
|
-
// src/version.ts
|
|
723
|
-
var VBOT_VERSION = "0.1.1";
|
|
724
|
-
|
|
725
758
|
// src/core/dispatcher.ts
|
|
726
|
-
var
|
|
727
|
-
|
|
728
|
-
/
|
|
729
|
-
/
|
|
730
|
-
/
|
|
731
|
-
/
|
|
732
|
-
/
|
|
733
|
-
/
|
|
734
|
-
/
|
|
735
|
-
|
|
736
|
-
|
|
759
|
+
var import_node_os2 = require("os");
|
|
760
|
+
var HELP_ROWS = [
|
|
761
|
+
["/new", "Start a new session"],
|
|
762
|
+
["/stop", "Abort the current request"],
|
|
763
|
+
["/status", "Show agent, session and queue status"],
|
|
764
|
+
["/agent [name]", "Show or switch agent backend"],
|
|
765
|
+
["/model [name]", "Show or switch model"],
|
|
766
|
+
["/cost", "Show context usage and cost"],
|
|
767
|
+
["/version", "Show vbot version"],
|
|
768
|
+
["/help", "Show this help"]
|
|
769
|
+
];
|
|
770
|
+
var HELP_FOOTER = "Other /commands are sent to the agent as prompts.";
|
|
771
|
+
var NEW_BUSY_TEXT = "Cannot reset the session while a request is running. Use /stop first.";
|
|
772
|
+
var MODEL_BUSY_TEXT = "Cannot switch model while a request is running. Use /stop first.";
|
|
773
|
+
var HELP_TEXT = [
|
|
774
|
+
table(
|
|
775
|
+
["Command", "Description"],
|
|
776
|
+
HELP_ROWS.map(([command, description]) => [command, description])
|
|
777
|
+
),
|
|
778
|
+
"",
|
|
779
|
+
HELP_FOOTER
|
|
780
|
+
].join("\n");
|
|
737
781
|
function createDispatcher(runtime) {
|
|
738
782
|
return (text, reply) => {
|
|
739
783
|
const send = (message) => {
|
|
@@ -747,11 +791,57 @@ function createDispatcher(runtime) {
|
|
|
747
791
|
});
|
|
748
792
|
};
|
|
749
793
|
}
|
|
794
|
+
function escapeCell(value) {
|
|
795
|
+
return value.replace(/\|/g, "\\|").replace(/\r?\n/g, " ");
|
|
796
|
+
}
|
|
797
|
+
function table(headers, rows) {
|
|
798
|
+
const head = `| ${headers.map(escapeCell).join(" | ")} |`;
|
|
799
|
+
const separator = `| ${headers.map(() => "---").join(" | ")} |`;
|
|
800
|
+
const body = rows.map((row) => `| ${row.map(escapeCell).join(" | ")} |`);
|
|
801
|
+
return [head, separator, ...body].join("\n");
|
|
802
|
+
}
|
|
803
|
+
function formatNumber(value) {
|
|
804
|
+
if (!Number.isFinite(value)) return String(value);
|
|
805
|
+
const negative = value < 0;
|
|
806
|
+
const digits = Math.abs(Math.trunc(value)).toString();
|
|
807
|
+
let out = "";
|
|
808
|
+
for (let i = 0; i < digits.length; i += 1) {
|
|
809
|
+
if (i > 0 && (digits.length - i) % 3 === 0) out += ",";
|
|
810
|
+
out += digits[i];
|
|
811
|
+
}
|
|
812
|
+
return negative ? "-" + out : out;
|
|
813
|
+
}
|
|
814
|
+
function percentOf(used, size) {
|
|
815
|
+
if (size <= 0) return null;
|
|
816
|
+
return `${(used / size * 100).toFixed(1)}%`;
|
|
817
|
+
}
|
|
818
|
+
function usageText(runtime) {
|
|
819
|
+
const { used, size } = runtime.state().usage;
|
|
820
|
+
if (size <= 0) return "not reported";
|
|
821
|
+
const base = `${formatNumber(used)} / ${formatNumber(size)}`;
|
|
822
|
+
const percent = percentOf(used, size);
|
|
823
|
+
return percent ? `${base} (${percent})` : base;
|
|
824
|
+
}
|
|
825
|
+
function compactPath(path) {
|
|
826
|
+
const home = (0, import_node_os2.homedir)();
|
|
827
|
+
if (!home) return path;
|
|
828
|
+
if (path === home) return "~";
|
|
829
|
+
const prefix = home + "/";
|
|
830
|
+
if (path.startsWith(prefix)) return "~" + path.slice(home.length);
|
|
831
|
+
return path;
|
|
832
|
+
}
|
|
833
|
+
function modelText(runtime) {
|
|
834
|
+
const current = runtime.agent().currentModel();
|
|
835
|
+
if (current) return current.label;
|
|
836
|
+
const configured = runtime.config().agent.model;
|
|
837
|
+
if (configured) return `${configured} (configured, not applied yet)`;
|
|
838
|
+
return "unknown (session not started)";
|
|
839
|
+
}
|
|
750
840
|
function handleSlashCommand(runtime, text, send) {
|
|
751
841
|
const [rawCommand, ...args] = text.split(/\s+/);
|
|
752
842
|
switch (rawCommand.toLowerCase()) {
|
|
753
843
|
case "/new":
|
|
754
|
-
handleNew(runtime, send);
|
|
844
|
+
void handleNew(runtime, send);
|
|
755
845
|
return null;
|
|
756
846
|
case "/stop":
|
|
757
847
|
handleStop(runtime, send);
|
|
@@ -763,7 +853,7 @@ function handleSlashCommand(runtime, text, send) {
|
|
|
763
853
|
handleAgent(runtime, args, send);
|
|
764
854
|
return null;
|
|
765
855
|
case "/model":
|
|
766
|
-
handleModel(runtime, args, send);
|
|
856
|
+
void handleModel(runtime, args, send);
|
|
767
857
|
return null;
|
|
768
858
|
case "/cost":
|
|
769
859
|
send(costText(runtime));
|
|
@@ -778,12 +868,18 @@ function handleSlashCommand(runtime, text, send) {
|
|
|
778
868
|
return text.slice(1);
|
|
779
869
|
}
|
|
780
870
|
}
|
|
781
|
-
function handleNew(runtime, send) {
|
|
782
|
-
runtime.
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
runtime.
|
|
871
|
+
async function handleNew(runtime, send) {
|
|
872
|
+
if (runtime.queue().status().busy) {
|
|
873
|
+
send(NEW_BUSY_TEXT);
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
876
|
+
await runtime.serialize(async () => {
|
|
877
|
+
runtime.agent().resetSession();
|
|
878
|
+
const state = runtime.state();
|
|
879
|
+
state.acpSessionId = null;
|
|
880
|
+
state.usage = { used: 0, size: 0, cost: null };
|
|
881
|
+
runtime.saveState();
|
|
882
|
+
});
|
|
787
883
|
send("Session reset. Starting a new conversation.");
|
|
788
884
|
}
|
|
789
885
|
function handleStop(runtime, send) {
|
|
@@ -799,12 +895,18 @@ function handleStop(runtime, send) {
|
|
|
799
895
|
}
|
|
800
896
|
function handleAgent(runtime, args, send) {
|
|
801
897
|
const name = args.join(" ").trim();
|
|
898
|
+
const current = runtime.config().agent.name;
|
|
802
899
|
if (!name) {
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
900
|
+
send(
|
|
901
|
+
table(
|
|
902
|
+
["Agent", "Backend", ""],
|
|
903
|
+
AGENT_NAMES.map((entry) => [
|
|
904
|
+
entry,
|
|
905
|
+
AGENTS[entry].label,
|
|
906
|
+
entry === current ? "\u2190 current" : ""
|
|
907
|
+
])
|
|
908
|
+
)
|
|
909
|
+
);
|
|
808
910
|
return;
|
|
809
911
|
}
|
|
810
912
|
if (!isAgentName(name)) {
|
|
@@ -820,56 +922,95 @@ ${list}`);
|
|
|
820
922
|
(error) => send(`Error: ${error instanceof Error ? error.message : String(error)}`)
|
|
821
923
|
);
|
|
822
924
|
}
|
|
823
|
-
function handleModel(runtime, args, send) {
|
|
925
|
+
async function handleModel(runtime, args, send) {
|
|
824
926
|
const model = args.join(" ").trim();
|
|
825
927
|
if (!model) {
|
|
826
|
-
send(
|
|
928
|
+
send(modelReport(runtime));
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
931
|
+
if (runtime.queue().status().busy) {
|
|
932
|
+
send(MODEL_BUSY_TEXT);
|
|
827
933
|
return;
|
|
828
934
|
}
|
|
829
935
|
const config = runtime.config();
|
|
830
936
|
const hadSession = runtime.agent().sessionId() !== null;
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
}
|
|
937
|
+
const supported = await runtime.serialize(async () => {
|
|
938
|
+
const accepted = await runtime.agent().setModel(model);
|
|
939
|
+
config.agent.model = model;
|
|
940
|
+
runtime.saveConfig();
|
|
941
|
+
return accepted;
|
|
942
|
+
});
|
|
943
|
+
const applied = runtime.agent().currentModel()?.label ?? model;
|
|
944
|
+
if (!supported) {
|
|
840
945
|
send(
|
|
841
|
-
|
|
946
|
+
`Model saved: ${model}. This agent does not expose a runtime model option; it will be used on the next session if supported.`
|
|
842
947
|
);
|
|
843
|
-
|
|
844
|
-
|
|
948
|
+
return;
|
|
949
|
+
}
|
|
950
|
+
send(
|
|
951
|
+
hadSession ? `Model switched to: ${applied}` : `Model set to: ${applied}. It will apply when the session starts.`
|
|
845
952
|
);
|
|
846
953
|
}
|
|
954
|
+
function modelReport(runtime) {
|
|
955
|
+
const current = runtime.agent().currentModel();
|
|
956
|
+
if (!current) {
|
|
957
|
+
const configured = runtime.config().agent.model;
|
|
958
|
+
const hint = configured ? `Configured: ${configured}
|
|
959
|
+
|
|
960
|
+
The agent has not reported a model yet; it becomes visible after the first message.` : "This agent does not expose a runtime model option, or the session has not started.";
|
|
961
|
+
return `Current: unknown
|
|
962
|
+
|
|
963
|
+
${hint}`;
|
|
964
|
+
}
|
|
965
|
+
const lines = [`Current: ${current.label}`];
|
|
966
|
+
if (current.options.length > 0) {
|
|
967
|
+
lines.push(
|
|
968
|
+
"",
|
|
969
|
+
table(
|
|
970
|
+
["Model", ""],
|
|
971
|
+
current.options.map((option) => [
|
|
972
|
+
option.label,
|
|
973
|
+
option.value === current.value ? "\u2190 current" : ""
|
|
974
|
+
])
|
|
975
|
+
)
|
|
976
|
+
);
|
|
977
|
+
}
|
|
978
|
+
lines.push("", "Switch: /model <name>");
|
|
979
|
+
return lines.join("\n");
|
|
980
|
+
}
|
|
847
981
|
function statusText(runtime) {
|
|
848
982
|
const config = runtime.config();
|
|
849
983
|
const agent = runtime.agent();
|
|
850
984
|
const state = runtime.state();
|
|
851
985
|
const queue = runtime.queue().status();
|
|
852
|
-
const
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
986
|
+
const { used, size } = state.usage;
|
|
987
|
+
const summary = [
|
|
988
|
+
`**${config.agent.name}**`,
|
|
989
|
+
queue.busy ? "busy" : "idle",
|
|
990
|
+
size > 0 ? `${formatNumber(used)} / ${formatNumber(size)}` : null
|
|
991
|
+
].filter(Boolean).join(" \xB7 ");
|
|
992
|
+
const rows = [
|
|
993
|
+
["Agent", `${config.agent.name} (${AGENTS[config.agent.name].label})`],
|
|
994
|
+
["Session", agent.sessionId() ?? "none"],
|
|
995
|
+
["Work dir", compactPath(config.agent.cwd)],
|
|
996
|
+
["Model", modelText(runtime)],
|
|
997
|
+
["Status", queue.busy ? "busy" : "idle"],
|
|
998
|
+
["Queue", String(queue.queued)],
|
|
999
|
+
["Usage", usageText(runtime)],
|
|
1000
|
+
["Capabilities", capabilitiesText(agent.capabilities())]
|
|
861
1001
|
];
|
|
862
|
-
return
|
|
1002
|
+
return [summary, "", table(["Field", "Value"], rows)].join("\n");
|
|
863
1003
|
}
|
|
864
1004
|
function costText(runtime) {
|
|
865
1005
|
const usage = runtime.state().usage;
|
|
866
|
-
const
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
1006
|
+
const cost = usage.cost ? `${usage.cost.amount} ${usage.cost.currency}` : "not reported by this agent";
|
|
1007
|
+
return table(
|
|
1008
|
+
["Field", "Value"],
|
|
1009
|
+
[
|
|
1010
|
+
["Context", usageText(runtime)],
|
|
1011
|
+
["Cost", cost]
|
|
1012
|
+
]
|
|
1013
|
+
);
|
|
873
1014
|
}
|
|
874
1015
|
function capabilitiesText(caps) {
|
|
875
1016
|
const enabled = [
|
|
@@ -924,10 +1065,10 @@ var import_node_path5 = require("path");
|
|
|
924
1065
|
// src/core/state.ts
|
|
925
1066
|
var import_node_crypto = require("crypto");
|
|
926
1067
|
var import_node_fs4 = require("fs");
|
|
927
|
-
var
|
|
1068
|
+
var import_node_os3 = require("os");
|
|
928
1069
|
var import_node_path4 = require("path");
|
|
929
1070
|
function vbotHome() {
|
|
930
|
-
return process.env.VBOT_HOME?.trim() || (0, import_node_path4.join)((0,
|
|
1071
|
+
return process.env.VBOT_HOME?.trim() || (0, import_node_path4.join)((0, import_node_os3.homedir)(), ".vbot");
|
|
931
1072
|
}
|
|
932
1073
|
function stateDir() {
|
|
933
1074
|
return (0, import_node_path4.join)(vbotHome(), "state");
|
|
@@ -1085,10 +1226,10 @@ var VicvicWire = class {
|
|
|
1085
1226
|
if (!socket || socket.readyState !== import_ws.default.OPEN) {
|
|
1086
1227
|
throw new Error("Bot connection is not open");
|
|
1087
1228
|
}
|
|
1088
|
-
await new Promise((
|
|
1229
|
+
await new Promise((resolve4, reject) => {
|
|
1089
1230
|
socket.send(JSON.stringify({ type: "reply", msgUid, text }), (error) => {
|
|
1090
1231
|
if (error) reject(error);
|
|
1091
|
-
else
|
|
1232
|
+
else resolve4();
|
|
1092
1233
|
});
|
|
1093
1234
|
});
|
|
1094
1235
|
}
|
|
@@ -1256,13 +1397,17 @@ async function startBot(configPath) {
|
|
|
1256
1397
|
saveConfig: () => saveConfig(config, configPath)
|
|
1257
1398
|
};
|
|
1258
1399
|
holder.queue = new BotQueue((item, signal) => gate(() => processPrompt(ctx, item, signal)));
|
|
1259
|
-
const runtime = createRuntime(
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
switching
|
|
1264
|
-
|
|
1265
|
-
|
|
1400
|
+
const runtime = createRuntime(
|
|
1401
|
+
ctx,
|
|
1402
|
+
(task) => gate(task),
|
|
1403
|
+
(name) => {
|
|
1404
|
+
if (switching) return Promise.reject(new Error("Agent switch already in progress."));
|
|
1405
|
+
switching = true;
|
|
1406
|
+
return gate(() => switchAgent(ctx, name)).finally(() => {
|
|
1407
|
+
switching = false;
|
|
1408
|
+
});
|
|
1409
|
+
}
|
|
1410
|
+
);
|
|
1266
1411
|
const dispatch = createDispatcher(runtime);
|
|
1267
1412
|
let notifySignal = () => void 0;
|
|
1268
1413
|
const wire = new VicvicWire({
|
|
@@ -1323,7 +1468,7 @@ async function runBot(configPath) {
|
|
|
1323
1468
|
const bot = await startBot(configPath);
|
|
1324
1469
|
await bot.finished;
|
|
1325
1470
|
}
|
|
1326
|
-
function createRuntime(ctx, switchAgent2) {
|
|
1471
|
+
function createRuntime(ctx, gate, switchAgent2) {
|
|
1327
1472
|
return {
|
|
1328
1473
|
config: () => ctx.config,
|
|
1329
1474
|
agent: () => ctx.getAgent(),
|
|
@@ -1331,6 +1476,7 @@ function createRuntime(ctx, switchAgent2) {
|
|
|
1331
1476
|
state: () => ctx.state,
|
|
1332
1477
|
saveState: () => ctx.saveState(),
|
|
1333
1478
|
saveConfig: () => ctx.saveConfig(),
|
|
1479
|
+
serialize: (task) => gate(task),
|
|
1334
1480
|
switchAgent: switchAgent2
|
|
1335
1481
|
};
|
|
1336
1482
|
}
|
|
@@ -1382,35 +1528,14 @@ ${error.stderr}`);
|
|
|
1382
1528
|
console.error("[server] agent run failed:", error);
|
|
1383
1529
|
replySafe(reply, `Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
1384
1530
|
}
|
|
1385
|
-
var entry = process.argv[1] ?? "";
|
|
1386
|
-
if (/(^|[\\/])server\.(js|cjs|ts)$/.test(entry)) {
|
|
1387
|
-
const configPath = process.argv[2];
|
|
1388
|
-
if (!configPath) {
|
|
1389
|
-
console.error("Usage: server.js <config-path>");
|
|
1390
|
-
process.exit(1);
|
|
1391
|
-
}
|
|
1392
|
-
runBot((0, import_node_path6.resolve)(configPath)).catch((error) => {
|
|
1393
|
-
console.error(error instanceof Error ? error.message : String(error));
|
|
1394
|
-
const expected = error instanceof ConfigError || error instanceof LockError;
|
|
1395
|
-
process.exit(expected ? 2 : 1);
|
|
1396
|
-
});
|
|
1397
|
-
}
|
|
1398
1531
|
|
|
1399
1532
|
// src/cli.ts
|
|
1400
1533
|
function processName(cwd) {
|
|
1401
|
-
return `vbot-${(0,
|
|
1402
|
-
}
|
|
1403
|
-
var ENV_BIN = "/usr/bin/env";
|
|
1404
|
-
function hasPm2() {
|
|
1405
|
-
try {
|
|
1406
|
-
(0, import_node_child_process2.execFileSync)(ENV_BIN, ["pm2", "--version"], { stdio: "ignore" });
|
|
1407
|
-
return true;
|
|
1408
|
-
} catch {
|
|
1409
|
-
return false;
|
|
1410
|
-
}
|
|
1534
|
+
return `vbot-${(0, import_node_path6.basename)((0, import_node_path6.resolve)(cwd)).replace(/[^a-zA-Z0-9._-]+/g, "-")}`;
|
|
1411
1535
|
}
|
|
1536
|
+
var ENV_BIN2 = "/usr/bin/env";
|
|
1412
1537
|
function pm2(args, capture = false) {
|
|
1413
|
-
return (0,
|
|
1538
|
+
return (0, import_node_child_process3.execFileSync)(ENV_BIN2, ["pm2", ...args], {
|
|
1414
1539
|
stdio: capture ? "pipe" : "inherit",
|
|
1415
1540
|
encoding: "utf8"
|
|
1416
1541
|
});
|
|
@@ -1434,12 +1559,14 @@ var program = new import_commander.Command();
|
|
|
1434
1559
|
program.name("vbot").description("Connect local coding agents to vicvic.im").version(readPackageVersion(cliPath()));
|
|
1435
1560
|
program.command("start").description("Start vbot (prompts for config on first run)").option("--foreground", "run in the foreground instead of pm2").option("-c, --config <path>", "config file path").action(async (options) => {
|
|
1436
1561
|
const { path } = await ensureConfig(options.config);
|
|
1437
|
-
if (options.foreground
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1562
|
+
if (options.foreground) {
|
|
1563
|
+
await runBot(path);
|
|
1564
|
+
return;
|
|
1565
|
+
}
|
|
1566
|
+
if (!hasPm2()) console.log(import_picocolors2.default.yellow("pm2 not found, installing globally..."));
|
|
1567
|
+
if (!ensurePm2()) {
|
|
1568
|
+
console.error(import_picocolors2.default.red("Failed to install pm2. Please install manually: npm i -g pm2"));
|
|
1569
|
+
console.log(import_picocolors2.default.yellow("Running in the foreground instead."));
|
|
1443
1570
|
await runBot(path);
|
|
1444
1571
|
return;
|
|
1445
1572
|
}
|