Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4aacea5453 | ||
|
|
faf532c579 | ||
|
|
e0722804e7 | ||
|
|
1c864e4506 | ||
|
|
6d5b6b5333 | ||
|
|
6696445e61 | ||
|
|
e189b743bd | ||
|
|
eaa9de4a94 | ||
|
|
1bcfddb492 |
@@ -1,22 +0,0 @@
|
|||||||
# anthropic-consumer's runtime: the tool runtime, carrying this module's compiled code.
|
|
||||||
#
|
|
||||||
# **Built from this module's own directory and nothing else.** The sdk and the tool runtime are in
|
|
||||||
# the base images, published like any other artifact — which is what makes this buildable by the
|
|
||||||
# mesh from a repository and a path (novox/hq ADR 0069) rather than only on a workstation that
|
|
||||||
# happens to have the siblings.
|
|
||||||
#
|
|
||||||
# Two bases, named rather than pinned (novox/hq issue 044): the image this is COMPILED in and the
|
|
||||||
# image it RUNS in — the second must not carry a compiler. Declared in module.json's `build.on`.
|
|
||||||
ARG BUILD_BASE
|
|
||||||
ARG RUNTIME_BASE
|
|
||||||
|
|
||||||
FROM ${BUILD_BASE} AS build
|
|
||||||
WORKDIR /app/modules/anthropic-consumer
|
|
||||||
COPY . .
|
|
||||||
RUN node /app/node_modules/typescript/bin/tsc apply/index.ts usage/index.ts \
|
|
||||||
--module NodeNext --moduleResolution NodeNext --target ES2022 --outDir dist
|
|
||||||
|
|
||||||
FROM ${RUNTIME_BASE}
|
|
||||||
COPY --from=build /app/modules/anthropic-consumer/dist /app/modules/anthropic-consumer/dist
|
|
||||||
# No serve-time entrypoints: every container of this module names its command (`run` on a
|
|
||||||
# schedule), so nothing here serves — deliberately no MESH_TOOL_MODULES.
|
|
||||||
@@ -14,19 +14,10 @@
|
|||||||
"secrets": {
|
"secrets": {
|
||||||
"model-access": "${dir:state}/access-token"
|
"model-access": "${dir:state}/access-token"
|
||||||
},
|
},
|
||||||
"own-secrets": {
|
|
||||||
"broker": "${dir:mesh-state}/broker"
|
|
||||||
},
|
|
||||||
"emits": [
|
"emits": [
|
||||||
"usage.session"
|
"usage.session"
|
||||||
],
|
],
|
||||||
"resources": [
|
"resources": [
|
||||||
{
|
|
||||||
"id": "mesh-state",
|
|
||||||
"type": "directory",
|
|
||||||
"mode": "0700",
|
|
||||||
"place": "mesh"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"id": "state",
|
"id": "state",
|
||||||
"type": "directory",
|
"type": "directory",
|
||||||
@@ -46,66 +37,39 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "apply",
|
"id": "apply",
|
||||||
"type": "container",
|
"type": "process",
|
||||||
"name": "mesh-anthropic-consumer-apply",
|
"name": "anthropic-consumer-apply",
|
||||||
"network": "host",
|
"artifact": "code",
|
||||||
|
"run": [
|
||||||
|
"node",
|
||||||
|
"apply/index.js"
|
||||||
|
],
|
||||||
"schedule": "*/5 * * * *",
|
"schedule": "*/5 * * * *",
|
||||||
"args": [
|
|
||||||
"run",
|
|
||||||
"/app/modules/anthropic-consumer/dist/apply/index.js"
|
|
||||||
],
|
|
||||||
"volumes": [
|
|
||||||
"${dir:state}:/run/state"
|
|
||||||
],
|
|
||||||
"env": {
|
"env": {
|
||||||
"MESH_MODEL_ACCESS_SECRET_FILE": "/run/state/access-token",
|
"MESH_MODEL_ACCESS_SECRET_FILE": "${dir:state}/access-token",
|
||||||
"MESH_MODEL_ACCESS_BIND_FILE": "/run/state/model.json",
|
"MESH_MODEL_ACCESS_BIND_FILE": "${dir:state}/model.json",
|
||||||
"MESH_CLAUDE_CREDENTIALS_FILE": "/run/state/claude/.credentials.json",
|
"MESH_CLAUDE_CREDENTIALS_FILE": "${dir:state}/claude/.credentials.json",
|
||||||
"MESH_CLAUDE_IDENTITY_FILE": "/run/state/claude/.claude.json"
|
"MESH_CLAUDE_IDENTITY_FILE": "${dir:state}/claude/.claude.json"
|
||||||
},
|
}
|
||||||
"artifact": "runtime"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "usage",
|
|
||||||
"type": "container",
|
|
||||||
"name": "mesh-anthropic-consumer-usage",
|
|
||||||
"network": "host",
|
|
||||||
"schedule": "*/5 * * * *",
|
|
||||||
"args": [
|
|
||||||
"run",
|
|
||||||
"/app/modules/anthropic-consumer/dist/usage/index.js"
|
|
||||||
],
|
|
||||||
"volumes": [
|
|
||||||
"${dir:mesh-state}/broker:/run/secrets/broker:ro",
|
|
||||||
"${dir:state}:/run/state"
|
|
||||||
],
|
|
||||||
"env": {
|
|
||||||
"MESH_BROKER_FILE": "/run/secrets/broker",
|
|
||||||
"MESH_CLAUDE_PROJECTS_DIR": "/run/state/claude/projects",
|
|
||||||
"MESH_ANTHROPIC_USAGE_OUT": "/run/state/out/session-usage.json",
|
|
||||||
"MESH_TOOLS_MAIN": "/app/dist/main.js"
|
|
||||||
},
|
|
||||||
"artifact": "runtime"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"build": {
|
"build": {
|
||||||
"on": [
|
|
||||||
{
|
|
||||||
"arg": "BUILD_BASE",
|
|
||||||
"module": "mesh-tools",
|
|
||||||
"artifact": "build"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"arg": "RUNTIME_BASE",
|
|
||||||
"module": "mesh-tools",
|
|
||||||
"artifact": "runtime"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"artifacts": [
|
"artifacts": [
|
||||||
{
|
{
|
||||||
"name": "runtime",
|
"name": "code",
|
||||||
"kind": "image",
|
"kind": "bundle",
|
||||||
"from": "Dockerfile"
|
"language": "typescript",
|
||||||
|
"entrypoints": [
|
||||||
|
"apply/index.js",
|
||||||
|
"usage/index.js"
|
||||||
|
],
|
||||||
|
"loads": [
|
||||||
|
"usage/index.js"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"MESH_CLAUDE_PROJECTS_DIR": "${dir:state}/claude/projects",
|
||||||
|
"MESH_ANTHROPIC_USAGE_OUT": "${dir:state}/out/session-usage.json"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,15 @@
|
|||||||
// per session. The consumer IS the (node,module) session's fixed binding, so no per-message account
|
// per session. The consumer IS the (node,module) session's fixed binding, so no per-message account
|
||||||
// attribution is done — just the totals (port map "don't-map" #3).
|
// attribution is done — just the totals (port map "don't-map" #3).
|
||||||
//
|
//
|
||||||
// Runs as `mesh-tools run` (no broker), so events are emitted best-effort via the sibling mesh-tools
|
// Runs in the node's runtime (novox/hq ADR 0198), every five minutes, so events are emitted through
|
||||||
// `emit` primitive; the totals are also written to a file so the reading is observable without one.
|
// the runtime as this module; the totals are also written to a file so the reading is observable
|
||||||
|
// without one.
|
||||||
|
|
||||||
import { readdirSync, statSync, readFileSync, writeFileSync, renameSync, mkdirSync } from "node:fs";
|
import { readdirSync, statSync, readFileSync, writeFileSync, renameSync, mkdirSync } from "node:fs";
|
||||||
import { join, dirname } from "node:path";
|
import { join, dirname } from "node:path";
|
||||||
|
|
||||||
|
import { emit } from "@novox/mesh-sdk/events";
|
||||||
|
|
||||||
import { readSessionFile, type SessionUsage } from "../transcript.js";
|
import { readSessionFile, type SessionUsage } from "../transcript.js";
|
||||||
|
|
||||||
/** The vendor-neutral usage row ADR 0054 fixes — the shape the model-usage store upserts. Kept local
|
/** The vendor-neutral usage row ADR 0054 fixes — the shape the model-usage store upserts. Kept local
|
||||||
@@ -116,22 +119,20 @@ function atomicWrite(path: string, content: string): void {
|
|||||||
renameSync(tmp, path);
|
renameSync(tmp, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Emit best-effort via the sibling mesh-tools `emit`, which wires a broker a run step has none. */
|
/** Emit best-effort through the runtime: a reading that could not be announced is still in the file. */
|
||||||
async function emitUsage(body: Record<string, unknown>): Promise<void> {
|
async function emitUsage(body: Record<string, unknown>): Promise<void> {
|
||||||
const main = process.env.MESH_TOOLS_MAIN ?? "/app/dist/main.js";
|
try {
|
||||||
const { spawn } = await import("node:child_process");
|
await emit("usage.session", body);
|
||||||
await new Promise<void>((resolve) => {
|
} catch (err) {
|
||||||
const child = spawn(
|
|
||||||
process.execPath,
|
|
||||||
[main, "emit", "usage.session", JSON.stringify(body)],
|
|
||||||
{ stdio: "inherit" },
|
|
||||||
);
|
|
||||||
child.on("exit", () => resolve());
|
|
||||||
child.on("error", (err) => {
|
|
||||||
console.error(`[anthropic-consumer] could not emit usage: ${err}`);
|
console.error(`[anthropic-consumer] could not emit usage: ${err}`);
|
||||||
resolve();
|
}
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await main();
|
// The cadence the scheduled container had: once at start, then every five minutes. Not awaited, so the
|
||||||
|
// runtime's handshake is answered while a long first reading is still under way.
|
||||||
|
const EVERY_MS = 5 * 60 * 1000;
|
||||||
|
const tick = (): void => {
|
||||||
|
void main().catch((err) => console.error(`[anthropic-consumer] usage reading failed: ${err}`));
|
||||||
|
};
|
||||||
|
tick();
|
||||||
|
setInterval(tick, EVERY_MS);
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
# audit-logger's runtime: the shared runtime image, carrying this module's compiled code.
|
|
||||||
#
|
|
||||||
# **Built from this module's own directory and nothing else.** The toolkit is in the base image, so
|
|
||||||
# nothing is copied out of a neighbouring checkout — which is what lets the mesh build this from a
|
|
||||||
# repository and a path (novox/hq ADR 0069) rather than only on a workstation that happens to have
|
|
||||||
# the siblings laid out beside it.
|
|
||||||
|
|
||||||
# Two bases, named rather than pinned: the image this is COMPILED in, and the image it RUNS in.
|
|
||||||
# They are different images on purpose — the first carries a compiler and the second must not, or
|
|
||||||
# every running container would carry one it never invokes. The mesh answers both with the copies it
|
|
||||||
# holds, because a fingerprint written here would name one particular copy and no other mesh has it
|
|
||||||
# (novox/hq issue 044). Declared in module.json's `build.on`; deliberately no defaults, so a build
|
|
||||||
# nobody told stops here and says which module to build first.
|
|
||||||
ARG BUILD_BASE
|
|
||||||
ARG RUNTIME_BASE
|
|
||||||
|
|
||||||
FROM ${BUILD_BASE} AS build
|
|
||||||
# Compiled under /app/modules so `@novox/mesh-sdk` resolves upward into the base's own
|
|
||||||
# node_modules — the module is compiled against exactly the toolkit it will run against.
|
|
||||||
WORKDIR /app/modules/audit-logger
|
|
||||||
COPY . .
|
|
||||||
# The compiler is invoked by its real path rather than through node_modules/.bin, whose entries are
|
|
||||||
# symlinks to a launcher that requires its library relatively — resolved away when the base image
|
|
||||||
# was assembled.
|
|
||||||
RUN node /app/node_modules/typescript/bin/tsc audit.ts index.ts \
|
|
||||||
--module NodeNext --moduleResolution NodeNext --target ES2022 --outDir dist
|
|
||||||
|
|
||||||
FROM ${RUNTIME_BASE}
|
|
||||||
COPY --from=build /app/modules/audit-logger/dist /app/modules/audit-logger/dist
|
|
||||||
# **Served, not run.** This subscribes on import, and the serve mode binds the broker before it
|
|
||||||
# imports anything — `run` exists for a step that works offline and exits, and would leave this
|
|
||||||
# with nothing to subscribe to.
|
|
||||||
ENV MESH_TOOL_MODULES=/app/modules/audit-logger/dist/index.js
|
|
||||||
@@ -5,27 +5,21 @@
|
|||||||
"consumes": [
|
"consumes": [
|
||||||
"**"
|
"**"
|
||||||
],
|
],
|
||||||
"own-secrets": {
|
|
||||||
"broker": "${dir:state}/broker"
|
|
||||||
},
|
|
||||||
"build": {
|
"build": {
|
||||||
"on": [
|
|
||||||
{
|
|
||||||
"arg": "BUILD_BASE",
|
|
||||||
"module": "mesh-tools",
|
|
||||||
"artifact": "build"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"arg": "RUNTIME_BASE",
|
|
||||||
"module": "mesh-tools",
|
|
||||||
"artifact": "runtime"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"artifacts": [
|
"artifacts": [
|
||||||
{
|
{
|
||||||
"name": "runtime",
|
"name": "code",
|
||||||
"kind": "image",
|
"kind": "bundle",
|
||||||
"from": "Dockerfile"
|
"language": "typescript",
|
||||||
|
"entrypoints": [
|
||||||
|
"index.js"
|
||||||
|
],
|
||||||
|
"loads": [
|
||||||
|
"index.js"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"AUDIT_LOG": "${dir:trail}/audit.log"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -40,21 +34,6 @@
|
|||||||
"id": "trail",
|
"id": "trail",
|
||||||
"type": "directory",
|
"type": "directory",
|
||||||
"mode": "0700"
|
"mode": "0700"
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "run",
|
|
||||||
"type": "container",
|
|
||||||
"name": "mesh-audit-logger",
|
|
||||||
"network": "host",
|
|
||||||
"volumes": [
|
|
||||||
"${dir:state}/broker:/run/secrets/broker:ro",
|
|
||||||
"${dir:trail}:/trail"
|
|
||||||
],
|
|
||||||
"env": {
|
|
||||||
"MESH_BROKER_FILE": "/run/secrets/broker",
|
|
||||||
"AUDIT_LOG": "/trail/audit.log"
|
|
||||||
},
|
|
||||||
"artifact": "runtime"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"capabilities": [
|
"capabilities": [
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
# gitea's runtime: the tool runtime, carrying this module's compiled provisioner, tools and event
|
|
||||||
# consumer.
|
|
||||||
#
|
|
||||||
# **Built from this module's own directory and nothing else.** The sdk is in the base image, so
|
|
||||||
# nothing is copied out of a neighbouring checkout — which is what lets the mesh build this from a
|
|
||||||
# repository and a path (novox/hq ADR 0069) rather than only on a workstation that happens to have
|
|
||||||
# the siblings.
|
|
||||||
#
|
|
||||||
# Two bases, named rather than pinned: the image this is COMPILED in, and the image it RUNS in.
|
|
||||||
# They are different images on purpose — the first carries a compiler and the second must not, or
|
|
||||||
# every running container would carry one it never invokes. The mesh answers both with the copies it
|
|
||||||
# holds, because a fingerprint written here would name one particular copy and no other mesh has it
|
|
||||||
# (novox/hq issue 044). Declared in module.json's `build.on`; deliberately no defaults, so a build
|
|
||||||
# nobody told stops here and says which module to build first.
|
|
||||||
ARG BUILD_BASE
|
|
||||||
ARG RUNTIME_BASE
|
|
||||||
|
|
||||||
FROM ${BUILD_BASE} AS build
|
|
||||||
# Compiled under /app/modules so `@novox/mesh-sdk` resolves upward into the base's own
|
|
||||||
# node_modules — the module is compiled against exactly the sdk it will run against.
|
|
||||||
WORKDIR /app/modules/gitea
|
|
||||||
COPY . .
|
|
||||||
# The compiler is invoked by its real path rather than through node_modules/.bin, whose entries are
|
|
||||||
# symlinks to a launcher that requires its library relatively — resolved away when the base image
|
|
||||||
# was assembled.
|
|
||||||
RUN node /app/node_modules/typescript/bin/tsc client.ts token.ts index.ts provisioner/index.ts tools/index.ts \
|
|
||||||
--module NodeNext --moduleResolution NodeNext --target ES2022 --outDir dist
|
|
||||||
|
|
||||||
FROM ${RUNTIME_BASE}
|
|
||||||
# **No apt packages.** gitea's provisioner talks to the forge over HTTP (the gitea REST API), not
|
|
||||||
# through a CLI the way postgres drives psql — so the runtime base holds everything this needs.
|
|
||||||
COPY --from=build /app/modules/gitea/dist /app/modules/gitea/dist
|
|
||||||
# What a tool host should load from this module: its event consumer and its tools, which are
|
|
||||||
# separate entrypoints because they are loaded by different things. The provisioner is the third,
|
|
||||||
# and is not listed here — the declaration names it in the container's `args`, because it is what
|
|
||||||
# this module's own container runs. One image, because they are one module and share a client.
|
|
||||||
ENV MESH_TOOL_MODULES=/app/modules/gitea/dist/index.js,/app/modules/gitea/dist/tools/index.js,/app/modules/gitea/dist/provisioner/index.js
|
|
||||||
+21
-44
@@ -85,9 +85,6 @@
|
|||||||
"scope": "mesh"
|
"scope": "mesh"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"own-secrets": {
|
|
||||||
"broker": "${dir:mesh-state}/broker"
|
|
||||||
},
|
|
||||||
"resources": [
|
"resources": [
|
||||||
{
|
{
|
||||||
"id": "mesh-state",
|
"id": "mesh-state",
|
||||||
@@ -180,32 +177,6 @@
|
|||||||
"mode": "0600",
|
"mode": "0600",
|
||||||
"content": "{}\n",
|
"content": "{}\n",
|
||||||
"merge": "json"
|
"merge": "json"
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "runtime",
|
|
||||||
"type": "container",
|
|
||||||
"name": "mesh-gitea",
|
|
||||||
"network": "host",
|
|
||||||
"volumes": [
|
|
||||||
"${dir:mesh-state}/broker:/run/secrets/broker:ro",
|
|
||||||
"${dir:mesh-state}/config.json:/run/config/config.json:ro",
|
|
||||||
"${dir:grants}:${dir:grants}:ro",
|
|
||||||
"${dir:state}/admin.secret:/run/secrets/admin:ro",
|
|
||||||
"${dir:runtime-state}:/run/state"
|
|
||||||
],
|
|
||||||
"env": {
|
|
||||||
"MESH_BROKER_FILE": "/run/secrets/broker",
|
|
||||||
"MESH_GITEA_URL": "http://127.0.0.1:${port:3000}",
|
|
||||||
"MESH_GITEA_CONFIG_FILE": "/run/config/config.json",
|
|
||||||
"MESH_GITEA_ADMIN_USER": "mesh-admin",
|
|
||||||
"MESH_GITEA_ADMIN_PASSWORD_FILE": "/run/secrets/admin",
|
|
||||||
"MESH_GITEA_STATE_DIR": "/run/state",
|
|
||||||
"MESH_RECEIVES": "${dir:grants}/npm.json"
|
|
||||||
},
|
|
||||||
"artifact": "runtime",
|
|
||||||
"restart-on": [
|
|
||||||
"runtime-config"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"provides": [
|
"provides": [
|
||||||
@@ -219,23 +190,29 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"build": {
|
"build": {
|
||||||
"on": [
|
|
||||||
{
|
|
||||||
"arg": "BUILD_BASE",
|
|
||||||
"module": "mesh-tools",
|
|
||||||
"artifact": "build"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"arg": "RUNTIME_BASE",
|
|
||||||
"module": "mesh-tools",
|
|
||||||
"artifact": "runtime"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"artifacts": [
|
"artifacts": [
|
||||||
{
|
{
|
||||||
"name": "runtime",
|
"name": "code",
|
||||||
"kind": "image",
|
"kind": "bundle",
|
||||||
"from": "Dockerfile"
|
"language": "typescript",
|
||||||
|
"entrypoints": [
|
||||||
|
"index.js",
|
||||||
|
"tools/index.js",
|
||||||
|
"provisioner/index.js"
|
||||||
|
],
|
||||||
|
"loads": [
|
||||||
|
"index.js",
|
||||||
|
"tools/index.js",
|
||||||
|
"provisioner/index.js"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"MESH_GITEA_URL": "http://127.0.0.1:${port:3000}",
|
||||||
|
"MESH_GITEA_CONFIG_FILE": "${dir:mesh-state}/config.json",
|
||||||
|
"MESH_GITEA_ADMIN_USER": "mesh-admin",
|
||||||
|
"MESH_GITEA_ADMIN_PASSWORD_FILE": "${dir:state}/admin.secret",
|
||||||
|
"MESH_GITEA_STATE_DIR": "${dir:runtime-state}",
|
||||||
|
"MESH_RECEIVES": "${dir:grants}/npm.json"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
# lab's runtime: the tool runtime, carrying this module's code, and the toolchain the lab's suite
|
|
||||||
# builds the mesh with (novox/hq ADR 0172). It reaches the machine's virtualisation and container
|
|
||||||
# runtime through their sockets, so what it raises is what a hand run on this machine raises.
|
|
||||||
#
|
|
||||||
# Every download is pinned by its checksum: an image that builds the mesh is the last place to take
|
|
||||||
# whatever an upstream serves today.
|
|
||||||
ARG BUILD_BASE
|
|
||||||
ARG RUNTIME_BASE
|
|
||||||
|
|
||||||
FROM ${BUILD_BASE} AS build
|
|
||||||
WORKDIR /app/modules/lab
|
|
||||||
COPY . .
|
|
||||||
RUN node /app/node_modules/typescript/bin/tsc tools/index.ts tools/runs.ts --rootDir . \
|
|
||||||
--module NodeNext --moduleResolution NodeNext --target ES2022 --outDir dist
|
|
||||||
|
|
||||||
FROM ${RUNTIME_BASE}
|
|
||||||
RUN apt-get update \
|
|
||||||
&& apt-get install -y --no-install-recommends git make ca-certificates curl python3 file iproute2 sudo \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
RUN curl -fsSL -o /tmp/go.tgz https://go.dev/dl/go1.26.8.linux-amd64.tar.gz \
|
|
||||||
&& echo "d0f743b33e8d8945e6b1f432edd15785c70507121d6e2a723b21285eddf8b57b /tmp/go.tgz" | sha256sum -c - \
|
|
||||||
&& tar -C /usr/local -xzf /tmp/go.tgz && rm /tmp/go.tgz
|
|
||||||
RUN curl -fsSL -o /usr/local/bin/incus https://github.com/lxc/incus/releases/download/v7.5.1/bin.linux.incus.x86_64 \
|
|
||||||
&& echo "7bd6223b369f4d693fcde695bd8549a73b5b3d403735329212483702aa22c179 /usr/local/bin/incus" | sha256sum -c - \
|
|
||||||
&& chmod 0755 /usr/local/bin/incus
|
|
||||||
RUN curl -fsSL -o /tmp/docker.tgz https://download.docker.com/linux/static/stable/x86_64/docker-28.5.2.tgz \
|
|
||||||
&& echo "ea90cfd12e1eeb12aa1c971741adb8bd4ed88e2a574eaac13f5029a1dbc6300d /tmp/docker.tgz" | sha256sum -c - \
|
|
||||||
&& tar -C /tmp -xzf /tmp/docker.tgz docker/docker && mv /tmp/docker/docker /usr/local/bin/docker && rm -rf /tmp/docker /tmp/docker.tgz
|
|
||||||
ENV PATH=/usr/local/go/bin:$PATH
|
|
||||||
COPY --from=build /app/modules/lab/dist /app/modules/lab/dist
|
|
||||||
ENV MESH_TOOL_MODULES=/app/modules/lab/dist/tools/index.js
|
|
||||||
+55
-44
@@ -5,16 +5,7 @@
|
|||||||
"container-runtime",
|
"container-runtime",
|
||||||
"virtualisation"
|
"virtualisation"
|
||||||
],
|
],
|
||||||
"own-secrets": {
|
|
||||||
"broker": "${dir:mesh-state}/broker"
|
|
||||||
},
|
|
||||||
"resources": [
|
"resources": [
|
||||||
{
|
|
||||||
"id": "mesh-state",
|
|
||||||
"type": "directory",
|
|
||||||
"mode": "0700",
|
|
||||||
"place": "mesh"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"id": "state",
|
"id": "state",
|
||||||
"type": "directory",
|
"type": "directory",
|
||||||
@@ -35,47 +26,67 @@
|
|||||||
"content": "MESH_LAB_FORGE=${setting:forge}\n"
|
"content": "MESH_LAB_FORGE=${setting:forge}\n"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "runtime",
|
"id": "git",
|
||||||
"type": "container",
|
"type": "package",
|
||||||
"name": "mesh-lab",
|
"package": "git"
|
||||||
"network": "host",
|
|
||||||
"env-file": [
|
|
||||||
"${dir:state}/lab.env"
|
|
||||||
],
|
|
||||||
"volumes": [
|
|
||||||
"${dir:mesh-state}/broker:/run/secrets/broker:ro",
|
|
||||||
"${dir:work}:${dir:work}",
|
|
||||||
"/var/run/docker.sock:/var/run/docker.sock",
|
|
||||||
"/var/lib/incus/unix.socket:/var/lib/incus/unix.socket"
|
|
||||||
],
|
|
||||||
"env": {
|
|
||||||
"MESH_BROKER_FILE": "/run/secrets/broker",
|
|
||||||
"MESH_LAB_WORK": "${dir:work}"
|
|
||||||
},
|
},
|
||||||
"restart-on": [
|
{
|
||||||
"runtime-env"
|
"id": "make",
|
||||||
],
|
"type": "package",
|
||||||
"artifact": "runtime"
|
"package": "make"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "python",
|
||||||
|
"type": "package",
|
||||||
|
"package": "python"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "file",
|
||||||
|
"type": "package",
|
||||||
|
"package": "file"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "iproute2",
|
||||||
|
"type": "package",
|
||||||
|
"package": "iproute2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "sudo",
|
||||||
|
"type": "package",
|
||||||
|
"package": "sudo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "npm",
|
||||||
|
"type": "package",
|
||||||
|
"package": "npm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "go",
|
||||||
|
"type": "package",
|
||||||
|
"package": "go"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "incus",
|
||||||
|
"type": "package",
|
||||||
|
"package": "incus"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"build": {
|
"build": {
|
||||||
"on": [
|
|
||||||
{
|
|
||||||
"arg": "BUILD_BASE",
|
|
||||||
"module": "mesh-tools",
|
|
||||||
"artifact": "build"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"arg": "RUNTIME_BASE",
|
|
||||||
"module": "mesh-tools",
|
|
||||||
"artifact": "runtime"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"artifacts": [
|
"artifacts": [
|
||||||
{
|
{
|
||||||
"name": "runtime",
|
"name": "code",
|
||||||
"kind": "image",
|
"kind": "bundle",
|
||||||
"from": "Dockerfile"
|
"language": "typescript",
|
||||||
|
"entrypoints": [
|
||||||
|
"tools/index.js"
|
||||||
|
],
|
||||||
|
"loads": [
|
||||||
|
"tools/index.js"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"MESH_LAB_WORK": "${dir:work}",
|
||||||
|
"MESH_LAB_ENV_FILE": "${dir:state}/lab.env"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,18 +2,23 @@
|
|||||||
// lab is assigned to, and only there: a bed raises virtual machines on that machine's virtualisation.
|
// lab is assigned to, and only there: a bed raises virtual machines on that machine's virtualisation.
|
||||||
|
|
||||||
import { spawnSync } from "node:child_process";
|
import { spawnSync } from "node:child_process";
|
||||||
|
import { readFileSync } from "node:fs";
|
||||||
import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools";
|
import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools";
|
||||||
import { listRuns, readStatus, REPOSITORIES, running, start, stop, tail } from "./runs.js";
|
import { listRuns, readStatus, REPOSITORIES, running, start, stop, tail } from "./runs.js";
|
||||||
|
|
||||||
export function getLabTools(env: NodeJS.ProcessEnv): ToolDefinition[] {
|
export function getLabTools(env: NodeJS.ProcessEnv): ToolDefinition[] {
|
||||||
const work = env.MESH_LAB_WORK ?? "/var/lib/mesh-lab-runs";
|
const work = env.MESH_LAB_WORK ?? "/var/lib/mesh-lab-runs";
|
||||||
const forge = (env.MESH_LAB_FORGE ?? "").replace(/\/+$/, "");
|
// The forge is an operator's setting, which reaches a file and never a bundle's words (novox/hq
|
||||||
|
// ADR 0192): read from the env-file the mesh fills, at each call, so a changed setting is used
|
||||||
|
// without restarting the runtime. MESH_LAB_FORGE itself still wins, for a hand-run instance.
|
||||||
|
const forgeOf = (): string => (env.MESH_LAB_FORGE ?? wordIn(env.MESH_LAB_ENV_FILE, "MESH_LAB_FORGE")).replace(/\/+$/, "");
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
name: "lab_check",
|
name: "lab_check",
|
||||||
description: "Whether this machine can run the lab's beds: the lab's own check, against the forge's main branch.",
|
description: "Whether this machine can run the lab's beds: the lab's own check, against the forge's main branch.",
|
||||||
input: {},
|
input: {},
|
||||||
run: async () => {
|
run: async () => {
|
||||||
|
const forge = forgeOf();
|
||||||
if (!forge) return { ok: false, output: "the lab's forge is not set: settings for lab, {\"forge\": \"<url>\"}" };
|
if (!forge) return { ok: false, output: "the lab's forge is not set: settings for lab, {\"forge\": \"<url>\"}" };
|
||||||
const dir = `${work}/check`;
|
const dir = `${work}/check`;
|
||||||
spawnSync("rm", ["-rf", dir]);
|
spawnSync("rm", ["-rf", dir]);
|
||||||
@@ -38,6 +43,7 @@ export function getLabTools(env: NodeJS.ProcessEnv): ToolDefinition[] {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
run: async (args) => {
|
run: async (args) => {
|
||||||
|
const forge = forgeOf();
|
||||||
if (!forge) return { started: false, reason: "the lab's forge is not set: settings for lab, {\"forge\": \"<url>\"}" };
|
if (!forge) return { started: false, reason: "the lab's forge is not set: settings for lab, {\"forge\": \"<url>\"}" };
|
||||||
const tests = String(args.tests ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
const tests = String(args.tests ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
||||||
if (tests.length === 0) return { started: false, reason: "name at least one bed test file" };
|
if (tests.length === 0) return { started: false, reason: "name at least one bed test file" };
|
||||||
@@ -83,4 +89,20 @@ export function getLabTools(env: NodeJS.ProcessEnv): ToolDefinition[] {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** One word from an env-file (`KEY=value` lines), or "" when the file or the word is absent. */
|
||||||
|
export function wordIn(file: string | undefined, word: string): string {
|
||||||
|
if (!file) return "";
|
||||||
|
let text: string;
|
||||||
|
try {
|
||||||
|
text = readFileSync(file, "utf8");
|
||||||
|
} catch {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
for (const line of text.split("\n")) {
|
||||||
|
const at = line.indexOf("=");
|
||||||
|
if (at > 0 && line.slice(0, at).trim() === word) return line.slice(at + 1).trim();
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
registerModuleTools("lab", (env) => getLabTools(env));
|
registerModuleTools("lab", (env) => getLabTools(env));
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
# mailu's runtime: the tool runtime, carrying this module's compiled code.
|
|
||||||
#
|
|
||||||
# **Built from this module's own directory and nothing else.** The sdk and the tool runtime are in
|
|
||||||
# the base images, published like any other artifact — which is what makes this buildable by the
|
|
||||||
# mesh from a repository and a path (novox/hq ADR 0069) rather than only on a workstation that
|
|
||||||
# happens to have the siblings.
|
|
||||||
#
|
|
||||||
# Two bases, named rather than pinned (novox/hq issue 044): the image this is COMPILED in and the
|
|
||||||
# image it RUNS in — the second must not carry a compiler. Declared in module.json's `build.on`.
|
|
||||||
ARG BUILD_BASE
|
|
||||||
ARG RUNTIME_BASE
|
|
||||||
|
|
||||||
FROM ${BUILD_BASE} AS build
|
|
||||||
# Compiled under /app/modules so `@novox/mesh-sdk` resolves upward into the base's own
|
|
||||||
# node_modules — the module is compiled against exactly the sdk it will run against. The compiler
|
|
||||||
# is invoked by its real path: node_modules/.bin entries are launcher symlinks the base image
|
|
||||||
# resolved away.
|
|
||||||
WORKDIR /app/modules/mailu
|
|
||||||
COPY . .
|
|
||||||
RUN node /app/node_modules/typescript/bin/tsc client.ts index.ts tools/index.ts provisioner/index.ts \
|
|
||||||
--module NodeNext --moduleResolution NodeNext --target ES2022 --outDir dist
|
|
||||||
|
|
||||||
FROM ${RUNTIME_BASE}
|
|
||||||
COPY --from=build /app/modules/mailu/dist /app/modules/mailu/dist
|
|
||||||
# Every serve-time entrypoint, loaded by the runtime in serve mode: tools and events serve, and a
|
|
||||||
# provider's provisioner runs its reconcile loop in the same process, with the broker connected —
|
|
||||||
# the convention novox/hq issues 060/061 settled. A container that instead ran only its
|
|
||||||
# provisioner (`run`) served no tools and emitted no events; a container that named no command
|
|
||||||
# ran no provisioner at all.
|
|
||||||
ENV MESH_TOOL_MODULES=/app/modules/mailu/dist/index.js,/app/modules/mailu/dist/tools/index.js,/app/modules/mailu/dist/provisioner/index.js
|
|
||||||
+30
-41
@@ -135,11 +135,15 @@
|
|||||||
"protocol": "tcp",
|
"protocol": "tcp",
|
||||||
"from": "mesh",
|
"from": "mesh",
|
||||||
"why": "automx: mail client autoconfiguration; the autoconfig, autodiscover and automx names are route grants reaching it here"
|
"why": "automx: mail client autoconfiguration; the autoconfig, autodiscover and automx names are route grants reaching it here"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "admin-api",
|
||||||
|
"port": 8080,
|
||||||
|
"protocol": "tcp",
|
||||||
|
"from": "machine",
|
||||||
|
"why": "the admin API, which this module's own code reaches on loopback from the node's runtime now that it runs outside the mailu network"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"own-secrets": {
|
|
||||||
"broker": "${dir:mesh-state}/broker"
|
|
||||||
},
|
|
||||||
"resources": [
|
"resources": [
|
||||||
{
|
{
|
||||||
"id": "mesh-state",
|
"id": "mesh-state",
|
||||||
@@ -305,6 +309,9 @@
|
|||||||
"name": "mailu-admin",
|
"name": "mailu-admin",
|
||||||
"image": "ghcr.io/mailu/admin@sha256:6dbfdadc4a9590dcb7652357b505200115b689b74008653bbf369e4599a3be5a",
|
"image": "ghcr.io/mailu/admin@sha256:6dbfdadc4a9590dcb7652357b505200115b689b74008653bbf369e4599a3be5a",
|
||||||
"network": "mailu",
|
"network": "mailu",
|
||||||
|
"ports": [
|
||||||
|
"8080"
|
||||||
|
],
|
||||||
"env-file": [
|
"env-file": [
|
||||||
"${dir:state}/mailu.env",
|
"${dir:state}/mailu.env",
|
||||||
"${dir:state}/secret.env",
|
"${dir:state}/secret.env",
|
||||||
@@ -473,31 +480,6 @@
|
|||||||
"content": "{}\n",
|
"content": "{}\n",
|
||||||
"merge": "json"
|
"merge": "json"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"id": "runtime",
|
|
||||||
"type": "container",
|
|
||||||
"name": "mesh-mailu",
|
|
||||||
"network": "mailu",
|
|
||||||
"volumes": [
|
|
||||||
"${dir:mesh-state}/broker:/run/secrets/broker:ro",
|
|
||||||
"${dir:state}/api-token.secret:/run/secrets/api-token:ro",
|
|
||||||
"${dir:grants}:${dir:grants}:ro",
|
|
||||||
"${dir:mesh-state}/config.json:/run/config/config.json:ro",
|
|
||||||
"/var/run/docker.sock:/var/run/docker.sock"
|
|
||||||
],
|
|
||||||
"env": {
|
|
||||||
"MESH_BROKER_FILE": "/run/secrets/broker",
|
|
||||||
"MESH_MAILU_URL": "http://mailu-admin:8080/api/v1",
|
|
||||||
"MESH_MAILU_API_KEY_FILE": "/run/secrets/api-token",
|
|
||||||
"MESH_MAILU_IMAP_CONTAINER": "mailu-imap",
|
|
||||||
"MESH_MAILU_CONFIG_FILE": "/run/config/config.json",
|
|
||||||
"MESH_RECEIVES": "${dir:grants}/mesh.json"
|
|
||||||
},
|
|
||||||
"restart-on": [
|
|
||||||
"runtime-config"
|
|
||||||
],
|
|
||||||
"artifact": "runtime"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"id": "automx",
|
"id": "automx",
|
||||||
"type": "container",
|
"type": "container",
|
||||||
@@ -517,16 +499,6 @@
|
|||||||
],
|
],
|
||||||
"build": {
|
"build": {
|
||||||
"on": [
|
"on": [
|
||||||
{
|
|
||||||
"arg": "BUILD_BASE",
|
|
||||||
"module": "mesh-tools",
|
|
||||||
"artifact": "build"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"arg": "RUNTIME_BASE",
|
|
||||||
"module": "mesh-tools",
|
|
||||||
"artifact": "runtime"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"arg": "PYTHON_BASE",
|
"arg": "PYTHON_BASE",
|
||||||
"image": "python@sha256:25f3cfeaceca14921366af4d1240b56457ef46273bdb508c7b0e8f469f6fd228"
|
"image": "python@sha256:25f3cfeaceca14921366af4d1240b56457ef46273bdb508c7b0e8f469f6fd228"
|
||||||
@@ -534,9 +506,26 @@
|
|||||||
],
|
],
|
||||||
"artifacts": [
|
"artifacts": [
|
||||||
{
|
{
|
||||||
"name": "runtime",
|
"name": "code",
|
||||||
"kind": "image",
|
"kind": "bundle",
|
||||||
"from": "Dockerfile"
|
"language": "typescript",
|
||||||
|
"entrypoints": [
|
||||||
|
"index.js",
|
||||||
|
"tools/index.js",
|
||||||
|
"provisioner/index.js"
|
||||||
|
],
|
||||||
|
"loads": [
|
||||||
|
"index.js",
|
||||||
|
"tools/index.js",
|
||||||
|
"provisioner/index.js"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"MESH_MAILU_URL": "http://127.0.0.1:${port:8080}/api/v1",
|
||||||
|
"MESH_MAILU_API_KEY_FILE": "${dir:state}/api-token.secret",
|
||||||
|
"MESH_MAILU_IMAP_CONTAINER": "mailu-imap",
|
||||||
|
"MESH_MAILU_CONFIG_FILE": "${dir:mesh-state}/config.json",
|
||||||
|
"MESH_RECEIVES": "${dir:grants}/mesh.json"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "automx",
|
"name": "automx",
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
# mesh-vault's runtime: the tool runtime, carrying this module's compiled provisioner, tools and event
|
|
||||||
# consumer. The same shape as postgres's, minus the client the database needs: mesh-vault reaches no
|
|
||||||
# server, because what it provides is a value the mesh already delivered to its node.
|
|
||||||
#
|
|
||||||
# **Built from this module's own directory and nothing else.** The sdk is in the base image, so
|
|
||||||
# nothing is copied out of a neighbouring checkout (novox/hq ADR 0069). Two bases, named rather than
|
|
||||||
# pinned — the image this is COMPILED in and the image it RUNS in — answered by the mesh from
|
|
||||||
# `build.on` in module.json (novox/hq issue 044).
|
|
||||||
ARG BUILD_BASE
|
|
||||||
ARG RUNTIME_BASE
|
|
||||||
|
|
||||||
FROM ${BUILD_BASE} AS build
|
|
||||||
WORKDIR /app/modules/vault
|
|
||||||
COPY . .
|
|
||||||
RUN node /app/node_modules/typescript/bin/tsc client.ts index.ts provisioner/index.ts tools/index.ts \
|
|
||||||
--module NodeNext --moduleResolution NodeNext --target ES2022 --outDir dist
|
|
||||||
|
|
||||||
FROM ${RUNTIME_BASE}
|
|
||||||
COPY --from=build /app/modules/vault/dist /app/modules/vault/dist
|
|
||||||
# The entrypoints a tool host loads from this module: its event consumer, its tools and its
|
|
||||||
# provisioner — one image, one process, one broker account (novox/hq ADR 0052).
|
|
||||||
ENV MESH_TOOL_MODULES=/app/modules/vault/dist/index.js,/app/modules/vault/dist/tools/index.js,/app/modules/vault/dist/provisioner/index.js
|
|
||||||
@@ -27,16 +27,7 @@
|
|||||||
"secret": "${dir:grants}"
|
"secret": "${dir:grants}"
|
||||||
},
|
},
|
||||||
"keeps": "/var/lib/mesh-vault/root",
|
"keeps": "/var/lib/mesh-vault/root",
|
||||||
"own-secrets": {
|
|
||||||
"broker": "${dir:mesh-state}/broker"
|
|
||||||
},
|
|
||||||
"resources": [
|
"resources": [
|
||||||
{
|
|
||||||
"id": "mesh-state",
|
|
||||||
"type": "directory",
|
|
||||||
"mode": "0700",
|
|
||||||
"place": "mesh"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"id": "state",
|
"id": "state",
|
||||||
"type": "directory",
|
"type": "directory",
|
||||||
@@ -57,45 +48,29 @@
|
|||||||
"id": "root",
|
"id": "root",
|
||||||
"type": "directory",
|
"type": "directory",
|
||||||
"mode": "0700"
|
"mode": "0700"
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "runtime",
|
|
||||||
"type": "container",
|
|
||||||
"name": "mesh-vault",
|
|
||||||
"network": "host",
|
|
||||||
"volumes": [
|
|
||||||
"${dir:mesh-state}/broker:/run/secrets/broker:ro",
|
|
||||||
"${dir:grants}:${dir:grants}:ro",
|
|
||||||
"${dir:ledger}:${dir:ledger}",
|
|
||||||
"${dir:root}:${dir:root}:ro"
|
|
||||||
],
|
|
||||||
"env": {
|
|
||||||
"MESH_BROKER_FILE": "/run/secrets/broker",
|
|
||||||
"MESH_RECEIVES": "${dir:grants}/mesh.json",
|
|
||||||
"MESH_VAULT_LEDGER": "${dir:ledger}",
|
|
||||||
"MESH_VAULT_ROOT": "${dir:root}"
|
|
||||||
},
|
|
||||||
"artifact": "runtime"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"build": {
|
"build": {
|
||||||
"on": [
|
|
||||||
{
|
|
||||||
"arg": "BUILD_BASE",
|
|
||||||
"module": "mesh-tools",
|
|
||||||
"artifact": "build"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"arg": "RUNTIME_BASE",
|
|
||||||
"module": "mesh-tools",
|
|
||||||
"artifact": "runtime"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"artifacts": [
|
"artifacts": [
|
||||||
{
|
{
|
||||||
"name": "runtime",
|
"name": "code",
|
||||||
"kind": "image",
|
"kind": "bundle",
|
||||||
"from": "Dockerfile"
|
"language": "typescript",
|
||||||
|
"entrypoints": [
|
||||||
|
"index.js",
|
||||||
|
"tools/index.js",
|
||||||
|
"provisioner/index.js"
|
||||||
|
],
|
||||||
|
"loads": [
|
||||||
|
"index.js",
|
||||||
|
"tools/index.js",
|
||||||
|
"provisioner/index.js"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"MESH_RECEIVES": "${dir:grants}/mesh.json",
|
||||||
|
"MESH_VAULT_LEDGER": "${dir:ledger}",
|
||||||
|
"MESH_VAULT_ROOT": "${dir:root}"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
# openai-consumer's runtime: the tool runtime, carrying this module's compiled code.
|
|
||||||
#
|
|
||||||
# **Built from this module's own directory and nothing else.** The sdk and the tool runtime are in
|
|
||||||
# the base images, published like any other artifact — which is what makes this buildable by the
|
|
||||||
# mesh from a repository and a path (novox/hq ADR 0069) rather than only on a workstation that
|
|
||||||
# happens to have the siblings.
|
|
||||||
#
|
|
||||||
# Two bases, named rather than pinned (novox/hq issue 044): the image this is COMPILED in and the
|
|
||||||
# image it RUNS in — the second must not carry a compiler. Declared in module.json's `build.on`.
|
|
||||||
ARG BUILD_BASE
|
|
||||||
ARG RUNTIME_BASE
|
|
||||||
|
|
||||||
FROM ${BUILD_BASE} AS build
|
|
||||||
WORKDIR /app/modules/openai-consumer
|
|
||||||
COPY . .
|
|
||||||
RUN node /app/node_modules/typescript/bin/tsc apply/index.ts \
|
|
||||||
--module NodeNext --moduleResolution NodeNext --target ES2022 --outDir dist
|
|
||||||
|
|
||||||
FROM ${RUNTIME_BASE}
|
|
||||||
COPY --from=build /app/modules/openai-consumer/dist /app/modules/openai-consumer/dist
|
|
||||||
# No serve-time entrypoints: every container of this module names its command (`run` on a
|
|
||||||
# schedule), so nothing here serves — deliberately no MESH_TOOL_MODULES.
|
|
||||||
@@ -28,44 +28,31 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "apply",
|
"id": "apply",
|
||||||
"type": "container",
|
"type": "process",
|
||||||
"name": "mesh-openai-consumer-apply",
|
"name": "openai-consumer-apply",
|
||||||
"network": "host",
|
"artifact": "code",
|
||||||
|
"run": [
|
||||||
|
"node",
|
||||||
|
"apply/index.js"
|
||||||
|
],
|
||||||
"schedule": "*/5 * * * *",
|
"schedule": "*/5 * * * *",
|
||||||
"args": [
|
|
||||||
"run",
|
|
||||||
"/app/modules/openai-consumer/dist/apply/index.js"
|
|
||||||
],
|
|
||||||
"volumes": [
|
|
||||||
"${dir:state}:/run/state"
|
|
||||||
],
|
|
||||||
"env": {
|
"env": {
|
||||||
"MESH_MODEL_ACCESS_SECRET_FILE": "/run/state/api-key",
|
"MESH_MODEL_ACCESS_SECRET_FILE": "${dir:state}/api-key",
|
||||||
"MESH_MODEL_ACCESS_BIND_FILE": "/run/state/model.json",
|
"MESH_MODEL_ACCESS_BIND_FILE": "${dir:state}/model.json",
|
||||||
"MESH_OPENAI_ENV_FILE": "/run/state/config/openai.env",
|
"MESH_OPENAI_ENV_FILE": "${dir:state}/config/openai.env",
|
||||||
"MESH_OPENAI_CREDENTIALS_FILE": "/run/state/config/auth.json"
|
"MESH_OPENAI_CREDENTIALS_FILE": "${dir:state}/config/auth.json"
|
||||||
},
|
}
|
||||||
"artifact": "runtime"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"build": {
|
"build": {
|
||||||
"on": [
|
|
||||||
{
|
|
||||||
"arg": "BUILD_BASE",
|
|
||||||
"module": "mesh-tools",
|
|
||||||
"artifact": "build"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"arg": "RUNTIME_BASE",
|
|
||||||
"module": "mesh-tools",
|
|
||||||
"artifact": "runtime"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"artifacts": [
|
"artifacts": [
|
||||||
{
|
{
|
||||||
"name": "runtime",
|
"name": "code",
|
||||||
"kind": "image",
|
"kind": "bundle",
|
||||||
"from": "Dockerfile"
|
"language": "typescript",
|
||||||
|
"entrypoints": [
|
||||||
|
"apply/index.js"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
# records' runtime: the tool runtime, carrying this module's compiled reader and its tools.
|
|
||||||
#
|
|
||||||
# **Built from this module's own directory and nothing else.** The sdk is in the base image, so
|
|
||||||
# nothing is copied out of a neighbouring checkout (novox/hq ADR 0069).
|
|
||||||
#
|
|
||||||
# Two bases, named rather than pinned: the image this is COMPILED in, and the image it RUNS in
|
|
||||||
# (novox/hq issue 044). Declared in module.json's `build.on`; deliberately no defaults.
|
|
||||||
ARG BUILD_BASE
|
|
||||||
ARG RUNTIME_BASE
|
|
||||||
|
|
||||||
FROM ${BUILD_BASE} AS build
|
|
||||||
WORKDIR /app/modules/records
|
|
||||||
COPY . .
|
|
||||||
RUN node /app/node_modules/typescript/bin/tsc records.ts index.ts tools/index.ts \
|
|
||||||
--module NodeNext --moduleResolution NodeNext --target ES2022 --outDir dist
|
|
||||||
|
|
||||||
FROM ${RUNTIME_BASE}
|
|
||||||
# **A module may need something the base image does not carry.** The reader keeps a checkout of the
|
|
||||||
# repository it reads (novox/hq ADR 0153) — a git working copy, kept current, not a derived copy — and
|
|
||||||
# the base image has no git. Certificates too, because the origin may be reached over TLS.
|
|
||||||
RUN apt-get update \
|
|
||||||
&& apt-get install -y --no-install-recommends git ca-certificates \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
COPY --from=build /app/modules/records/dist /app/modules/records/dist
|
|
||||||
# Both entrypoints, loaded in serve mode: the consumer that pulls on a merge, and the tools.
|
|
||||||
ENV MESH_TOOL_MODULES=/app/modules/records/dist/index.js,/app/modules/records/dist/tools/index.js
|
|
||||||
+19
-39
@@ -11,9 +11,6 @@
|
|||||||
"binds": {
|
"binds": {
|
||||||
"git": "${dir:mesh-state}/git.json"
|
"git": "${dir:mesh-state}/git.json"
|
||||||
},
|
},
|
||||||
"own-secrets": {
|
|
||||||
"broker": "${dir:mesh-state}/broker"
|
|
||||||
},
|
|
||||||
"consumes": [
|
"consumes": [
|
||||||
"gitea.pull.merged"
|
"gitea.pull.merged"
|
||||||
],
|
],
|
||||||
@@ -53,47 +50,30 @@
|
|||||||
"content": "${bound:git:scheme}://${bound:git:at}:${bound:git:port}\n"
|
"content": "${bound:git:scheme}://${bound:git:at}:${bound:git:port}\n"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "runtime",
|
"id": "git",
|
||||||
"type": "container",
|
"type": "package",
|
||||||
"name": "records",
|
"package": "git"
|
||||||
"network": "host",
|
|
||||||
"volumes": [
|
|
||||||
"${dir:mesh-state}/broker:/run/secrets/broker:ro",
|
|
||||||
"${dir:mesh-state}/config.json:/run/config/config.json:ro",
|
|
||||||
"${dir:mesh-state}/origin:/run/config/origin:ro",
|
|
||||||
"${dir:checkout}:${dir:checkout}"
|
|
||||||
],
|
|
||||||
"env": {
|
|
||||||
"MESH_BROKER_FILE": "/run/secrets/broker",
|
|
||||||
"MESH_RECORDS_CONFIG_FILE": "/run/config/config.json",
|
|
||||||
"MESH_RECORDS_ORIGIN_FILE": "/run/config/origin",
|
|
||||||
"MESH_RECORDS_DIR": "${dir:checkout}"
|
|
||||||
},
|
|
||||||
"artifact": "runtime",
|
|
||||||
"restart-on": [
|
|
||||||
"config",
|
|
||||||
"origin"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"build": {
|
"build": {
|
||||||
"on": [
|
|
||||||
{
|
|
||||||
"arg": "BUILD_BASE",
|
|
||||||
"module": "mesh-tools",
|
|
||||||
"artifact": "build"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"arg": "RUNTIME_BASE",
|
|
||||||
"module": "mesh-tools",
|
|
||||||
"artifact": "runtime"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"artifacts": [
|
"artifacts": [
|
||||||
{
|
{
|
||||||
"name": "runtime",
|
"name": "code",
|
||||||
"kind": "image",
|
"kind": "bundle",
|
||||||
"from": "Dockerfile"
|
"language": "typescript",
|
||||||
|
"entrypoints": [
|
||||||
|
"index.js",
|
||||||
|
"tools/index.js"
|
||||||
|
],
|
||||||
|
"loads": [
|
||||||
|
"index.js",
|
||||||
|
"tools/index.js"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"MESH_RECORDS_CONFIG_FILE": "${dir:mesh-state}/config.json",
|
||||||
|
"MESH_RECORDS_ORIGIN_FILE": "${dir:mesh-state}/origin",
|
||||||
|
"MESH_RECORDS_DIR": "${dir:checkout}"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
# route-adapter's runtime: the tool runtime, carrying this module's compiled code.
|
|
||||||
#
|
|
||||||
# **Built from this module's own directory and nothing else.** The sdk and the tool runtime are in
|
|
||||||
# the base images, published like any other artifact — which is what makes this buildable by the
|
|
||||||
# mesh from a repository and a path (novox/hq ADR 0069) rather than only on a workstation that
|
|
||||||
# happens to have the siblings.
|
|
||||||
#
|
|
||||||
# Two bases, named rather than pinned (novox/hq issue 044): the image this is COMPILED in and the
|
|
||||||
# image it RUNS in — the second must not carry a compiler. Declared in module.json's `build.on`.
|
|
||||||
ARG BUILD_BASE
|
|
||||||
ARG RUNTIME_BASE
|
|
||||||
|
|
||||||
FROM ${BUILD_BASE} AS build
|
|
||||||
WORKDIR /app/modules/route-adapter
|
|
||||||
COPY . .
|
|
||||||
RUN node /app/node_modules/typescript/bin/tsc adapter.ts index.ts \
|
|
||||||
--module NodeNext --moduleResolution NodeNext --target ES2022 --outDir dist
|
|
||||||
|
|
||||||
FROM ${RUNTIME_BASE}
|
|
||||||
COPY --from=build /app/modules/route-adapter/dist /app/modules/route-adapter/dist
|
|
||||||
# **No MESH_TOOL_MODULES, deliberately.** This module serves no tool and consumes no event: it is a
|
|
||||||
# step the host runs to completion, named by the container's `args` as `mesh-tools run …`. Setting a
|
|
||||||
# serve-time entrypoint here would give the image a second way to be started — one that connects to
|
|
||||||
# the broker and never exits.
|
|
||||||
@@ -47,23 +47,18 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "adapt",
|
"id": "adapt",
|
||||||
"type": "container",
|
"type": "process",
|
||||||
"name": "mesh-route-adapter",
|
"name": "route-adapter",
|
||||||
"artifact": "runtime",
|
"artifact": "code",
|
||||||
"run-once": true,
|
"run": [
|
||||||
"volumes": [
|
"node",
|
||||||
"${dir:routes-dir}/mesh.json:${dir:routes-dir}/mesh.json:ro",
|
"index.js"
|
||||||
"${dir:state}/config.json:/run/config/config.json:ro",
|
|
||||||
"${access:dynamic}:/services/traefik/dynamic"
|
|
||||||
],
|
],
|
||||||
|
"run-once": true,
|
||||||
"env": {
|
"env": {
|
||||||
"MESH_RECEIVES": "${dir:routes-dir}/mesh.json",
|
"MESH_RECEIVES": "${dir:routes-dir}/mesh.json",
|
||||||
"MESH_ROUTE_ADAPTER_CONFIG": "/run/config/config.json"
|
"MESH_ROUTE_ADAPTER_CONFIG": "${dir:state}/config.json"
|
||||||
},
|
},
|
||||||
"args": [
|
|
||||||
"run",
|
|
||||||
"/app/modules/route-adapter/dist/index.js"
|
|
||||||
],
|
|
||||||
"restart-on": [
|
"restart-on": [
|
||||||
"received-route",
|
"received-route",
|
||||||
"config"
|
"config"
|
||||||
@@ -71,23 +66,14 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"build": {
|
"build": {
|
||||||
"on": [
|
|
||||||
{
|
|
||||||
"arg": "BUILD_BASE",
|
|
||||||
"module": "mesh-tools",
|
|
||||||
"artifact": "build"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"arg": "RUNTIME_BASE",
|
|
||||||
"module": "mesh-tools",
|
|
||||||
"artifact": "runtime"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"artifacts": [
|
"artifacts": [
|
||||||
{
|
{
|
||||||
"name": "runtime",
|
"name": "code",
|
||||||
"kind": "image",
|
"kind": "bundle",
|
||||||
"from": "Dockerfile"
|
"language": "typescript",
|
||||||
|
"entrypoints": [
|
||||||
|
"index.js"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user