@atolis-hq/wake 0.4.21 → 0.4.23
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/src/bootstrap/initialise.js +2 -2
- package/dist/src/bootstrap/surface-cli-applications.js +16 -2
- package/dist/src/bootstrap/version.js +1 -1
- package/dist/src/surfaces/cli/infrastructure/docker-cli.d.ts +2 -0
- package/dist/src/surfaces/cli/infrastructure/docker-cli.js +12 -4
- package/dist/src/surfaces/cli/infrastructure/sandbox-workspace-ownership.d.ts +10 -0
- package/dist/src/surfaces/cli/infrastructure/sandbox-workspace-ownership.js +14 -0
- package/docker/Dockerfile +1 -1
- package/docker/Dockerfile.packaged +1 -1
- package/package.json +1 -1
|
@@ -401,7 +401,7 @@ ENV WAKE_MAIN_JS=/app/dist/src/main.js
|
|
|
401
401
|
# Startup ownership repair is best effort because bind-mounted paths or host
|
|
402
402
|
# permissions may prevent chown. The bounded direct-child pass avoids traversing
|
|
403
403
|
# accumulated runtime data.
|
|
404
|
-
ENTRYPOINT ["sh", "-c", "set -eu; mkdir -p /wake/.wake; chown wake:wake /wake/.wake || true; find /wake/.wake -mindepth 1 -maxdepth 1 -type d -exec chown wake:wake {} + || true; if [ -n \\"$WAKE_HOME_INIT_DIRS\\" ]; then printf '%s\\n' \\"$WAKE_HOME_INIT_DIRS\\" | while IFS= read -r directory; do case \\"$directory\\" in \\"$WAKE_HOME_INIT_ROOT\\"/*) mkdir -p \\"$directory\\"; chown wake:wake \\"$directory\\" ;; *) exit 1 ;; esac; done; fi; exec su wake -s /bin/sh -c 'HOME=/home/wake exec node \\"$WAKE_MAIN_JS\\" sandbox-entrypoint --wake-root /wake'"]
|
|
404
|
+
ENTRYPOINT ["sh", "-c", "set -eu; mkdir -p /wake/.wake /wake/workspaces; chown wake:wake /wake/.wake /wake/workspaces || true; find /wake/.wake -mindepth 1 -maxdepth 1 -type d -exec chown wake:wake {} + || true; if [ -n \\"$WAKE_HOME_INIT_DIRS\\" ]; then printf '%s\\n' \\"$WAKE_HOME_INIT_DIRS\\" | while IFS= read -r directory; do case \\"$directory\\" in \\"$WAKE_HOME_INIT_ROOT\\"/*) mkdir -p \\"$directory\\"; chown wake:wake \\"$directory\\" ;; *) exit 1 ;; esac; done; fi; exec su wake -s /bin/sh -c 'HOME=/home/wake exec node \\"$WAKE_MAIN_JS\\" sandbox-entrypoint --wake-root /wake'"]
|
|
405
405
|
`;
|
|
406
406
|
const packagedDockerfile = `# syntax=docker/dockerfile:1
|
|
407
407
|
FROM node:24-bookworm-slim
|
|
@@ -468,7 +468,7 @@ EXPOSE 4317
|
|
|
468
468
|
# Startup ownership repair is best effort because bind-mounted paths or host
|
|
469
469
|
# permissions may prevent chown. The bounded direct-child pass avoids traversing
|
|
470
470
|
# accumulated runtime data.
|
|
471
|
-
ENTRYPOINT ["sh", "-c", "set -eu; mkdir -p /wake/.wake; chown wake:wake /wake/.wake || true; find /wake/.wake -mindepth 1 -maxdepth 1 -type d -exec chown wake:wake {} + || true; if [ -n \\"$WAKE_HOME_INIT_DIRS\\" ]; then printf '%s\\n' \\"$WAKE_HOME_INIT_DIRS\\" | while IFS= read -r directory; do case \\"$directory\\" in \\"$WAKE_HOME_INIT_ROOT\\"/*) mkdir -p \\"$directory\\"; chown wake:wake \\"$directory\\" ;; *) exit 1 ;; esac; done; fi; exec su wake -s /bin/sh -c 'HOME=/home/wake exec wake sandbox-entrypoint'"]
|
|
471
|
+
ENTRYPOINT ["sh", "-c", "set -eu; mkdir -p /wake/.wake /wake/workspaces; chown wake:wake /wake/.wake /wake/workspaces || true; find /wake/.wake -mindepth 1 -maxdepth 1 -type d -exec chown wake:wake {} + || true; if [ -n \\"$WAKE_HOME_INIT_DIRS\\" ]; then printf '%s\\n' \\"$WAKE_HOME_INIT_DIRS\\" | while IFS= read -r directory; do case \\"$directory\\" in \\"$WAKE_HOME_INIT_ROOT\\"/*) mkdir -p \\"$directory\\"; chown wake:wake \\"$directory\\" ;; *) exit 1 ;; esac; done; fi; exec su wake -s /bin/sh -c 'HOME=/home/wake exec wake sandbox-entrypoint'"]
|
|
472
472
|
`;
|
|
473
473
|
/** Creates an immediately-valid, human-readable target Wake root. */
|
|
474
474
|
export async function initialiseWakeRoot(wakeRoot) {
|
|
@@ -9,7 +9,7 @@ import { BuiltInActivityName, agentActivityDefinition } from '../activities/inde
|
|
|
9
9
|
import { IntakeHost, ResidentHost, TickHost, } from '../control-plane/index.js';
|
|
10
10
|
import { ExecutionCancellationReason, ExecutionFailureCode, RunStatus, isActiveRunStatus, loadPromptTemplate, } from '../execution/index.js';
|
|
11
11
|
import { ResourceCorrelationRole, resourceId } from '../resources/index.js';
|
|
12
|
-
import { DockerProcessError, createApiDispatcher, createLoggedDockerCli, createPackagedAssetSource, createPairingGrant, createProcessLogSink, createSandboxDockerPort, createSurfaceHttpServer, drainProcessOutput, loadOrCreateCredentials, redeemPairingGrant, replaceAccessKey, runDoctor, runSandbox, runSandboxEntrypoint, runSandboxSetup, runSelfUpdateLatestLoop, runTargetSmoke, verifyResidentStart, waitForActiveRuns, waitForever, } from '../surfaces/index.js';
|
|
12
|
+
import { DockerProcessError, createApiDispatcher, createLoggedDockerCli, createPackagedAssetSource, createPairingGrant, createProcessLogSink, createSandboxDockerPort, createSurfaceHttpServer, drainProcessOutput, loadOrCreateCredentials, promoteSandboxImage, redeemPairingGrant, replaceAccessKey, runDoctor, runSandbox, runSandboxEntrypoint, runSandboxSetup, runSelfUpdateLatestLoop, runTargetSmoke, verifyResidentStart, waitForActiveRuns, waitForever, } from '../surfaces/index.js';
|
|
13
13
|
import { WorkStreamKind, workItemId } from '../work/index.js';
|
|
14
14
|
import { loadConfig } from './config/load-config.js';
|
|
15
15
|
import { createNpmUpdatePort } from './npm-update-port.js';
|
|
@@ -333,6 +333,18 @@ function sandboxDockerOptions(root, overrides) {
|
|
|
333
333
|
function createSandboxDocker(root, overrides) {
|
|
334
334
|
return createSandboxDockerPort(createDockerCliForRoot(root), sandboxDockerOptions(root, overrides));
|
|
335
335
|
}
|
|
336
|
+
async function createSandboxDockerForCommand(root, arguments_) {
|
|
337
|
+
if (arguments_[0] !== 'build' || root.config.host.development.mode === 'source')
|
|
338
|
+
return createSandboxDocker(root);
|
|
339
|
+
const buildVersion = await createNpmUpdatePort({
|
|
340
|
+
packageName: root.config.host.selfUpdate.npm.package,
|
|
341
|
+
distTag: root.config.host.selfUpdate.npm.distTag,
|
|
342
|
+
...(root.config.host.selfUpdate.npm.registry === undefined
|
|
343
|
+
? {}
|
|
344
|
+
: { registry: root.config.host.selfUpdate.npm.registry }),
|
|
345
|
+
}).latestTag();
|
|
346
|
+
return createSandboxDocker(root, { buildVersion });
|
|
347
|
+
}
|
|
336
348
|
async function resolveSandboxBuildVersion(root) {
|
|
337
349
|
const development = root.config.host.development;
|
|
338
350
|
return development.mode === 'source' && development.repoRoot !== undefined
|
|
@@ -422,6 +434,7 @@ async function deploySandboxTag(root, docker, tag, image, overrides) {
|
|
|
422
434
|
await verifyResidentStart(docker, root.config.host.sandbox.containerName, [...wakeInvocation, 'start', '--wake-root', '/wake'].join(' '));
|
|
423
435
|
await port.exec([...wakeInvocation, 'init', healthcheckRoot]);
|
|
424
436
|
await port.exec([...wakeInvocation, 'tick', '--wake-root', healthcheckRoot, '--no-sandbox']);
|
|
437
|
+
await promoteSandboxImage(docker, image, root.config.host.sandbox.image);
|
|
425
438
|
}
|
|
426
439
|
async function rollbackSandboxTag(root, docker, image, development) {
|
|
427
440
|
const port = createSandboxDockerPort(docker, sandboxDockerOptions(root, { image }));
|
|
@@ -430,6 +443,7 @@ async function rollbackSandboxTag(root, docker, image, development) {
|
|
|
430
443
|
? ['node', '/app/dist/src/main.js']
|
|
431
444
|
: sandboxWakeInvocation(root);
|
|
432
445
|
await verifyResidentStart(docker, root.config.host.sandbox.containerName, [...wakeInvocation, 'start', '--wake-root', '/wake'].join(' '));
|
|
446
|
+
await promoteSandboxImage(docker, image, root.config.host.sandbox.image);
|
|
433
447
|
}
|
|
434
448
|
function createOperationalApplications(root) {
|
|
435
449
|
return {
|
|
@@ -450,7 +464,7 @@ function createOperationalApplications(root) {
|
|
|
450
464
|
throw new Error('wake sandbox-setup accepts no arguments');
|
|
451
465
|
await runSandboxSetup(createSandboxSetupDependencies(root.config.host.sandbox.containerHomeMountPath));
|
|
452
466
|
},
|
|
453
|
-
sandbox: async (arguments_) => runSandbox(arguments_,
|
|
467
|
+
sandbox: async (arguments_) => runSandbox(arguments_, await createSandboxDockerForCommand(root, arguments_)),
|
|
454
468
|
sandboxEntrypoint: async () => {
|
|
455
469
|
await runSandboxEntrypoint(createSandboxEntrypointDependencies(root));
|
|
456
470
|
},
|
|
@@ -124,6 +124,6 @@ export function resolveWakeVersion(options = {}) {
|
|
|
124
124
|
return `g${headHash.slice(0, 7)}`;
|
|
125
125
|
return '0.1.0-dev';
|
|
126
126
|
}
|
|
127
|
-
export const wakeVersion = "v0.4.
|
|
127
|
+
export const wakeVersion = "v0.4.23";
|
|
128
128
|
/** The registry-installable version used by packaged sandbox images. */
|
|
129
129
|
export const wakePackageVersion = packageVersion(defaultRepoRoot) ?? wakeVersion;
|
|
@@ -59,6 +59,8 @@ export declare function createSandboxDockerPort(docker: DockerCli, options: Sand
|
|
|
59
59
|
setup: () => Promise<DockerInvocationResult>;
|
|
60
60
|
resume: ({ sessionId, cwd, cli }: SandboxResumeTarget) => Promise<DockerInvocationResult>;
|
|
61
61
|
};
|
|
62
|
+
/** Promotes a verified versioned sandbox image to the configured default image. */
|
|
63
|
+
export declare function promoteSandboxImage(docker: DockerCli, sourceImage: string, defaultImage: string): Promise<void>;
|
|
62
64
|
export interface SandboxResumeTarget {
|
|
63
65
|
readonly sessionId: string;
|
|
64
66
|
readonly cwd: string;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { dirname, normalize } from 'node:path/posix';
|
|
2
2
|
import { DockerProcessError, } from './docker-invocation.js';
|
|
3
3
|
import { scrubProcessLog } from './process-log.js';
|
|
4
|
+
import { ensureSandboxWorkspaceOwnership } from './sandbox-workspace-ownership.js';
|
|
4
5
|
export { DockerProcessError, } from './docker-invocation.js';
|
|
5
6
|
export { verifyResidentStart } from './resident-start.js';
|
|
6
7
|
const emptyDockerInvocationResult = { stdout: '', stderr: '' };
|
|
@@ -82,13 +83,13 @@ export function createSandboxDockerPort(docker, options) {
|
|
|
82
83
|
up: async () => {
|
|
83
84
|
await requireImage(inspect, options.image);
|
|
84
85
|
const state = await inspect.containerState(options.containerName);
|
|
85
|
-
if (state === 'live')
|
|
86
|
-
return;
|
|
87
86
|
if (state === 'halted') {
|
|
88
87
|
await docker.invoke(['start', options.containerName]);
|
|
89
|
-
return;
|
|
90
88
|
}
|
|
91
|
-
|
|
89
|
+
else if (state === null) {
|
|
90
|
+
await createContainer(docker, options);
|
|
91
|
+
}
|
|
92
|
+
await ensureSandboxWorkspaceOwnership(docker, options);
|
|
92
93
|
},
|
|
93
94
|
down: () => docker.invoke(['stop', '--time', '60', options.containerName]),
|
|
94
95
|
update: async () => {
|
|
@@ -99,6 +100,7 @@ export function createSandboxDockerPort(docker, options) {
|
|
|
99
100
|
if (state !== null)
|
|
100
101
|
await docker.invoke(['rm', options.containerName]);
|
|
101
102
|
await createContainer(docker, options);
|
|
103
|
+
await ensureSandboxWorkspaceOwnership(docker, options);
|
|
102
104
|
},
|
|
103
105
|
exec: (command) => docker.invoke(command.length === 0
|
|
104
106
|
? ['exec', '-u', 'wake', '-it', options.containerName, 'bash']
|
|
@@ -138,6 +140,12 @@ export function createSandboxDockerPort(docker, options) {
|
|
|
138
140
|
},
|
|
139
141
|
};
|
|
140
142
|
}
|
|
143
|
+
/** Promotes a verified versioned sandbox image to the configured default image. */
|
|
144
|
+
export async function promoteSandboxImage(docker, sourceImage, defaultImage) {
|
|
145
|
+
if (sourceImage === defaultImage)
|
|
146
|
+
return;
|
|
147
|
+
await docker.invoke(['tag', sourceImage, defaultImage]);
|
|
148
|
+
}
|
|
141
149
|
function shellQuote(value) {
|
|
142
150
|
return `'${value.replaceAll("'", `'\\''`)}'`;
|
|
143
151
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
interface RootDockerExecutor {
|
|
2
|
+
invoke(arguments_: readonly string[]): Promise<unknown>;
|
|
3
|
+
}
|
|
4
|
+
interface WorkspaceSandbox {
|
|
5
|
+
readonly containerName: string;
|
|
6
|
+
readonly wakeMountPath?: string;
|
|
7
|
+
}
|
|
8
|
+
/** Ensures the bind-mounted workspace root is usable by the unprivileged sandbox process. */
|
|
9
|
+
export declare function ensureSandboxWorkspaceOwnership(docker: RootDockerExecutor, options: WorkspaceSandbox): Promise<void>;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Ensures the bind-mounted workspace root is usable by the unprivileged sandbox process. */
|
|
2
|
+
export async function ensureSandboxWorkspaceOwnership(docker, options) {
|
|
3
|
+
await docker.invoke([
|
|
4
|
+
'exec',
|
|
5
|
+
'-u',
|
|
6
|
+
'root',
|
|
7
|
+
options.containerName,
|
|
8
|
+
'sh',
|
|
9
|
+
'-c',
|
|
10
|
+
'mkdir -p "$1" && chown wake:wake "$1"',
|
|
11
|
+
'wake-workspace-ownership',
|
|
12
|
+
`${options.wakeMountPath ?? '/wake'}/workspaces`,
|
|
13
|
+
]);
|
|
14
|
+
}
|
package/docker/Dockerfile
CHANGED
|
@@ -82,4 +82,4 @@ ENV WAKE_MAIN_JS=/app/dist/src/main.js
|
|
|
82
82
|
# Startup ownership repair is best effort because bind-mounted paths or host
|
|
83
83
|
# permissions may prevent chown. The bounded direct-child pass avoids traversing
|
|
84
84
|
# accumulated runtime data.
|
|
85
|
-
ENTRYPOINT ["sh", "-c", "set -eu; mkdir -p /wake/.wake; chown wake:wake /wake/.wake || true; find /wake/.wake -mindepth 1 -maxdepth 1 -type d -exec chown wake:wake {} + || true; exec su wake -s /bin/sh -c 'if [ \"$WAKE_START_ENABLED\" = \"true\" ]; then exec node /app/dist/src/main.js start --wake-root /wake --no-sandbox; else exec sleep infinity; fi'"]
|
|
85
|
+
ENTRYPOINT ["sh", "-c", "set -eu; mkdir -p /wake/.wake /wake/workspaces; chown wake:wake /wake/.wake /wake/workspaces || true; find /wake/.wake -mindepth 1 -maxdepth 1 -type d -exec chown wake:wake {} + || true; exec su wake -s /bin/sh -c 'if [ \"$WAKE_START_ENABLED\" = \"true\" ]; then exec node /app/dist/src/main.js start --wake-root /wake --no-sandbox; else exec sleep infinity; fi'"]
|
|
@@ -65,4 +65,4 @@ EXPOSE 4317
|
|
|
65
65
|
# Startup ownership repair is best effort because bind-mounted paths or host
|
|
66
66
|
# permissions may prevent chown. The bounded direct-child pass avoids traversing
|
|
67
67
|
# accumulated runtime data.
|
|
68
|
-
ENTRYPOINT ["sh", "-c", "set -eu; mkdir -p /wake/.wake; chown wake:wake /wake/.wake || true; find /wake/.wake -mindepth 1 -maxdepth 1 -type d -exec chown wake:wake {} + || true; exec su wake -s /bin/sh -c 'exec wake sandbox-entrypoint'"]
|
|
68
|
+
ENTRYPOINT ["sh", "-c", "set -eu; mkdir -p /wake/.wake /wake/workspaces; chown wake:wake /wake/.wake /wake/workspaces || true; find /wake/.wake -mindepth 1 -maxdepth 1 -type d -exec chown wake:wake {} + || true; exec su wake -s /bin/sh -c 'exec wake sandbox-entrypoint'"]
|