chore: portless setup
This commit is contained in:
parent
e4898b97ea
commit
4ea9f34154
8 changed files with 42 additions and 13 deletions
|
|
@ -4,6 +4,6 @@ RESTIC_CACHE_DIR=./data/restic/cache
|
|||
ZEROBYTE_REPOSITORIES_DIR=./data/repositories
|
||||
ZEROBYTE_VOLUMES_DIR=./data/volumes
|
||||
APP_SECRET=<openssl rand -hex 32>
|
||||
BASE_URL=http://localhost:300
|
||||
BASE_URL=http://*.localhost
|
||||
TRUST_PROXY=false
|
||||
PORT=3000
|
||||
|
|
|
|||
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -15,7 +15,7 @@ notes.md
|
|||
smb-password.txt
|
||||
cache.db
|
||||
|
||||
data/
|
||||
/data
|
||||
|
||||
.env*
|
||||
!.env.example
|
||||
|
|
@ -34,8 +34,11 @@ node_modules/
|
|||
|
||||
playwright/.auth
|
||||
playwright/temp
|
||||
playwright/data
|
||||
|
||||
.idea/
|
||||
.deepsec/
|
||||
.codex/
|
||||
|
||||
# OpenAPI error logs
|
||||
openapi-ts-error-*.log
|
||||
|
|
|
|||
|
|
@ -22,7 +22,11 @@ const processWithAgentRuntime = process as ProcessWithAgentRuntime;
|
|||
|
||||
const setAgentRuntime = () => {
|
||||
processWithAgentRuntime.__zerobyteAgentRuntime = {
|
||||
agentManager: fromAny({ stop: Effect.void, waitForAgentReady: vi.fn(async () => true) }),
|
||||
agentManager: fromAny({
|
||||
stop: Effect.void,
|
||||
getControllerUrl: vi.fn(() => "ws://127.0.0.1:4567"),
|
||||
waitForAgentReady: vi.fn(async () => true),
|
||||
}),
|
||||
localAgent: null,
|
||||
isStoppingLocalAgent: false,
|
||||
localAgentRestartTimeout: null,
|
||||
|
|
@ -88,6 +92,13 @@ test("respawns the local agent after an unexpected exit", async () => {
|
|||
await vi.advanceTimersByTimeAsync(1_000);
|
||||
|
||||
expect(spawnMock).toHaveBeenCalledTimes(2);
|
||||
expect(spawnMock).toHaveBeenLastCalledWith(
|
||||
"bun",
|
||||
expect.any(Array),
|
||||
expect.objectContaining({
|
||||
env: expect.objectContaining({ ZEROBYTE_CONTROLLER_URL: "ws://127.0.0.1:4567" }),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
test("does not respawn the local agent after an intentional stop", async () => {
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ afterEach(() => {
|
|||
test("websocket fetch rejects requests without a bearer token", async () => {
|
||||
const serve = vi
|
||||
.spyOn(Bun, "serve")
|
||||
.mockReturnValue(fromPartial({ port: 3001, stop: vi.fn(() => Promise.resolve()) }));
|
||||
.mockReturnValue(fromPartial({ port: 4567, stop: vi.fn(() => Promise.resolve()) }));
|
||||
const { runtime } = await startRuntime();
|
||||
const fetch = serve.mock.calls[0]?.[0].fetch;
|
||||
const upgrade = vi.fn();
|
||||
|
|
@ -120,6 +120,7 @@ test("websocket fetch rejects requests without a bearer token", async () => {
|
|||
const response = await invokeFetch(fetch, new Request("http://localhost:3001/agent"), srv);
|
||||
await Effect.runPromise(runtime.stop);
|
||||
|
||||
expect(runtime.getControllerUrl()).toBeNull();
|
||||
expect(response?.status).toBe(401);
|
||||
expect(await response?.text()).toBe("Missing token");
|
||||
expect(upgrade).not.toHaveBeenCalled();
|
||||
|
|
@ -129,8 +130,9 @@ test("websocket fetch rejects invalid bearer tokens", async () => {
|
|||
tokenMocks.validateAgentToken.mockResolvedValue(undefined);
|
||||
const serve = vi
|
||||
.spyOn(Bun, "serve")
|
||||
.mockReturnValue(fromPartial({ port: 3001, stop: vi.fn(() => Promise.resolve()) }));
|
||||
.mockReturnValue(fromPartial({ port: 4567, stop: vi.fn(() => Promise.resolve()) }));
|
||||
const { runtime } = await startRuntime();
|
||||
expect(runtime.getControllerUrl()).toBe("ws://127.0.0.1:4567");
|
||||
const fetch = serve.mock.calls[0]?.[0].fetch;
|
||||
const upgrade = vi.fn();
|
||||
const srv = fromPartial<Parameters<NonNullable<typeof fetch>>[1]>({ upgrade });
|
||||
|
|
|
|||
|
|
@ -311,14 +311,20 @@ export const agentManager = {
|
|||
|
||||
export const startLocalAgent = async () => {
|
||||
const runtime = getAgentRuntimeState();
|
||||
await spawnLocalAgentProcess(runtime);
|
||||
|
||||
if (!runtime.agentManager) {
|
||||
throw new Error(
|
||||
`startLocalAgent spawned ${LOCAL_AGENT_ID} via spawnLocalAgentProcess, but runtime.agentManager is missing; waitForAgentReady cannot check readiness`,
|
||||
`startLocalAgent cannot spawn ${LOCAL_AGENT_ID} because runtime.agentManager is missing; waitForAgentReady cannot check readiness`,
|
||||
);
|
||||
}
|
||||
|
||||
const controllerUrl = runtime.agentManager.getControllerUrl();
|
||||
if (!controllerUrl) {
|
||||
throw new Error(`startLocalAgent cannot spawn ${LOCAL_AGENT_ID} because the controller URL is not available`);
|
||||
}
|
||||
|
||||
await spawnLocalAgentProcess(runtime, controllerUrl);
|
||||
|
||||
if (!(await runtime.agentManager.waitForAgentReady(LOCAL_AGENT_ID))) {
|
||||
throw new Error("Local agent did not become ready before startup");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ class StopAgentManagerServerError extends Data.TaggedError("StopAgentManagerServ
|
|||
export function createAgentManagerRuntime(onEvent: (event: AgentManagerEvent) => void) {
|
||||
let sessions = new Map<string, ControllerAgentSessionHandle>();
|
||||
let runtimeScope: Scope.CloseableScope | null = null;
|
||||
let controllerUrl: string | null = null;
|
||||
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
const closeSession = (sessionHandle: ControllerAgentSessionHandle) =>
|
||||
|
|
@ -215,7 +216,8 @@ export function createAgentManagerRuntime(onEvent: (event: AgentManagerEvent) =>
|
|||
const acquireServer = Effect.acquireRelease(
|
||||
Effect.sync(() =>
|
||||
Bun.serve<AgentConnectionData>({
|
||||
port: 3001,
|
||||
hostname: "127.0.0.1",
|
||||
port: 0,
|
||||
async fetch(req, srv) {
|
||||
const authorizationHeader = req.headers.get("authorization");
|
||||
const token = authorizationHeader?.slice("Bearer ".length);
|
||||
|
|
@ -279,6 +281,7 @@ export function createAgentManagerRuntime(onEvent: (event: AgentManagerEvent) =>
|
|||
logger.info("Stopping Agent Manager...");
|
||||
const scope = runtimeScope;
|
||||
runtimeScope = null;
|
||||
controllerUrl = null;
|
||||
yield* Scope.close(scope, Exit.succeed(undefined));
|
||||
});
|
||||
|
||||
|
|
@ -296,11 +299,13 @@ export function createAgentManagerRuntime(onEvent: (event: AgentManagerEvent) =>
|
|||
),
|
||||
);
|
||||
runtimeScope = scope;
|
||||
controllerUrl = `ws://127.0.0.1:${server.port}`;
|
||||
logger.info(`Agent Manager listening on port ${server.port}`);
|
||||
});
|
||||
|
||||
return {
|
||||
start,
|
||||
getControllerUrl: () => controllerUrl,
|
||||
waitForAgentReady: async (agentId: string, timeoutMs = 10_000) => {
|
||||
const deadline = Date.now() + timeoutMs;
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ type LocalAgentState = {
|
|||
localAgentRestartTimeout: ReturnType<typeof setTimeout> | null;
|
||||
};
|
||||
|
||||
export async function spawnLocalAgentProcess(runtime: LocalAgentState) {
|
||||
export async function spawnLocalAgentProcess(runtime: LocalAgentState, controllerUrl: string) {
|
||||
await stopLocalAgentProcess(runtime);
|
||||
|
||||
if (!config.flags.enableLocalAgent) {
|
||||
|
|
@ -31,7 +31,7 @@ export async function spawnLocalAgentProcess(runtime: LocalAgentState) {
|
|||
const agentProcess = spawn("bun", args, {
|
||||
env: {
|
||||
...process.env,
|
||||
ZEROBYTE_CONTROLLER_URL: "ws://localhost:3001",
|
||||
ZEROBYTE_CONTROLLER_URL: controllerUrl,
|
||||
ZEROBYTE_AGENT_TOKEN: agentToken,
|
||||
},
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
|
|
@ -62,8 +62,10 @@ export async function spawnLocalAgentProcess(runtime: LocalAgentState) {
|
|||
|
||||
runtime.localAgentRestartTimeout = setTimeout(() => {
|
||||
runtime.localAgentRestartTimeout = null;
|
||||
void spawnLocalAgentProcess(runtime).catch((error) => {
|
||||
logger.error(`Failed to restart local agent: ${error instanceof Error ? error.message : String(error)}`);
|
||||
void spawnLocalAgentProcess(runtime, controllerUrl).catch((error) => {
|
||||
logger.error(
|
||||
`Failed to restart local agent: ${error instanceof Error ? error.message : String(error)}`,
|
||||
);
|
||||
});
|
||||
}, 1_000);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
"fmt": "vp fmt",
|
||||
"lint": "vp lint --type-aware --type-check",
|
||||
"check": "vp check",
|
||||
"dev": "NODE_ENV=development bunx --bun vite",
|
||||
"dev": "NODE_ENV=development portless run bunx --bun vite",
|
||||
"build": "bunx --bun vite build",
|
||||
"start": "bun run .output/server/index.mjs",
|
||||
"build:backend-integration": "bun build app/test/backend-integration/index.ts --outdir .output/backend-integration --target bun",
|
||||
|
|
|
|||
Loading…
Reference in a new issue