@geminixiang/mikan 1.0.0-beta.77 → 1.0.0-beta.78
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/CHANGELOG.md +19 -0
- package/dist/adapters/web/login/oauth.d.ts.map +1 -1
- package/dist/adapters/web/login/oauth.js +3 -2
- package/dist/adapters/web/login/oauth.js.map +1 -1
- package/dist/adapters/web/login/portal.js +3 -2
- package/dist/adapters/web/login/portal.js.map +1 -1
- package/dist/cli/boot.d.ts.map +1 -1
- package/dist/cli/boot.js +3 -1
- package/dist/cli/boot.js.map +1 -1
- package/dist/cli/sandbox.d.ts +3 -0
- package/dist/cli/sandbox.d.ts.map +1 -0
- package/dist/cli/sandbox.js +79 -0
- package/dist/cli/sandbox.js.map +1 -0
- package/dist/cli/types.d.ts +2 -1
- package/dist/cli/types.d.ts.map +1 -1
- package/dist/cli/types.js.map +1 -1
- package/dist/harness/index.d.ts +4 -0
- package/dist/harness/index.d.ts.map +1 -1
- package/dist/harness/index.js +4 -0
- package/dist/harness/index.js.map +1 -1
- package/dist/harness/mcp-result.d.ts +20 -0
- package/dist/harness/mcp-result.d.ts.map +1 -0
- package/dist/harness/mcp-result.js +171 -0
- package/dist/harness/mcp-result.js.map +1 -0
- package/dist/harness/mcp.d.ts.map +1 -1
- package/dist/harness/mcp.js +7 -32
- package/dist/harness/mcp.js.map +1 -1
- package/dist/harness/presenter.d.ts +1 -0
- package/dist/harness/presenter.d.ts.map +1 -1
- package/dist/harness/presenter.js +1 -1
- package/dist/harness/presenter.js.map +1 -1
- package/dist/harness/prompt.js +1 -1
- package/dist/harness/prompt.js.map +1 -1
- package/dist/harness/runner.d.ts.map +1 -1
- package/dist/harness/runner.js +8 -3
- package/dist/harness/runner.js.map +1 -1
- package/dist/harness/tools/pi-tools.d.ts +1 -0
- package/dist/harness/tools/pi-tools.d.ts.map +1 -1
- package/dist/harness/tools/pi-tools.js +1 -1
- package/dist/harness/tools/pi-tools.js.map +1 -1
- package/dist/harness/types.d.ts +1 -1
- package/dist/harness/types.d.ts.map +1 -1
- package/dist/harness/types.js.map +1 -1
- package/dist/main.js +7 -5
- package/dist/main.js.map +1 -1
- package/dist/memory-capture/index.d.ts +16 -0
- package/dist/memory-capture/index.d.ts.map +1 -0
- package/dist/memory-capture/index.js +194 -0
- package/dist/memory-capture/index.js.map +1 -0
- package/dist/memory-capture/types.d.ts +27 -0
- package/dist/memory-capture/types.d.ts.map +1 -0
- package/dist/memory-capture/types.js.map +1 -0
- package/dist/observability/types.d.ts +1 -1
- package/dist/observability/types.d.ts.map +1 -1
- package/dist/observability/types.js.map +1 -1
- package/dist/office/index.d.ts.map +1 -1
- package/dist/office/index.js +2 -1
- package/dist/office/index.js.map +1 -1
- package/dist/office/projection.d.ts.map +1 -1
- package/dist/office/projection.js +5 -4
- package/dist/office/projection.js.map +1 -1
- package/dist/runtime/conversation-runtime.d.ts.map +1 -1
- package/dist/runtime/conversation-runtime.js +16 -59
- package/dist/runtime/conversation-runtime.js.map +1 -1
- package/dist/runtime/types.d.ts +2 -1
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/types.js.map +1 -1
- package/dist/sandbox/cloudflare.d.ts.map +1 -1
- package/dist/sandbox/cloudflare.js +2 -1
- package/dist/sandbox/cloudflare.js.map +1 -1
- package/dist/sandbox/container.d.ts.map +1 -1
- package/dist/sandbox/container.js +21 -14
- package/dist/sandbox/container.js.map +1 -1
- package/dist/sandbox/index.d.ts.map +1 -1
- package/dist/sandbox/index.js +2 -1
- package/dist/sandbox/index.js.map +1 -1
- package/dist/sandbox/layout.d.ts +7 -0
- package/dist/sandbox/layout.d.ts.map +1 -0
- package/dist/sandbox/layout.js +13 -0
- package/dist/sandbox/layout.js.map +1 -0
- package/dist/sandbox/provisioner.d.ts +24 -4
- package/dist/sandbox/provisioner.d.ts.map +1 -1
- package/dist/sandbox/provisioner.js +210 -20
- package/dist/sandbox/provisioner.js.map +1 -1
- package/dist/sessions/session-store.d.ts +1 -1
- package/dist/sessions/session-store.d.ts.map +1 -1
- package/dist/sessions/store.d.ts +0 -1
- package/dist/sessions/store.d.ts.map +1 -1
- package/dist/sessions/store.js +0 -31
- package/dist/sessions/store.js.map +1 -1
- package/dist/types.d.ts +9 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/vault/index.d.ts.map +1 -1
- package/dist/vault/index.js +7 -6
- package/dist/vault/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/dream/index.d.ts +0 -20
- package/dist/dream/index.d.ts.map +0 -1
- package/dist/dream/index.js +0 -262
- package/dist/dream/index.js.map +0 -1
- package/dist/dream/types.d.ts +0 -30
- package/dist/dream/types.d.ts.map +0 -1
- package/dist/dream/types.js.map +0 -1
- /package/dist/{dream → memory-capture}/types.js +0 -0
package/dist/sandbox/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import { ContainerExecutor, containerSandboxAdapter } from "./container.js";
|
|
|
2
2
|
import { CloudflareSandboxExecutor, cloudflareSandboxAdapter } from "./cloudflare.js";
|
|
3
3
|
import { HostExecutor, hostSandboxAdapter } from "./host.js";
|
|
4
4
|
import { createMountedRuntimePathContext, execSimple, SandboxError } from "./utils.js";
|
|
5
|
+
import { GUEST_WORKSPACE_ROOT } from "./layout.js";
|
|
5
6
|
import * as log from "../log.js";
|
|
6
7
|
export { CloudflareSandboxExecutor, ContainerExecutor, HostExecutor };
|
|
7
8
|
export { SandboxError } from "./utils.js";
|
|
@@ -87,7 +88,7 @@ export function createExecutor(config, env, ensureReady) {
|
|
|
87
88
|
}
|
|
88
89
|
export function getUnresolvedSandboxPathContext(sandboxConfig, hostWorkspaceRoot) {
|
|
89
90
|
if (sandboxConfig.type === "image") {
|
|
90
|
-
return createMountedRuntimePathContext(hostWorkspaceRoot,
|
|
91
|
+
return createMountedRuntimePathContext(hostWorkspaceRoot, GUEST_WORKSPACE_ROOT);
|
|
91
92
|
}
|
|
92
93
|
return createExecutor(sandboxConfig).getPathContext(hostWorkspaceRoot);
|
|
93
94
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sandbox/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sandbox/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { ContainerExecutor, containerSandboxAdapter } from \"./container.js\";\nimport { CloudflareSandboxExecutor, cloudflareSandboxAdapter } from \"./cloudflare.js\";\nimport { HostExecutor, hostSandboxAdapter } from \"./host.js\";\nimport { createMountedRuntimePathContext, execSimple, SandboxError } from \"./utils.js\";\nimport { GUEST_WORKSPACE_ROOT } from \"./layout.js\";\nimport * as log from \"../log.js\";\nimport type {\n Executor,\n ImageSandboxConfig,\n RuntimePathContext,\n SandboxAdapter,\n SandboxConfig,\n} from \"./types.js\";\n\nexport type {\n CloudflareSandboxConfig,\n ExecOptions,\n ExecResult,\n Executor,\n RuntimePathContext,\n SandboxAdapter,\n SandboxConfig,\n} from \"./types.js\";\nexport { CloudflareSandboxExecutor, ContainerExecutor, HostExecutor };\nexport { SandboxError } from \"./utils.js\";\n\nfunction parseImageSandboxArg(value: string): ImageSandboxConfig | undefined {\n if (!value.startsWith(\"image:\")) {\n return undefined;\n }\n\n const image = value.slice(\"image:\".length);\n if (!image) {\n throw new SandboxError(\"Error: image sandbox requires image name (e.g., image:ubuntu:24.04)\");\n }\n return { type: \"image\", image };\n}\n\nasync function validateImageSandbox(config: ImageSandboxConfig): Promise<void> {\n try {\n await execSimple(\"docker\", [\"--version\"]);\n } catch {\n throw new SandboxError(\"Error: Docker is not installed or not in PATH\");\n }\n console.log(` Image auto-provisioning enabled. Image: ${config.image}`);\n}\n\nconst imageSandboxAdapter: SandboxAdapter<ImageSandboxConfig> = {\n type: \"image\",\n credentials: { env: true, fileMounts: true },\n workspace: { managedProjection: true },\n parse: parseImageSandboxArg,\n validate: validateImageSandbox,\n};\n\nconst sandboxAdapters = [\n hostSandboxAdapter,\n containerSandboxAdapter,\n imageSandboxAdapter,\n cloudflareSandboxAdapter,\n] as const;\nconst sandboxAdapterByType = new Map(\n sandboxAdapters.map((adapter) => [adapter.type, adapter]),\n) as Map<SandboxConfig[\"type\"], SandboxAdapter>;\n\nexport function getSandboxAdapters(): readonly [...typeof sandboxAdapters] {\n return sandboxAdapters;\n}\n\nfunction requireSandboxAdapter(type: SandboxConfig[\"type\"]): SandboxAdapter {\n const adapter = sandboxAdapterByType.get(type);\n if (!adapter) throw new SandboxError(`Error: Unsupported sandbox type '${type}'`);\n return adapter;\n}\n\nexport function getSandboxCredentialCapabilities(\n type: SandboxConfig[\"type\"],\n): SandboxAdapter[\"credentials\"] {\n return requireSandboxAdapter(type).credentials;\n}\n\nexport function getSandboxWorkspaceCapabilities(\n type: SandboxConfig[\"type\"],\n): SandboxAdapter[\"workspace\"] {\n return requireSandboxAdapter(type).workspace;\n}\n\nconst warnedUnenforcedPrivacy = new Set<string>();\n\nexport function assertSandboxSupportsWorkspacePolicy(\n sandboxConfig: SandboxConfig,\n visibility: \"public\" | \"private\",\n officeKey: string,\n): void {\n const capabilities = getSandboxWorkspaceCapabilities(sandboxConfig.type);\n if (capabilities.managedProjection || visibility === \"public\") return;\n if (warnedUnenforcedPrivacy.has(officeKey)) return;\n warnedUnenforcedPrivacy.add(officeKey);\n log.logWarning(\n `Sandbox '${sandboxConfig.type}' cannot enforce private office visibility for ${officeKey}`,\n \"host, container, and cloudflare backends share one filesystem; use image:* to isolate private channels and DMs\",\n );\n}\n\nexport function parseSandboxArg(value: string): SandboxConfig {\n for (const adapter of sandboxAdapters) {\n const config = adapter.parse(value);\n if (config) {\n return config;\n }\n }\n\n if (value.startsWith(\"docker:\")) {\n throw new SandboxError(\n `Error: '${value}' is not supported. Use 'container:<container-name>' for the shared-container mode or 'image:<image-name>' for mikan-managed per-user containers.`,\n );\n }\n\n throw new SandboxError(\n `Error: Invalid sandbox type '${value}'. Use 'host', 'container:<container-name>', 'image:<image-name>', or 'cloudflare:<sandbox-id>'`,\n );\n}\n\nexport async function validateSandbox(config: SandboxConfig): Promise<void> {\n await requireSandboxAdapter(config.type).validate?.(config);\n}\n\nexport function createExecutor(\n config: SandboxConfig,\n env?: Record<string, string>,\n ensureReady?: () => Promise<void>,\n): Executor {\n const adapter = requireSandboxAdapter(config.type);\n if (!adapter.createExecutor) {\n throw new SandboxError(\"Error: image sandbox must resolve to a concrete container executor\");\n }\n return adapter.createExecutor(config, env, ensureReady);\n}\n\nexport function getUnresolvedSandboxPathContext(\n sandboxConfig: SandboxConfig,\n hostWorkspaceRoot: string,\n): RuntimePathContext {\n if (sandboxConfig.type === \"image\") {\n return createMountedRuntimePathContext(hostWorkspaceRoot, GUEST_WORKSPACE_ROOT);\n }\n\n return createExecutor(sandboxConfig).getPathContext(hostWorkspaceRoot);\n}\n"]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const GUEST_WORKSPACE_ROOT = "/workspace";
|
|
2
|
+
export declare const GUEST_PUBLIC_OFFICES_DIR = "/workspace/public";
|
|
3
|
+
export declare const GUEST_HOME = "/root";
|
|
4
|
+
export declare function guestWorkspacePath(relativePath: string): string;
|
|
5
|
+
export declare function guestPublicOfficePath(officeKey: string): string;
|
|
6
|
+
export declare function guestHomePath(relativePath: string): string;
|
|
7
|
+
//# sourceMappingURL=layout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../src/sandbox/layout.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,oBAAoB,eAAe,CAAC;AACjD,eAAO,MAAM,wBAAwB,sBAAmC,CAAC;AACzE,eAAO,MAAM,UAAU,UAAU,CAAC;AAElC,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED,wBAAgB,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAE1D"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const GUEST_WORKSPACE_ROOT = "/workspace";
|
|
2
|
+
export const GUEST_PUBLIC_OFFICES_DIR = `${GUEST_WORKSPACE_ROOT}/public`;
|
|
3
|
+
export const GUEST_HOME = "/root";
|
|
4
|
+
export function guestWorkspacePath(relativePath) {
|
|
5
|
+
return `${GUEST_WORKSPACE_ROOT}/${relativePath.replace(/^\/+/, "")}`;
|
|
6
|
+
}
|
|
7
|
+
export function guestPublicOfficePath(officeKey) {
|
|
8
|
+
return `${GUEST_PUBLIC_OFFICES_DIR}/${officeKey}`;
|
|
9
|
+
}
|
|
10
|
+
export function guestHomePath(relativePath) {
|
|
11
|
+
return `${GUEST_HOME}/${relativePath.replace(/^\/+/, "")}`;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=layout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layout.js","sourceRoot":"","sources":["../../src/sandbox/layout.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,oBAAoB,GAAG,YAAY,CAAC;AACjD,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAG,oBAAoB,SAAS,CAAC;AACzE,MAAM,CAAC,MAAM,UAAU,GAAG,OAAO,CAAC;AAElC,MAAM,UAAU,kBAAkB,CAAC,YAAoB;IACrD,OAAO,GAAG,oBAAoB,IAAI,YAAY,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC;AACvE,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,SAAiB;IACrD,OAAO,GAAG,wBAAwB,IAAI,SAAS,EAAE,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,YAAoB;IAChD,OAAO,GAAG,UAAU,IAAI,YAAY,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC;AAC7D,CAAC","sourcesContent":["export const GUEST_WORKSPACE_ROOT = \"/workspace\";\nexport const GUEST_PUBLIC_OFFICES_DIR = `${GUEST_WORKSPACE_ROOT}/public`;\nexport const GUEST_HOME = \"/root\";\n\nexport function guestWorkspacePath(relativePath: string): string {\n return `${GUEST_WORKSPACE_ROOT}/${relativePath.replace(/^\\/+/, \"\")}`;\n}\n\nexport function guestPublicOfficePath(officeKey: string): string {\n return `${GUEST_PUBLIC_OFFICES_DIR}/${officeKey}`;\n}\n\nexport function guestHomePath(relativePath: string): string {\n return `${GUEST_HOME}/${relativePath.replace(/^\\/+/, \"\")}`;\n}\n"]}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
export type { ContainerBindTranslator, ContainerMount, DockerContainerManagerOptions, ProvisionOptions, ResourceLimits, SandboxLimitStatus, } from "../types.js";
|
|
2
|
-
import type { ContainerBindTranslator, DockerContainerManagerOptions, ProvisionOptions, ResourceLimits, SandboxLimitStatus } from "../types.js";
|
|
1
|
+
export type { ContainerBindTranslator, ContainerMount, DockerContainerManagerOptions, HomeVolumeMigrationOutcome, ManagedContainerInventoryEntry, ProvisionOptions, ResourceLimits, SandboxLimitStatus, } from "../types.js";
|
|
2
|
+
import type { ContainerBindTranslator, DockerContainerManagerOptions, HomeVolumeMigrationOutcome, ManagedContainerInventoryEntry, ProvisionOptions, ResourceLimits, SandboxLimitStatus } from "../types.js";
|
|
3
3
|
export declare class DockerContainerManager {
|
|
4
4
|
private readonly image;
|
|
5
5
|
private state;
|
|
6
6
|
private inflight;
|
|
7
7
|
private bindTranslator?;
|
|
8
8
|
private layoutMigrations;
|
|
9
|
+
private keyQueues;
|
|
9
10
|
private static readonly MANAGED_LABEL;
|
|
10
11
|
private static readonly IMAGE_MODE_LABEL;
|
|
11
12
|
private static readonly VAULT_ID_LABEL_KEY;
|
|
@@ -22,6 +23,8 @@ export declare class DockerContainerManager {
|
|
|
22
23
|
static sanitizeSegment(value: string): string;
|
|
23
24
|
static containerName(containerKey: string): string;
|
|
24
25
|
static networkName(containerKey: string): string;
|
|
26
|
+
static homeVolumeName(containerKey: string): string;
|
|
27
|
+
private serialize;
|
|
25
28
|
provision(containerKey: string, options?: ProvisionOptions): Promise<string>;
|
|
26
29
|
private provisionInner;
|
|
27
30
|
boost(containerKey: string): Promise<SandboxLimitStatus>;
|
|
@@ -30,7 +33,11 @@ export declare class DockerContainerManager {
|
|
|
30
33
|
getDefaultLimits(): ResourceLimits | undefined;
|
|
31
34
|
getBoostLimits(): ResourceLimits | undefined;
|
|
32
35
|
stop(containerKey: string): Promise<void>;
|
|
33
|
-
|
|
36
|
+
private stopInner;
|
|
37
|
+
remove(containerKey: string, options?: {
|
|
38
|
+
purgeHome?: boolean;
|
|
39
|
+
}): Promise<void>;
|
|
40
|
+
private removeInner;
|
|
34
41
|
stopIdle(maxIdleMs: number): Promise<void>;
|
|
35
42
|
removeContainersForConversations(conversationIds: ReadonlySet<string>): Promise<void>;
|
|
36
43
|
armContainerLayoutMigration(translator: ContainerBindTranslator): void;
|
|
@@ -54,7 +61,7 @@ export declare class DockerContainerManager {
|
|
|
54
61
|
private effectiveLimits;
|
|
55
62
|
private resourceLimitArgs;
|
|
56
63
|
private applyResourceLimits;
|
|
57
|
-
private
|
|
64
|
+
private runtimeDrift;
|
|
58
65
|
private hasBindMountDrift;
|
|
59
66
|
private expectedBinds;
|
|
60
67
|
private sameBinds;
|
|
@@ -62,6 +69,19 @@ export declare class DockerContainerManager {
|
|
|
62
69
|
private mountSignature;
|
|
63
70
|
private mountSourceFingerprint;
|
|
64
71
|
private inspectBindMounts;
|
|
72
|
+
private isHomeVolumeBind;
|
|
73
|
+
private hasHomeVolumeBind;
|
|
74
|
+
private hasHomeVolume;
|
|
75
|
+
private hasImageDrift;
|
|
76
|
+
private localImageId;
|
|
77
|
+
private ensureHomeVolume;
|
|
78
|
+
private removeHomeVolume;
|
|
79
|
+
private replaceHomeVolumeContainer;
|
|
80
|
+
inventory(): Promise<ManagedContainerInventoryEntry[]>;
|
|
81
|
+
systemChanges(containerName: string): Promise<string[]>;
|
|
82
|
+
private isUnderGuestPath;
|
|
83
|
+
migrateToHomeVolume(containerKey: string): Promise<HomeVolumeMigrationOutcome>;
|
|
84
|
+
private migrateToHomeVolumeInner;
|
|
65
85
|
private hasNetworkModeDrift;
|
|
66
86
|
private ensureNetwork;
|
|
67
87
|
private inspectStatus;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provisioner.d.ts","sourceRoot":"","sources":["../../src/sandbox/provisioner.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"provisioner.d.ts","sourceRoot":"","sources":["../../src/sandbox/provisioner.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { execFile } from "node:child_process";
|
|
2
2
|
import { legacyConversationResourceKey, sanitizeIdentitySegment } from "./identity.js";
|
|
3
|
+
import { GUEST_HOME, GUEST_PUBLIC_OFFICES_DIR } from "./layout.js";
|
|
3
4
|
import { createHash } from "node:crypto";
|
|
4
5
|
import { readFileSync, statSync } from "node:fs";
|
|
5
6
|
import { promisify } from "node:util";
|
|
@@ -42,6 +43,7 @@ export class DockerContainerManager {
|
|
|
42
43
|
this.state = new Map();
|
|
43
44
|
this.inflight = new Map();
|
|
44
45
|
this.layoutMigrations = new Map();
|
|
46
|
+
this.keyQueues = new Map();
|
|
45
47
|
this.boostedKeys = new Set();
|
|
46
48
|
this.overrideLimits = new Map();
|
|
47
49
|
this.limits = options.limits;
|
|
@@ -57,11 +59,25 @@ export class DockerContainerManager {
|
|
|
57
59
|
static networkName(containerKey) {
|
|
58
60
|
return `mikan-sandbox-net-${containerKey}`;
|
|
59
61
|
}
|
|
62
|
+
static homeVolumeName(containerKey) {
|
|
63
|
+
return `mikan-home-${containerKey}`;
|
|
64
|
+
}
|
|
65
|
+
serialize(containerKey, task) {
|
|
66
|
+
const previous = this.keyQueues.get(containerKey) ?? Promise.resolve();
|
|
67
|
+
const next = previous.then(task, task);
|
|
68
|
+
const settled = next.catch(() => undefined);
|
|
69
|
+
this.keyQueues.set(containerKey, settled);
|
|
70
|
+
void settled.then(() => {
|
|
71
|
+
if (this.keyQueues.get(containerKey) === settled)
|
|
72
|
+
this.keyQueues.delete(containerKey);
|
|
73
|
+
});
|
|
74
|
+
return next;
|
|
75
|
+
}
|
|
60
76
|
async provision(containerKey, options = {}) {
|
|
61
77
|
const existing = this.inflight.get(containerKey);
|
|
62
78
|
if (existing)
|
|
63
79
|
return existing;
|
|
64
|
-
const pending = this.provisionInner(containerKey, options).finally(() => {
|
|
80
|
+
const pending = this.serialize(containerKey, () => this.provisionInner(containerKey, options)).finally(() => {
|
|
65
81
|
this.inflight.delete(containerKey);
|
|
66
82
|
});
|
|
67
83
|
this.inflight.set(containerKey, pending);
|
|
@@ -73,9 +89,22 @@ export class DockerContainerManager {
|
|
|
73
89
|
await this.migrateContainerLayout(containerName);
|
|
74
90
|
const status = await this.inspectStatus(containerName);
|
|
75
91
|
try {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
92
|
+
const binds = status === "missing" ? [] : await this.inspectBindMounts(containerName);
|
|
93
|
+
const homeVolume = this.hasHomeVolumeBind(containerKey, binds);
|
|
94
|
+
const drift = status === "missing"
|
|
95
|
+
? undefined
|
|
96
|
+
: await this.runtimeDrift(containerKey, containerName, mounts, {
|
|
97
|
+
binds,
|
|
98
|
+
status,
|
|
99
|
+
homeVolume,
|
|
100
|
+
});
|
|
101
|
+
if (drift && homeVolume) {
|
|
102
|
+
log.logInfo(`Container ${containerName} is out of date (${drift}); replacing container`);
|
|
103
|
+
await this.replaceHomeVolumeContainer(containerKey, containerName, mounts, options);
|
|
104
|
+
log.logInfo(`Container ${containerName} replaced from image ${this.image}`);
|
|
105
|
+
}
|
|
106
|
+
else if (drift) {
|
|
107
|
+
log.logInfo(`Container ${containerName} configuration changed (${drift}); recreating container`);
|
|
79
108
|
await this.recreateContainerPreservingContents(containerKey, containerName, mounts);
|
|
80
109
|
log.logInfo(`Container ${containerName} recreated`);
|
|
81
110
|
}
|
|
@@ -127,7 +156,10 @@ export class DockerContainerManager {
|
|
|
127
156
|
getBoostLimits() {
|
|
128
157
|
return this.boostLimits;
|
|
129
158
|
}
|
|
130
|
-
|
|
159
|
+
stop(containerKey) {
|
|
160
|
+
return this.serialize(containerKey, () => this.stopInner(containerKey));
|
|
161
|
+
}
|
|
162
|
+
async stopInner(containerKey) {
|
|
131
163
|
const containerName = this.getContainerName(containerKey);
|
|
132
164
|
try {
|
|
133
165
|
await this.execFileImpl("docker", ["stop", containerName]);
|
|
@@ -140,7 +172,10 @@ export class DockerContainerManager {
|
|
|
140
172
|
log.logWarning(`Failed to stop container ${containerName}`, err instanceof Error ? err.message : String(err));
|
|
141
173
|
}
|
|
142
174
|
}
|
|
143
|
-
|
|
175
|
+
remove(containerKey, options = {}) {
|
|
176
|
+
return this.serialize(containerKey, () => this.removeInner(containerKey, options));
|
|
177
|
+
}
|
|
178
|
+
async removeInner(containerKey, options) {
|
|
144
179
|
const containerName = this.getContainerName(containerKey);
|
|
145
180
|
const networkName = DockerContainerManager.networkName(containerKey);
|
|
146
181
|
const removed = await this.forceRemoveContainer(containerName, `Container ${containerName} removed`, `Failed to remove container ${containerName}`);
|
|
@@ -155,6 +190,8 @@ export class DockerContainerManager {
|
|
|
155
190
|
log.logWarning(`Failed to remove network ${networkName}`, err instanceof Error ? err.message : String(err));
|
|
156
191
|
}
|
|
157
192
|
await this.removeMigrateSnapshot(containerName);
|
|
193
|
+
if (options.purgeHome)
|
|
194
|
+
await this.removeHomeVolume(containerKey);
|
|
158
195
|
this.state.delete(containerKey);
|
|
159
196
|
this.boostedKeys.delete(containerKey);
|
|
160
197
|
this.overrideLimits.delete(containerKey);
|
|
@@ -177,7 +214,10 @@ export class DockerContainerManager {
|
|
|
177
214
|
const details = await this.inspectContainerDetails(containerName);
|
|
178
215
|
if (!details?.conversationId || !conversationIds.has(details.conversationId))
|
|
179
216
|
return;
|
|
180
|
-
await this.forceRemoveContainer(containerName, `Removed container ${containerName} after office migration`, `Failed to remove container ${containerName} after office migration`);
|
|
217
|
+
const removed = await this.forceRemoveContainer(containerName, `Removed container ${containerName} after office migration`, `Failed to remove container ${containerName} after office migration`);
|
|
218
|
+
const containerKey = this.containerKeyFromContainerName(containerName);
|
|
219
|
+
if (removed && containerKey)
|
|
220
|
+
await this.removeHomeVolume(containerKey);
|
|
181
221
|
}));
|
|
182
222
|
}
|
|
183
223
|
armContainerLayoutMigration(translator) {
|
|
@@ -290,11 +330,11 @@ export class DockerContainerManager {
|
|
|
290
330
|
async removeStaleMountpoints(containerName, bindSpecs) {
|
|
291
331
|
const keep = new Set(bindSpecs
|
|
292
332
|
.map((bind) => bindSpecToMount(bind).target)
|
|
293
|
-
.filter((target) => target.startsWith(
|
|
294
|
-
.map((target) => target.slice(
|
|
333
|
+
.filter((target) => target.startsWith(`${GUEST_PUBLIC_OFFICES_DIR}/`))
|
|
334
|
+
.map((target) => target.slice(GUEST_PUBLIC_OFFICES_DIR.length + 1)));
|
|
295
335
|
const script = [
|
|
296
|
-
|
|
297
|
-
"for d in
|
|
336
|
+
`cd ${GUEST_PUBLIC_OFFICES_DIR} 2>/dev/null || exit 0`,
|
|
337
|
+
"for d in *; do",
|
|
298
338
|
' [ -e "$d" ] || continue',
|
|
299
339
|
' case " $KEEP " in *" ${d##*/} "*) continue;; esac',
|
|
300
340
|
' rmdir "$d" 2>/dev/null || true',
|
|
@@ -459,6 +499,7 @@ export class DockerContainerManager {
|
|
|
459
499
|
}
|
|
460
500
|
async runContainer(containerKey, containerName, mounts, options) {
|
|
461
501
|
const networkName = await this.ensureNetwork(containerKey);
|
|
502
|
+
const homeVolume = await this.ensureHomeVolume(containerKey);
|
|
462
503
|
log.logInfo(`Creating container ${containerName} from image ${this.image}`);
|
|
463
504
|
const labels = [
|
|
464
505
|
"--label",
|
|
@@ -489,6 +530,8 @@ export class DockerContainerManager {
|
|
|
489
530
|
"1024",
|
|
490
531
|
...labels,
|
|
491
532
|
...this.resourceLimitArgs(this.effectiveLimits(containerKey)),
|
|
533
|
+
"-v",
|
|
534
|
+
`${homeVolume}:${GUEST_HOME}`,
|
|
492
535
|
...this.mountArgs(mounts),
|
|
493
536
|
this.image,
|
|
494
537
|
"sleep",
|
|
@@ -538,18 +581,22 @@ export class DockerContainerManager {
|
|
|
538
581
|
});
|
|
539
582
|
}
|
|
540
583
|
}
|
|
541
|
-
async
|
|
542
|
-
|
|
543
|
-
|
|
584
|
+
async runtimeDrift(containerKey, containerName, mounts, current) {
|
|
585
|
+
const { binds, status, homeVolume } = current;
|
|
586
|
+
if (this.hasBindMountDrift(binds, mounts))
|
|
587
|
+
return "binds";
|
|
588
|
+
if (await this.hasMountSignatureDrift(containerName, mounts))
|
|
589
|
+
return "mount-content";
|
|
590
|
+
if (await this.hasNetworkModeDrift(containerKey, containerName))
|
|
591
|
+
return "network";
|
|
592
|
+
if (homeVolume && status === "stopped" && (await this.hasImageDrift(containerName))) {
|
|
593
|
+
return "image";
|
|
544
594
|
}
|
|
545
|
-
|
|
546
|
-
return true;
|
|
547
|
-
}
|
|
548
|
-
return this.hasNetworkModeDrift(containerKey, containerName);
|
|
595
|
+
return undefined;
|
|
549
596
|
}
|
|
550
|
-
|
|
597
|
+
hasBindMountDrift(binds, mounts) {
|
|
551
598
|
const expected = this.expectedBinds(mounts);
|
|
552
|
-
const actual =
|
|
599
|
+
const actual = binds.filter((bind) => !this.isHomeVolumeBind(bind));
|
|
553
600
|
return !this.sameBinds(expected, actual);
|
|
554
601
|
}
|
|
555
602
|
expectedBinds(mounts) {
|
|
@@ -616,6 +663,149 @@ export class DockerContainerManager {
|
|
|
616
663
|
}
|
|
617
664
|
return [...parsed].toSorted();
|
|
618
665
|
}
|
|
666
|
+
isHomeVolumeBind(bind) {
|
|
667
|
+
const separator = bind.indexOf(":");
|
|
668
|
+
if (separator === -1)
|
|
669
|
+
return false;
|
|
670
|
+
const source = bind.slice(0, separator);
|
|
671
|
+
const target = bind.slice(separator + 1).replace(/:r[ow]$/, "");
|
|
672
|
+
return source.startsWith(DockerContainerManager.homeVolumeName("")) && target === GUEST_HOME;
|
|
673
|
+
}
|
|
674
|
+
hasHomeVolumeBind(containerKey, binds) {
|
|
675
|
+
const expected = `${DockerContainerManager.homeVolumeName(containerKey)}:${GUEST_HOME}`;
|
|
676
|
+
return binds.includes(expected);
|
|
677
|
+
}
|
|
678
|
+
async hasHomeVolume(containerKey, containerName) {
|
|
679
|
+
return this.hasHomeVolumeBind(containerKey, await this.inspectBindMounts(containerName));
|
|
680
|
+
}
|
|
681
|
+
async hasImageDrift(containerName) {
|
|
682
|
+
const desired = await this.localImageId();
|
|
683
|
+
if (!desired)
|
|
684
|
+
return false;
|
|
685
|
+
const { stdout } = await this.execFileImpl("docker", [
|
|
686
|
+
"inspect",
|
|
687
|
+
"-f",
|
|
688
|
+
"{{.Image}}",
|
|
689
|
+
containerName,
|
|
690
|
+
]);
|
|
691
|
+
return stdout.trim() !== desired;
|
|
692
|
+
}
|
|
693
|
+
async localImageId() {
|
|
694
|
+
try {
|
|
695
|
+
const { stdout } = await this.execFileImpl("docker", [
|
|
696
|
+
"image",
|
|
697
|
+
"inspect",
|
|
698
|
+
"-f",
|
|
699
|
+
"{{.Id}}",
|
|
700
|
+
this.image,
|
|
701
|
+
]);
|
|
702
|
+
return this.normalizeDockerValue(stdout.trim());
|
|
703
|
+
}
|
|
704
|
+
catch {
|
|
705
|
+
return undefined;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
async ensureHomeVolume(containerKey) {
|
|
709
|
+
const volumeName = DockerContainerManager.homeVolumeName(containerKey);
|
|
710
|
+
await this.execFileImpl("docker", [
|
|
711
|
+
"volume",
|
|
712
|
+
"create",
|
|
713
|
+
"--label",
|
|
714
|
+
DockerContainerManager.MANAGED_LABEL,
|
|
715
|
+
"--label",
|
|
716
|
+
`${DockerContainerManager.VAULT_ID_LABEL_KEY}=${containerKey}`,
|
|
717
|
+
volumeName,
|
|
718
|
+
]);
|
|
719
|
+
return volumeName;
|
|
720
|
+
}
|
|
721
|
+
async removeHomeVolume(containerKey) {
|
|
722
|
+
const volumeName = DockerContainerManager.homeVolumeName(containerKey);
|
|
723
|
+
try {
|
|
724
|
+
await this.execFileImpl("docker", ["volume", "rm", volumeName]);
|
|
725
|
+
log.logInfo(`Home volume ${volumeName} removed`);
|
|
726
|
+
}
|
|
727
|
+
catch (err) {
|
|
728
|
+
if (isDockerNotFoundError(err))
|
|
729
|
+
return;
|
|
730
|
+
log.logWarning(`Failed to remove home volume ${volumeName}`, err instanceof Error ? err.message : String(err));
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
async replaceHomeVolumeContainer(containerKey, containerName, mounts, options) {
|
|
734
|
+
const removed = await this.forceRemoveContainer(containerName, `Container ${containerName} removed for replacement`, `Failed to remove container ${containerName} for replacement`);
|
|
735
|
+
if (!removed)
|
|
736
|
+
throw new Error(`Failed to remove container ${containerName} for replacement`);
|
|
737
|
+
await this.runContainer(containerKey, containerName, mounts, options);
|
|
738
|
+
}
|
|
739
|
+
async inventory() {
|
|
740
|
+
const names = await this.listContainerNamesByLabel();
|
|
741
|
+
const entries = [];
|
|
742
|
+
for (const containerName of names) {
|
|
743
|
+
const containerKey = this.containerKeyFromContainerName(containerName);
|
|
744
|
+
const status = await this.inspectStatus(containerName);
|
|
745
|
+
if (status === "missing")
|
|
746
|
+
continue;
|
|
747
|
+
const homeVolume = containerKey
|
|
748
|
+
? await this.hasHomeVolume(containerKey, containerName)
|
|
749
|
+
: false;
|
|
750
|
+
entries.push({
|
|
751
|
+
containerName,
|
|
752
|
+
...(containerKey ? { containerKey } : {}),
|
|
753
|
+
running: status === "running",
|
|
754
|
+
homeVolume,
|
|
755
|
+
imageStale: await this.hasImageDrift(containerName),
|
|
756
|
+
});
|
|
757
|
+
}
|
|
758
|
+
return entries;
|
|
759
|
+
}
|
|
760
|
+
async systemChanges(containerName) {
|
|
761
|
+
const { stdout } = await this.execFileImpl("docker", ["diff", containerName]);
|
|
762
|
+
return this.parseNameLines(stdout).filter((line) => {
|
|
763
|
+
const path = line.slice(2);
|
|
764
|
+
return !this.isUnderGuestPath(path, GUEST_HOME) && !this.isUnderGuestPath(path, "/workspace");
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
isUnderGuestPath(path, root) {
|
|
768
|
+
return path === root || path.startsWith(`${root}/`);
|
|
769
|
+
}
|
|
770
|
+
migrateToHomeVolume(containerKey) {
|
|
771
|
+
return this.serialize(containerKey, () => this.migrateToHomeVolumeInner(containerKey));
|
|
772
|
+
}
|
|
773
|
+
async migrateToHomeVolumeInner(containerKey) {
|
|
774
|
+
const containerName = DockerContainerManager.containerName(containerKey);
|
|
775
|
+
const status = await this.inspectStatus(containerName);
|
|
776
|
+
if (status === "missing")
|
|
777
|
+
return "missing";
|
|
778
|
+
if (await this.hasHomeVolume(containerKey, containerName))
|
|
779
|
+
return "already-migrated";
|
|
780
|
+
const details = await this.inspectContainerDetails(containerName);
|
|
781
|
+
const mounts = (await this.inspectBindMounts(containerName))
|
|
782
|
+
.filter((bind) => !this.isHomeVolumeBind(bind))
|
|
783
|
+
.map(bindSpecToMount);
|
|
784
|
+
const snapshotImage = `${DockerContainerManager.MIGRATE_IMAGE_PREFIX}:${containerName}`;
|
|
785
|
+
log.logInfo(`Migrating container ${containerName} to a home volume`);
|
|
786
|
+
await this.execFileImpl("docker", ["commit", containerName, snapshotImage]);
|
|
787
|
+
const homeVolume = await this.ensureHomeVolume(containerKey);
|
|
788
|
+
await this.execFileImpl("docker", [
|
|
789
|
+
"run",
|
|
790
|
+
"--rm",
|
|
791
|
+
"--network",
|
|
792
|
+
"none",
|
|
793
|
+
"-v",
|
|
794
|
+
`${homeVolume}:${GUEST_HOME}`,
|
|
795
|
+
snapshotImage,
|
|
796
|
+
"true",
|
|
797
|
+
]);
|
|
798
|
+
const removed = await this.forceRemoveContainer(containerName, `Container ${containerName} removed for home-volume migration`, `Failed to remove container ${containerName} for home-volume migration`);
|
|
799
|
+
if (!removed)
|
|
800
|
+
throw new Error(`Failed to remove container ${containerName}`);
|
|
801
|
+
await this.runContainer(containerKey, containerName, mounts, details?.conversationId ? { conversationId: details.conversationId } : {});
|
|
802
|
+
if (status === "stopped")
|
|
803
|
+
await this.execFileImpl("docker", ["stop", containerName]);
|
|
804
|
+
await this.removeMigrateSnapshot(containerName);
|
|
805
|
+
this.setState(containerKey, status === "stopped" ? "stopped" : "running", containerName);
|
|
806
|
+
log.logInfo(`Container ${containerName} migrated to home volume ${homeVolume}`);
|
|
807
|
+
return "migrated";
|
|
808
|
+
}
|
|
619
809
|
async hasNetworkModeDrift(containerKey, containerName) {
|
|
620
810
|
const expected = DockerContainerManager.networkName(containerKey);
|
|
621
811
|
const { stdout } = await this.execFileImpl("docker", [
|