mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
A sandbox is a new env type (`type: "gondolin"`): a long-lived Gondolin micro-VM on an SSH environment, with listed host folders and an HTTP(S) host allowlist. The worker reaches it over SSH to the host, where p2's launcher (plugins/envs/sandbox: native, or Docker when the host's Node/QEMU are too old) runs pi-env's daemon inside the VM, so tools, workspace, terminals, uploads and MCP work unchanged. Names outside the allowlist are refused before the host resolves them. Conversations working in a sandbox cannot leave it: location tools, subagent spawn/configure, AgentLocations.move, the terminal tool, and one-shot agents (which now inherit their caller's own location) all keep them there; only the owner's Relocate moves a session out, after a confirmation. conversationConfinement() exposes it to plugins. Owner-only management in the Envs sheet: sandboxes listed under their host, host test reports sandbox support, create/edit/start/stop/restart/test, lock icons in the top bar and sidebar. Tests in test/p2/sandboxes.test.ts (real VM cases with P2_TEST_GONDOLIN=1) and ui-sandboxes.test.ts; docs in docs/remote-envs.md.
46 lines
1.7 KiB
TypeScript
46 lines
1.7 KiB
TypeScript
// The supervisor's pi-env connections to environments: one per environment and configuration revision, shared by every
|
|
// instance running there and closed with the last one. A connection lost meanwhile starts again at its next request
|
|
// (pi-env); the daemon killed the programs it ran, and their instances reconnect.
|
|
|
|
import type { Connection } from "@earendil-works/pi-env";
|
|
import type { EnvConfig } from "../envs/config.ts";
|
|
|
|
/** A connection to environment `name` configured as `config`, which starts at its first request. */
|
|
export type ConnectEnv = (name: string, config: EnvConfig) => Connection;
|
|
|
|
/** One instance's use of an environment's connection; released once, when the instance closes. */
|
|
export type EnvLease = { readonly connection: Connection; release(): void };
|
|
|
|
/** The open connections by environment and revision, with how many instances use each. */
|
|
export class EnvConnections {
|
|
readonly #connect: ConnectEnv;
|
|
readonly #open = new Map<string, { readonly connection: Connection; users: number }>();
|
|
|
|
constructor(connect: ConnectEnv) {
|
|
this.#connect = connect;
|
|
}
|
|
|
|
/**
|
|
* The connection to `name` as configured now (`revision`: its connection revision; opened for its first user);
|
|
* release it once done.
|
|
*/
|
|
acquire(name: string, config: EnvConfig, revision: string): EnvLease {
|
|
const key = JSON.stringify([name, revision]);
|
|
let entry = this.#open.get(key);
|
|
if (entry === undefined) {
|
|
entry = { connection: this.#connect(name, config), users: 0 };
|
|
this.#open.set(key, entry);
|
|
}
|
|
const used = entry;
|
|
used.users++;
|
|
return {
|
|
connection: used.connection,
|
|
release: () => {
|
|
used.users--;
|
|
if (used.users > 0) return;
|
|
this.#open.delete(key);
|
|
used.connection.close();
|
|
},
|
|
};
|
|
}
|
|
}
|