@pleri/olam-cli 0.1.100 → 0.1.102
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/commands/bootstrap.d.ts +8 -0
- package/dist/commands/bootstrap.d.ts.map +1 -1
- package/dist/commands/bootstrap.js.map +1 -1
- package/dist/commands/kg-build.d.ts +59 -0
- package/dist/commands/kg-build.d.ts.map +1 -0
- package/dist/commands/kg-build.js +247 -0
- package/dist/commands/kg-build.js.map +1 -0
- package/dist/commands/upgrade.d.ts.map +1 -1
- package/dist/commands/upgrade.js +35 -8
- package/dist/commands/upgrade.js.map +1 -1
- package/dist/docker-host.d.ts +13 -34
- package/dist/docker-host.d.ts.map +1 -1
- package/dist/docker-host.js +12 -62
- package/dist/docker-host.js.map +1 -1
- package/dist/image-digests.json +6 -5
- package/dist/index.js +476 -137
- package/dist/index.js.map +1 -1
- package/dist/mcp-server.js +146 -8
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -487,8 +487,8 @@ var init_parseUtil = __esm({
|
|
|
487
487
|
init_errors();
|
|
488
488
|
init_en();
|
|
489
489
|
makeIssue = (params) => {
|
|
490
|
-
const { data, path:
|
|
491
|
-
const fullPath = [...
|
|
490
|
+
const { data, path: path50, errorMaps, issueData } = params;
|
|
491
|
+
const fullPath = [...path50, ...issueData.path || []];
|
|
492
492
|
const fullIssue = {
|
|
493
493
|
...issueData,
|
|
494
494
|
path: fullPath
|
|
@@ -796,11 +796,11 @@ var init_types = __esm({
|
|
|
796
796
|
init_parseUtil();
|
|
797
797
|
init_util();
|
|
798
798
|
ParseInputLazyPath = class {
|
|
799
|
-
constructor(parent, value,
|
|
799
|
+
constructor(parent, value, path50, key) {
|
|
800
800
|
this._cachedPath = [];
|
|
801
801
|
this.parent = parent;
|
|
802
802
|
this.data = value;
|
|
803
|
-
this._path =
|
|
803
|
+
this._path = path50;
|
|
804
804
|
this._key = key;
|
|
805
805
|
}
|
|
806
806
|
get path() {
|
|
@@ -4281,7 +4281,7 @@ import YAML from "yaml";
|
|
|
4281
4281
|
function bootstrapStepCmd(entry) {
|
|
4282
4282
|
return typeof entry === "string" ? entry : entry.cmd;
|
|
4283
4283
|
}
|
|
4284
|
-
function refineForbiddenKeys(value,
|
|
4284
|
+
function refineForbiddenKeys(value, path50, ctx, rejectSource) {
|
|
4285
4285
|
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
4286
4286
|
return;
|
|
4287
4287
|
}
|
|
@@ -4289,12 +4289,12 @@ function refineForbiddenKeys(value, path49, ctx, rejectSource) {
|
|
|
4289
4289
|
if (FORBIDDEN_KEYS.has(key)) {
|
|
4290
4290
|
ctx.addIssue({
|
|
4291
4291
|
code: external_exports.ZodIssueCode.custom,
|
|
4292
|
-
path: [...
|
|
4292
|
+
path: [...path50, key],
|
|
4293
4293
|
message: `forbidden key "${key}" (prototype-pollution surface)`
|
|
4294
4294
|
});
|
|
4295
4295
|
continue;
|
|
4296
4296
|
}
|
|
4297
|
-
if (rejectSource &&
|
|
4297
|
+
if (rejectSource && path50.length === 0 && key === "source") {
|
|
4298
4298
|
ctx.addIssue({
|
|
4299
4299
|
code: external_exports.ZodIssueCode.custom,
|
|
4300
4300
|
path: ["source"],
|
|
@@ -4302,21 +4302,21 @@ function refineForbiddenKeys(value, path49, ctx, rejectSource) {
|
|
|
4302
4302
|
});
|
|
4303
4303
|
continue;
|
|
4304
4304
|
}
|
|
4305
|
-
refineForbiddenKeys(value[key], [...
|
|
4305
|
+
refineForbiddenKeys(value[key], [...path50, key], ctx, false);
|
|
4306
4306
|
}
|
|
4307
4307
|
}
|
|
4308
|
-
function rejectForbiddenKeys(value,
|
|
4308
|
+
function rejectForbiddenKeys(value, path50, rejectSource) {
|
|
4309
4309
|
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
4310
4310
|
return;
|
|
4311
4311
|
}
|
|
4312
4312
|
for (const key of Object.keys(value)) {
|
|
4313
4313
|
if (FORBIDDEN_KEYS.has(key)) {
|
|
4314
|
-
throw new Error(`[manifest] ${
|
|
4314
|
+
throw new Error(`[manifest] ${path50}: forbidden key "${key}" (prototype-pollution surface)`);
|
|
4315
4315
|
}
|
|
4316
4316
|
if (rejectSource && key === "source") {
|
|
4317
|
-
throw new Error(`[manifest] ${
|
|
4317
|
+
throw new Error(`[manifest] ${path50}: top-level "source" is loader-stamped \u2014 manifests must not author it`);
|
|
4318
4318
|
}
|
|
4319
|
-
rejectForbiddenKeys(value[key], `${
|
|
4319
|
+
rejectForbiddenKeys(value[key], `${path50}.${key}`, false);
|
|
4320
4320
|
}
|
|
4321
4321
|
}
|
|
4322
4322
|
function unknownTopLevelKeys(parsed) {
|
|
@@ -5198,7 +5198,7 @@ async function safeText(res) {
|
|
|
5198
5198
|
}
|
|
5199
5199
|
}
|
|
5200
5200
|
function sleep(ms) {
|
|
5201
|
-
return new Promise((
|
|
5201
|
+
return new Promise((resolve11) => setTimeout(resolve11, ms));
|
|
5202
5202
|
}
|
|
5203
5203
|
var DEFAULT_BASE_URL, DEFAULT_TIMEOUT_MS, RETRY_COUNT, RETRY_BACKOFF_MS, AuthClient;
|
|
5204
5204
|
var init_client = __esm({
|
|
@@ -5309,8 +5309,8 @@ var init_client = __esm({
|
|
|
5309
5309
|
throw new Error(`failed to report rate-limit for ${accountId} (HTTP ${res.status})`);
|
|
5310
5310
|
}
|
|
5311
5311
|
}
|
|
5312
|
-
async request(method,
|
|
5313
|
-
const url = `${this.baseUrl}${
|
|
5312
|
+
async request(method, path50, body, attempt = 0) {
|
|
5313
|
+
const url = `${this.baseUrl}${path50}`;
|
|
5314
5314
|
const controller = new AbortController();
|
|
5315
5315
|
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
5316
5316
|
const headers = {};
|
|
@@ -5328,7 +5328,7 @@ var init_client = __esm({
|
|
|
5328
5328
|
} catch (err) {
|
|
5329
5329
|
if (attempt < RETRY_COUNT && isTransient(err)) {
|
|
5330
5330
|
await sleep(RETRY_BACKOFF_MS * (attempt + 1));
|
|
5331
|
-
return this.request(method,
|
|
5331
|
+
return this.request(method, path50, body, attempt + 1);
|
|
5332
5332
|
}
|
|
5333
5333
|
throw err;
|
|
5334
5334
|
} finally {
|
|
@@ -5350,7 +5350,7 @@ function resolveAuthServicePath() {
|
|
|
5350
5350
|
return path9.join(pkgsDir, "auth-service");
|
|
5351
5351
|
}
|
|
5352
5352
|
function sleep2(ms) {
|
|
5353
|
-
return new Promise((
|
|
5353
|
+
return new Promise((resolve11) => setTimeout(resolve11, ms));
|
|
5354
5354
|
}
|
|
5355
5355
|
var DEFAULT_PORT, DEFAULT_VOLUME, DEFAULT_CONTAINER, DEFAULT_IMAGE, AuthContainerController;
|
|
5356
5356
|
var init_container = __esm({
|
|
@@ -5798,7 +5798,7 @@ var init_container2 = __esm({
|
|
|
5798
5798
|
await container.start();
|
|
5799
5799
|
return container;
|
|
5800
5800
|
};
|
|
5801
|
-
DEFAULT_IMAGE2 = "olam-devbox:
|
|
5801
|
+
DEFAULT_IMAGE2 = "olam-devbox:base";
|
|
5802
5802
|
CONTROL_PLANE_PORT = 8080;
|
|
5803
5803
|
HOST_CONTROL_PLANE_BASE = 19080;
|
|
5804
5804
|
HOST_APP_PORT_BASE_DELTA = 1e4;
|
|
@@ -5951,7 +5951,7 @@ var demuxStream, execInContainer;
|
|
|
5951
5951
|
var init_exec = __esm({
|
|
5952
5952
|
"../adapters/dist/docker/exec.js"() {
|
|
5953
5953
|
"use strict";
|
|
5954
|
-
demuxStream = (stream) => new Promise((
|
|
5954
|
+
demuxStream = (stream) => new Promise((resolve11, reject) => {
|
|
5955
5955
|
const stdoutChunks = [];
|
|
5956
5956
|
const stderrChunks = [];
|
|
5957
5957
|
const stdout = new PassThrough();
|
|
@@ -5965,7 +5965,7 @@ var init_exec = __esm({
|
|
|
5965
5965
|
stream.pipe(stdout);
|
|
5966
5966
|
}
|
|
5967
5967
|
stream.on("end", () => {
|
|
5968
|
-
|
|
5968
|
+
resolve11({
|
|
5969
5969
|
stdout: Buffer.concat(stdoutChunks).toString("utf-8"),
|
|
5970
5970
|
stderr: Buffer.concat(stderrChunks).toString("utf-8")
|
|
5971
5971
|
});
|
|
@@ -6003,6 +6003,51 @@ var init_exec = __esm({
|
|
|
6003
6003
|
}
|
|
6004
6004
|
});
|
|
6005
6005
|
|
|
6006
|
+
// ../adapters/dist/docker/host.js
|
|
6007
|
+
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
6008
|
+
function resolveDockerHostOptions(spawnImpl = spawnSync2) {
|
|
6009
|
+
if (process.env.DOCKER_HOST && process.env.DOCKER_HOST.length > 0) {
|
|
6010
|
+
return {};
|
|
6011
|
+
}
|
|
6012
|
+
try {
|
|
6013
|
+
const result = spawnImpl("docker", ["context", "inspect", "--format", "{{.Endpoints.docker.Host}}"], { encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] });
|
|
6014
|
+
if (result.status === 0) {
|
|
6015
|
+
const host = (result.stdout ?? "").trim();
|
|
6016
|
+
if (host.startsWith("unix://")) {
|
|
6017
|
+
return { socketPath: host.slice("unix://".length) };
|
|
6018
|
+
}
|
|
6019
|
+
if (host.startsWith("npipe://")) {
|
|
6020
|
+
return { socketPath: host.slice("npipe://".length) };
|
|
6021
|
+
}
|
|
6022
|
+
if (host.startsWith("tcp://") || host.startsWith("http://") || host.startsWith("https://")) {
|
|
6023
|
+
const url = new URL(host.startsWith("tcp://") ? host.replace("tcp://", "http://") : host);
|
|
6024
|
+
const protocol = host.startsWith("https://") ? "https" : "http";
|
|
6025
|
+
const port = url.port ? parseInt(url.port, 10) : protocol === "https" ? 2376 : 2375;
|
|
6026
|
+
return { host: url.hostname, port, protocol };
|
|
6027
|
+
}
|
|
6028
|
+
}
|
|
6029
|
+
} catch {
|
|
6030
|
+
}
|
|
6031
|
+
return { socketPath: "/var/run/docker.sock" };
|
|
6032
|
+
}
|
|
6033
|
+
function describeDockerEndpoint(opts) {
|
|
6034
|
+
if (!opts || Object.keys(opts).length === 0) {
|
|
6035
|
+
return process.env.DOCKER_HOST ? `DOCKER_HOST=${process.env.DOCKER_HOST}` : "/var/run/docker.sock (default)";
|
|
6036
|
+
}
|
|
6037
|
+
if (opts.socketPath) {
|
|
6038
|
+
return `socketPath=${opts.socketPath}`;
|
|
6039
|
+
}
|
|
6040
|
+
if (opts.host) {
|
|
6041
|
+
return `${opts.protocol ?? "http"}://${opts.host}:${opts.port ?? "?"}`;
|
|
6042
|
+
}
|
|
6043
|
+
return JSON.stringify(opts);
|
|
6044
|
+
}
|
|
6045
|
+
var init_host = __esm({
|
|
6046
|
+
"../adapters/dist/docker/host.js"() {
|
|
6047
|
+
"use strict";
|
|
6048
|
+
}
|
|
6049
|
+
});
|
|
6050
|
+
|
|
6006
6051
|
// ../adapters/dist/docker/provider.js
|
|
6007
6052
|
import Dockerode from "dockerode";
|
|
6008
6053
|
var DockerWorld, DockerProvider, dockerStateToWorldStatus;
|
|
@@ -6012,6 +6057,7 @@ var init_provider = __esm({
|
|
|
6012
6057
|
init_types2();
|
|
6013
6058
|
init_container2();
|
|
6014
6059
|
init_exec();
|
|
6060
|
+
init_host();
|
|
6015
6061
|
init_network();
|
|
6016
6062
|
init_volume();
|
|
6017
6063
|
DockerWorld = class {
|
|
@@ -6065,9 +6111,11 @@ var init_provider = __esm({
|
|
|
6065
6111
|
maxLifetimeMinutes: null
|
|
6066
6112
|
};
|
|
6067
6113
|
docker;
|
|
6114
|
+
dockerOptions;
|
|
6068
6115
|
constructor(dockerOptions) {
|
|
6069
6116
|
super();
|
|
6070
6117
|
this.docker = new Dockerode(dockerOptions);
|
|
6118
|
+
this.dockerOptions = dockerOptions;
|
|
6071
6119
|
}
|
|
6072
6120
|
// -----------------------------------------------------------------------
|
|
6073
6121
|
// createWorld
|
|
@@ -6076,8 +6124,10 @@ var init_provider = __esm({
|
|
|
6076
6124
|
const { id, name, services = [], portOffset = 0 } = config;
|
|
6077
6125
|
try {
|
|
6078
6126
|
await this.docker.ping();
|
|
6079
|
-
} catch {
|
|
6080
|
-
|
|
6127
|
+
} catch (err) {
|
|
6128
|
+
const where = describeDockerEndpoint(this.dockerOptions);
|
|
6129
|
+
const cause = err instanceof Error ? err.message : String(err);
|
|
6130
|
+
throw new Error(`Docker daemon is not available at ${where}. Ensure Docker is running and the resolved endpoint is correct. (underlying: ${cause})`);
|
|
6081
6131
|
}
|
|
6082
6132
|
await createNetwork(this.docker, id, name);
|
|
6083
6133
|
for (const svc of services) {
|
|
@@ -6339,7 +6389,7 @@ var init_connection = __esm({
|
|
|
6339
6389
|
// -----------------------------------------------------------------------
|
|
6340
6390
|
async exec(host, command) {
|
|
6341
6391
|
const client = await this.getConnection(host);
|
|
6342
|
-
return new Promise((
|
|
6392
|
+
return new Promise((resolve11, reject) => {
|
|
6343
6393
|
client.exec(command, (err, stream) => {
|
|
6344
6394
|
if (err) {
|
|
6345
6395
|
reject(new Error(`SSH exec failed on ${host}: ${err.message}`));
|
|
@@ -6354,7 +6404,7 @@ var init_connection = __esm({
|
|
|
6354
6404
|
stderr += data.toString();
|
|
6355
6405
|
});
|
|
6356
6406
|
stream.on("close", (code) => {
|
|
6357
|
-
|
|
6407
|
+
resolve11({
|
|
6358
6408
|
exitCode: code ?? 0,
|
|
6359
6409
|
stdout: stdout.trimEnd(),
|
|
6360
6410
|
stderr: stderr.trimEnd()
|
|
@@ -6385,10 +6435,10 @@ var init_connection = __esm({
|
|
|
6385
6435
|
throw new Error(`No SSH configuration found for host: ${host}`);
|
|
6386
6436
|
}
|
|
6387
6437
|
const client = new SSHClient();
|
|
6388
|
-
return new Promise((
|
|
6438
|
+
return new Promise((resolve11, reject) => {
|
|
6389
6439
|
client.on("ready", () => {
|
|
6390
6440
|
this.connections.set(host, client);
|
|
6391
|
-
|
|
6441
|
+
resolve11(client);
|
|
6392
6442
|
}).on("error", (err) => {
|
|
6393
6443
|
this.connections.delete(host);
|
|
6394
6444
|
reject(new Error(`SSH connection to ${host} failed: ${err.message}`));
|
|
@@ -6827,8 +6877,8 @@ var init_provider3 = __esm({
|
|
|
6827
6877
|
// -----------------------------------------------------------------------
|
|
6828
6878
|
// Internal fetch helper
|
|
6829
6879
|
// -----------------------------------------------------------------------
|
|
6830
|
-
async request(
|
|
6831
|
-
const url = `${this.config.workerUrl}${
|
|
6880
|
+
async request(path50, method, body) {
|
|
6881
|
+
const url = `${this.config.workerUrl}${path50}`;
|
|
6832
6882
|
const bearer = await this.config.mintToken();
|
|
6833
6883
|
const headers = {
|
|
6834
6884
|
Authorization: `Bearer ${bearer}`
|
|
@@ -6960,7 +7010,9 @@ __export(dist_exports, {
|
|
|
6960
7010
|
SSHProvider: () => SSHProvider,
|
|
6961
7011
|
auditPortsForZombies: () => auditPortsForZombies,
|
|
6962
7012
|
buildPackageManagerCacheBinds: () => buildPackageManagerCacheBinds,
|
|
6963
|
-
cleanupOrphanedResources: () => cleanupOrphanedResources
|
|
7013
|
+
cleanupOrphanedResources: () => cleanupOrphanedResources,
|
|
7014
|
+
describeDockerEndpoint: () => describeDockerEndpoint,
|
|
7015
|
+
resolveDockerHostOptions: () => resolveDockerHostOptions
|
|
6964
7016
|
});
|
|
6965
7017
|
var init_dist = __esm({
|
|
6966
7018
|
"../adapters/dist/index.js"() {
|
|
@@ -6968,6 +7020,7 @@ var init_dist = __esm({
|
|
|
6968
7020
|
init_types2();
|
|
6969
7021
|
init_provider();
|
|
6970
7022
|
init_cleanup();
|
|
7023
|
+
init_host();
|
|
6971
7024
|
init_container2();
|
|
6972
7025
|
init_container2();
|
|
6973
7026
|
init_provider2();
|
|
@@ -6978,41 +7031,13 @@ var init_dist = __esm({
|
|
|
6978
7031
|
// src/docker-host.ts
|
|
6979
7032
|
var docker_host_exports = {};
|
|
6980
7033
|
__export(docker_host_exports, {
|
|
7034
|
+
describeDockerEndpoint: () => describeDockerEndpoint,
|
|
6981
7035
|
resolveDockerHostOptions: () => resolveDockerHostOptions
|
|
6982
7036
|
});
|
|
6983
|
-
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
6984
|
-
function resolveDockerHostOptions(spawnImpl = spawnSync2) {
|
|
6985
|
-
if (process.env.DOCKER_HOST && process.env.DOCKER_HOST.length > 0) {
|
|
6986
|
-
return {};
|
|
6987
|
-
}
|
|
6988
|
-
try {
|
|
6989
|
-
const result = spawnImpl(
|
|
6990
|
-
"docker",
|
|
6991
|
-
["context", "inspect", "--format", "{{.Endpoints.docker.Host}}"],
|
|
6992
|
-
{ encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }
|
|
6993
|
-
);
|
|
6994
|
-
if (result.status === 0) {
|
|
6995
|
-
const host = (result.stdout ?? "").trim();
|
|
6996
|
-
if (host.startsWith("unix://")) {
|
|
6997
|
-
return { socketPath: host.slice("unix://".length) };
|
|
6998
|
-
}
|
|
6999
|
-
if (host.startsWith("npipe://")) {
|
|
7000
|
-
return { socketPath: host.slice("npipe://".length) };
|
|
7001
|
-
}
|
|
7002
|
-
if (host.startsWith("tcp://") || host.startsWith("http://") || host.startsWith("https://")) {
|
|
7003
|
-
const url = new URL(host.startsWith("tcp://") ? host.replace("tcp://", "http://") : host);
|
|
7004
|
-
const protocol = host.startsWith("https://") ? "https" : "http";
|
|
7005
|
-
const port = url.port ? parseInt(url.port, 10) : protocol === "https" ? 2376 : 2375;
|
|
7006
|
-
return { host: url.hostname, port, protocol };
|
|
7007
|
-
}
|
|
7008
|
-
}
|
|
7009
|
-
} catch {
|
|
7010
|
-
}
|
|
7011
|
-
return { socketPath: "/var/run/docker.sock" };
|
|
7012
|
-
}
|
|
7013
7037
|
var init_docker_host = __esm({
|
|
7014
7038
|
"src/docker-host.ts"() {
|
|
7015
7039
|
"use strict";
|
|
7040
|
+
init_dist();
|
|
7016
7041
|
}
|
|
7017
7042
|
});
|
|
7018
7043
|
|
|
@@ -7224,7 +7249,7 @@ CREATE TABLE IF NOT EXISTS meta (
|
|
|
7224
7249
|
this.db.pragma("foreign_keys = ON");
|
|
7225
7250
|
this.db.exec(CREATE_TABLE);
|
|
7226
7251
|
this.db.exec(CREATE_META);
|
|
7227
|
-
for (const col of ["readiness_chain TEXT", "expected_services TEXT", "app_port_urls TEXT"]) {
|
|
7252
|
+
for (const col of ["readiness_chain TEXT", "expected_services TEXT", "app_port_urls TEXT", "tailscale_paths TEXT"]) {
|
|
7228
7253
|
try {
|
|
7229
7254
|
this.db.exec(`ALTER TABLE worlds ADD COLUMN ${col}`);
|
|
7230
7255
|
} catch {
|
|
@@ -7342,6 +7367,27 @@ CREATE TABLE IF NOT EXISTS meta (
|
|
|
7342
7367
|
const max = rows.length > 0 ? rows[rows.length - 1].port_offset : -100;
|
|
7343
7368
|
return max + 100;
|
|
7344
7369
|
}
|
|
7370
|
+
/**
|
|
7371
|
+
* Persist tailscale serve paths registered for a world.
|
|
7372
|
+
* Paths are stored as a JSON array in the tailscale_paths column.
|
|
7373
|
+
*/
|
|
7374
|
+
storeTailscalePaths(worldId, paths) {
|
|
7375
|
+
this.db.prepare("UPDATE worlds SET tailscale_paths = ?, updated_at = ? WHERE id = ?").run(JSON.stringify(paths), (/* @__PURE__ */ new Date()).toISOString(), worldId);
|
|
7376
|
+
}
|
|
7377
|
+
/**
|
|
7378
|
+
* Load tailscale serve paths for a world. Returns [] when none stored or
|
|
7379
|
+
* column absent (pre-migration rows).
|
|
7380
|
+
*/
|
|
7381
|
+
loadTailscalePaths(worldId) {
|
|
7382
|
+
const row = this.db.prepare("SELECT tailscale_paths FROM worlds WHERE id = ?").get(worldId);
|
|
7383
|
+
if (!row?.tailscale_paths)
|
|
7384
|
+
return [];
|
|
7385
|
+
try {
|
|
7386
|
+
return JSON.parse(row.tailscale_paths);
|
|
7387
|
+
} catch {
|
|
7388
|
+
return [];
|
|
7389
|
+
}
|
|
7390
|
+
}
|
|
7345
7391
|
close() {
|
|
7346
7392
|
this.db.close();
|
|
7347
7393
|
}
|
|
@@ -8124,8 +8170,8 @@ import { execFileSync as execFileSync3 } from "node:child_process";
|
|
|
8124
8170
|
import * as fs14 from "node:fs";
|
|
8125
8171
|
import * as os9 from "node:os";
|
|
8126
8172
|
import * as path15 from "node:path";
|
|
8127
|
-
function expandHome(p,
|
|
8128
|
-
return p.replace(/^~(?=$|\/|\\)/,
|
|
8173
|
+
function expandHome(p, homedir27) {
|
|
8174
|
+
return p.replace(/^~(?=$|\/|\\)/, homedir27());
|
|
8129
8175
|
}
|
|
8130
8176
|
function sanitizeRepoFilename(name) {
|
|
8131
8177
|
const sanitized = name.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
@@ -8148,7 +8194,7 @@ ${stderr}`;
|
|
|
8148
8194
|
}
|
|
8149
8195
|
function snapshotBaselineDiff(repos, workspacePath, deps = {}) {
|
|
8150
8196
|
const exec = deps.exec ?? ((cmd, args, opts) => execFileSync3(cmd, args, opts));
|
|
8151
|
-
const
|
|
8197
|
+
const homedir27 = deps.homedir ?? (() => os9.homedir());
|
|
8152
8198
|
const baselineDir = path15.join(workspacePath, ".olam", "baseline");
|
|
8153
8199
|
try {
|
|
8154
8200
|
fs14.mkdirSync(baselineDir, { recursive: true });
|
|
@@ -8164,7 +8210,7 @@ function snapshotBaselineDiff(repos, workspacePath, deps = {}) {
|
|
|
8164
8210
|
continue;
|
|
8165
8211
|
const filename = `${sanitizeRepoFilename(repo.name)}.diff`;
|
|
8166
8212
|
const outPath = path15.join(baselineDir, filename);
|
|
8167
|
-
const repoPath = expandHome(repo.path,
|
|
8213
|
+
const repoPath = expandHome(repo.path, homedir27);
|
|
8168
8214
|
if (!fs14.existsSync(repoPath)) {
|
|
8169
8215
|
writeBaselineFile(outPath, `# repo: ${repo.name}
|
|
8170
8216
|
# (skipped: path ${repoPath} does not exist)
|
|
@@ -10284,7 +10330,9 @@ __export(manager_exports, {
|
|
|
10284
10330
|
WorldManager: () => WorldManager,
|
|
10285
10331
|
applyPostgresNetworkOverrides: () => applyPostgresNetworkOverrides,
|
|
10286
10332
|
buildManifestRuntimeForTest: () => buildManifestRuntime,
|
|
10333
|
+
cleanupWorldTailscale: () => cleanupWorldTailscale,
|
|
10287
10334
|
deriveReadinessChain: () => deriveReadinessChain,
|
|
10335
|
+
exposeWorldOverTailscale: () => exposeWorldOverTailscale,
|
|
10288
10336
|
getTokenScopes: () => getTokenScopes,
|
|
10289
10337
|
planManifestPipeline: () => planManifestPipeline,
|
|
10290
10338
|
runManifestRuntime: () => runManifestRuntime
|
|
@@ -10400,7 +10448,7 @@ ${stderr.split("\n").slice(0, 3).join(" ")}`);
|
|
|
10400
10448
|
Likely cause: corrupt local image, a custom devbox image without
|
|
10401
10449
|
the olam user (line 10 of devbox.Dockerfile), or NSS resolver
|
|
10402
10450
|
failure under cross-arch QEMU emulation.
|
|
10403
|
-
Remedy: docker rmi olam-devbox:
|
|
10451
|
+
Remedy: docker rmi olam-devbox:base && olam upgrade -y
|
|
10404
10452
|
(or set OLAM_DEVBOX_IMAGE to a known-good ref before \`olam create\`).
|
|
10405
10453
|
PR push from inside the world will not work until this is resolved.`);
|
|
10406
10454
|
return;
|
|
@@ -10634,6 +10682,71 @@ function buildManifestRuntime(worldId, repos) {
|
|
|
10634
10682
|
}
|
|
10635
10683
|
return { worldId, repos: runtimeRepos };
|
|
10636
10684
|
}
|
|
10685
|
+
function exposeWorldOverTailscale(appPortUrls, worldId, registry, _exec = execSync5) {
|
|
10686
|
+
if (process.env["OLAM_TAILSCALE_SERVE"] !== "true")
|
|
10687
|
+
return;
|
|
10688
|
+
if (appPortUrls.length === 0)
|
|
10689
|
+
return;
|
|
10690
|
+
const bin = resolveTailscaleBin(_exec);
|
|
10691
|
+
if (!bin) {
|
|
10692
|
+
console.warn("[tailscale] WARN: OLAM_TAILSCALE_SERVE=true but no tailscale binary found. Set TAILSCALE_BIN or install tailscale. Skipping serve mapping.");
|
|
10693
|
+
return;
|
|
10694
|
+
}
|
|
10695
|
+
const registeredPaths = [];
|
|
10696
|
+
for (const { repoName, hostPort } of appPortUrls) {
|
|
10697
|
+
const servePath = `/world/${worldId}/${repoName}`;
|
|
10698
|
+
try {
|
|
10699
|
+
_exec(`"${bin}" serve --bg --set-path=${servePath} http://localhost:${hostPort}`, {
|
|
10700
|
+
timeout: 1e4
|
|
10701
|
+
});
|
|
10702
|
+
console.log(`[tailscale] exposed ${worldId}/${repoName} at https://<machine>.<tailnet>/world/${worldId}/${repoName}`);
|
|
10703
|
+
registeredPaths.push(servePath);
|
|
10704
|
+
} catch (err) {
|
|
10705
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
10706
|
+
console.warn(`[tailscale] WARN: serve failed for ${repoName} (port ${hostPort}): ${msg}`);
|
|
10707
|
+
}
|
|
10708
|
+
}
|
|
10709
|
+
if (registeredPaths.length > 0) {
|
|
10710
|
+
registry.storeTailscalePaths(worldId, registeredPaths);
|
|
10711
|
+
}
|
|
10712
|
+
}
|
|
10713
|
+
function cleanupWorldTailscale(worldId, registry, _exec = execSync5) {
|
|
10714
|
+
const paths = registry.loadTailscalePaths(worldId);
|
|
10715
|
+
if (paths.length === 0)
|
|
10716
|
+
return;
|
|
10717
|
+
const bin = resolveTailscaleBin(_exec);
|
|
10718
|
+
if (!bin) {
|
|
10719
|
+
console.warn("[tailscale] WARN: Cannot cleanup tailscale serve paths \u2014 binary not found.");
|
|
10720
|
+
return;
|
|
10721
|
+
}
|
|
10722
|
+
for (const servePath of paths) {
|
|
10723
|
+
try {
|
|
10724
|
+
_exec(`"${bin}" serve --bg --set-path=${servePath} off`, { timeout: 1e4 });
|
|
10725
|
+
console.log(`[tailscale] cleaned up serve path ${servePath}`);
|
|
10726
|
+
} catch (err) {
|
|
10727
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
10728
|
+
console.warn(`[tailscale] WARN: cleanup failed for ${servePath}: ${msg}`);
|
|
10729
|
+
}
|
|
10730
|
+
}
|
|
10731
|
+
}
|
|
10732
|
+
function resolveTailscaleBin(_exec = execSync5) {
|
|
10733
|
+
const candidates = [
|
|
10734
|
+
process.env["TAILSCALE_BIN"],
|
|
10735
|
+
"/Applications/Tailscale.app/Contents/MacOS/Tailscale",
|
|
10736
|
+
"/usr/local/bin/tailscale",
|
|
10737
|
+
"/opt/homebrew/bin/tailscale"
|
|
10738
|
+
];
|
|
10739
|
+
for (const c of candidates) {
|
|
10740
|
+
if (!c)
|
|
10741
|
+
continue;
|
|
10742
|
+
try {
|
|
10743
|
+
_exec(`test -x "${c}"`, { timeout: 2e3 });
|
|
10744
|
+
return c;
|
|
10745
|
+
} catch {
|
|
10746
|
+
}
|
|
10747
|
+
}
|
|
10748
|
+
return void 0;
|
|
10749
|
+
}
|
|
10637
10750
|
function applyPostgresNetworkOverrides(worldEnv, enrichedRepos) {
|
|
10638
10751
|
const hasPostgres = enrichedRepos.some((r) => r.manifest?.services?.["postgres"] !== void 0);
|
|
10639
10752
|
if (!hasPostgres)
|
|
@@ -11379,6 +11492,10 @@ ${opts.task}`;
|
|
|
11379
11492
|
}
|
|
11380
11493
|
} catch {
|
|
11381
11494
|
}
|
|
11495
|
+
try {
|
|
11496
|
+
exposeWorldOverTailscale(appPortUrls, worldId, this.registry);
|
|
11497
|
+
} catch {
|
|
11498
|
+
}
|
|
11382
11499
|
return {
|
|
11383
11500
|
...metadata,
|
|
11384
11501
|
status: "running",
|
|
@@ -11426,6 +11543,10 @@ ${opts.task}`;
|
|
|
11426
11543
|
} catch {
|
|
11427
11544
|
}
|
|
11428
11545
|
}
|
|
11546
|
+
try {
|
|
11547
|
+
cleanupWorldTailscale(worldId, this.registry);
|
|
11548
|
+
} catch {
|
|
11549
|
+
}
|
|
11429
11550
|
try {
|
|
11430
11551
|
await this.provider.destroyWorld(worldId);
|
|
11431
11552
|
} catch {
|
|
@@ -12827,7 +12948,7 @@ function isCloudflaredAvailable() {
|
|
|
12827
12948
|
}
|
|
12828
12949
|
}
|
|
12829
12950
|
function startTunnel(port) {
|
|
12830
|
-
return new Promise((
|
|
12951
|
+
return new Promise((resolve11, reject) => {
|
|
12831
12952
|
const child = spawn2("cloudflared", ["tunnel", "--url", `http://localhost:${port}`], {
|
|
12832
12953
|
stdio: ["ignore", "pipe", "pipe"],
|
|
12833
12954
|
detached: false
|
|
@@ -12849,7 +12970,7 @@ function startTunnel(port) {
|
|
|
12849
12970
|
if (match2) {
|
|
12850
12971
|
resolved = true;
|
|
12851
12972
|
clearTimeout(timeout);
|
|
12852
|
-
|
|
12973
|
+
resolve11(match2[0]);
|
|
12853
12974
|
}
|
|
12854
12975
|
}
|
|
12855
12976
|
child.stdout?.on("data", scan);
|
|
@@ -12936,8 +13057,8 @@ var init_dashboard = __esm({
|
|
|
12936
13057
|
}
|
|
12937
13058
|
throw err;
|
|
12938
13059
|
}
|
|
12939
|
-
await new Promise((
|
|
12940
|
-
this.server.on("listening",
|
|
13060
|
+
await new Promise((resolve11, reject) => {
|
|
13061
|
+
this.server.on("listening", resolve11);
|
|
12941
13062
|
this.server.on("error", reject);
|
|
12942
13063
|
});
|
|
12943
13064
|
this.info = { localUrl: `http://localhost:${port}` };
|
|
@@ -12983,8 +13104,8 @@ var init_dashboard = __esm({
|
|
|
12983
13104
|
async stop() {
|
|
12984
13105
|
stopTunnel();
|
|
12985
13106
|
if (this.server) {
|
|
12986
|
-
await new Promise((
|
|
12987
|
-
this.server.close(() =>
|
|
13107
|
+
await new Promise((resolve11) => {
|
|
13108
|
+
this.server.close(() => resolve11());
|
|
12988
13109
|
});
|
|
12989
13110
|
this.server = null;
|
|
12990
13111
|
}
|
|
@@ -13673,10 +13794,10 @@ async function readHostCpToken2() {
|
|
|
13673
13794
|
if (!fs25.existsSync(tp)) return null;
|
|
13674
13795
|
return fs25.readFileSync(tp, "utf-8").trim();
|
|
13675
13796
|
}
|
|
13676
|
-
async function callHostCpProxy(method, worldId,
|
|
13797
|
+
async function callHostCpProxy(method, worldId, path50, body) {
|
|
13677
13798
|
const token = await readHostCpToken2();
|
|
13678
13799
|
if (!token) return { ok: false, status: 0, error: "no token (host CP not started)" };
|
|
13679
|
-
const url = `http://127.0.0.1:${HOST_CP_PORT}/api/world/${encodeURIComponent(worldId)}${
|
|
13800
|
+
const url = `http://127.0.0.1:${HOST_CP_PORT}/api/world/${encodeURIComponent(worldId)}${path50}`;
|
|
13680
13801
|
try {
|
|
13681
13802
|
const headers = {
|
|
13682
13803
|
Authorization: `Bearer ${token}`
|
|
@@ -14561,9 +14682,9 @@ var UnknownArchetypeError = class extends Error {
|
|
|
14561
14682
|
};
|
|
14562
14683
|
var ArchetypeCycleError = class extends Error {
|
|
14563
14684
|
path;
|
|
14564
|
-
constructor(
|
|
14565
|
-
super(`Archetype inheritance cycle detected: ${
|
|
14566
|
-
this.path =
|
|
14685
|
+
constructor(path50) {
|
|
14686
|
+
super(`Archetype inheritance cycle detected: ${path50.join(" \u2192 ")} \u2192 ${path50[0] ?? "?"}`);
|
|
14687
|
+
this.path = path50;
|
|
14567
14688
|
this.name = "ArchetypeCycleError";
|
|
14568
14689
|
}
|
|
14569
14690
|
};
|
|
@@ -14837,7 +14958,7 @@ var realDocker = {
|
|
|
14837
14958
|
}
|
|
14838
14959
|
};
|
|
14839
14960
|
function spawnAsync(cmd, args, opts = {}) {
|
|
14840
|
-
return new Promise((
|
|
14961
|
+
return new Promise((resolve11) => {
|
|
14841
14962
|
const child = spawn3(cmd, [...args], {
|
|
14842
14963
|
stdio: ["ignore", "pipe", "pipe"],
|
|
14843
14964
|
signal: opts.signal
|
|
@@ -14851,10 +14972,10 @@ function spawnAsync(cmd, args, opts = {}) {
|
|
|
14851
14972
|
stderr += chunk.toString();
|
|
14852
14973
|
});
|
|
14853
14974
|
child.on("error", (err) => {
|
|
14854
|
-
|
|
14975
|
+
resolve11({ exitCode: -1, stdout, stderr: stderr + err.message });
|
|
14855
14976
|
});
|
|
14856
14977
|
child.on("close", (code) => {
|
|
14857
|
-
|
|
14978
|
+
resolve11({ exitCode: code ?? -1, stdout, stderr });
|
|
14858
14979
|
});
|
|
14859
14980
|
});
|
|
14860
14981
|
}
|
|
@@ -15197,10 +15318,10 @@ async function confirm(message) {
|
|
|
15197
15318
|
if (!process.stdin.isTTY) return true;
|
|
15198
15319
|
const { createInterface: createInterface5 } = await import("node:readline");
|
|
15199
15320
|
const rl = createInterface5({ input: process.stdin, output: process.stdout });
|
|
15200
|
-
return new Promise((
|
|
15321
|
+
return new Promise((resolve11) => {
|
|
15201
15322
|
rl.question(`${message} [y/N] `, (answer) => {
|
|
15202
15323
|
rl.close();
|
|
15203
|
-
|
|
15324
|
+
resolve11(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
|
|
15204
15325
|
});
|
|
15205
15326
|
});
|
|
15206
15327
|
}
|
|
@@ -15574,7 +15695,7 @@ var McpAuthContainerController = class {
|
|
|
15574
15695
|
}
|
|
15575
15696
|
};
|
|
15576
15697
|
function sleep3(ms) {
|
|
15577
|
-
return new Promise((
|
|
15698
|
+
return new Promise((resolve11) => setTimeout(resolve11, ms));
|
|
15578
15699
|
}
|
|
15579
15700
|
function dumpContainerLogs(container, tail = 40) {
|
|
15580
15701
|
try {
|
|
@@ -15861,9 +15982,10 @@ import pc10 from "picocolors";
|
|
|
15861
15982
|
import { execSync as execSync7 } from "node:child_process";
|
|
15862
15983
|
import { existsSync as existsSync26, statSync as statSync6 } from "node:fs";
|
|
15863
15984
|
import { join as join31 } from "node:path";
|
|
15864
|
-
var DEFAULT_DEVBOX_IMAGE = "olam-devbox:
|
|
15985
|
+
var DEFAULT_DEVBOX_IMAGE = "olam-devbox:base";
|
|
15865
15986
|
var DEVBOX_BAKED_SOURCES = [
|
|
15866
|
-
"packages/adapters/src/docker/devbox.Dockerfile",
|
|
15987
|
+
"packages/adapters/src/docker/devbox.base.Dockerfile",
|
|
15988
|
+
"packages/adapters/src/docker/devbox.browser.Dockerfile",
|
|
15867
15989
|
"packages/control-plane/standalone/server.mjs",
|
|
15868
15990
|
"packages/control-plane/standalone/intelligence-bridge.mjs",
|
|
15869
15991
|
"packages/control-plane/standalone/d1-sqlite-adapter.mjs",
|
|
@@ -15928,9 +16050,9 @@ function formatFreshnessWarning(result, image = DEFAULT_DEVBOX_IMAGE) {
|
|
|
15928
16050
|
"These source files have changed since the image was built; the",
|
|
15929
16051
|
"changes will NOT take effect in fresh worlds until you rebuild:"
|
|
15930
16052
|
];
|
|
15931
|
-
for (const { path:
|
|
16053
|
+
for (const { path: path50, mtimeMs } of result.newerSources) {
|
|
15932
16054
|
const when = new Date(mtimeMs).toISOString();
|
|
15933
|
-
lines.push(` \u2022 ${
|
|
16055
|
+
lines.push(` \u2022 ${path50} (modified ${when})`);
|
|
15934
16056
|
}
|
|
15935
16057
|
lines.push("");
|
|
15936
16058
|
lines.push("Rebuild with:");
|
|
@@ -16089,15 +16211,15 @@ init_host_cp();
|
|
|
16089
16211
|
var HOST_CP_URL = "http://127.0.0.1:19000";
|
|
16090
16212
|
async function readHostCpTokenForCreate() {
|
|
16091
16213
|
try {
|
|
16092
|
-
const { default:
|
|
16214
|
+
const { default: fs48 } = await import("node:fs");
|
|
16093
16215
|
const { default: os28 } = await import("node:os");
|
|
16094
|
-
const { default:
|
|
16095
|
-
const tp =
|
|
16096
|
-
process.env.OLAM_HOME ??
|
|
16216
|
+
const { default: path50 } = await import("node:path");
|
|
16217
|
+
const tp = path50.join(
|
|
16218
|
+
process.env.OLAM_HOME ?? path50.join(os28.homedir(), ".olam"),
|
|
16097
16219
|
"host-cp.token"
|
|
16098
16220
|
);
|
|
16099
|
-
if (!
|
|
16100
|
-
return
|
|
16221
|
+
if (!fs48.existsSync(tp)) return null;
|
|
16222
|
+
return fs48.readFileSync(tp, "utf-8").trim();
|
|
16101
16223
|
} catch {
|
|
16102
16224
|
return null;
|
|
16103
16225
|
}
|
|
@@ -16460,12 +16582,12 @@ function defaultNameFromPrompt(prompt) {
|
|
|
16460
16582
|
}
|
|
16461
16583
|
async function readHostCpToken3() {
|
|
16462
16584
|
try {
|
|
16463
|
-
const { default:
|
|
16585
|
+
const { default: fs48 } = await import("node:fs");
|
|
16464
16586
|
const { default: os28 } = await import("node:os");
|
|
16465
|
-
const { default:
|
|
16466
|
-
const tp =
|
|
16467
|
-
if (!
|
|
16468
|
-
const raw =
|
|
16587
|
+
const { default: path50 } = await import("node:path");
|
|
16588
|
+
const tp = path50.join(os28.homedir(), ".olam", "host-cp.token");
|
|
16589
|
+
if (!fs48.existsSync(tp)) return null;
|
|
16590
|
+
const raw = fs48.readFileSync(tp, "utf-8").trim();
|
|
16469
16591
|
return raw.length > 0 ? raw : null;
|
|
16470
16592
|
} catch {
|
|
16471
16593
|
return null;
|
|
@@ -17133,14 +17255,14 @@ function printTable(entries) {
|
|
|
17133
17255
|
async function confirmInteractive() {
|
|
17134
17256
|
process.stdout.write(" Type `yes` to proceed: ");
|
|
17135
17257
|
const buf = [];
|
|
17136
|
-
return new Promise((
|
|
17258
|
+
return new Promise((resolve11) => {
|
|
17137
17259
|
const onData = (chunk) => {
|
|
17138
17260
|
buf.push(chunk);
|
|
17139
17261
|
if (Buffer.concat(buf).toString("utf-8").includes("\n")) {
|
|
17140
17262
|
process.stdin.removeListener("data", onData);
|
|
17141
17263
|
process.stdin.pause();
|
|
17142
17264
|
const answer = Buffer.concat(buf).toString("utf-8").trim();
|
|
17143
|
-
|
|
17265
|
+
resolve11(answer.toLowerCase() === "yes");
|
|
17144
17266
|
}
|
|
17145
17267
|
};
|
|
17146
17268
|
process.stdin.resume();
|
|
@@ -20196,11 +20318,11 @@ function zodIssueToError(issue, doc, lineCounter) {
|
|
|
20196
20318
|
suggestion: deriveSuggestion(issue)
|
|
20197
20319
|
};
|
|
20198
20320
|
}
|
|
20199
|
-
function formatJsonPath(
|
|
20200
|
-
if (
|
|
20321
|
+
function formatJsonPath(path50) {
|
|
20322
|
+
if (path50.length === 0)
|
|
20201
20323
|
return "<root>";
|
|
20202
20324
|
let out = "";
|
|
20203
|
-
for (const seg of
|
|
20325
|
+
for (const seg of path50) {
|
|
20204
20326
|
if (typeof seg === "number") {
|
|
20205
20327
|
out += `[${seg}]`;
|
|
20206
20328
|
} else {
|
|
@@ -20209,11 +20331,11 @@ function formatJsonPath(path49) {
|
|
|
20209
20331
|
}
|
|
20210
20332
|
return out;
|
|
20211
20333
|
}
|
|
20212
|
-
function resolveYamlLocation(
|
|
20334
|
+
function resolveYamlLocation(path50, doc, lineCounter) {
|
|
20213
20335
|
let bestLine = 0;
|
|
20214
20336
|
let bestColumn = 0;
|
|
20215
|
-
for (let depth =
|
|
20216
|
-
const segment =
|
|
20337
|
+
for (let depth = path50.length; depth >= 0; depth -= 1) {
|
|
20338
|
+
const segment = path50.slice(0, depth);
|
|
20217
20339
|
try {
|
|
20218
20340
|
const node = doc.getIn(segment, true);
|
|
20219
20341
|
if (node && typeof node === "object" && "range" in node) {
|
|
@@ -20431,11 +20553,11 @@ function topoSort(nodes) {
|
|
|
20431
20553
|
}
|
|
20432
20554
|
function traceCycle(start, byId) {
|
|
20433
20555
|
const seen = /* @__PURE__ */ new Set();
|
|
20434
|
-
const
|
|
20556
|
+
const path50 = [];
|
|
20435
20557
|
let current = start;
|
|
20436
20558
|
while (current && !seen.has(current)) {
|
|
20437
20559
|
seen.add(current);
|
|
20438
|
-
|
|
20560
|
+
path50.push(current);
|
|
20439
20561
|
const node = byId.get(current);
|
|
20440
20562
|
const next = node?.dependsOn[0];
|
|
20441
20563
|
if (next === void 0)
|
|
@@ -20443,10 +20565,10 @@ function traceCycle(start, byId) {
|
|
|
20443
20565
|
current = next;
|
|
20444
20566
|
}
|
|
20445
20567
|
if (current && seen.has(current)) {
|
|
20446
|
-
const idx =
|
|
20447
|
-
return [...
|
|
20568
|
+
const idx = path50.indexOf(current);
|
|
20569
|
+
return [...path50.slice(idx), current];
|
|
20448
20570
|
}
|
|
20449
|
-
return
|
|
20571
|
+
return path50;
|
|
20450
20572
|
}
|
|
20451
20573
|
|
|
20452
20574
|
// ../core/dist/executor/types.js
|
|
@@ -22855,10 +22977,10 @@ async function confirm2(message) {
|
|
|
22855
22977
|
if (!process.stdin.isTTY) return true;
|
|
22856
22978
|
const { createInterface: createInterface5 } = await import("node:readline");
|
|
22857
22979
|
const rl = createInterface5({ input: process.stdin, output: process.stdout });
|
|
22858
|
-
return new Promise((
|
|
22980
|
+
return new Promise((resolve11) => {
|
|
22859
22981
|
rl.question(`${message} [y/N] `, (answer) => {
|
|
22860
22982
|
rl.close();
|
|
22861
|
-
|
|
22983
|
+
resolve11(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
|
|
22862
22984
|
});
|
|
22863
22985
|
});
|
|
22864
22986
|
}
|
|
@@ -22977,11 +23099,18 @@ async function runUpgradePullByDigest(deps = {}) {
|
|
|
22977
23099
|
}
|
|
22978
23100
|
infoSpinner.succeed("docker daemon reachable");
|
|
22979
23101
|
const hasMcpAuthDigest = typeof digests["mcp-auth"] === "string" && digests["mcp-auth"].length > 0;
|
|
23102
|
+
const hasDevboxBaseDigest = typeof digests["devbox-base"] === "string" && digests["devbox-base"].length > 0;
|
|
22980
23103
|
const baseRefs = [
|
|
22981
23104
|
{ name: "host-cp", ref: `${registry}/olam-host-cp@${digests["host-cp"]}` },
|
|
22982
23105
|
{ name: "auth", ref: `${registry}/olam-auth@${digests.auth}` },
|
|
22983
23106
|
{ name: "devbox", ref: `${registry}/olam-devbox@${digests.devbox}` }
|
|
22984
23107
|
];
|
|
23108
|
+
if (hasDevboxBaseDigest) {
|
|
23109
|
+
baseRefs.push({
|
|
23110
|
+
name: "devbox-base",
|
|
23111
|
+
ref: `${registry}/olam-devbox@${digests["devbox-base"]}`
|
|
23112
|
+
});
|
|
23113
|
+
}
|
|
22985
23114
|
if (hasMcpAuthDigest) {
|
|
22986
23115
|
baseRefs.push({
|
|
22987
23116
|
name: "mcp-auth",
|
|
@@ -23046,18 +23175,29 @@ async function runUpgradePullByDigest(deps = {}) {
|
|
|
23046
23175
|
}
|
|
23047
23176
|
}
|
|
23048
23177
|
handshakeSpinner.succeed(`Protocol handshake passed (all ${imageRefs.length} images)`);
|
|
23178
|
+
const refByName = (n) => {
|
|
23179
|
+
const found = imageRefs.find((r) => r.name === n);
|
|
23180
|
+
if (!found) throw new Error(`upgrade: missing imageRefs entry for "${n}"`);
|
|
23181
|
+
return found.ref;
|
|
23182
|
+
};
|
|
23049
23183
|
const tagPlanBase = [
|
|
23050
|
-
{ from:
|
|
23051
|
-
{ from:
|
|
23052
|
-
{ from:
|
|
23053
|
-
{ from:
|
|
23054
|
-
{ from:
|
|
23055
|
-
{ from:
|
|
23184
|
+
{ from: refByName("host-cp"), to: "olam-host-cp:latest", name: "host-cp (bare)" },
|
|
23185
|
+
{ from: refByName("host-cp"), to: `${registry}/olam-host-cp:latest`, name: "host-cp (registry)" },
|
|
23186
|
+
{ from: refByName("auth"), to: "olam-auth:local", name: "auth (bare)" },
|
|
23187
|
+
{ from: refByName("auth"), to: `${registry}/olam-auth:latest`, name: "auth (registry)" },
|
|
23188
|
+
{ from: refByName("devbox"), to: "olam-devbox:latest", name: "devbox (bare)" },
|
|
23189
|
+
{ from: refByName("devbox"), to: `${registry}/olam-devbox:latest`, name: "devbox (registry)" }
|
|
23056
23190
|
];
|
|
23057
|
-
if (
|
|
23191
|
+
if (hasDevboxBaseDigest) {
|
|
23058
23192
|
tagPlanBase.push(
|
|
23059
|
-
{ from:
|
|
23060
|
-
{ from:
|
|
23193
|
+
{ from: refByName("devbox-base"), to: "olam-devbox:base", name: "devbox-base (bare)" },
|
|
23194
|
+
{ from: refByName("devbox-base"), to: `${registry}/olam-devbox:base`, name: "devbox-base (registry)" }
|
|
23195
|
+
);
|
|
23196
|
+
}
|
|
23197
|
+
if (hasMcpAuthDigest) {
|
|
23198
|
+
tagPlanBase.push(
|
|
23199
|
+
{ from: refByName("mcp-auth"), to: "olam-mcp-auth:local", name: "mcp-auth (bare)" },
|
|
23200
|
+
{ from: refByName("mcp-auth"), to: `${registry}/olam-mcp-auth:latest`, name: "mcp-auth (registry)" }
|
|
23061
23201
|
);
|
|
23062
23202
|
}
|
|
23063
23203
|
const tagPlan = tagPlanBase;
|
|
@@ -25599,8 +25739,8 @@ var SECRET = process.env["OLAM_MCP_AUTH_SECRET"] ?? "";
|
|
|
25599
25739
|
function authHeaders() {
|
|
25600
25740
|
return SECRET ? { "X-Olam-Mcp-Secret": SECRET } : {};
|
|
25601
25741
|
}
|
|
25602
|
-
async function apiFetch(
|
|
25603
|
-
const res = await fetch(`${BASE_URL}${
|
|
25742
|
+
async function apiFetch(path50, init = {}) {
|
|
25743
|
+
const res = await fetch(`${BASE_URL}${path50}`, {
|
|
25604
25744
|
...init,
|
|
25605
25745
|
headers: {
|
|
25606
25746
|
"Content-Type": "application/json",
|
|
@@ -25687,7 +25827,7 @@ function registerMcpLogin(cmd) {
|
|
|
25687
25827
|
init_output();
|
|
25688
25828
|
import * as readline2 from "node:readline";
|
|
25689
25829
|
async function readTokenSilent(prompt) {
|
|
25690
|
-
return new Promise((
|
|
25830
|
+
return new Promise((resolve11, reject) => {
|
|
25691
25831
|
const rl = readline2.createInterface({
|
|
25692
25832
|
input: process.stdin,
|
|
25693
25833
|
output: process.stdout,
|
|
@@ -25705,7 +25845,7 @@ async function readTokenSilent(prompt) {
|
|
|
25705
25845
|
process.stdin.removeListener("data", onData);
|
|
25706
25846
|
process.stdout.write("\n");
|
|
25707
25847
|
rl.close();
|
|
25708
|
-
|
|
25848
|
+
resolve11(token);
|
|
25709
25849
|
} else if (char === "") {
|
|
25710
25850
|
if (process.stdin.isTTY) process.stdin.setRawMode(false);
|
|
25711
25851
|
process.stdin.removeListener("data", onData);
|
|
@@ -25980,7 +26120,7 @@ async function discoverMcpSources(repoPaths) {
|
|
|
25980
26120
|
import { spawn as spawn6 } from "node:child_process";
|
|
25981
26121
|
var VALIDATION_TIMEOUT_MS = 5e3;
|
|
25982
26122
|
async function validateMcpEntry(entry) {
|
|
25983
|
-
return new Promise((
|
|
26123
|
+
return new Promise((resolve11) => {
|
|
25984
26124
|
let stdout = "";
|
|
25985
26125
|
let timedOut = false;
|
|
25986
26126
|
let child;
|
|
@@ -25990,7 +26130,7 @@ async function validateMcpEntry(entry) {
|
|
|
25990
26130
|
env: { ...process.env, ...entry.env ?? {} }
|
|
25991
26131
|
});
|
|
25992
26132
|
} catch (err) {
|
|
25993
|
-
|
|
26133
|
+
resolve11({
|
|
25994
26134
|
name: entry.name,
|
|
25995
26135
|
validated: false,
|
|
25996
26136
|
reason: err instanceof Error ? err.message : "spawn failed"
|
|
@@ -26007,11 +26147,11 @@ async function validateMcpEntry(entry) {
|
|
|
26007
26147
|
child.on("close", (code) => {
|
|
26008
26148
|
clearTimeout(timer);
|
|
26009
26149
|
if (timedOut) {
|
|
26010
|
-
|
|
26150
|
+
resolve11({ name: entry.name, validated: false, reason: "timeout (5s)" });
|
|
26011
26151
|
return;
|
|
26012
26152
|
}
|
|
26013
26153
|
const validated = code === 0 && stdout.trim().length > 0;
|
|
26014
|
-
|
|
26154
|
+
resolve11({
|
|
26015
26155
|
name: entry.name,
|
|
26016
26156
|
validated,
|
|
26017
26157
|
reason: validated ? "ok" : `exit code ${code ?? "null"}`
|
|
@@ -26019,7 +26159,7 @@ async function validateMcpEntry(entry) {
|
|
|
26019
26159
|
});
|
|
26020
26160
|
child.on("error", (err) => {
|
|
26021
26161
|
clearTimeout(timer);
|
|
26022
|
-
|
|
26162
|
+
resolve11({ name: entry.name, validated: false, reason: err.message });
|
|
26023
26163
|
});
|
|
26024
26164
|
});
|
|
26025
26165
|
}
|
|
@@ -26034,11 +26174,11 @@ async function multiSelectPicker(entries) {
|
|
|
26034
26174
|
);
|
|
26035
26175
|
});
|
|
26036
26176
|
console.log("\n" + pc29.dim('Enter numbers to import (e.g. 1,2,3 or "all" or Enter to skip):'));
|
|
26037
|
-
const answer = await new Promise((
|
|
26177
|
+
const answer = await new Promise((resolve11) => {
|
|
26038
26178
|
const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
|
|
26039
26179
|
rl.question("> ", (ans) => {
|
|
26040
26180
|
rl.close();
|
|
26041
|
-
|
|
26181
|
+
resolve11(ans.trim());
|
|
26042
26182
|
});
|
|
26043
26183
|
});
|
|
26044
26184
|
if (!answer || answer === "") return [];
|
|
@@ -26176,19 +26316,217 @@ function registerMcp(program2) {
|
|
|
26176
26316
|
registerMcpRevoke(mcp);
|
|
26177
26317
|
}
|
|
26178
26318
|
|
|
26319
|
+
// src/commands/kg-build.ts
|
|
26320
|
+
import { spawnSync as spawnSync14 } from "node:child_process";
|
|
26321
|
+
import fs46 from "node:fs";
|
|
26322
|
+
import path48 from "node:path";
|
|
26323
|
+
|
|
26324
|
+
// ../core/dist/kg/storage-paths.js
|
|
26325
|
+
import { homedir as homedir26 } from "node:os";
|
|
26326
|
+
import { join as join48, resolve as resolve10 } from "node:path";
|
|
26327
|
+
|
|
26328
|
+
// ../core/dist/world/workspace-name.js
|
|
26329
|
+
var InvalidWorkspaceNameError = class extends Error {
|
|
26330
|
+
constructor(name, reason) {
|
|
26331
|
+
super(`invalid workspace name ${JSON.stringify(name)}: ${reason}`);
|
|
26332
|
+
this.name = "InvalidWorkspaceNameError";
|
|
26333
|
+
}
|
|
26334
|
+
};
|
|
26335
|
+
var WORKSPACE_NAME_RE = /^[a-z0-9][a-z0-9_-]*$/;
|
|
26336
|
+
function validateWorkspaceName(name) {
|
|
26337
|
+
if (typeof name !== "string" || name.length === 0) {
|
|
26338
|
+
throw new InvalidWorkspaceNameError(String(name), "must be a non-empty string");
|
|
26339
|
+
}
|
|
26340
|
+
if (!WORKSPACE_NAME_RE.test(name)) {
|
|
26341
|
+
throw new InvalidWorkspaceNameError(name, "must match ^[a-z0-9][a-z0-9_-]*$ (lowercase letters, digits, hyphens, underscores; must start with letter or digit)");
|
|
26342
|
+
}
|
|
26343
|
+
}
|
|
26344
|
+
|
|
26345
|
+
// ../core/dist/kg/storage-paths.js
|
|
26346
|
+
function olamHome3() {
|
|
26347
|
+
return process.env.OLAM_HOME ?? join48(homedir26(), ".olam");
|
|
26348
|
+
}
|
|
26349
|
+
function kgRoot() {
|
|
26350
|
+
return join48(olamHome3(), "kg");
|
|
26351
|
+
}
|
|
26352
|
+
function worldsRoot() {
|
|
26353
|
+
return join48(olamHome3(), "worlds");
|
|
26354
|
+
}
|
|
26355
|
+
function assertWithinPrefix(path50, prefix, label) {
|
|
26356
|
+
if (!path50.startsWith(prefix + "/")) {
|
|
26357
|
+
throw new Error(`${label} escape: ${path50} not under ${prefix}/`);
|
|
26358
|
+
}
|
|
26359
|
+
}
|
|
26360
|
+
function kgPristinePath(workspace) {
|
|
26361
|
+
validateWorkspaceName(workspace);
|
|
26362
|
+
const root = kgRoot();
|
|
26363
|
+
const path50 = resolve10(join48(root, workspace));
|
|
26364
|
+
assertWithinPrefix(path50, root, "kgPristinePath");
|
|
26365
|
+
return path50;
|
|
26366
|
+
}
|
|
26367
|
+
var KG_PATHS_INTERNALS = Object.freeze({
|
|
26368
|
+
olamHome: olamHome3,
|
|
26369
|
+
kgRoot,
|
|
26370
|
+
worldsRoot
|
|
26371
|
+
});
|
|
26372
|
+
|
|
26373
|
+
// src/commands/kg-build.ts
|
|
26374
|
+
init_output();
|
|
26375
|
+
var DEFAULT_DEVBOX_IMAGE2 = "olam-devbox:latest";
|
|
26376
|
+
function resolveWorkspace(arg) {
|
|
26377
|
+
const cwd = process.cwd();
|
|
26378
|
+
const name = arg ?? path48.basename(cwd).toLowerCase();
|
|
26379
|
+
validateWorkspaceName(name);
|
|
26380
|
+
return { name, sourcePath: cwd };
|
|
26381
|
+
}
|
|
26382
|
+
function copyWorkspaceToScratch(source, scratch) {
|
|
26383
|
+
if (process.platform === "darwin") {
|
|
26384
|
+
const r2 = spawnSync14("cp", ["-c", "-r", source + "/.", scratch], {
|
|
26385
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
26386
|
+
encoding: "utf-8"
|
|
26387
|
+
});
|
|
26388
|
+
if (r2.status === 0) return "cp-c-r-reflink";
|
|
26389
|
+
}
|
|
26390
|
+
const r = spawnSync14("cp", ["-r", source + "/.", scratch], {
|
|
26391
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
26392
|
+
encoding: "utf-8"
|
|
26393
|
+
});
|
|
26394
|
+
if (r.status !== 0) {
|
|
26395
|
+
throw new Error(`cp -r failed: ${(r.stderr ?? "").trim() || r.error?.message}`);
|
|
26396
|
+
}
|
|
26397
|
+
return "cp-r";
|
|
26398
|
+
}
|
|
26399
|
+
function parseNodeCount(graphifyOutDir) {
|
|
26400
|
+
const graphPath = path48.join(graphifyOutDir, "graph.json");
|
|
26401
|
+
if (!fs46.existsSync(graphPath)) return null;
|
|
26402
|
+
try {
|
|
26403
|
+
const content = fs46.readFileSync(graphPath, "utf-8");
|
|
26404
|
+
const data = JSON.parse(content);
|
|
26405
|
+
return Array.isArray(data.nodes) ? data.nodes.length : null;
|
|
26406
|
+
} catch {
|
|
26407
|
+
return null;
|
|
26408
|
+
}
|
|
26409
|
+
}
|
|
26410
|
+
function readGraphifyVersion(image) {
|
|
26411
|
+
const r = spawnSync14(
|
|
26412
|
+
"docker",
|
|
26413
|
+
[
|
|
26414
|
+
"run",
|
|
26415
|
+
"--rm",
|
|
26416
|
+
"--entrypoint=/opt/graphify-venv/bin/pip",
|
|
26417
|
+
image,
|
|
26418
|
+
"show",
|
|
26419
|
+
"graphifyy"
|
|
26420
|
+
],
|
|
26421
|
+
{ encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }
|
|
26422
|
+
);
|
|
26423
|
+
if (r.status !== 0) return "unknown";
|
|
26424
|
+
const m = (r.stdout ?? "").match(/^Version:\s*(\S+)/m);
|
|
26425
|
+
return m?.[1] ?? "unknown";
|
|
26426
|
+
}
|
|
26427
|
+
async function runKgBuild(workspaceArg, options = {}) {
|
|
26428
|
+
const image = options.image ?? DEFAULT_DEVBOX_IMAGE2;
|
|
26429
|
+
let workspace;
|
|
26430
|
+
try {
|
|
26431
|
+
workspace = resolveWorkspace(workspaceArg);
|
|
26432
|
+
} catch (err) {
|
|
26433
|
+
printError(err instanceof Error ? err.message : String(err));
|
|
26434
|
+
return { exitCode: 2 };
|
|
26435
|
+
}
|
|
26436
|
+
const outDir = kgPristinePath(workspace.name);
|
|
26437
|
+
const scratchDir = path48.join(outDir, "scratch");
|
|
26438
|
+
fs46.mkdirSync(outDir, { recursive: true });
|
|
26439
|
+
if (fs46.existsSync(scratchDir)) fs46.rmSync(scratchDir, { recursive: true, force: true });
|
|
26440
|
+
fs46.mkdirSync(scratchDir);
|
|
26441
|
+
const human = !options.json;
|
|
26442
|
+
if (human) {
|
|
26443
|
+
printInfo("kg build", `workspace=${workspace.name} source=${workspace.sourcePath}`);
|
|
26444
|
+
printInfo("kg build", `scratch=${scratchDir} \u2192 out=${outDir}/graphify-out/`);
|
|
26445
|
+
}
|
|
26446
|
+
const started = Date.now();
|
|
26447
|
+
let scratchStrategy;
|
|
26448
|
+
try {
|
|
26449
|
+
scratchStrategy = copyWorkspaceToScratch(workspace.sourcePath, scratchDir);
|
|
26450
|
+
if (human) printInfo("kg build", `copied via ${scratchStrategy}`);
|
|
26451
|
+
const dockerArgs = [
|
|
26452
|
+
"run",
|
|
26453
|
+
"--rm",
|
|
26454
|
+
"-v",
|
|
26455
|
+
`${scratchDir}:/work`,
|
|
26456
|
+
"-w",
|
|
26457
|
+
"/work",
|
|
26458
|
+
"--entrypoint=graphify",
|
|
26459
|
+
image,
|
|
26460
|
+
"update",
|
|
26461
|
+
"."
|
|
26462
|
+
];
|
|
26463
|
+
const r = human ? spawnSync14("docker", dockerArgs, { stdio: "inherit" }) : spawnSync14("docker", dockerArgs, { stdio: ["ignore", "ignore", "pipe"] });
|
|
26464
|
+
if (r.status !== 0) {
|
|
26465
|
+
printError(`graphify update failed (exit ${r.status})`);
|
|
26466
|
+
return { exitCode: r.status ?? 1 };
|
|
26467
|
+
}
|
|
26468
|
+
const scratchOut = path48.join(scratchDir, "graphify-out");
|
|
26469
|
+
const finalOut = path48.join(outDir, "graphify-out");
|
|
26470
|
+
if (!fs46.existsSync(scratchOut)) {
|
|
26471
|
+
printError(`graphify produced no graphify-out/ in scratch (${scratchOut})`);
|
|
26472
|
+
return { exitCode: 1 };
|
|
26473
|
+
}
|
|
26474
|
+
if (fs46.existsSync(finalOut)) fs46.rmSync(finalOut, { recursive: true, force: true });
|
|
26475
|
+
fs46.renameSync(scratchOut, finalOut);
|
|
26476
|
+
const durationMs = Date.now() - started;
|
|
26477
|
+
const nodeCount = parseNodeCount(finalOut);
|
|
26478
|
+
const version = readGraphifyVersion(image);
|
|
26479
|
+
const freshness = {
|
|
26480
|
+
built_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
26481
|
+
duration_ms: durationMs,
|
|
26482
|
+
node_count: nodeCount,
|
|
26483
|
+
graphify_version: version,
|
|
26484
|
+
workspace: workspace.name,
|
|
26485
|
+
scratch_strategy: scratchStrategy
|
|
26486
|
+
};
|
|
26487
|
+
fs46.writeFileSync(
|
|
26488
|
+
path48.join(outDir, "freshness.json"),
|
|
26489
|
+
JSON.stringify(freshness, null, 2) + "\n",
|
|
26490
|
+
"utf-8"
|
|
26491
|
+
);
|
|
26492
|
+
if (options.json) {
|
|
26493
|
+
process.stdout.write(JSON.stringify(freshness) + "\n");
|
|
26494
|
+
} else {
|
|
26495
|
+
printSuccess(
|
|
26496
|
+
`kg build ${workspace.name} \u2014 ${nodeCount ?? "?"} nodes in ${(durationMs / 1e3).toFixed(1)}s (graphify ${version})`
|
|
26497
|
+
);
|
|
26498
|
+
printInfo("output", `${finalOut}/graph.json`);
|
|
26499
|
+
}
|
|
26500
|
+
return { exitCode: 0, freshness, outputDir: finalOut };
|
|
26501
|
+
} finally {
|
|
26502
|
+
if (fs46.existsSync(scratchDir)) {
|
|
26503
|
+
fs46.rmSync(scratchDir, { recursive: true, force: true });
|
|
26504
|
+
}
|
|
26505
|
+
}
|
|
26506
|
+
}
|
|
26507
|
+
function registerKg(program2) {
|
|
26508
|
+
const kg = program2.command("kg").description("Knowledge-graph operations (graphify-backed)");
|
|
26509
|
+
kg.command("build").description(
|
|
26510
|
+
"Build pristine KG for a workspace (default: current dir). Scratch-dir pattern; ~/.olam/kg/<ws>/graphify-out/"
|
|
26511
|
+
).argument("[workspace]", "workspace name (lowercase alphanumeric + hyphens/underscores)").option("--image <ref>", `devbox image to invoke (default: ${DEFAULT_DEVBOX_IMAGE2})`).option("--json", "emit freshness record as JSON instead of human-readable status").action(async (workspaceArg, opts) => {
|
|
26512
|
+
const r = await runKgBuild(workspaceArg, opts);
|
|
26513
|
+
if (r.exitCode !== 0) process.exitCode = r.exitCode;
|
|
26514
|
+
});
|
|
26515
|
+
}
|
|
26516
|
+
|
|
26179
26517
|
// src/pleri-config.ts
|
|
26180
|
-
import * as
|
|
26181
|
-
import * as
|
|
26518
|
+
import * as fs47 from "node:fs";
|
|
26519
|
+
import * as path49 from "node:path";
|
|
26182
26520
|
function isPleriConfigured(configDir = process.env.OLAM_CONFIG_DIR ?? ".olam") {
|
|
26183
26521
|
if (process.env.PLERI_BASE_URL) {
|
|
26184
26522
|
return true;
|
|
26185
26523
|
}
|
|
26186
|
-
const configPath =
|
|
26187
|
-
if (!
|
|
26524
|
+
const configPath = path49.join(configDir, "config.yaml");
|
|
26525
|
+
if (!fs47.existsSync(configPath)) {
|
|
26188
26526
|
return false;
|
|
26189
26527
|
}
|
|
26190
26528
|
try {
|
|
26191
|
-
const contents =
|
|
26529
|
+
const contents = fs47.readFileSync(configPath, "utf8");
|
|
26192
26530
|
return /^[^#\n]*\bpleri:/m.test(contents);
|
|
26193
26531
|
} catch {
|
|
26194
26532
|
return false;
|
|
@@ -26230,6 +26568,7 @@ registerBegin(program);
|
|
|
26230
26568
|
registerStop(program);
|
|
26231
26569
|
registerWorldUpgrade(program);
|
|
26232
26570
|
registerMcp(program);
|
|
26571
|
+
registerKg(program);
|
|
26233
26572
|
registerConfig(program);
|
|
26234
26573
|
registerRepos(program);
|
|
26235
26574
|
registerRunbooks(program);
|