43 lines · 1.8 KB · TypeScript
RawDownload
import * as NodeCrypto from "node:crypto";import * as Schema from "effect/Schema";import * as Effect from "effect/Effect";import * as FileSystem from "effect/FileSystem";import * as Path from "effect/Path"; import type { AgentDeviceEndpoint } from "./DeviceHost.ts"; const encodeEndpoint = Schema.encodeEffect(  Schema.fromJsonString(    Schema.Struct({ daemonBaseUrl: Schema.String, daemonAuthToken: Schema.String }),  ),); const key = (value: string) =>  NodeCrypto.createHash("sha256").update(value).digest("hex").slice(0, 24); /** A stable file per host lets forwarded endpoints change without retargeting other commands. */export const agentDeviceConfigPath = (stateDir: string, hostId: string, path: Path.Path) =>  path.join(stateDir, "device", "hosts", `${key(hostId)}.json`); export const agentDeviceSession = (threadId: string, hostId: string, deviceId: string) =>  `t3-${key(JSON.stringify([threadId, hostId, deviceId]))}`; export const writeAgentDeviceConfig = Effect.fn("AgentDeviceTarget.writeConfig")(function* (  file: string,  endpoint: AgentDeviceEndpoint,) {  const fs = yield* FileSystem.FileSystem;  const path = yield* Path.Path;  yield* fs.makeDirectory(path.dirname(file), { recursive: true });  const content = yield* encodeEndpoint({    daemonBaseUrl: endpoint.baseUrl,    daemonAuthToken: endpoint.token,  });  if ((yield* fs.readFileString(file).pipe(Effect.orElseSucceed(() => ""))) === content) return;  const temporary = yield* fs.makeTempFile({ directory: path.dirname(file), prefix: ".endpoint-" });  yield* Effect.gen(function* () {    yield* fs.chmod(temporary, 0o600);    yield* fs.writeFileString(temporary, content);    yield* fs.rename(temporary, file);  }).pipe(Effect.ensuring(fs.remove(temporary, { force: true }).pipe(Effect.ignore)));});