doer-agent 0.1.9 → 0.2.1
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.js +416 -3
- package/package.json +1 -1
package/dist/agent.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { spawn, spawnSync } from "node:child_process";
|
|
2
2
|
import { existsSync, statSync } from "node:fs";
|
|
3
|
-
import { chmod, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { chmod, mkdir, open, readFile, readdir, stat, writeFile } from "node:fs/promises";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
-
import { AckPolicy, connect, DeliverPolicy, JSONCodec, RetentionPolicy, StorageType } from "nats";
|
|
6
|
+
import { AckPolicy, connect, DeliverPolicy, JSONCodec, RetentionPolicy, StorageType, StringCodec } from "nats";
|
|
7
7
|
const DEFAULT_SERVER_BASE_URL = "https://doer.cranix.net";
|
|
8
8
|
const AGENT_MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
9
9
|
const AGENT_PROJECT_DIR = path.join(AGENT_MODULE_DIR, "..");
|
|
@@ -11,6 +11,8 @@ const AGENT_PACKAGE_JSON_PATH = path.join(AGENT_PROJECT_DIR, "package.json");
|
|
|
11
11
|
let activeTaskLogContext = null;
|
|
12
12
|
const activeTaskCancelRequests = new Map();
|
|
13
13
|
let workspaceRootOverride = null;
|
|
14
|
+
const fsRpcCodec = StringCodec();
|
|
15
|
+
const shellRpcCodec = StringCodec();
|
|
14
16
|
function sanitizeUserId(userId) {
|
|
15
17
|
const normalized = userId.trim().replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
16
18
|
return normalized.length > 0 ? normalized : "anonymous";
|
|
@@ -534,6 +536,380 @@ function resolveTaskWorkspace(rawCwd) {
|
|
|
534
536
|
}
|
|
535
537
|
return resolvedCwd;
|
|
536
538
|
}
|
|
539
|
+
function buildAgentFsRpcSubject(userId, agentId) {
|
|
540
|
+
return `doer.agent.fs.rpc.${sanitizeUserId(userId)}.${agentId.trim()}`;
|
|
541
|
+
}
|
|
542
|
+
function buildAgentShellRpcSubject(userId, agentId) {
|
|
543
|
+
return `doer.agent.shell.rpc.${sanitizeUserId(userId)}.${agentId.trim()}`;
|
|
544
|
+
}
|
|
545
|
+
function normalizeFsRpcPath(rawPath) {
|
|
546
|
+
const root = workspaceRootOverride ?? (process.env.WORKSPACE?.trim() || process.cwd());
|
|
547
|
+
const raw = typeof rawPath === "string" && rawPath.trim() ? rawPath.trim() : ".";
|
|
548
|
+
const normalizedRaw = raw.replace(/\\/g, "/");
|
|
549
|
+
const useAbsolute = path.isAbsolute(normalizedRaw);
|
|
550
|
+
const rel = normalizedRaw.replace(/^\/+/, "") || ".";
|
|
551
|
+
const abs = useAbsolute ? path.resolve(normalizedRaw) : path.resolve(root, rel);
|
|
552
|
+
if (!useAbsolute && abs !== root && !abs.startsWith(root + path.sep)) {
|
|
553
|
+
throw new Error("path escapes workspace root");
|
|
554
|
+
}
|
|
555
|
+
const formatPath = (target) => {
|
|
556
|
+
if (useAbsolute) {
|
|
557
|
+
return target.split(path.sep).join("/") || "/";
|
|
558
|
+
}
|
|
559
|
+
return path.relative(root, target).split(path.sep).join("/") || ".";
|
|
560
|
+
};
|
|
561
|
+
return { abs, formatPath };
|
|
562
|
+
}
|
|
563
|
+
function parseFsRpcAction(value) {
|
|
564
|
+
if (value === "list" || value === "stat" || value === "fetch_file" || value === "read_text") {
|
|
565
|
+
return value;
|
|
566
|
+
}
|
|
567
|
+
throw new Error("unsupported action");
|
|
568
|
+
}
|
|
569
|
+
function normalizeFsRpcNumber(value, fallback) {
|
|
570
|
+
const n = Number(value);
|
|
571
|
+
if (!Number.isFinite(n)) {
|
|
572
|
+
return fallback;
|
|
573
|
+
}
|
|
574
|
+
return Math.floor(n);
|
|
575
|
+
}
|
|
576
|
+
async function executeFsRpc(args) {
|
|
577
|
+
const action = parseFsRpcAction(args.request.action);
|
|
578
|
+
const { abs, formatPath } = normalizeFsRpcPath(args.request.path);
|
|
579
|
+
if (action === "stat") {
|
|
580
|
+
const entry = await stat(abs);
|
|
581
|
+
return {
|
|
582
|
+
ok: true,
|
|
583
|
+
action,
|
|
584
|
+
path: formatPath(abs),
|
|
585
|
+
kind: entry.isDirectory() ? "dir" : "file",
|
|
586
|
+
size: entry.size,
|
|
587
|
+
mtimeMs: entry.mtimeMs,
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
if (action === "list") {
|
|
591
|
+
const entry = await stat(abs);
|
|
592
|
+
if (!entry.isDirectory()) {
|
|
593
|
+
throw new Error("path is not a directory");
|
|
594
|
+
}
|
|
595
|
+
const limit = Math.max(1, Math.min(normalizeFsRpcNumber(args.request.limit, 200), 1000));
|
|
596
|
+
const rows = await readdir(abs, { withFileTypes: true });
|
|
597
|
+
const items = await Promise.all(rows.map(async (row) => {
|
|
598
|
+
const child = path.join(abs, row.name);
|
|
599
|
+
const childStat = await stat(child);
|
|
600
|
+
return {
|
|
601
|
+
name: row.name,
|
|
602
|
+
path: formatPath(child),
|
|
603
|
+
kind: row.isDirectory() ? "dir" : "file",
|
|
604
|
+
size: childStat.size,
|
|
605
|
+
mtimeMs: childStat.mtimeMs,
|
|
606
|
+
};
|
|
607
|
+
}));
|
|
608
|
+
items.sort((a, b) => (a.kind === b.kind ? a.name.localeCompare(b.name) : a.kind === "dir" ? -1 : 1));
|
|
609
|
+
return {
|
|
610
|
+
ok: true,
|
|
611
|
+
action,
|
|
612
|
+
path: formatPath(abs),
|
|
613
|
+
items: items.slice(0, limit),
|
|
614
|
+
truncated: items.length > limit,
|
|
615
|
+
total: items.length,
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
if (action === "fetch_file") {
|
|
619
|
+
const entry = await stat(abs);
|
|
620
|
+
if (!entry.isFile()) {
|
|
621
|
+
throw new Error("path is not a file");
|
|
622
|
+
}
|
|
623
|
+
const uploadUrl = typeof args.request.uploadUrl === "string" ? args.request.uploadUrl : "";
|
|
624
|
+
const chatId = typeof args.request.chatId === "string" ? args.request.chatId : "";
|
|
625
|
+
const agentId = typeof args.request.agentId === "string" ? args.request.agentId : "";
|
|
626
|
+
if (!uploadUrl || !chatId || !agentId) {
|
|
627
|
+
throw new Error("missing upload parameters");
|
|
628
|
+
}
|
|
629
|
+
const data = await readFile(abs);
|
|
630
|
+
const fileName = path.basename(abs) || "file";
|
|
631
|
+
const form = new FormData();
|
|
632
|
+
form.append("file", new File([data], fileName));
|
|
633
|
+
form.append("chatId", chatId);
|
|
634
|
+
form.append("agentId", agentId);
|
|
635
|
+
const response = await fetch(uploadUrl, {
|
|
636
|
+
method: "POST",
|
|
637
|
+
headers: { Authorization: `Bearer ${args.agentToken}` },
|
|
638
|
+
body: form,
|
|
639
|
+
});
|
|
640
|
+
const text = await response.text();
|
|
641
|
+
let upload = {};
|
|
642
|
+
try {
|
|
643
|
+
upload = JSON.parse(text || "{}");
|
|
644
|
+
}
|
|
645
|
+
catch {
|
|
646
|
+
upload = {};
|
|
647
|
+
}
|
|
648
|
+
if (!response.ok) {
|
|
649
|
+
const message = typeof upload.error === "string" ? upload.error : `upload failed: ${response.status}`;
|
|
650
|
+
throw new Error(message);
|
|
651
|
+
}
|
|
652
|
+
return {
|
|
653
|
+
ok: true,
|
|
654
|
+
action,
|
|
655
|
+
path: formatPath(abs),
|
|
656
|
+
size: entry.size,
|
|
657
|
+
upload,
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
const entry = await stat(abs);
|
|
661
|
+
if (!entry.isFile()) {
|
|
662
|
+
throw new Error("path is not a file");
|
|
663
|
+
}
|
|
664
|
+
const offset = Math.max(0, normalizeFsRpcNumber(args.request.offset, 0));
|
|
665
|
+
const length = Math.max(1, Math.min(normalizeFsRpcNumber(args.request.length, 65536), 262144));
|
|
666
|
+
const encoding = typeof args.request.encoding === "string" && args.request.encoding ? args.request.encoding : "utf8";
|
|
667
|
+
const fd = await open(abs, "r");
|
|
668
|
+
try {
|
|
669
|
+
const buffer = Buffer.alloc(length);
|
|
670
|
+
const readResult = await fd.read(buffer, 0, length, offset);
|
|
671
|
+
const slice = buffer.subarray(0, readResult.bytesRead);
|
|
672
|
+
try {
|
|
673
|
+
const text = slice.toString(encoding);
|
|
674
|
+
return {
|
|
675
|
+
ok: true,
|
|
676
|
+
action,
|
|
677
|
+
path: formatPath(abs),
|
|
678
|
+
offset,
|
|
679
|
+
length: readResult.bytesRead,
|
|
680
|
+
totalSize: entry.size,
|
|
681
|
+
eof: offset + readResult.bytesRead >= entry.size,
|
|
682
|
+
encoding,
|
|
683
|
+
text,
|
|
684
|
+
bytesRead: readResult.bytesRead,
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
catch (error) {
|
|
688
|
+
const message = error instanceof Error ? error.message : "failed to decode text";
|
|
689
|
+
return {
|
|
690
|
+
ok: false,
|
|
691
|
+
action,
|
|
692
|
+
path: formatPath(abs),
|
|
693
|
+
error: message,
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
finally {
|
|
698
|
+
await fd.close();
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
async function handleFsRpcMessage(args) {
|
|
702
|
+
let payload = {};
|
|
703
|
+
try {
|
|
704
|
+
payload = JSON.parse(fsRpcCodec.decode(args.msg.data));
|
|
705
|
+
if (typeof payload.agentId === "string" && payload.agentId.trim() && payload.agentId !== args.agentId) {
|
|
706
|
+
throw new Error("agent id mismatch");
|
|
707
|
+
}
|
|
708
|
+
const result = await executeFsRpc({ request: payload, agentToken: args.agentToken });
|
|
709
|
+
args.msg.respond(fsRpcCodec.encode(JSON.stringify(result)));
|
|
710
|
+
}
|
|
711
|
+
catch (error) {
|
|
712
|
+
const message = error instanceof Error ? error.message : "unknown error";
|
|
713
|
+
const action = typeof payload.action === "string" ? payload.action : "";
|
|
714
|
+
const response = {
|
|
715
|
+
ok: false,
|
|
716
|
+
action,
|
|
717
|
+
path: typeof payload.path === "string" ? payload.path : ".",
|
|
718
|
+
error: message,
|
|
719
|
+
};
|
|
720
|
+
args.msg.respond(fsRpcCodec.encode(JSON.stringify(response)));
|
|
721
|
+
writeAgentError(`fs rpc failed action=${action || "unknown"} error=${message}`);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
function subscribeToFsRpc(args) {
|
|
725
|
+
const subject = buildAgentFsRpcSubject(args.userId, args.agentId);
|
|
726
|
+
args.jetstream.nc.subscribe(subject, {
|
|
727
|
+
callback: (error, msg) => {
|
|
728
|
+
if (error) {
|
|
729
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
730
|
+
writeAgentError(`fs rpc subscription error: ${message}`);
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
void handleFsRpcMessage({
|
|
734
|
+
msg,
|
|
735
|
+
serverBaseUrl: args.serverBaseUrl,
|
|
736
|
+
userId: args.userId,
|
|
737
|
+
agentId: args.agentId,
|
|
738
|
+
agentToken: args.agentToken,
|
|
739
|
+
});
|
|
740
|
+
},
|
|
741
|
+
});
|
|
742
|
+
writeAgentInfo(`fs rpc subscribed subject=${subject}`);
|
|
743
|
+
}
|
|
744
|
+
function normalizeShellRpcRequest(args) {
|
|
745
|
+
const requestId = typeof args.request.requestId === "string" ? args.request.requestId.trim() : "";
|
|
746
|
+
if (!requestId) {
|
|
747
|
+
throw new Error("missing requestId");
|
|
748
|
+
}
|
|
749
|
+
const requestAgentId = typeof args.request.agentId === "string" ? args.request.agentId.trim() : "";
|
|
750
|
+
if (!requestAgentId) {
|
|
751
|
+
throw new Error("missing agentId");
|
|
752
|
+
}
|
|
753
|
+
if (requestAgentId !== args.agentId) {
|
|
754
|
+
throw new Error("agent id mismatch");
|
|
755
|
+
}
|
|
756
|
+
const command = typeof args.request.command === "string" ? args.request.command.trim() : "";
|
|
757
|
+
if (!command) {
|
|
758
|
+
throw new Error("missing command");
|
|
759
|
+
}
|
|
760
|
+
const responseSubject = typeof args.request.responseSubject === "string" ? args.request.responseSubject.trim() : "";
|
|
761
|
+
if (!responseSubject) {
|
|
762
|
+
throw new Error("missing responseSubject");
|
|
763
|
+
}
|
|
764
|
+
const cwd = typeof args.request.cwd === "string" && args.request.cwd.trim() ? args.request.cwd.trim() : null;
|
|
765
|
+
const timeoutRaw = Number(args.request.timeoutMs);
|
|
766
|
+
const timeoutMs = Number.isFinite(timeoutRaw) ? Math.max(1000, Math.min(Math.floor(timeoutRaw), 300000)) : 30000;
|
|
767
|
+
return {
|
|
768
|
+
requestId,
|
|
769
|
+
command,
|
|
770
|
+
cwd,
|
|
771
|
+
timeoutMs,
|
|
772
|
+
responseSubject,
|
|
773
|
+
runtimeEnvPatch: normalizeEnvPatch(args.request.runtimeEnvPatch),
|
|
774
|
+
codexAuthBundle: normalizeShellRpcCodexAuthBundle(args.request.codexAuth),
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
function normalizeShellRpcCodexAuthBundle(value) {
|
|
778
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
779
|
+
return null;
|
|
780
|
+
}
|
|
781
|
+
const row = value;
|
|
782
|
+
const authJson = typeof row.authJson === "string" ? row.authJson : null;
|
|
783
|
+
if (!authJson) {
|
|
784
|
+
return null;
|
|
785
|
+
}
|
|
786
|
+
return {
|
|
787
|
+
taskId: typeof row.taskId === "string" ? row.taskId : undefined,
|
|
788
|
+
authMode: row.authMode === "oauth" ? "oauth" : row.authMode === "api_key" ? "api_key" : undefined,
|
|
789
|
+
issuedAt: typeof row.issuedAt === "string" ? row.issuedAt : undefined,
|
|
790
|
+
expiresAt: typeof row.expiresAt === "string" ? row.expiresAt : undefined,
|
|
791
|
+
authJson,
|
|
792
|
+
apiKey: typeof row.apiKey === "string" || row.apiKey === null ? row.apiKey : undefined,
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
function publishShellRpcResponse(args) {
|
|
796
|
+
args.nc.publish(args.responseSubject, shellRpcCodec.encode(JSON.stringify(args.payload)));
|
|
797
|
+
}
|
|
798
|
+
async function handleShellRpcMessage(args) {
|
|
799
|
+
let requestId = "unknown";
|
|
800
|
+
let responseSubject = "";
|
|
801
|
+
let stdout = "";
|
|
802
|
+
let stderr = "";
|
|
803
|
+
try {
|
|
804
|
+
const payload = JSON.parse(shellRpcCodec.decode(args.msg.data));
|
|
805
|
+
const request = normalizeShellRpcRequest({ request: payload, agentId: args.agentId });
|
|
806
|
+
requestId = request.requestId;
|
|
807
|
+
responseSubject = request.responseSubject;
|
|
808
|
+
const shellPath = resolveShellPath();
|
|
809
|
+
const taskWorkspace = resolveTaskWorkspace(request.cwd);
|
|
810
|
+
const codexAuth = await prepareCodexAuthBundle(request.codexAuthBundle);
|
|
811
|
+
const baseTaskEnvPatch = {
|
|
812
|
+
...request.runtimeEnvPatch,
|
|
813
|
+
...(codexAuth?.envPatch ?? {}),
|
|
814
|
+
WORKSPACE: taskWorkspace,
|
|
815
|
+
};
|
|
816
|
+
const taskGitEnv = await prepareTaskGitEnv({
|
|
817
|
+
cwd: taskWorkspace,
|
|
818
|
+
baseEnvPatch: baseTaskEnvPatch,
|
|
819
|
+
});
|
|
820
|
+
const runtimeBinPath = path.join(AGENT_PROJECT_DIR, "runtime/bin");
|
|
821
|
+
const taskPath = [runtimeBinPath, process.env.PATH || ""].filter(Boolean).join(path.delimiter);
|
|
822
|
+
const child = spawn(request.command, {
|
|
823
|
+
cwd: taskWorkspace,
|
|
824
|
+
shell: shellPath,
|
|
825
|
+
detached: process.platform !== "win32",
|
|
826
|
+
env: {
|
|
827
|
+
...process.env,
|
|
828
|
+
...baseTaskEnvPatch,
|
|
829
|
+
...taskGitEnv.envPatch,
|
|
830
|
+
PATH: taskPath,
|
|
831
|
+
DOER_AGENT_TOKEN: args.agentToken,
|
|
832
|
+
},
|
|
833
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
834
|
+
});
|
|
835
|
+
child.stdout.setEncoding("utf8");
|
|
836
|
+
child.stderr.setEncoding("utf8");
|
|
837
|
+
child.stdout.on("data", (chunk) => {
|
|
838
|
+
stdout += chunk;
|
|
839
|
+
});
|
|
840
|
+
child.stderr.on("data", (chunk) => {
|
|
841
|
+
stderr += chunk;
|
|
842
|
+
});
|
|
843
|
+
let timedOut = false;
|
|
844
|
+
const timeout = setTimeout(() => {
|
|
845
|
+
timedOut = true;
|
|
846
|
+
sendSignalToTaskProcess(child, "SIGTERM");
|
|
847
|
+
setTimeout(() => {
|
|
848
|
+
sendSignalToTaskProcess(child, "SIGKILL");
|
|
849
|
+
}, 1000).unref?.();
|
|
850
|
+
}, request.timeoutMs);
|
|
851
|
+
timeout.unref?.();
|
|
852
|
+
const result = await new Promise((resolve, reject) => {
|
|
853
|
+
child.once("error", reject);
|
|
854
|
+
child.once("close", (code, signal) => {
|
|
855
|
+
resolve({ exitCode: typeof code === "number" ? code : null, signal });
|
|
856
|
+
});
|
|
857
|
+
}).finally(() => {
|
|
858
|
+
clearTimeout(timeout);
|
|
859
|
+
});
|
|
860
|
+
publishShellRpcResponse({
|
|
861
|
+
nc: args.jetstream.nc,
|
|
862
|
+
responseSubject,
|
|
863
|
+
payload: {
|
|
864
|
+
requestId,
|
|
865
|
+
ok: !timedOut,
|
|
866
|
+
exitCode: result.exitCode,
|
|
867
|
+
signal: result.signal,
|
|
868
|
+
stdout,
|
|
869
|
+
stderr,
|
|
870
|
+
...(timedOut ? { error: `Command timed out after ${request.timeoutMs}ms` } : {}),
|
|
871
|
+
},
|
|
872
|
+
});
|
|
873
|
+
}
|
|
874
|
+
catch (error) {
|
|
875
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
876
|
+
if (responseSubject) {
|
|
877
|
+
publishShellRpcResponse({
|
|
878
|
+
nc: args.jetstream.nc,
|
|
879
|
+
responseSubject,
|
|
880
|
+
payload: {
|
|
881
|
+
requestId,
|
|
882
|
+
ok: false,
|
|
883
|
+
exitCode: null,
|
|
884
|
+
signal: null,
|
|
885
|
+
stdout,
|
|
886
|
+
stderr,
|
|
887
|
+
error: message,
|
|
888
|
+
},
|
|
889
|
+
});
|
|
890
|
+
}
|
|
891
|
+
writeAgentError(`shell rpc failed requestId=${requestId} error=${message}`);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
function subscribeToShellRpc(args) {
|
|
895
|
+
const subject = buildAgentShellRpcSubject(args.userId, args.agentId);
|
|
896
|
+
args.jetstream.nc.subscribe(subject, {
|
|
897
|
+
callback: (error, msg) => {
|
|
898
|
+
if (error) {
|
|
899
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
900
|
+
writeAgentError(`shell rpc subscription error: ${message}`);
|
|
901
|
+
return;
|
|
902
|
+
}
|
|
903
|
+
void handleShellRpcMessage({
|
|
904
|
+
msg,
|
|
905
|
+
jetstream: args.jetstream,
|
|
906
|
+
agentId: args.agentId,
|
|
907
|
+
agentToken: args.agentToken,
|
|
908
|
+
});
|
|
909
|
+
},
|
|
910
|
+
});
|
|
911
|
+
writeAgentInfo(`shell rpc subscribed subject=${subject}`);
|
|
912
|
+
}
|
|
537
913
|
async function postJson(url, body) {
|
|
538
914
|
const res = await fetch(url, {
|
|
539
915
|
method: "POST",
|
|
@@ -705,6 +1081,9 @@ async function prepareTaskCodexAuth(args) {
|
|
|
705
1081
|
writeAgentError(`task=${args.taskId} codex auth sync skipped: ${message}`);
|
|
706
1082
|
return null;
|
|
707
1083
|
});
|
|
1084
|
+
return await prepareCodexAuthBundle(bundle);
|
|
1085
|
+
}
|
|
1086
|
+
async function prepareCodexAuthBundle(bundle) {
|
|
708
1087
|
if (!bundle || typeof bundle.authJson !== "string") {
|
|
709
1088
|
return null;
|
|
710
1089
|
}
|
|
@@ -980,12 +1359,16 @@ async function main() {
|
|
|
980
1359
|
});
|
|
981
1360
|
const maxConcurrency = Math.max(1, parseEnvInteger(process.env.DOER_AGENT_MAX_CONCURRENCY, 5));
|
|
982
1361
|
const agentVersion = await resolveAgentVersion();
|
|
1362
|
+
const initialAgentId = typeof natsBootstrap.agentId === "string" ? natsBootstrap.agentId : "";
|
|
1363
|
+
if (!initialAgentId) {
|
|
1364
|
+
throw new Error("agent id missing from bootstrap");
|
|
1365
|
+
}
|
|
983
1366
|
process.stdout.write(`\n[doer-agent v${agentVersion}]\n`);
|
|
984
1367
|
if (!usesDefaultServer) {
|
|
985
1368
|
process.stdout.write(`- server: ${serverBaseUrl}\n`);
|
|
986
1369
|
}
|
|
987
1370
|
process.stdout.write(`- userId: ${userId}\n`);
|
|
988
|
-
process.stdout.write(`- agentId: ${
|
|
1371
|
+
process.stdout.write(`- agentId: ${initialAgentId}\n`);
|
|
989
1372
|
process.stdout.write(`\n- transport: nats\n`);
|
|
990
1373
|
process.stdout.write(`- natsServers: ${jetstream.servers.join(",")}\n`);
|
|
991
1374
|
process.stdout.write(`- natsStream: ${jetstream.stream}\n`);
|
|
@@ -1038,6 +1421,19 @@ async function main() {
|
|
|
1038
1421
|
const taskPromise = taskPromiseFactory();
|
|
1039
1422
|
trackInFlight(taskPromise);
|
|
1040
1423
|
}
|
|
1424
|
+
subscribeToFsRpc({
|
|
1425
|
+
jetstream,
|
|
1426
|
+
serverBaseUrl,
|
|
1427
|
+
userId,
|
|
1428
|
+
agentId: initialAgentId,
|
|
1429
|
+
agentToken,
|
|
1430
|
+
});
|
|
1431
|
+
subscribeToShellRpc({
|
|
1432
|
+
jetstream,
|
|
1433
|
+
userId,
|
|
1434
|
+
agentId: initialAgentId,
|
|
1435
|
+
agentToken,
|
|
1436
|
+
});
|
|
1041
1437
|
for (const pendingTaskId of pendingTaskIds) {
|
|
1042
1438
|
await waitForAvailableSlot();
|
|
1043
1439
|
scheduleTask(async () => {
|
|
@@ -1167,6 +1563,23 @@ async function main() {
|
|
|
1167
1563
|
natsBootstrap = refreshed.natsBootstrap;
|
|
1168
1564
|
pendingTaskIds = refreshed.pendingTaskIds;
|
|
1169
1565
|
jetstream = refreshed.jetstream;
|
|
1566
|
+
const refreshedAgentId = typeof natsBootstrap.agentId === "string" ? natsBootstrap.agentId : "";
|
|
1567
|
+
if (!refreshedAgentId) {
|
|
1568
|
+
throw new Error("agent id missing from refreshed bootstrap");
|
|
1569
|
+
}
|
|
1570
|
+
subscribeToFsRpc({
|
|
1571
|
+
jetstream,
|
|
1572
|
+
serverBaseUrl,
|
|
1573
|
+
userId,
|
|
1574
|
+
agentId: refreshedAgentId,
|
|
1575
|
+
agentToken,
|
|
1576
|
+
});
|
|
1577
|
+
subscribeToShellRpc({
|
|
1578
|
+
jetstream,
|
|
1579
|
+
userId,
|
|
1580
|
+
agentId: refreshedAgentId,
|
|
1581
|
+
agentToken,
|
|
1582
|
+
});
|
|
1170
1583
|
for (const pendingTaskId of pendingTaskIds) {
|
|
1171
1584
|
await waitForAvailableSlot();
|
|
1172
1585
|
scheduleTask(async () => {
|