@pleri/olam-cli 0.1.143 → 0.1.145
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/doctor.d.ts +53 -23
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +117 -46
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/logs.d.ts +17 -3
- package/dist/commands/logs.d.ts.map +1 -1
- package/dist/commands/logs.js +38 -35
- package/dist/commands/logs.js.map +1 -1
- package/dist/commands/memory/bridge.d.ts +57 -0
- package/dist/commands/memory/bridge.d.ts.map +1 -0
- package/dist/commands/memory/bridge.js +156 -0
- package/dist/commands/memory/bridge.js.map +1 -0
- package/dist/commands/memory/index.d.ts +3 -0
- package/dist/commands/memory/index.d.ts.map +1 -1
- package/dist/commands/memory/index.js +10 -1
- package/dist/commands/memory/index.js.map +1 -1
- package/dist/commands/memory/reclassify.d.ts +56 -0
- package/dist/commands/memory/reclassify.d.ts.map +1 -0
- package/dist/commands/memory/reclassify.js +177 -0
- package/dist/commands/memory/reclassify.js.map +1 -0
- package/dist/commands/memory/stats.d.ts +69 -0
- package/dist/commands/memory/stats.d.ts.map +1 -0
- package/dist/commands/memory/stats.js +164 -0
- package/dist/commands/memory/stats.js.map +1 -0
- package/dist/commands/skills-source.d.ts +12 -0
- package/dist/commands/skills-source.d.ts.map +1 -0
- package/dist/commands/skills-source.js +133 -0
- package/dist/commands/skills-source.js.map +1 -0
- package/dist/commands/skills.d.ts +11 -0
- package/dist/commands/skills.d.ts.map +1 -0
- package/dist/commands/skills.js +163 -0
- package/dist/commands/skills.js.map +1 -0
- package/dist/commands/status.d.ts +27 -0
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +102 -1
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/upgrade.d.ts +24 -0
- package/dist/commands/upgrade.d.ts.map +1 -1
- package/dist/commands/upgrade.js +73 -0
- package/dist/commands/upgrade.js.map +1 -1
- package/dist/image-digests.json +7 -7
- package/dist/index.js +2093 -538
- package/dist/index.js.map +1 -1
- package/dist/lib/health-probes.d.ts +72 -0
- package/dist/lib/health-probes.d.ts.map +1 -1
- package/dist/lib/health-probes.js +218 -0
- package/dist/lib/health-probes.js.map +1 -1
- package/dist/mcp-server.js +1248 -353
- package/host-cp/src/agent-runtime-trigger.mjs +262 -0
- package/host-cp/src/engine-identity.mjs +32 -0
- package/host-cp/src/server.mjs +246 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -492,8 +492,8 @@ var init_parseUtil = __esm({
|
|
|
492
492
|
init_errors();
|
|
493
493
|
init_en();
|
|
494
494
|
makeIssue = (params) => {
|
|
495
|
-
const { data, path:
|
|
496
|
-
const fullPath = [...
|
|
495
|
+
const { data, path: path66, errorMaps, issueData } = params;
|
|
496
|
+
const fullPath = [...path66, ...issueData.path || []];
|
|
497
497
|
const fullIssue = {
|
|
498
498
|
...issueData,
|
|
499
499
|
path: fullPath
|
|
@@ -801,11 +801,11 @@ var init_types = __esm({
|
|
|
801
801
|
init_parseUtil();
|
|
802
802
|
init_util();
|
|
803
803
|
ParseInputLazyPath = class {
|
|
804
|
-
constructor(parent, value,
|
|
804
|
+
constructor(parent, value, path66, key) {
|
|
805
805
|
this._cachedPath = [];
|
|
806
806
|
this.parent = parent;
|
|
807
807
|
this.data = value;
|
|
808
|
-
this._path =
|
|
808
|
+
this._path = path66;
|
|
809
809
|
this._key = key;
|
|
810
810
|
}
|
|
811
811
|
get path() {
|
|
@@ -4286,7 +4286,7 @@ import YAML from "yaml";
|
|
|
4286
4286
|
function bootstrapStepCmd(entry) {
|
|
4287
4287
|
return typeof entry === "string" ? entry : entry.cmd;
|
|
4288
4288
|
}
|
|
4289
|
-
function refineForbiddenKeys(value,
|
|
4289
|
+
function refineForbiddenKeys(value, path66, ctx, rejectSource) {
|
|
4290
4290
|
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
4291
4291
|
return;
|
|
4292
4292
|
}
|
|
@@ -4294,12 +4294,12 @@ function refineForbiddenKeys(value, path60, ctx, rejectSource) {
|
|
|
4294
4294
|
if (FORBIDDEN_KEYS.has(key)) {
|
|
4295
4295
|
ctx.addIssue({
|
|
4296
4296
|
code: external_exports.ZodIssueCode.custom,
|
|
4297
|
-
path: [...
|
|
4297
|
+
path: [...path66, key],
|
|
4298
4298
|
message: `forbidden key "${key}" (prototype-pollution surface)`
|
|
4299
4299
|
});
|
|
4300
4300
|
continue;
|
|
4301
4301
|
}
|
|
4302
|
-
if (rejectSource &&
|
|
4302
|
+
if (rejectSource && path66.length === 0 && key === "source") {
|
|
4303
4303
|
ctx.addIssue({
|
|
4304
4304
|
code: external_exports.ZodIssueCode.custom,
|
|
4305
4305
|
path: ["source"],
|
|
@@ -4307,21 +4307,21 @@ function refineForbiddenKeys(value, path60, ctx, rejectSource) {
|
|
|
4307
4307
|
});
|
|
4308
4308
|
continue;
|
|
4309
4309
|
}
|
|
4310
|
-
refineForbiddenKeys(value[key], [...
|
|
4310
|
+
refineForbiddenKeys(value[key], [...path66, key], ctx, false);
|
|
4311
4311
|
}
|
|
4312
4312
|
}
|
|
4313
|
-
function rejectForbiddenKeys(value,
|
|
4313
|
+
function rejectForbiddenKeys(value, path66, rejectSource) {
|
|
4314
4314
|
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
4315
4315
|
return;
|
|
4316
4316
|
}
|
|
4317
4317
|
for (const key of Object.keys(value)) {
|
|
4318
4318
|
if (FORBIDDEN_KEYS.has(key)) {
|
|
4319
|
-
throw new Error(`[manifest] ${
|
|
4319
|
+
throw new Error(`[manifest] ${path66}: forbidden key "${key}" (prototype-pollution surface)`);
|
|
4320
4320
|
}
|
|
4321
4321
|
if (rejectSource && key === "source") {
|
|
4322
|
-
throw new Error(`[manifest] ${
|
|
4322
|
+
throw new Error(`[manifest] ${path66}: top-level "source" is loader-stamped \u2014 manifests must not author it`);
|
|
4323
4323
|
}
|
|
4324
|
-
rejectForbiddenKeys(value[key], `${
|
|
4324
|
+
rejectForbiddenKeys(value[key], `${path66}.${key}`, false);
|
|
4325
4325
|
}
|
|
4326
4326
|
}
|
|
4327
4327
|
function unknownTopLevelKeys(parsed) {
|
|
@@ -5500,8 +5500,8 @@ var init_client = __esm({
|
|
|
5500
5500
|
throw new Error(`failed to report rate-limit for ${accountId} (HTTP ${res.status})`);
|
|
5501
5501
|
}
|
|
5502
5502
|
}
|
|
5503
|
-
async request(method,
|
|
5504
|
-
const url2 = `${this.baseUrl}${
|
|
5503
|
+
async request(method, path66, body, attempt = 0) {
|
|
5504
|
+
const url2 = `${this.baseUrl}${path66}`;
|
|
5505
5505
|
const controller = new AbortController();
|
|
5506
5506
|
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
5507
5507
|
const headers = {};
|
|
@@ -5519,7 +5519,7 @@ var init_client = __esm({
|
|
|
5519
5519
|
} catch (err) {
|
|
5520
5520
|
if (attempt < RETRY_COUNT && isTransient(err)) {
|
|
5521
5521
|
await sleep(RETRY_BACKOFF_MS * (attempt + 1));
|
|
5522
|
-
return this.request(method,
|
|
5522
|
+
return this.request(method, path66, body, attempt + 1);
|
|
5523
5523
|
}
|
|
5524
5524
|
throw err;
|
|
5525
5525
|
} finally {
|
|
@@ -7292,8 +7292,8 @@ var init_provider3 = __esm({
|
|
|
7292
7292
|
// -----------------------------------------------------------------------
|
|
7293
7293
|
// Internal fetch helper
|
|
7294
7294
|
// -----------------------------------------------------------------------
|
|
7295
|
-
async request(
|
|
7296
|
-
const url2 = `${this.config.workerUrl}${
|
|
7295
|
+
async request(path66, method, body) {
|
|
7296
|
+
const url2 = `${this.config.workerUrl}${path66}`;
|
|
7297
7297
|
const bearer = await this.config.mintToken();
|
|
7298
7298
|
const headers = {
|
|
7299
7299
|
Authorization: `Bearer ${bearer}`
|
|
@@ -8640,17 +8640,17 @@ function kgRoot() {
|
|
|
8640
8640
|
function worldsRoot() {
|
|
8641
8641
|
return join16(olamHome(), "worlds");
|
|
8642
8642
|
}
|
|
8643
|
-
function assertWithinPrefix(
|
|
8644
|
-
if (!
|
|
8645
|
-
throw new Error(`${label} escape: ${
|
|
8643
|
+
function assertWithinPrefix(path66, prefix, label) {
|
|
8644
|
+
if (!path66.startsWith(prefix + "/")) {
|
|
8645
|
+
throw new Error(`${label} escape: ${path66} not under ${prefix}/`);
|
|
8646
8646
|
}
|
|
8647
8647
|
}
|
|
8648
8648
|
function kgPristinePath(workspace) {
|
|
8649
8649
|
validateWorkspaceName(workspace);
|
|
8650
8650
|
const root = kgRoot();
|
|
8651
|
-
const
|
|
8652
|
-
assertWithinPrefix(
|
|
8653
|
-
return
|
|
8651
|
+
const path66 = resolve5(join16(root, workspace));
|
|
8652
|
+
assertWithinPrefix(path66, root, "kgPristinePath");
|
|
8653
|
+
return path66;
|
|
8654
8654
|
}
|
|
8655
8655
|
var KG_PATHS_INTERNALS;
|
|
8656
8656
|
var init_storage_paths = __esm({
|
|
@@ -8765,8 +8765,8 @@ import { execFileSync as execFileSync4 } from "node:child_process";
|
|
|
8765
8765
|
import * as fs15 from "node:fs";
|
|
8766
8766
|
import * as os9 from "node:os";
|
|
8767
8767
|
import * as path16 from "node:path";
|
|
8768
|
-
function expandHome2(p,
|
|
8769
|
-
return p.replace(/^~(?=$|\/|\\)/,
|
|
8768
|
+
function expandHome2(p, homedir40) {
|
|
8769
|
+
return p.replace(/^~(?=$|\/|\\)/, homedir40());
|
|
8770
8770
|
}
|
|
8771
8771
|
function sanitizeRepoFilename(name) {
|
|
8772
8772
|
const sanitized = name.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
@@ -8789,7 +8789,7 @@ ${stderr}`;
|
|
|
8789
8789
|
}
|
|
8790
8790
|
function snapshotBaselineDiff(repos, workspacePath, deps = {}) {
|
|
8791
8791
|
const exec = deps.exec ?? ((cmd, args, opts) => execFileSync4(cmd, args, opts));
|
|
8792
|
-
const
|
|
8792
|
+
const homedir40 = deps.homedir ?? (() => os9.homedir());
|
|
8793
8793
|
const baselineDir = path16.join(workspacePath, ".olam", "baseline");
|
|
8794
8794
|
try {
|
|
8795
8795
|
fs15.mkdirSync(baselineDir, { recursive: true });
|
|
@@ -8805,7 +8805,7 @@ function snapshotBaselineDiff(repos, workspacePath, deps = {}) {
|
|
|
8805
8805
|
continue;
|
|
8806
8806
|
const filename = `${sanitizeRepoFilename(repo.name)}.diff`;
|
|
8807
8807
|
const outPath = path16.join(baselineDir, filename);
|
|
8808
|
-
const repoPath = expandHome2(repo.path,
|
|
8808
|
+
const repoPath = expandHome2(repo.path, homedir40);
|
|
8809
8809
|
if (!fs15.existsSync(repoPath)) {
|
|
8810
8810
|
writeBaselineFile(outPath, `# repo: ${repo.name}
|
|
8811
8811
|
# (skipped: path ${repoPath} does not exist)
|
|
@@ -8925,21 +8925,21 @@ function extractStderr(err) {
|
|
|
8925
8925
|
}
|
|
8926
8926
|
function carryUncommittedEdits(repos, workspacePath, deps = {}) {
|
|
8927
8927
|
const exec = deps.exec ?? ((cmd, args, opts) => execFileSync4(cmd, args, opts));
|
|
8928
|
-
const
|
|
8929
|
-
const
|
|
8930
|
-
const
|
|
8931
|
-
const
|
|
8928
|
+
const homedir40 = deps.homedir ?? (() => os9.homedir());
|
|
8929
|
+
const existsSync71 = deps.existsSync ?? ((p) => fs15.existsSync(p));
|
|
8930
|
+
const copyFileSync9 = deps.copyFileSync ?? ((src, dest) => fs15.copyFileSync(src, dest));
|
|
8931
|
+
const mkdirSync39 = deps.mkdirSync ?? ((dirPath, opts) => {
|
|
8932
8932
|
fs15.mkdirSync(dirPath, opts);
|
|
8933
8933
|
});
|
|
8934
8934
|
const plans = [];
|
|
8935
8935
|
for (const repo of repos) {
|
|
8936
8936
|
if (!repo.path)
|
|
8937
8937
|
continue;
|
|
8938
|
-
const repoPath = expandHome2(repo.path,
|
|
8938
|
+
const repoPath = expandHome2(repo.path, homedir40);
|
|
8939
8939
|
const worktreePath = path16.join(workspacePath, repo.name);
|
|
8940
|
-
if (!
|
|
8940
|
+
if (!existsSync71(repoPath))
|
|
8941
8941
|
continue;
|
|
8942
|
-
if (!
|
|
8942
|
+
if (!existsSync71(worktreePath)) {
|
|
8943
8943
|
console.warn(`[carry] ${repo.name}: world worktree ${worktreePath} missing; skipping carry for this repo`);
|
|
8944
8944
|
continue;
|
|
8945
8945
|
}
|
|
@@ -8999,11 +8999,11 @@ function carryUncommittedEdits(repos, workspacePath, deps = {}) {
|
|
|
8999
8999
|
for (const rel of plan.diff.untracked) {
|
|
9000
9000
|
const src = path16.join(plan.repoPath, rel);
|
|
9001
9001
|
const dest = path16.join(plan.worktreePath, rel);
|
|
9002
|
-
if (!
|
|
9002
|
+
if (!existsSync71(src))
|
|
9003
9003
|
continue;
|
|
9004
9004
|
try {
|
|
9005
|
-
|
|
9006
|
-
|
|
9005
|
+
mkdirSync39(path16.dirname(dest), { recursive: true });
|
|
9006
|
+
copyFileSync9(src, dest);
|
|
9007
9007
|
} catch (err) {
|
|
9008
9008
|
const msg = err instanceof Error ? err.message : String(err);
|
|
9009
9009
|
console.warn(`[carry] ${plan.name}: copy untracked ${rel} failed: ${msg}`);
|
|
@@ -9066,10 +9066,10 @@ import * as fs16 from "node:fs";
|
|
|
9066
9066
|
import * as path17 from "node:path";
|
|
9067
9067
|
function injectWorldContext(opts) {
|
|
9068
9068
|
const { world } = opts;
|
|
9069
|
-
const
|
|
9070
|
-
fs16.mkdirSync(
|
|
9069
|
+
const claudeDir2 = path17.join(world.workspacePath, ".claude");
|
|
9070
|
+
fs16.mkdirSync(claudeDir2, { recursive: true });
|
|
9071
9071
|
const content = WORLD_CLAUDE_MD.replace("{{worldName}}", world.name).replace("{{worldId}}", world.id).replace("{{branch}}", world.branch).replace("{{taskBlock}}", buildTaskBlock(opts)).replace("{{reposList}}", buildReposList(world)).replace("{{servicesLine}}", buildServicesLine(opts.services)).replace("{{pleriPlaneLine}}", buildPleriPlaneLine(opts.pleriPlaneUrl)).replace("{{planFileBlock}}", buildPlanFileBlock(world)).replace("{{extraContextBlock}}", buildExtraContextBlock(opts.claudeMdExtra));
|
|
9072
|
-
fs16.writeFileSync(path17.join(
|
|
9072
|
+
fs16.writeFileSync(path17.join(claudeDir2, "CLAUDE.md"), content);
|
|
9073
9073
|
writeOlamDocs(world.workspacePath);
|
|
9074
9074
|
}
|
|
9075
9075
|
function buildTaskBlock(opts) {
|
|
@@ -10589,6 +10589,26 @@ var init_auto_dispatch_task = __esm({
|
|
|
10589
10589
|
}
|
|
10590
10590
|
});
|
|
10591
10591
|
|
|
10592
|
+
// ../core/dist/skill-sources/schema.js
|
|
10593
|
+
var SKILL_SOURCE_ID_LENGTH, NAME_PATTERN, URL_PATTERN, SkillSourceSchema;
|
|
10594
|
+
var init_schema3 = __esm({
|
|
10595
|
+
"../core/dist/skill-sources/schema.js"() {
|
|
10596
|
+
"use strict";
|
|
10597
|
+
init_v3();
|
|
10598
|
+
SKILL_SOURCE_ID_LENGTH = 12;
|
|
10599
|
+
NAME_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/;
|
|
10600
|
+
URL_PATTERN = /^(?:https?:\/\/|git@|ssh:\/\/|file:\/\/|\/).+/;
|
|
10601
|
+
SkillSourceSchema = external_exports.object({
|
|
10602
|
+
id: external_exports.string().length(SKILL_SOURCE_ID_LENGTH, `skill-source id must be exactly ${SKILL_SOURCE_ID_LENGTH} chars (sha256-of-url prefix)`).regex(/^[a-f0-9]+$/, "skill-source id must be lowercase hex"),
|
|
10603
|
+
name: external_exports.string().regex(NAME_PATTERN, "skill-source name must be ASCII lowercase + digits + dash (1-64 chars, no leading/trailing dash)"),
|
|
10604
|
+
gitUrl: external_exports.string().min(1, "gitUrl must not be empty").regex(URL_PATTERN, "gitUrl must look like a git URL (https://, git@, ssh://, file://, or absolute path)"),
|
|
10605
|
+
branch: external_exports.string().min(1, "branch must not be empty").default("main"),
|
|
10606
|
+
addedAt: external_exports.number().int().nonnegative(),
|
|
10607
|
+
lastPulledSha: external_exports.string().regex(/^[a-f0-9]{40}$/, "lastPulledSha must be a 40-char lowercase hex git SHA").optional()
|
|
10608
|
+
});
|
|
10609
|
+
}
|
|
10610
|
+
});
|
|
10611
|
+
|
|
10592
10612
|
// ../core/dist/global-config/schema.js
|
|
10593
10613
|
function isAbsoluteOrTilde(p) {
|
|
10594
10614
|
return p.startsWith("/") || p === "~" || p.startsWith("~/");
|
|
@@ -10597,11 +10617,12 @@ function hasNoTraversalComponents(p) {
|
|
|
10597
10617
|
return !p.split("/").some((seg) => seg === "..");
|
|
10598
10618
|
}
|
|
10599
10619
|
var RepoEntrySchema, PortMapSchema, SeedSqlFileSchema, SeedCommandSchema, SeedFixtureCopySchema, SeedSchema, RunbookSchema, GlobalConfigSchema, DEFAULT_GLOBAL_CONFIG;
|
|
10600
|
-
var
|
|
10620
|
+
var init_schema4 = __esm({
|
|
10601
10621
|
"../core/dist/global-config/schema.js"() {
|
|
10602
10622
|
"use strict";
|
|
10603
10623
|
init_v3();
|
|
10604
10624
|
init_schema();
|
|
10625
|
+
init_schema3();
|
|
10605
10626
|
RepoEntrySchema = external_exports.object({
|
|
10606
10627
|
name: external_exports.string().regex(REPO_NAME_PATTERN, "repo name must be ASCII lowercase + digits + dash (1-64 chars, no leading/trailing dash)"),
|
|
10607
10628
|
path: external_exports.string().min(1, "path must not be empty").refine(isAbsoluteOrTilde, {
|
|
@@ -10648,7 +10669,8 @@ var init_schema3 = __esm({
|
|
|
10648
10669
|
GlobalConfigSchema = external_exports.object({
|
|
10649
10670
|
schemaVersion: external_exports.literal(1),
|
|
10650
10671
|
repos: external_exports.array(RepoEntrySchema).optional().default([]),
|
|
10651
|
-
runbooks: external_exports.array(RunbookSchema).optional().default([])
|
|
10672
|
+
runbooks: external_exports.array(RunbookSchema).optional().default([]),
|
|
10673
|
+
skillSources: external_exports.array(SkillSourceSchema).optional().default([])
|
|
10652
10674
|
}).strip().superRefine((val, ctx) => {
|
|
10653
10675
|
const repoNames = val.repos.map((r) => r.name);
|
|
10654
10676
|
const repoDupes = repoNames.filter((n, i) => repoNames.indexOf(n) !== i);
|
|
@@ -10660,11 +10682,22 @@ var init_schema3 = __esm({
|
|
|
10660
10682
|
for (const d of rbDupes) {
|
|
10661
10683
|
ctx.addIssue({ code: external_exports.ZodIssueCode.custom, message: `duplicate runbook name: "${d}"`, path: ["runbooks"] });
|
|
10662
10684
|
}
|
|
10685
|
+
const skillIds = val.skillSources.map((s) => s.id);
|
|
10686
|
+
const skillIdDupes = skillIds.filter((n, i) => skillIds.indexOf(n) !== i);
|
|
10687
|
+
for (const d of skillIdDupes) {
|
|
10688
|
+
ctx.addIssue({ code: external_exports.ZodIssueCode.custom, message: `duplicate skill-source id: "${d}"`, path: ["skillSources"] });
|
|
10689
|
+
}
|
|
10690
|
+
const skillNames = val.skillSources.map((s) => s.name);
|
|
10691
|
+
const skillNameDupes = skillNames.filter((n, i) => skillNames.indexOf(n) !== i);
|
|
10692
|
+
for (const d of skillNameDupes) {
|
|
10693
|
+
ctx.addIssue({ code: external_exports.ZodIssueCode.custom, message: `duplicate skill-source name: "${d}"`, path: ["skillSources"] });
|
|
10694
|
+
}
|
|
10663
10695
|
});
|
|
10664
10696
|
DEFAULT_GLOBAL_CONFIG = {
|
|
10665
10697
|
schemaVersion: 1,
|
|
10666
10698
|
repos: [],
|
|
10667
|
-
runbooks: []
|
|
10699
|
+
runbooks: [],
|
|
10700
|
+
skillSources: []
|
|
10668
10701
|
};
|
|
10669
10702
|
}
|
|
10670
10703
|
});
|
|
@@ -10715,7 +10748,7 @@ var GlobalConfigReadError;
|
|
|
10715
10748
|
var init_store2 = __esm({
|
|
10716
10749
|
"../core/dist/global-config/store.js"() {
|
|
10717
10750
|
"use strict";
|
|
10718
|
-
|
|
10751
|
+
init_schema4();
|
|
10719
10752
|
GlobalConfigReadError = class extends Error {
|
|
10720
10753
|
constructor(configPath, cause) {
|
|
10721
10754
|
const msg = cause instanceof Error ? cause.message : String(cause);
|
|
@@ -11701,7 +11734,7 @@ function applyPostgresTemplateClone(worldId, enrichedRepos, options = {}) {
|
|
|
11701
11734
|
if (seedTemplates.length === 0) {
|
|
11702
11735
|
return { worldDbNames: [] };
|
|
11703
11736
|
}
|
|
11704
|
-
const
|
|
11737
|
+
const spawn12 = options.spawn ?? spawnSync4;
|
|
11705
11738
|
const seedToWorldDb = /* @__PURE__ */ new Map();
|
|
11706
11739
|
for (const seed of seedTemplates) {
|
|
11707
11740
|
seedToWorldDb.set(seed, deriveWorldDbName(seed, worldId));
|
|
@@ -11709,7 +11742,7 @@ function applyPostgresTemplateClone(worldId, enrichedRepos, options = {}) {
|
|
|
11709
11742
|
const created = [];
|
|
11710
11743
|
for (const seed of seedTemplates) {
|
|
11711
11744
|
const worldDb = seedToWorldDb.get(seed);
|
|
11712
|
-
const exists =
|
|
11745
|
+
const exists = spawn12("docker", [
|
|
11713
11746
|
"exec",
|
|
11714
11747
|
container,
|
|
11715
11748
|
"psql",
|
|
@@ -11721,7 +11754,7 @@ function applyPostgresTemplateClone(worldId, enrichedRepos, options = {}) {
|
|
|
11721
11754
|
const dbAlreadyExists = exists.status === 0 && (exists.stdout || "").trim() === "1";
|
|
11722
11755
|
if (!dbAlreadyExists) {
|
|
11723
11756
|
const sql = `CREATE DATABASE "${worldDb}" TEMPLATE "${seed}"`;
|
|
11724
|
-
const create =
|
|
11757
|
+
const create = spawn12("docker", ["exec", container, "psql", "-U", user, "-d", "postgres", "-v", "ON_ERROR_STOP=1", "-c", sql], { encoding: "utf-8" });
|
|
11725
11758
|
if (create.status !== 0) {
|
|
11726
11759
|
throw new Error(`failed to CREATE DATABASE "${worldDb}" TEMPLATE "${seed}": ${(create.stderr || "").trim()}`);
|
|
11727
11760
|
}
|
|
@@ -11731,7 +11764,7 @@ function applyPostgresTemplateClone(worldId, enrichedRepos, options = {}) {
|
|
|
11731
11764
|
if (stmts !== void 0 && stmts.length > 0) {
|
|
11732
11765
|
for (const rawStmt of stmts) {
|
|
11733
11766
|
const stmt = interpolatePostCloneSql(rawStmt, worldId, seedToWorldDb);
|
|
11734
|
-
const fixup =
|
|
11767
|
+
const fixup = spawn12("docker", ["exec", container, "psql", "-U", user, "-d", worldDb, "-v", "ON_ERROR_STOP=1", "-c", stmt], { encoding: "utf-8" });
|
|
11735
11768
|
if (fixup.status !== 0) {
|
|
11736
11769
|
throw new Error(`post_clone_sql against "${worldDb}" failed (statement: ${truncate(stmt, 120)}): ${(fixup.stderr || "").trim()}`);
|
|
11737
11770
|
}
|
|
@@ -11759,11 +11792,11 @@ function applyPostgresTestTemplateClone(worldId, enrichedRepos, options = {}) {
|
|
|
11759
11792
|
if (testTemplates.length === 0) {
|
|
11760
11793
|
return { worldTestDbNames: [] };
|
|
11761
11794
|
}
|
|
11762
|
-
const
|
|
11795
|
+
const spawn12 = options.spawn ?? spawnSync4;
|
|
11763
11796
|
const created = [];
|
|
11764
11797
|
for (const seed of testTemplates) {
|
|
11765
11798
|
const worldDb = deriveWorldDbName(seed, worldId);
|
|
11766
|
-
const exists =
|
|
11799
|
+
const exists = spawn12("docker", [
|
|
11767
11800
|
"exec",
|
|
11768
11801
|
container,
|
|
11769
11802
|
"psql",
|
|
@@ -11775,7 +11808,7 @@ function applyPostgresTestTemplateClone(worldId, enrichedRepos, options = {}) {
|
|
|
11775
11808
|
const dbAlreadyExists = exists.status === 0 && (exists.stdout || "").trim() === "1";
|
|
11776
11809
|
if (!dbAlreadyExists) {
|
|
11777
11810
|
const sql = `CREATE DATABASE "${worldDb}" TEMPLATE "${seed}"`;
|
|
11778
|
-
const create =
|
|
11811
|
+
const create = spawn12("docker", ["exec", container, "psql", "-U", user, "-d", "postgres", "-v", "ON_ERROR_STOP=1", "-c", sql], { encoding: "utf-8" });
|
|
11779
11812
|
if (create.status !== 0) {
|
|
11780
11813
|
throw new Error(`failed to CREATE DATABASE "${worldDb}" TEMPLATE "${seed}": ${(create.stderr || "").trim()}`);
|
|
11781
11814
|
}
|
|
@@ -11816,13 +11849,13 @@ function applyPostgresWorldRole(worldId, worldDbNames, options = {}) {
|
|
|
11816
11849
|
if (worldDbNames.length === 0) {
|
|
11817
11850
|
throw new Error(`applyPostgresWorldRole called with empty worldDbNames for "${worldId}" \u2014 should only run when applyPostgresTemplateClone produced clones`);
|
|
11818
11851
|
}
|
|
11819
|
-
const
|
|
11852
|
+
const spawn12 = options.spawn ?? spawnSync4;
|
|
11820
11853
|
const container = options.singletonContainer ?? "olam-postgres";
|
|
11821
11854
|
const user = options.postgresUser ?? "development";
|
|
11822
11855
|
const genPassword = options.generatePassword ?? defaultPasswordGenerator;
|
|
11823
11856
|
const worldRoleName = deriveWorldRoleName(worldId);
|
|
11824
11857
|
const password = genPassword();
|
|
11825
|
-
const exists =
|
|
11858
|
+
const exists = spawn12("docker", [
|
|
11826
11859
|
"exec",
|
|
11827
11860
|
container,
|
|
11828
11861
|
"psql",
|
|
@@ -11834,12 +11867,12 @@ function applyPostgresWorldRole(worldId, worldDbNames, options = {}) {
|
|
|
11834
11867
|
const roleExists = exists.status === 0 && (exists.stdout || "").trim() === "1";
|
|
11835
11868
|
const escapedPassword = escapeSqlLiteral(password);
|
|
11836
11869
|
const roleDdl = roleExists ? `ALTER ROLE "${worldRoleName}" WITH LOGIN PASSWORD '${escapedPassword}' NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION` : `CREATE ROLE "${worldRoleName}" WITH LOGIN PASSWORD '${escapedPassword}' NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION`;
|
|
11837
|
-
const dml =
|
|
11870
|
+
const dml = spawn12("docker", ["exec", container, "psql", "-U", user, "-d", "postgres", "-v", "ON_ERROR_STOP=1", "-c", roleDdl], { encoding: "utf-8" });
|
|
11838
11871
|
if (dml.status !== 0) {
|
|
11839
11872
|
throw new Error(`failed to ${roleExists ? "ALTER" : "CREATE"} ROLE "${worldRoleName}": ` + redactCreateRolePassword((dml.stderr || "").trim()));
|
|
11840
11873
|
}
|
|
11841
11874
|
for (const worldDb of worldDbNames) {
|
|
11842
|
-
const grantConnect =
|
|
11875
|
+
const grantConnect = spawn12("docker", [
|
|
11843
11876
|
"exec",
|
|
11844
11877
|
container,
|
|
11845
11878
|
"psql",
|
|
@@ -11864,7 +11897,7 @@ function applyPostgresWorldRole(worldId, worldDbNames, options = {}) {
|
|
|
11864
11897
|
`ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO "${worldRoleName}"`,
|
|
11865
11898
|
`ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO "${worldRoleName}"`
|
|
11866
11899
|
].join("; ");
|
|
11867
|
-
const grantResult =
|
|
11900
|
+
const grantResult = spawn12("docker", ["exec", container, "psql", "-U", user, "-d", worldDb, "-v", "ON_ERROR_STOP=1", "-c", perDbGrants], { encoding: "utf-8" });
|
|
11868
11901
|
if (grantResult.status !== 0) {
|
|
11869
11902
|
throw new Error(`failed to GRANT privileges on "${worldDb}" to "${worldRoleName}": ${(grantResult.stderr || "").trim()}`);
|
|
11870
11903
|
}
|
|
@@ -11884,11 +11917,11 @@ function dropPostgresWorldDbs(worldId, worldDbNames, options = {}) {
|
|
|
11884
11917
|
if (worldDbNames.length === 0)
|
|
11885
11918
|
return;
|
|
11886
11919
|
assertSafeWorldId(worldId);
|
|
11887
|
-
const
|
|
11920
|
+
const spawn12 = options.spawn ?? spawnSync4;
|
|
11888
11921
|
const container = options.container ?? "olam-postgres";
|
|
11889
11922
|
const user = options.user ?? "development";
|
|
11890
11923
|
for (const db of worldDbNames) {
|
|
11891
|
-
const drop =
|
|
11924
|
+
const drop = spawn12("docker", [
|
|
11892
11925
|
"exec",
|
|
11893
11926
|
container,
|
|
11894
11927
|
"psql",
|
|
@@ -11908,10 +11941,10 @@ function dropPostgresWorldRole(worldId, worldRoleName, options = {}) {
|
|
|
11908
11941
|
if (!worldRoleName)
|
|
11909
11942
|
return;
|
|
11910
11943
|
assertSafeWorldId(worldId);
|
|
11911
|
-
const
|
|
11944
|
+
const spawn12 = options.spawn ?? spawnSync4;
|
|
11912
11945
|
const container = options.container ?? "olam-postgres";
|
|
11913
11946
|
const user = options.user ?? "development";
|
|
11914
|
-
const cleanup =
|
|
11947
|
+
const cleanup = spawn12("docker", [
|
|
11915
11948
|
"exec",
|
|
11916
11949
|
container,
|
|
11917
11950
|
"psql",
|
|
@@ -15062,10 +15095,10 @@ async function readHostCpToken2() {
|
|
|
15062
15095
|
if (!fs26.existsSync(tp)) return null;
|
|
15063
15096
|
return fs26.readFileSync(tp, "utf-8").trim();
|
|
15064
15097
|
}
|
|
15065
|
-
async function callHostCpProxy(method, worldId,
|
|
15098
|
+
async function callHostCpProxy(method, worldId, path66, body) {
|
|
15066
15099
|
const token = await readHostCpToken2();
|
|
15067
15100
|
if (!token) return { ok: false, status: 0, error: "no token (host CP not started)" };
|
|
15068
|
-
const url2 = `http://127.0.0.1:${HOST_CP_PORT}/api/world/${encodeURIComponent(worldId)}${
|
|
15101
|
+
const url2 = `http://127.0.0.1:${HOST_CP_PORT}/api/world/${encodeURIComponent(worldId)}${path66}`;
|
|
15069
15102
|
try {
|
|
15070
15103
|
const headers = {
|
|
15071
15104
|
Authorization: `Bearer ${token}`
|
|
@@ -16102,9 +16135,9 @@ var UnknownArchetypeError = class extends Error {
|
|
|
16102
16135
|
};
|
|
16103
16136
|
var ArchetypeCycleError = class extends Error {
|
|
16104
16137
|
path;
|
|
16105
|
-
constructor(
|
|
16106
|
-
super(`Archetype inheritance cycle detected: ${
|
|
16107
|
-
this.path =
|
|
16138
|
+
constructor(path66) {
|
|
16139
|
+
super(`Archetype inheritance cycle detected: ${path66.join(" \u2192 ")} \u2192 ${path66[0] ?? "?"}`);
|
|
16140
|
+
this.path = path66;
|
|
16108
16141
|
this.name = "ArchetypeCycleError";
|
|
16109
16142
|
}
|
|
16110
16143
|
};
|
|
@@ -17692,9 +17725,9 @@ function formatFreshnessWarning(result, image = DEFAULT_DEVBOX_IMAGE) {
|
|
|
17692
17725
|
"These source files have changed since the image was built; the",
|
|
17693
17726
|
"changes will NOT take effect in fresh worlds until you rebuild:"
|
|
17694
17727
|
];
|
|
17695
|
-
for (const { path:
|
|
17728
|
+
for (const { path: path66, mtimeMs } of result.newerSources) {
|
|
17696
17729
|
const when = new Date(mtimeMs).toISOString();
|
|
17697
|
-
lines.push(` \u2022 ${
|
|
17730
|
+
lines.push(` \u2022 ${path66} (modified ${when})`);
|
|
17698
17731
|
}
|
|
17699
17732
|
lines.push("");
|
|
17700
17733
|
lines.push("Rebuild with:");
|
|
@@ -17862,56 +17895,56 @@ var SECRET_LEN_BYTES = 32;
|
|
|
17862
17895
|
function generateSecret() {
|
|
17863
17896
|
return randomBytes6(SECRET_LEN_BYTES).toString("hex");
|
|
17864
17897
|
}
|
|
17865
|
-
function writeSecretAtPath(
|
|
17866
|
-
mkdirSync18(dirname19(
|
|
17867
|
-
const tmp = `${
|
|
17898
|
+
function writeSecretAtPath(path66, value) {
|
|
17899
|
+
mkdirSync18(dirname19(path66), { recursive: true });
|
|
17900
|
+
const tmp = `${path66}.tmp.${process.pid}`;
|
|
17868
17901
|
writeFileSync13(tmp, value, { mode: 384 });
|
|
17869
17902
|
chmodSync3(tmp, 384);
|
|
17870
|
-
renameSync4(tmp,
|
|
17903
|
+
renameSync4(tmp, path66);
|
|
17871
17904
|
}
|
|
17872
|
-
function readSecretAtPathOrNull(
|
|
17873
|
-
if (!existsSync28(
|
|
17874
|
-
const mode = statSync7(
|
|
17905
|
+
function readSecretAtPathOrNull(path66) {
|
|
17906
|
+
if (!existsSync28(path66)) return null;
|
|
17907
|
+
const mode = statSync7(path66).mode & 511;
|
|
17875
17908
|
if (mode !== 384) {
|
|
17876
17909
|
process.stderr.write(
|
|
17877
|
-
`warn: ${
|
|
17910
|
+
`warn: ${path66} has mode 0${mode.toString(8)}; expected 0600. Run 'olam memory secret rotate' to regenerate.
|
|
17878
17911
|
`
|
|
17879
17912
|
);
|
|
17880
17913
|
}
|
|
17881
|
-
return readFileSync20(
|
|
17914
|
+
return readFileSync20(path66, "utf8").trim();
|
|
17882
17915
|
}
|
|
17883
|
-
function readSecretAtPath(
|
|
17884
|
-
const v = readSecretAtPathOrNull(
|
|
17916
|
+
function readSecretAtPath(path66) {
|
|
17917
|
+
const v = readSecretAtPathOrNull(path66);
|
|
17885
17918
|
if (v === null) {
|
|
17886
17919
|
throw new Error(
|
|
17887
|
-
`Secret not found at ${
|
|
17920
|
+
`Secret not found at ${path66}. Run 'olam memory start' to generate it.`
|
|
17888
17921
|
);
|
|
17889
17922
|
}
|
|
17890
17923
|
return v;
|
|
17891
17924
|
}
|
|
17892
|
-
function ensureMemorySecret(
|
|
17893
|
-
const existing = readSecretAtPathOrNull(
|
|
17925
|
+
function ensureMemorySecret(path66 = MEMORY_SECRET_PATH) {
|
|
17926
|
+
const existing = readSecretAtPathOrNull(path66);
|
|
17894
17927
|
if (existing) return existing;
|
|
17895
17928
|
const fresh = generateSecret();
|
|
17896
|
-
writeSecretAtPath(
|
|
17929
|
+
writeSecretAtPath(path66, fresh);
|
|
17897
17930
|
return fresh;
|
|
17898
17931
|
}
|
|
17899
|
-
function readMemorySecretOrNull(
|
|
17900
|
-
return readSecretAtPathOrNull(
|
|
17932
|
+
function readMemorySecretOrNull(path66 = MEMORY_SECRET_PATH) {
|
|
17933
|
+
return readSecretAtPathOrNull(path66);
|
|
17901
17934
|
}
|
|
17902
|
-
function readMemorySecret(
|
|
17903
|
-
return readSecretAtPath(
|
|
17935
|
+
function readMemorySecret(path66 = MEMORY_SECRET_PATH) {
|
|
17936
|
+
return readSecretAtPath(path66);
|
|
17904
17937
|
}
|
|
17905
|
-
function rotateMemorySecret(
|
|
17938
|
+
function rotateMemorySecret(path66 = MEMORY_SECRET_PATH) {
|
|
17906
17939
|
const fresh = generateSecret();
|
|
17907
|
-
writeSecretAtPath(
|
|
17940
|
+
writeSecretAtPath(path66, fresh);
|
|
17908
17941
|
return fresh;
|
|
17909
17942
|
}
|
|
17910
|
-
function hasMemorySecret(
|
|
17911
|
-
return existsSync28(
|
|
17943
|
+
function hasMemorySecret(path66 = MEMORY_SECRET_PATH) {
|
|
17944
|
+
return existsSync28(path66);
|
|
17912
17945
|
}
|
|
17913
|
-
function writeCloudMemorySecret(value,
|
|
17914
|
-
writeSecretAtPath(
|
|
17946
|
+
function writeCloudMemorySecret(value, path66 = CLOUD_MEMORY_SECRET_PATH) {
|
|
17947
|
+
writeSecretAtPath(path66, value);
|
|
17915
17948
|
}
|
|
17916
17949
|
|
|
17917
17950
|
// src/lib/world-mcp-register.ts
|
|
@@ -17983,15 +18016,15 @@ var AGENTMEMORY_LOCAL_URL = "http://host.docker.internal:3111";
|
|
|
17983
18016
|
var HOST_CP_URL = "http://127.0.0.1:19000";
|
|
17984
18017
|
async function readHostCpTokenForCreate() {
|
|
17985
18018
|
try {
|
|
17986
|
-
const { default:
|
|
17987
|
-
const { default:
|
|
17988
|
-
const { default:
|
|
17989
|
-
const tp =
|
|
17990
|
-
process.env.OLAM_HOME ??
|
|
18019
|
+
const { default: fs62 } = await import("node:fs");
|
|
18020
|
+
const { default: os36 } = await import("node:os");
|
|
18021
|
+
const { default: path66 } = await import("node:path");
|
|
18022
|
+
const tp = path66.join(
|
|
18023
|
+
process.env.OLAM_HOME ?? path66.join(os36.homedir(), ".olam"),
|
|
17991
18024
|
"host-cp.token"
|
|
17992
18025
|
);
|
|
17993
|
-
if (!
|
|
17994
|
-
return
|
|
18026
|
+
if (!fs62.existsSync(tp)) return null;
|
|
18027
|
+
return fs62.readFileSync(tp, "utf-8").trim();
|
|
17995
18028
|
} catch {
|
|
17996
18029
|
return null;
|
|
17997
18030
|
}
|
|
@@ -18399,12 +18432,12 @@ function defaultNameFromPrompt(prompt) {
|
|
|
18399
18432
|
}
|
|
18400
18433
|
async function readHostCpToken3() {
|
|
18401
18434
|
try {
|
|
18402
|
-
const { default:
|
|
18403
|
-
const { default:
|
|
18404
|
-
const { default:
|
|
18405
|
-
const tp =
|
|
18406
|
-
if (!
|
|
18407
|
-
const raw =
|
|
18435
|
+
const { default: fs62 } = await import("node:fs");
|
|
18436
|
+
const { default: os36 } = await import("node:os");
|
|
18437
|
+
const { default: path66 } = await import("node:path");
|
|
18438
|
+
const tp = path66.join(os36.homedir(), ".olam", "host-cp.token");
|
|
18439
|
+
if (!fs62.existsSync(tp)) return null;
|
|
18440
|
+
const raw = fs62.readFileSync(tp, "utf-8").trim();
|
|
18408
18441
|
return raw.length > 0 ? raw : null;
|
|
18409
18442
|
} catch {
|
|
18410
18443
|
return null;
|
|
@@ -18617,10 +18650,72 @@ function registerList(program2) {
|
|
|
18617
18650
|
// src/commands/status.ts
|
|
18618
18651
|
init_output();
|
|
18619
18652
|
import * as fs30 from "node:fs";
|
|
18653
|
+
import * as http3 from "node:http";
|
|
18620
18654
|
import * as os16 from "node:os";
|
|
18621
18655
|
import * as path31 from "node:path";
|
|
18622
18656
|
var CLI_VERSION2 = process.env["OLAM_CLI_VERSION"] ?? "0.0.0";
|
|
18623
18657
|
var HOST_CP_PORT2 = 19e3;
|
|
18658
|
+
var STATE_ENUM = [
|
|
18659
|
+
"running",
|
|
18660
|
+
"starting",
|
|
18661
|
+
"stopped",
|
|
18662
|
+
"crashed",
|
|
18663
|
+
"unknown"
|
|
18664
|
+
];
|
|
18665
|
+
function parseRuntimeStatus(raw) {
|
|
18666
|
+
if (!raw || typeof raw !== "object") return null;
|
|
18667
|
+
const obj = raw;
|
|
18668
|
+
const state = obj.state;
|
|
18669
|
+
if (typeof state !== "string") return null;
|
|
18670
|
+
if (!STATE_ENUM.includes(state)) return null;
|
|
18671
|
+
const ready_replicas = typeof obj.ready_replicas === "number" ? obj.ready_replicas : 0;
|
|
18672
|
+
const restarts = typeof obj.restarts === "number" ? obj.restarts : 0;
|
|
18673
|
+
const last_event_type = typeof obj.last_event_type === "string" ? obj.last_event_type : null;
|
|
18674
|
+
const last_event_age_seconds = typeof obj.last_event_age_seconds === "number" ? obj.last_event_age_seconds : null;
|
|
18675
|
+
return {
|
|
18676
|
+
state,
|
|
18677
|
+
ready_replicas,
|
|
18678
|
+
restarts,
|
|
18679
|
+
last_event_type,
|
|
18680
|
+
last_event_age_seconds
|
|
18681
|
+
};
|
|
18682
|
+
}
|
|
18683
|
+
var fetchWorldRuntimeStatus = (worldId, token) => new Promise((resolve15) => {
|
|
18684
|
+
const opts = {
|
|
18685
|
+
host: "127.0.0.1",
|
|
18686
|
+
port: HOST_CP_PORT2,
|
|
18687
|
+
path: `/v1/worlds/${encodeURIComponent(worldId)}/status`,
|
|
18688
|
+
method: "GET",
|
|
18689
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
18690
|
+
timeout: 2e3
|
|
18691
|
+
};
|
|
18692
|
+
const req = http3.request(opts, (res) => {
|
|
18693
|
+
if (res.statusCode !== 200) {
|
|
18694
|
+
res.resume();
|
|
18695
|
+
return resolve15(null);
|
|
18696
|
+
}
|
|
18697
|
+
let body = "";
|
|
18698
|
+
res.setEncoding("utf-8");
|
|
18699
|
+
res.on("data", (c) => {
|
|
18700
|
+
body += c;
|
|
18701
|
+
});
|
|
18702
|
+
res.on("end", () => {
|
|
18703
|
+
try {
|
|
18704
|
+
const parsed = parseRuntimeStatus(JSON.parse(body));
|
|
18705
|
+
resolve15(parsed);
|
|
18706
|
+
} catch {
|
|
18707
|
+
resolve15(null);
|
|
18708
|
+
}
|
|
18709
|
+
});
|
|
18710
|
+
res.on("error", () => resolve15(null));
|
|
18711
|
+
});
|
|
18712
|
+
req.on("error", () => resolve15(null));
|
|
18713
|
+
req.on("timeout", () => {
|
|
18714
|
+
req.destroy();
|
|
18715
|
+
resolve15(null);
|
|
18716
|
+
});
|
|
18717
|
+
req.end();
|
|
18718
|
+
});
|
|
18624
18719
|
async function getMachineStatus(_probe, _loadCtx, _readToken) {
|
|
18625
18720
|
const probe2 = _probe ?? (async () => {
|
|
18626
18721
|
const { probeHostCp: probeHostCp2 } = await Promise.resolve().then(() => (init_host_cp(), host_cp_exports));
|
|
@@ -18673,7 +18768,7 @@ async function getMachineStatus(_probe, _loadCtx, _readToken) {
|
|
|
18673
18768
|
};
|
|
18674
18769
|
}
|
|
18675
18770
|
function registerStatus(program2) {
|
|
18676
|
-
program2.command("status").description("Show status (machine status when no world given; world details with a world ID)").argument("[world]", "World ID \u2014 omit to show machine status").option("--json", "Output as JSON").action(async (worldId, opts) => {
|
|
18771
|
+
program2.command("status").description("Show status (machine status when no world given; world details with a world ID)").argument("[world]", "World ID \u2014 omit to show machine status").option("--json", "Output as JSON").option("--pretty", "Render world status as a human-readable table (engine-aware view)").action(async (worldId, opts) => {
|
|
18677
18772
|
if (!worldId) {
|
|
18678
18773
|
const ms = await getMachineStatus();
|
|
18679
18774
|
if (opts.json) {
|
|
@@ -18710,8 +18805,13 @@ function registerStatus(program2) {
|
|
|
18710
18805
|
}
|
|
18711
18806
|
const cost = ctx.costTracker.getWorldCost(worldId);
|
|
18712
18807
|
const dashboardUrl = `http://localhost:${19080 + world.portOffset}`;
|
|
18808
|
+
const { readToken: readToken2 } = await Promise.resolve().then(() => (init_host_cp(), host_cp_exports));
|
|
18809
|
+
const token = readToken2();
|
|
18810
|
+
const runtime = token ? await fetchWorldRuntimeStatus(worldId, token) : null;
|
|
18713
18811
|
if (opts.json) {
|
|
18714
|
-
|
|
18812
|
+
const payload = { ...world, dashboardUrl, cost };
|
|
18813
|
+
if (runtime) payload.runtime = runtime;
|
|
18814
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
18715
18815
|
return;
|
|
18716
18816
|
}
|
|
18717
18817
|
printHeader(`${world.name} (${world.id})`);
|
|
@@ -18732,6 +18832,15 @@ function registerStatus(program2) {
|
|
|
18732
18832
|
printInfo("Created", world.createdAt);
|
|
18733
18833
|
printInfo("Uptime", formatAge(world.createdAt));
|
|
18734
18834
|
printInfo("Updated", world.updatedAt);
|
|
18835
|
+
if (runtime) {
|
|
18836
|
+
printInfo("Runtime state", runtime.state);
|
|
18837
|
+
printInfo("Ready replicas", String(runtime.ready_replicas));
|
|
18838
|
+
printInfo("Restarts", String(runtime.restarts));
|
|
18839
|
+
if (runtime.last_event_type) {
|
|
18840
|
+
const ageSuffix = runtime.last_event_age_seconds != null ? ` (${runtime.last_event_age_seconds}s ago)` : "";
|
|
18841
|
+
printInfo("Last event", `${runtime.last_event_type}${ageSuffix}`);
|
|
18842
|
+
}
|
|
18843
|
+
}
|
|
18735
18844
|
});
|
|
18736
18845
|
}
|
|
18737
18846
|
|
|
@@ -19726,7 +19835,7 @@ var DEFAULT_TIMEOUT_MS2 = 3e3;
|
|
|
19726
19835
|
function worldBaseUrl(portOffset) {
|
|
19727
19836
|
return `http://127.0.0.1:${HOST_CONTROL_PLANE_BASE2 + portOffset}`;
|
|
19728
19837
|
}
|
|
19729
|
-
async function
|
|
19838
|
+
async function request2(url2, init = {}, timeoutMs = DEFAULT_TIMEOUT_MS2) {
|
|
19730
19839
|
const controller = new AbortController();
|
|
19731
19840
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
19732
19841
|
try {
|
|
@@ -19736,13 +19845,13 @@ async function request(url2, init = {}, timeoutMs = DEFAULT_TIMEOUT_MS2) {
|
|
|
19736
19845
|
}
|
|
19737
19846
|
}
|
|
19738
19847
|
async function listGates(portOffset) {
|
|
19739
|
-
const res = await
|
|
19848
|
+
const res = await request2(`${worldBaseUrl(portOffset)}/api/pr-gate`);
|
|
19740
19849
|
if (!res.ok)
|
|
19741
19850
|
throw new Error(`list gates failed: HTTP ${res.status}`);
|
|
19742
19851
|
return await res.json();
|
|
19743
19852
|
}
|
|
19744
19853
|
async function getGate(portOffset, id) {
|
|
19745
|
-
const res = await
|
|
19854
|
+
const res = await request2(`${worldBaseUrl(portOffset)}/api/pr-gate/${encodeURIComponent(id)}`);
|
|
19746
19855
|
if (res.status === 404)
|
|
19747
19856
|
return null;
|
|
19748
19857
|
if (!res.ok)
|
|
@@ -19750,7 +19859,7 @@ async function getGate(portOffset, id) {
|
|
|
19750
19859
|
return await res.json();
|
|
19751
19860
|
}
|
|
19752
19861
|
async function decideGate(portOffset, id, payload) {
|
|
19753
|
-
const res = await
|
|
19862
|
+
const res = await request2(`${worldBaseUrl(portOffset)}/api/pr-gate/${encodeURIComponent(id)}/decision`, {
|
|
19754
19863
|
method: "POST",
|
|
19755
19864
|
headers: { "Content-Type": "application/json" },
|
|
19756
19865
|
body: JSON.stringify(payload)
|
|
@@ -22135,11 +22244,11 @@ function zodIssueToError(issue, doc, lineCounter) {
|
|
|
22135
22244
|
suggestion: deriveSuggestion(issue)
|
|
22136
22245
|
};
|
|
22137
22246
|
}
|
|
22138
|
-
function formatJsonPath(
|
|
22139
|
-
if (
|
|
22247
|
+
function formatJsonPath(path66) {
|
|
22248
|
+
if (path66.length === 0)
|
|
22140
22249
|
return "<root>";
|
|
22141
22250
|
let out = "";
|
|
22142
|
-
for (const seg of
|
|
22251
|
+
for (const seg of path66) {
|
|
22143
22252
|
if (typeof seg === "number") {
|
|
22144
22253
|
out += `[${seg}]`;
|
|
22145
22254
|
} else {
|
|
@@ -22148,11 +22257,11 @@ function formatJsonPath(path60) {
|
|
|
22148
22257
|
}
|
|
22149
22258
|
return out;
|
|
22150
22259
|
}
|
|
22151
|
-
function resolveYamlLocation(
|
|
22260
|
+
function resolveYamlLocation(path66, doc, lineCounter) {
|
|
22152
22261
|
let bestLine = 0;
|
|
22153
22262
|
let bestColumn = 0;
|
|
22154
|
-
for (let depth =
|
|
22155
|
-
const segment =
|
|
22263
|
+
for (let depth = path66.length; depth >= 0; depth -= 1) {
|
|
22264
|
+
const segment = path66.slice(0, depth);
|
|
22156
22265
|
try {
|
|
22157
22266
|
const node = doc.getIn(segment, true);
|
|
22158
22267
|
if (node && typeof node === "object" && "range" in node) {
|
|
@@ -22370,11 +22479,11 @@ function topoSort(nodes) {
|
|
|
22370
22479
|
}
|
|
22371
22480
|
function traceCycle(start, byId) {
|
|
22372
22481
|
const seen = /* @__PURE__ */ new Set();
|
|
22373
|
-
const
|
|
22482
|
+
const path66 = [];
|
|
22374
22483
|
let current = start;
|
|
22375
22484
|
while (current && !seen.has(current)) {
|
|
22376
22485
|
seen.add(current);
|
|
22377
|
-
|
|
22486
|
+
path66.push(current);
|
|
22378
22487
|
const node = byId.get(current);
|
|
22379
22488
|
const next = node?.dependsOn[0];
|
|
22380
22489
|
if (next === void 0)
|
|
@@ -22382,10 +22491,10 @@ function traceCycle(start, byId) {
|
|
|
22382
22491
|
current = next;
|
|
22383
22492
|
}
|
|
22384
22493
|
if (current && seen.has(current)) {
|
|
22385
|
-
const idx =
|
|
22386
|
-
return [...
|
|
22494
|
+
const idx = path66.indexOf(current);
|
|
22495
|
+
return [...path66.slice(idx), current];
|
|
22387
22496
|
}
|
|
22388
|
-
return
|
|
22497
|
+
return path66;
|
|
22389
22498
|
}
|
|
22390
22499
|
|
|
22391
22500
|
// ../core/dist/executor/types.js
|
|
@@ -25071,6 +25180,32 @@ async function runUpgradePullByDigest(deps = {}) {
|
|
|
25071
25180
|
return { exitCode: EXIT_GENERIC_ERROR, summary: "socket-proxy recreate failed" };
|
|
25072
25181
|
}
|
|
25073
25182
|
proxySpinner.succeed("docker-socket-proxy recreated");
|
|
25183
|
+
const inspectContainer = deps.inspectContainerImpl ?? defaultInspectContainerLabels;
|
|
25184
|
+
const removeContainer = deps.removeContainerImpl ?? defaultRemoveContainer;
|
|
25185
|
+
const orphanCheck = inspectContainer("olam-host-cp");
|
|
25186
|
+
if (!orphanCheck.ok) {
|
|
25187
|
+
process.stderr.write(
|
|
25188
|
+
`${pc18.red("error")} docker inspect olam-host-cp failed:
|
|
25189
|
+
${orphanCheck.stderr.split("\n").slice(0, 3).join("\n ")}
|
|
25190
|
+
`
|
|
25191
|
+
);
|
|
25192
|
+
return { exitCode: EXIT_GENERIC_ERROR, summary: "orphan inspect failed" };
|
|
25193
|
+
}
|
|
25194
|
+
if (orphanCheck.exists && orphanCheck.service !== "olam-host-cp") {
|
|
25195
|
+
process.stderr.write(
|
|
25196
|
+
`${pc18.yellow("info")} Removing pre-rename orphan container olam-host-cp (old project=${orphanCheck.project || "<none>"}, old service=${orphanCheck.service || "<none>"})
|
|
25197
|
+
`
|
|
25198
|
+
);
|
|
25199
|
+
const rm = removeContainer("olam-host-cp");
|
|
25200
|
+
if (!rm.ok) {
|
|
25201
|
+
process.stderr.write(
|
|
25202
|
+
`${pc18.red("error")} docker rm -f olam-host-cp failed:
|
|
25203
|
+
${rm.stderr.split("\n").slice(0, 3).join("\n ")}
|
|
25204
|
+
`
|
|
25205
|
+
);
|
|
25206
|
+
return { exitCode: EXIT_GENERIC_ERROR, summary: "orphan cleanup failed" };
|
|
25207
|
+
}
|
|
25208
|
+
}
|
|
25074
25209
|
const composeSpinner = ora7("docker compose recreate olam-host-cp").start();
|
|
25075
25210
|
const composeResult = composeRunner(
|
|
25076
25211
|
["up", "-d", "--force-recreate", "--no-deps", "olam-host-cp"],
|
|
@@ -25175,6 +25310,37 @@ async function defaultRecreateAuthForUpgrade() {
|
|
|
25175
25310
|
};
|
|
25176
25311
|
}
|
|
25177
25312
|
}
|
|
25313
|
+
function defaultInspectContainerLabels(containerName) {
|
|
25314
|
+
const result = spawnSync15(
|
|
25315
|
+
"docker",
|
|
25316
|
+
[
|
|
25317
|
+
"inspect",
|
|
25318
|
+
"--format",
|
|
25319
|
+
'{{index .Config.Labels "com.docker.compose.project"}}|{{index .Config.Labels "com.docker.compose.service"}}',
|
|
25320
|
+
containerName
|
|
25321
|
+
],
|
|
25322
|
+
{ encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }
|
|
25323
|
+
);
|
|
25324
|
+
const stderr = result.stderr ?? "";
|
|
25325
|
+
if (result.status === 0) {
|
|
25326
|
+
const [project = "", service = ""] = (result.stdout ?? "").trim().split("|");
|
|
25327
|
+
return { ok: true, exists: true, project, service, stderr: "" };
|
|
25328
|
+
}
|
|
25329
|
+
if (/No such (object|container)/i.test(stderr)) {
|
|
25330
|
+
return { ok: true, exists: false, project: "", service: "", stderr: "" };
|
|
25331
|
+
}
|
|
25332
|
+
if (result.status === null) {
|
|
25333
|
+
return { ok: true, exists: false, project: "", service: "", stderr: "" };
|
|
25334
|
+
}
|
|
25335
|
+
return { ok: false, exists: false, project: "", service: "", stderr };
|
|
25336
|
+
}
|
|
25337
|
+
function defaultRemoveContainer(containerName) {
|
|
25338
|
+
const result = spawnSync15("docker", ["rm", "-f", containerName], {
|
|
25339
|
+
encoding: "utf-8",
|
|
25340
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
25341
|
+
});
|
|
25342
|
+
return { ok: result.status === 0, stderr: result.stderr ?? "" };
|
|
25343
|
+
}
|
|
25178
25344
|
async function handleUpgrade(opts) {
|
|
25179
25345
|
const cwd = process.cwd();
|
|
25180
25346
|
const rootCheck = validateRepoRoot(cwd);
|
|
@@ -25721,7 +25887,7 @@ function registerUpgrade(program2) {
|
|
|
25721
25887
|
init_host_cp();
|
|
25722
25888
|
init_context();
|
|
25723
25889
|
init_output();
|
|
25724
|
-
import * as
|
|
25890
|
+
import * as http4 from "node:http";
|
|
25725
25891
|
import pc19 from "picocolors";
|
|
25726
25892
|
var HOST_CP_PORT3 = 19e3;
|
|
25727
25893
|
function colorLine(line) {
|
|
@@ -25730,25 +25896,16 @@ function colorLine(line) {
|
|
|
25730
25896
|
if (/\bINFO\b/.test(line)) return pc19.dim(line);
|
|
25731
25897
|
return line;
|
|
25732
25898
|
}
|
|
25733
|
-
function
|
|
25734
|
-
const
|
|
25735
|
-
|
|
25736
|
-
|
|
25737
|
-
|
|
25738
|
-
if (
|
|
25739
|
-
|
|
25740
|
-
const parsed = JSON.parse(raw.slice(6));
|
|
25741
|
-
if (!parsed || typeof parsed !== "object") return null;
|
|
25742
|
-
const ev = parsed;
|
|
25743
|
-
if (ev.type === "replay" && Array.isArray(ev.lines)) return ev;
|
|
25744
|
-
if (ev.type === "line" && typeof ev.line === "string") return ev;
|
|
25745
|
-
return null;
|
|
25746
|
-
} catch {
|
|
25747
|
-
return null;
|
|
25748
|
-
}
|
|
25899
|
+
function buildLogsUrl(worldId, opts, port2 = HOST_CP_PORT3) {
|
|
25900
|
+
const qs = new URLSearchParams({
|
|
25901
|
+
tail: String(opts.tail),
|
|
25902
|
+
follow: opts.follow ? "1" : "0"
|
|
25903
|
+
});
|
|
25904
|
+
if (opts.service) qs.set("service", opts.service);
|
|
25905
|
+
return `http://127.0.0.1:${port2}/v1/worlds/${encodeURIComponent(worldId)}/logs?${qs.toString()}`;
|
|
25749
25906
|
}
|
|
25750
25907
|
function registerLogs(program2) {
|
|
25751
|
-
program2.command("logs").description("Stream application logs from a world").argument("<world>", "World ID").option("--service <name>", "Stream a specific
|
|
25908
|
+
program2.command("logs").description("Stream application logs from a world (engine-agnostic)").argument("<world>", "World ID").option("--service <name>", "Stream a specific container within the world").option("--follow", "Stream indefinitely until Ctrl-C", false).option("--tail <n>", "Number of lines to display before exiting", "200").action(async (worldId, opts) => {
|
|
25752
25909
|
const { ctx, error } = await loadContext();
|
|
25753
25910
|
if (!ctx) {
|
|
25754
25911
|
printError(error?.message ?? "Olam is not configured. Run `olam init` first.");
|
|
@@ -25768,10 +25925,11 @@ function registerLogs(program2) {
|
|
|
25768
25925
|
return;
|
|
25769
25926
|
}
|
|
25770
25927
|
const tailLimit = Math.max(1, parseInt(opts.tail, 10) || 200);
|
|
25771
|
-
const
|
|
25772
|
-
|
|
25773
|
-
|
|
25774
|
-
|
|
25928
|
+
const url2 = buildLogsUrl(worldId, {
|
|
25929
|
+
tail: tailLimit,
|
|
25930
|
+
follow: opts.follow,
|
|
25931
|
+
...opts.service ? { service: opts.service } : {}
|
|
25932
|
+
});
|
|
25775
25933
|
let done = false;
|
|
25776
25934
|
let resolveStream;
|
|
25777
25935
|
const streamDone = new Promise((r) => {
|
|
@@ -25783,16 +25941,7 @@ function registerLogs(program2) {
|
|
|
25783
25941
|
resolveStream();
|
|
25784
25942
|
}
|
|
25785
25943
|
};
|
|
25786
|
-
const
|
|
25787
|
-
process.stdout.write(formatLine(line, service, showService) + "\n");
|
|
25788
|
-
lineCount++;
|
|
25789
|
-
if (!opts.follow && lineCount >= tailLimit) {
|
|
25790
|
-
finish();
|
|
25791
|
-
return false;
|
|
25792
|
-
}
|
|
25793
|
-
return true;
|
|
25794
|
-
};
|
|
25795
|
-
const req = http3.get(url2, { headers: { Authorization: `Bearer ${token}` } }, (res) => {
|
|
25944
|
+
const req = http4.get(url2, { headers: { Authorization: `Bearer ${token}` } }, (res) => {
|
|
25796
25945
|
if (res.statusCode !== 200) {
|
|
25797
25946
|
printError(`Log stream returned HTTP ${res.statusCode ?? "unknown"}`);
|
|
25798
25947
|
process.exitCode = 1;
|
|
@@ -25808,21 +25957,14 @@ function registerLogs(program2) {
|
|
|
25808
25957
|
buf = rawLines.pop() ?? "";
|
|
25809
25958
|
for (const raw of rawLines) {
|
|
25810
25959
|
if (done) break;
|
|
25811
|
-
|
|
25812
|
-
|
|
25813
|
-
if (ev.type === "replay") {
|
|
25814
|
-
for (const l of ev.lines) {
|
|
25815
|
-
if (!emit2(l, ev.service)) {
|
|
25816
|
-
req.destroy();
|
|
25817
|
-
break;
|
|
25818
|
-
}
|
|
25819
|
-
}
|
|
25820
|
-
} else if (ev.type === "line") {
|
|
25821
|
-
if (!emit2(ev.line, ev.service)) req.destroy();
|
|
25822
|
-
}
|
|
25960
|
+
if (!raw) continue;
|
|
25961
|
+
process.stdout.write(colorLine(raw) + "\n");
|
|
25823
25962
|
}
|
|
25824
25963
|
});
|
|
25825
|
-
res.on("end", () =>
|
|
25964
|
+
res.on("end", () => {
|
|
25965
|
+
if (buf) process.stdout.write(colorLine(buf) + "\n");
|
|
25966
|
+
finish();
|
|
25967
|
+
});
|
|
25826
25968
|
res.on("error", (err) => {
|
|
25827
25969
|
if (!done) printError(`Stream error: ${err.message}`);
|
|
25828
25970
|
finish();
|
|
@@ -26845,6 +26987,11 @@ var HARD_CAP_BYTES = 5e9;
|
|
|
26845
26987
|
|
|
26846
26988
|
// src/lib/health-probes.ts
|
|
26847
26989
|
var HEALTH_TIMEOUT_MS2 = 5e3;
|
|
26990
|
+
var COLIMA_010_WARN_TEXT = (
|
|
26991
|
+
// eslint-disable-next-line @typescript-eslint/quotes
|
|
26992
|
+
"Colima 0.10.1's --kubernetes mode is broken upstream. Either switch to OLAM_HOST_CP_ENGINE=docker (recommended) or downgrade Colima to 0.9.x. Track abiosoft/colima issues for fix."
|
|
26993
|
+
);
|
|
26994
|
+
var COLIMA_010_RANGE = /^v?0\.10\.\d+$/;
|
|
26848
26995
|
var defaultDockerExec2 = (cmd, args) => {
|
|
26849
26996
|
const r = spawnSync18(cmd, [...args], {
|
|
26850
26997
|
encoding: "utf-8",
|
|
@@ -27068,6 +27215,146 @@ function formatBytes4(n) {
|
|
|
27068
27215
|
if (n < 1024 * 1024 * 1024) return `${(n / 1024 / 1024).toFixed(1)} MB`;
|
|
27069
27216
|
return `${(n / 1024 / 1024 / 1024).toFixed(2)} GB`;
|
|
27070
27217
|
}
|
|
27218
|
+
async function probeEngine(fetchImpl = defaultFetch) {
|
|
27219
|
+
const controller = new AbortController();
|
|
27220
|
+
const timeout = setTimeout(() => controller.abort(), HEALTH_TIMEOUT_MS2);
|
|
27221
|
+
try {
|
|
27222
|
+
const res = await fetchImpl("http://127.0.0.1:19000/health", { signal: controller.signal });
|
|
27223
|
+
await res.text().catch(() => "");
|
|
27224
|
+
if (res.status !== 200) {
|
|
27225
|
+
return {
|
|
27226
|
+
ok: false,
|
|
27227
|
+
message: `host-cp /health returned ${res.status}; engine unknown`,
|
|
27228
|
+
remedy: "Restart host-cp via `olam host-cp start`; verify port 19000 is bound to 127.0.0.1."
|
|
27229
|
+
};
|
|
27230
|
+
}
|
|
27231
|
+
const engineHeader = res.headers.get("x-olam-engine") ?? res.headers.get("X-Olam-Engine");
|
|
27232
|
+
if (!engineHeader) {
|
|
27233
|
+
return {
|
|
27234
|
+
ok: true,
|
|
27235
|
+
message: "engine unknown (X-Olam-Engine header absent \u2014 older host-cp?)"
|
|
27236
|
+
};
|
|
27237
|
+
}
|
|
27238
|
+
return {
|
|
27239
|
+
ok: true,
|
|
27240
|
+
message: `engine ${engineHeader}`,
|
|
27241
|
+
engineName: engineHeader
|
|
27242
|
+
};
|
|
27243
|
+
} catch (err) {
|
|
27244
|
+
const e = err;
|
|
27245
|
+
const reason = e.name === "AbortError" ? `timeout (${HEALTH_TIMEOUT_MS2}ms)` : e.message;
|
|
27246
|
+
return {
|
|
27247
|
+
ok: false,
|
|
27248
|
+
message: `engine probe unreachable: ${reason}`,
|
|
27249
|
+
remedy: "Restart host-cp via `olam host-cp start`; verify port 19000 is bound to 127.0.0.1."
|
|
27250
|
+
};
|
|
27251
|
+
} finally {
|
|
27252
|
+
clearTimeout(timeout);
|
|
27253
|
+
}
|
|
27254
|
+
}
|
|
27255
|
+
async function probeColimaVersion(dockerExec = defaultDockerExec2) {
|
|
27256
|
+
const r = dockerExec("colima", ["version"]);
|
|
27257
|
+
if (r.status === null || r.status !== 0 && /not found|ENOENT|not.found/i.test(r.stderr)) {
|
|
27258
|
+
return { ok: true, message: "colima not installed (not in use)" };
|
|
27259
|
+
}
|
|
27260
|
+
if (r.status !== 0) {
|
|
27261
|
+
return {
|
|
27262
|
+
ok: true,
|
|
27263
|
+
message: `colima present but version probe failed: ${(r.stderr || "").trim()}`
|
|
27264
|
+
};
|
|
27265
|
+
}
|
|
27266
|
+
const tokens = r.stdout.split(/\s+/);
|
|
27267
|
+
const version = tokens.find((t) => /^v?\d+\.\d+\.\d+/.test(t)) ?? "";
|
|
27268
|
+
if (!version) {
|
|
27269
|
+
return {
|
|
27270
|
+
ok: true,
|
|
27271
|
+
message: `colima present but version unrecognised: ${r.stdout.trim().slice(0, 80)}`
|
|
27272
|
+
};
|
|
27273
|
+
}
|
|
27274
|
+
if (COLIMA_010_RANGE.test(version)) {
|
|
27275
|
+
return {
|
|
27276
|
+
ok: true,
|
|
27277
|
+
warn: true,
|
|
27278
|
+
message: `colima ${version} detected \u2014 known kubernetes-mode regression`,
|
|
27279
|
+
remedy: COLIMA_010_WARN_TEXT
|
|
27280
|
+
};
|
|
27281
|
+
}
|
|
27282
|
+
return { ok: true, message: `colima ${version} (clear)` };
|
|
27283
|
+
}
|
|
27284
|
+
async function probeK8sApiReachable(exec = defaultDockerExec2) {
|
|
27285
|
+
const r = exec("kubectl", ["version", "--output=json", "--request-timeout=2s"]);
|
|
27286
|
+
if (r.status === 0 && r.stdout.length > 0) {
|
|
27287
|
+
let parsed;
|
|
27288
|
+
try {
|
|
27289
|
+
parsed = JSON.parse(r.stdout);
|
|
27290
|
+
} catch {
|
|
27291
|
+
parsed = null;
|
|
27292
|
+
}
|
|
27293
|
+
const obj = parsed && typeof parsed === "object" ? parsed : {};
|
|
27294
|
+
const serverInfo = obj.serverVersion;
|
|
27295
|
+
const gitVersion = typeof serverInfo?.gitVersion === "string" ? serverInfo.gitVersion : "unknown";
|
|
27296
|
+
return { ok: true, message: `api server ${gitVersion} reachable` };
|
|
27297
|
+
}
|
|
27298
|
+
return {
|
|
27299
|
+
ok: false,
|
|
27300
|
+
message: "api server not reachable",
|
|
27301
|
+
remedy: "Confirm the cluster is up (e.g. `k3d cluster list`) and your context points at it."
|
|
27302
|
+
};
|
|
27303
|
+
}
|
|
27304
|
+
async function probeK8sImagePresence(refs, exec = defaultDockerExec2) {
|
|
27305
|
+
const r = exec("kubectl", ["get", "nodes", "-o", "json", "--request-timeout=2s"]);
|
|
27306
|
+
if (r.status !== 0) {
|
|
27307
|
+
return {
|
|
27308
|
+
ok: false,
|
|
27309
|
+
message: "unable to list cluster nodes for image presence check",
|
|
27310
|
+
remedy: "Confirm the cluster is up and your context points at it; re-run `olam doctor`."
|
|
27311
|
+
};
|
|
27312
|
+
}
|
|
27313
|
+
let parsed;
|
|
27314
|
+
try {
|
|
27315
|
+
parsed = JSON.parse(r.stdout);
|
|
27316
|
+
} catch {
|
|
27317
|
+
return {
|
|
27318
|
+
ok: false,
|
|
27319
|
+
message: "cluster node list returned malformed output",
|
|
27320
|
+
remedy: "Restart your cluster; if persistent, re-create it."
|
|
27321
|
+
};
|
|
27322
|
+
}
|
|
27323
|
+
const nodes = parsed && typeof parsed === "object" ? parsed.items : void 0;
|
|
27324
|
+
if (!Array.isArray(nodes) || nodes.length === 0) {
|
|
27325
|
+
return {
|
|
27326
|
+
ok: false,
|
|
27327
|
+
message: "cluster has no nodes",
|
|
27328
|
+
remedy: "Start the cluster and try again."
|
|
27329
|
+
};
|
|
27330
|
+
}
|
|
27331
|
+
const presentImages = /* @__PURE__ */ new Set();
|
|
27332
|
+
for (const node of nodes) {
|
|
27333
|
+
const status2 = node.status;
|
|
27334
|
+
const images = status2?.images;
|
|
27335
|
+
if (!Array.isArray(images)) continue;
|
|
27336
|
+
for (const img of images) {
|
|
27337
|
+
const names = img.names;
|
|
27338
|
+
if (!Array.isArray(names)) continue;
|
|
27339
|
+
for (const n of names) presentImages.add(n);
|
|
27340
|
+
}
|
|
27341
|
+
}
|
|
27342
|
+
for (const ref of refs) {
|
|
27343
|
+
if (!hasImageRef(presentImages, ref)) {
|
|
27344
|
+
return {
|
|
27345
|
+
ok: false,
|
|
27346
|
+
message: `image ${ref} not present on any cluster node`,
|
|
27347
|
+
remedy: "Run `olam bootstrap` to import the published image into the cluster."
|
|
27348
|
+
};
|
|
27349
|
+
}
|
|
27350
|
+
}
|
|
27351
|
+
return { ok: true, message: `${refs.length} image(s) present on cluster` };
|
|
27352
|
+
}
|
|
27353
|
+
function hasImageRef(present, ref) {
|
|
27354
|
+
if (present.has(ref)) return true;
|
|
27355
|
+
const variants = [`docker.io/library/${ref}`, `docker.io/${ref}`];
|
|
27356
|
+
return variants.some((v) => present.has(v));
|
|
27357
|
+
}
|
|
27071
27358
|
|
|
27072
27359
|
// src/commands/doctor.ts
|
|
27073
27360
|
init_output();
|
|
@@ -27091,27 +27378,56 @@ async function runDoctor(opts, deps = {}) {
|
|
|
27091
27378
|
const fetchImpl = deps.fetchImpl;
|
|
27092
27379
|
const olamHomeOverride = deps.olamHomeOverride;
|
|
27093
27380
|
const registry = deps.registry ?? DEFAULT_REGISTRY;
|
|
27094
|
-
const
|
|
27095
|
-
const
|
|
27096
|
-
|
|
27097
|
-
|
|
27381
|
+
const engine = deps.engine ?? resolveEngineFromEnv();
|
|
27382
|
+
const isK8s = engine === "kubernetes";
|
|
27383
|
+
const slot1Result = isK8s ? await probeK8sApiReachable(dockerExec) : await probeDockerDaemon(dockerExec);
|
|
27384
|
+
const slot1Name = isK8s ? "api server" : "docker daemon";
|
|
27385
|
+
const rows = [{ name: slot1Name, result: slot1Result }];
|
|
27386
|
+
if (!slot1Result.ok) {
|
|
27387
|
+
const [engineRes, colimaRes] = await Promise.all([
|
|
27388
|
+
probeEngine(fetchImpl),
|
|
27389
|
+
probeColimaVersion(dockerExec)
|
|
27390
|
+
]);
|
|
27391
|
+
rows.push(
|
|
27392
|
+
{ name: "engine", result: engineRes },
|
|
27393
|
+
{ name: "colima version", result: colimaRes }
|
|
27394
|
+
);
|
|
27395
|
+
return emit(makeReport(rows), opts);
|
|
27098
27396
|
}
|
|
27099
27397
|
const imageRefs = buildRequiredImageRefs(registry);
|
|
27100
|
-
const
|
|
27101
|
-
|
|
27398
|
+
const slot2Promise = isK8s ? probeK8sImagePresence(imageRefs, dockerExec) : probeImagePresence(imageRefs, dockerExec);
|
|
27399
|
+
const [imageResult, hostCpResult, authResult, kgResult, engineResult, colimaResult] = await Promise.all([
|
|
27400
|
+
slot2Promise,
|
|
27102
27401
|
probeHostCpHealth(fetchImpl),
|
|
27103
27402
|
probeAuthVault(olamHomeOverride),
|
|
27104
|
-
probeKgStorage(olamHomeOverride)
|
|
27403
|
+
probeKgStorage(olamHomeOverride),
|
|
27404
|
+
probeEngine(fetchImpl),
|
|
27405
|
+
probeColimaVersion(dockerExec)
|
|
27105
27406
|
]);
|
|
27106
27407
|
rows.push(
|
|
27107
27408
|
{ name: "images", result: imageResult },
|
|
27108
27409
|
{ name: "host-cp /health", result: hostCpResult },
|
|
27109
27410
|
{ name: "auth vault", result: authResult },
|
|
27110
|
-
{ name: "KG storage", result: kgResult }
|
|
27411
|
+
{ name: "KG storage", result: kgResult },
|
|
27412
|
+
{ name: "engine", result: engineResult },
|
|
27413
|
+
{ name: "colima version", result: colimaResult }
|
|
27111
27414
|
);
|
|
27415
|
+
return emit(makeReport(rows), opts);
|
|
27416
|
+
}
|
|
27417
|
+
function makeReport(rows) {
|
|
27112
27418
|
const failureCount = rows.filter((r) => !r.result.ok).length;
|
|
27419
|
+
const warnCount = rows.filter((r) => isWarn(r.result)).length;
|
|
27113
27420
|
const summary = failureCount === 0 ? "OK" : "FAILED";
|
|
27114
|
-
return
|
|
27421
|
+
return { rows, summary, failureCount, warnCount };
|
|
27422
|
+
}
|
|
27423
|
+
function resolveEngineFromEnv() {
|
|
27424
|
+
const explicit = process.env.OLAM_HOST_CP_ENGINE;
|
|
27425
|
+
if (explicit === "kubernetes") return "kubernetes";
|
|
27426
|
+
if (explicit === "docker") return "docker";
|
|
27427
|
+
return process.env.KUBERNETES_SERVICE_HOST ? "kubernetes" : "docker";
|
|
27428
|
+
}
|
|
27429
|
+
function isWarn(r) {
|
|
27430
|
+
return r.ok === true && r.warn === true;
|
|
27115
27431
|
}
|
|
27116
27432
|
function emit(report, opts) {
|
|
27117
27433
|
if (opts.json) {
|
|
@@ -27120,12 +27436,19 @@ function emit(report, opts) {
|
|
|
27120
27436
|
{
|
|
27121
27437
|
summary: report.summary,
|
|
27122
27438
|
failureCount: report.failureCount,
|
|
27123
|
-
|
|
27124
|
-
|
|
27125
|
-
|
|
27126
|
-
|
|
27127
|
-
|
|
27128
|
-
|
|
27439
|
+
warnCount: report.warnCount,
|
|
27440
|
+
probes: report.rows.map((r) => {
|
|
27441
|
+
const isFail = !r.result.ok;
|
|
27442
|
+
const warn = isWarn(r.result);
|
|
27443
|
+
const base = {
|
|
27444
|
+
name: r.name,
|
|
27445
|
+
ok: r.result.ok,
|
|
27446
|
+
message: r.result.message
|
|
27447
|
+
};
|
|
27448
|
+
if (warn) base.warn = true;
|
|
27449
|
+
if (isFail || warn) base.remedy = r.result.remedy;
|
|
27450
|
+
return base;
|
|
27451
|
+
})
|
|
27129
27452
|
},
|
|
27130
27453
|
null,
|
|
27131
27454
|
2
|
|
@@ -27142,23 +27465,28 @@ function renderHuman(report) {
|
|
|
27142
27465
|
const namePad = Math.max(...report.rows.map((r) => r.name.length));
|
|
27143
27466
|
for (const row of report.rows) {
|
|
27144
27467
|
const label = row.name.padEnd(namePad);
|
|
27145
|
-
if (row.result
|
|
27146
|
-
|
|
27468
|
+
if (isWarn(row.result)) {
|
|
27469
|
+
printWarning(`[WARN] ${label} ${row.result.message}`);
|
|
27470
|
+
const remedy = row.result.remedy;
|
|
27471
|
+
if (remedy) printWarning(`${"".padEnd(namePad + 7)} remedy: ${remedy}`);
|
|
27472
|
+
} else if (row.result.ok) {
|
|
27473
|
+
printSuccess(`[PASS] ${label} ${row.result.message}`);
|
|
27147
27474
|
} else {
|
|
27148
|
-
printError(
|
|
27149
|
-
printWarning(`${"".padEnd(namePad)} remedy: ${row.result.remedy}`);
|
|
27475
|
+
printError(`[FAIL] ${label} ${row.result.message}`);
|
|
27476
|
+
printWarning(`${"".padEnd(namePad + 7)} remedy: ${row.result.remedy}`);
|
|
27150
27477
|
}
|
|
27151
27478
|
}
|
|
27152
27479
|
process.stdout.write("\n");
|
|
27153
27480
|
if (report.summary === "OK") {
|
|
27154
|
-
|
|
27481
|
+
const warnNote = report.warnCount > 0 ? ` (${report.warnCount} warn)` : "";
|
|
27482
|
+
printSuccess(`OK \u2014 all ${report.rows.length} probes green${warnNote}`);
|
|
27155
27483
|
} else {
|
|
27156
27484
|
printError(`FAILED \u2014 ${report.failureCount} of ${report.rows.length} probes failed`);
|
|
27157
27485
|
}
|
|
27158
27486
|
}
|
|
27159
27487
|
function registerDoctor(program2) {
|
|
27160
27488
|
program2.command("doctor").description(
|
|
27161
|
-
"Pass/fail host-health check. Exits 0 on a healthy host; 1 on any
|
|
27489
|
+
"Pass/fail host-health check. Exits 0 on a healthy host (including WARN-only); 1 on any FAIL probe. Runs daemon / image / host-cp /health / auth / KG / engine / colima probes (7 total)."
|
|
27162
27490
|
).option("--json", "emit the report as JSON instead of a human-readable table").action(async (opts) => {
|
|
27163
27491
|
const r = await runDoctor(opts);
|
|
27164
27492
|
if (r.exitCode !== 0) process.exitCode = r.exitCode;
|
|
@@ -27913,11 +28241,11 @@ function registerBegin(program2) {
|
|
|
27913
28241
|
}
|
|
27914
28242
|
|
|
27915
28243
|
// src/commands/config.ts
|
|
27916
|
-
import * as
|
|
28244
|
+
import * as fs51 from "node:fs";
|
|
27917
28245
|
import { createRequire as createRequire4 } from "node:module";
|
|
27918
28246
|
|
|
27919
28247
|
// ../core/dist/global-config/index.js
|
|
27920
|
-
|
|
28248
|
+
init_schema4();
|
|
27921
28249
|
init_store2();
|
|
27922
28250
|
|
|
27923
28251
|
// ../core/dist/global-config/repos.js
|
|
@@ -27991,88 +28319,714 @@ init_runbooks();
|
|
|
27991
28319
|
init_port_validator();
|
|
27992
28320
|
init_bridge();
|
|
27993
28321
|
|
|
27994
|
-
//
|
|
28322
|
+
// ../core/dist/skill-sources/index.js
|
|
28323
|
+
init_schema3();
|
|
28324
|
+
|
|
28325
|
+
// ../core/dist/skill-sources/store.js
|
|
27995
28326
|
init_store2();
|
|
27996
|
-
|
|
27997
|
-
|
|
27998
|
-
function
|
|
27999
|
-
|
|
28000
|
-
config.command("validate [path]").description("Validate ~/.olam/config.json (or a custom path) against the schema").action((filePath) => {
|
|
28001
|
-
const resolvedPath = filePath ?? globalConfigPath();
|
|
28002
|
-
if (!fs46.existsSync(resolvedPath)) {
|
|
28003
|
-
process.stderr.write(`config file not found: ${resolvedPath}
|
|
28004
|
-
`);
|
|
28005
|
-
process.exit(1);
|
|
28006
|
-
}
|
|
28007
|
-
let raw;
|
|
28008
|
-
try {
|
|
28009
|
-
raw = fs46.readFileSync(resolvedPath, "utf-8");
|
|
28010
|
-
} catch (err) {
|
|
28011
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
28012
|
-
process.stderr.write(`cannot read ${resolvedPath}: ${msg}
|
|
28013
|
-
`);
|
|
28014
|
-
process.exit(1);
|
|
28015
|
-
}
|
|
28016
|
-
let parsed;
|
|
28017
|
-
let pointers;
|
|
28018
|
-
try {
|
|
28019
|
-
const result2 = parseWithMap(raw);
|
|
28020
|
-
parsed = result2.data;
|
|
28021
|
-
pointers = result2.pointers;
|
|
28022
|
-
} catch (err) {
|
|
28023
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
28024
|
-
process.stderr.write(`${resolvedPath}: invalid JSON \u2014 ${msg}
|
|
28025
|
-
`);
|
|
28026
|
-
process.exit(1);
|
|
28027
|
-
}
|
|
28028
|
-
const result = GlobalConfigSchema.safeParse(parsed);
|
|
28029
|
-
if (result.success) {
|
|
28030
|
-
process.exit(0);
|
|
28031
|
-
}
|
|
28032
|
-
for (const issue of result.error.issues) {
|
|
28033
|
-
const pointer = "/" + issue.path.join("/");
|
|
28034
|
-
const entry = pointers[pointer];
|
|
28035
|
-
const rawLine = entry?.value?.line ?? entry?.key?.line ?? -1;
|
|
28036
|
-
const lineStr = rawLine >= 0 ? `Line ${rawLine + 1}: ` : "";
|
|
28037
|
-
process.stderr.write(`${lineStr}${pointer}: ${issue.message}
|
|
28038
|
-
`);
|
|
28039
|
-
}
|
|
28040
|
-
process.exit(1);
|
|
28041
|
-
});
|
|
28327
|
+
init_schema3();
|
|
28328
|
+
import * as crypto7 from "node:crypto";
|
|
28329
|
+
function deriveSkillSourceId(gitUrl) {
|
|
28330
|
+
return crypto7.createHash("sha256").update(gitUrl).digest("hex").slice(0, SKILL_SOURCE_ID_LENGTH);
|
|
28042
28331
|
}
|
|
28043
|
-
|
|
28044
|
-
|
|
28045
|
-
import pc25 from "picocolors";
|
|
28046
|
-
init_output();
|
|
28047
|
-
function asMessage(err) {
|
|
28048
|
-
return err instanceof Error ? err.message : String(err);
|
|
28332
|
+
function listSkillSources() {
|
|
28333
|
+
return readGlobalConfig().skillSources;
|
|
28049
28334
|
}
|
|
28050
|
-
function
|
|
28051
|
-
|
|
28052
|
-
|
|
28053
|
-
|
|
28054
|
-
|
|
28055
|
-
|
|
28056
|
-
|
|
28057
|
-
}
|
|
28058
|
-
|
|
28059
|
-
|
|
28060
|
-
|
|
28061
|
-
|
|
28062
|
-
|
|
28063
|
-
|
|
28064
|
-
|
|
28335
|
+
function getSkillSource(id) {
|
|
28336
|
+
return readGlobalConfig().skillSources.find((s) => s.id === id);
|
|
28337
|
+
}
|
|
28338
|
+
function addSkillSource(entry) {
|
|
28339
|
+
const config = readGlobalConfig();
|
|
28340
|
+
const id = deriveSkillSourceId(entry.gitUrl);
|
|
28341
|
+
if (config.skillSources.some((s) => s.id === id)) {
|
|
28342
|
+
throw new Error(`skill-source "${entry.gitUrl}" already registered (id "${id}"). Use "olam skills source list" to inspect.`);
|
|
28343
|
+
}
|
|
28344
|
+
if (config.skillSources.some((s) => s.name === entry.name)) {
|
|
28345
|
+
throw new Error(`skill-source name "${entry.name}" already in use. Pick a different display name.`);
|
|
28346
|
+
}
|
|
28347
|
+
const now = Date.now();
|
|
28348
|
+
const newEntry = {
|
|
28349
|
+
id,
|
|
28350
|
+
name: entry.name,
|
|
28351
|
+
gitUrl: entry.gitUrl,
|
|
28352
|
+
branch: entry.branch ?? "main",
|
|
28353
|
+
addedAt: now
|
|
28354
|
+
};
|
|
28355
|
+
writeGlobalConfig({ ...config, skillSources: [...config.skillSources, newEntry] });
|
|
28356
|
+
return newEntry;
|
|
28357
|
+
}
|
|
28358
|
+
function removeSkillSource(id) {
|
|
28359
|
+
const config = readGlobalConfig();
|
|
28360
|
+
if (!config.skillSources.some((s) => s.id === id)) {
|
|
28361
|
+
throw new Error(`skill-source "${id}" is not registered. Run "olam skills source list" to see registered sources.`);
|
|
28362
|
+
}
|
|
28363
|
+
writeGlobalConfig({
|
|
28364
|
+
...config,
|
|
28365
|
+
skillSources: config.skillSources.filter((s) => s.id !== id)
|
|
28065
28366
|
});
|
|
28066
|
-
|
|
28067
|
-
|
|
28068
|
-
|
|
28069
|
-
|
|
28070
|
-
|
|
28071
|
-
|
|
28072
|
-
|
|
28073
|
-
|
|
28074
|
-
|
|
28075
|
-
|
|
28367
|
+
}
|
|
28368
|
+
function updateSkillSource(id, patch) {
|
|
28369
|
+
const config = readGlobalConfig();
|
|
28370
|
+
const idx = config.skillSources.findIndex((s) => s.id === id);
|
|
28371
|
+
if (idx === -1) {
|
|
28372
|
+
throw new Error(`skill-source "${id}" is not registered. Run "olam skills source list" to see registered sources.`);
|
|
28373
|
+
}
|
|
28374
|
+
if (patch.name !== void 0) {
|
|
28375
|
+
const collision = config.skillSources.find((s, i) => i !== idx && s.name === patch.name);
|
|
28376
|
+
if (collision !== void 0) {
|
|
28377
|
+
throw new Error(`skill-source name "${patch.name}" already in use by id "${collision.id}".`);
|
|
28378
|
+
}
|
|
28379
|
+
}
|
|
28380
|
+
const existing = config.skillSources[idx];
|
|
28381
|
+
const updated = {
|
|
28382
|
+
...existing,
|
|
28383
|
+
...patch.name !== void 0 ? { name: patch.name } : {},
|
|
28384
|
+
...patch.branch !== void 0 ? { branch: patch.branch } : {},
|
|
28385
|
+
...patch.lastPulledSha !== void 0 ? { lastPulledSha: patch.lastPulledSha } : {}
|
|
28386
|
+
};
|
|
28387
|
+
const next = [...config.skillSources];
|
|
28388
|
+
next[idx] = updated;
|
|
28389
|
+
writeGlobalConfig({ ...config, skillSources: next });
|
|
28390
|
+
return updated;
|
|
28391
|
+
}
|
|
28392
|
+
|
|
28393
|
+
// ../core/dist/skill-sources/clone.js
|
|
28394
|
+
import { execFileSync as execFileSync12 } from "node:child_process";
|
|
28395
|
+
import * as fs46 from "node:fs";
|
|
28396
|
+
import * as os27 from "node:os";
|
|
28397
|
+
import * as path51 from "node:path";
|
|
28398
|
+
function skillSourcesRootDir() {
|
|
28399
|
+
const override = process.env["OLAM_SKILL_SOURCES_DIR"];
|
|
28400
|
+
if (override && override.length > 0)
|
|
28401
|
+
return override;
|
|
28402
|
+
return path51.join(os27.homedir(), ".olam", "state", "skill-sources");
|
|
28403
|
+
}
|
|
28404
|
+
function skillSourceClonePath(id) {
|
|
28405
|
+
return path51.join(skillSourcesRootDir(), id);
|
|
28406
|
+
}
|
|
28407
|
+
var SkillSourceGitError = class extends Error {
|
|
28408
|
+
op;
|
|
28409
|
+
gitUrl;
|
|
28410
|
+
constructor(op, gitUrl, cause) {
|
|
28411
|
+
const msg = cause instanceof Error ? cause.message : String(cause);
|
|
28412
|
+
super(`git ${op} failed for "${gitUrl}": ${msg}`);
|
|
28413
|
+
this.op = op;
|
|
28414
|
+
this.gitUrl = gitUrl;
|
|
28415
|
+
this.name = "SkillSourceGitError";
|
|
28416
|
+
this.cause = cause;
|
|
28417
|
+
}
|
|
28418
|
+
};
|
|
28419
|
+
function runGit(args, cwd) {
|
|
28420
|
+
try {
|
|
28421
|
+
return execFileSync12("git", args, {
|
|
28422
|
+
cwd,
|
|
28423
|
+
encoding: "utf-8",
|
|
28424
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
28425
|
+
});
|
|
28426
|
+
} catch (err) {
|
|
28427
|
+
throw err;
|
|
28428
|
+
}
|
|
28429
|
+
}
|
|
28430
|
+
function cloneSkillSource(opts) {
|
|
28431
|
+
const clonePath = skillSourceClonePath(opts.id);
|
|
28432
|
+
if (fs46.existsSync(clonePath)) {
|
|
28433
|
+
throw new Error(`clone path "${clonePath}" already exists. Remove the existing skill-source first.`);
|
|
28434
|
+
}
|
|
28435
|
+
fs46.mkdirSync(skillSourcesRootDir(), { recursive: true });
|
|
28436
|
+
try {
|
|
28437
|
+
runGit(["clone", "--depth", "1", "--branch", opts.branch, opts.gitUrl, clonePath]);
|
|
28438
|
+
} catch (err) {
|
|
28439
|
+
if (fs46.existsSync(clonePath)) {
|
|
28440
|
+
fs46.rmSync(clonePath, { recursive: true, force: true });
|
|
28441
|
+
}
|
|
28442
|
+
throw new SkillSourceGitError("clone", opts.gitUrl, err);
|
|
28443
|
+
}
|
|
28444
|
+
let headSha;
|
|
28445
|
+
try {
|
|
28446
|
+
headSha = runGit(["rev-parse", "HEAD"], clonePath).trim();
|
|
28447
|
+
} catch (err) {
|
|
28448
|
+
throw new SkillSourceGitError("rev-parse", opts.gitUrl, err);
|
|
28449
|
+
}
|
|
28450
|
+
return { clonePath, headSha };
|
|
28451
|
+
}
|
|
28452
|
+
function pullSkillSource(opts) {
|
|
28453
|
+
const clonePath = skillSourceClonePath(opts.id);
|
|
28454
|
+
if (!fs46.existsSync(clonePath)) {
|
|
28455
|
+
throw new Error(`clone path "${clonePath}" does not exist. Run "olam skills source add" first.`);
|
|
28456
|
+
}
|
|
28457
|
+
try {
|
|
28458
|
+
runGit(["fetch", "origin", opts.branch], clonePath);
|
|
28459
|
+
runGit(["reset", "--hard", `origin/${opts.branch}`], clonePath);
|
|
28460
|
+
} catch (err) {
|
|
28461
|
+
throw new SkillSourceGitError("fetch/reset", opts.gitUrl, err);
|
|
28462
|
+
}
|
|
28463
|
+
try {
|
|
28464
|
+
const headSha = runGit(["rev-parse", "HEAD"], clonePath).trim();
|
|
28465
|
+
return { headSha };
|
|
28466
|
+
} catch (err) {
|
|
28467
|
+
throw new SkillSourceGitError("rev-parse", opts.gitUrl, err);
|
|
28468
|
+
}
|
|
28469
|
+
}
|
|
28470
|
+
function removeSkillSourceClone(id) {
|
|
28471
|
+
const clonePath = skillSourceClonePath(id);
|
|
28472
|
+
if (fs46.existsSync(clonePath)) {
|
|
28473
|
+
fs46.rmSync(clonePath, { recursive: true, force: true });
|
|
28474
|
+
}
|
|
28475
|
+
}
|
|
28476
|
+
|
|
28477
|
+
// ../core/dist/skill-sync/artifact-resolver.js
|
|
28478
|
+
import * as fs47 from "node:fs";
|
|
28479
|
+
import * as path52 from "node:path";
|
|
28480
|
+
function resolveSubscriptions(opts) {
|
|
28481
|
+
const { clonePath, atlasUser } = opts;
|
|
28482
|
+
if (atlasUser) {
|
|
28483
|
+
const subsPath = path52.join(clonePath, "members", atlasUser, "subscriptions.json");
|
|
28484
|
+
if (fs47.existsSync(subsPath)) {
|
|
28485
|
+
try {
|
|
28486
|
+
const parsed = JSON.parse(fs47.readFileSync(subsPath, "utf-8"));
|
|
28487
|
+
if (Array.isArray(parsed?.categories)) {
|
|
28488
|
+
return {
|
|
28489
|
+
categories: parsed.categories.filter((c) => typeof c === "string"),
|
|
28490
|
+
fromSubscriptionsFile: true,
|
|
28491
|
+
atlasUser
|
|
28492
|
+
};
|
|
28493
|
+
}
|
|
28494
|
+
} catch {
|
|
28495
|
+
}
|
|
28496
|
+
}
|
|
28497
|
+
}
|
|
28498
|
+
const catsPath = path52.join(clonePath, "shared", "categories.json");
|
|
28499
|
+
if (fs47.existsSync(catsPath)) {
|
|
28500
|
+
try {
|
|
28501
|
+
const parsed = JSON.parse(fs47.readFileSync(catsPath, "utf-8"));
|
|
28502
|
+
if (Array.isArray(parsed?.categories)) {
|
|
28503
|
+
return {
|
|
28504
|
+
categories: parsed.categories.map((c) => c.id).filter((id) => typeof id === "string"),
|
|
28505
|
+
fromSubscriptionsFile: false,
|
|
28506
|
+
atlasUser
|
|
28507
|
+
};
|
|
28508
|
+
}
|
|
28509
|
+
} catch {
|
|
28510
|
+
}
|
|
28511
|
+
}
|
|
28512
|
+
const sharedDir = path52.join(clonePath, "shared");
|
|
28513
|
+
const cats = [];
|
|
28514
|
+
if (fs47.existsSync(sharedDir)) {
|
|
28515
|
+
for (const name of fs47.readdirSync(sharedDir)) {
|
|
28516
|
+
const dir = path52.join(sharedDir, name);
|
|
28517
|
+
if (!fs47.statSync(dir).isDirectory())
|
|
28518
|
+
continue;
|
|
28519
|
+
if (fs47.existsSync(path52.join(dir, "skills")) || fs47.existsSync(path52.join(dir, "agents"))) {
|
|
28520
|
+
cats.push(name);
|
|
28521
|
+
}
|
|
28522
|
+
}
|
|
28523
|
+
}
|
|
28524
|
+
return { categories: cats, fromSubscriptionsFile: false, atlasUser };
|
|
28525
|
+
}
|
|
28526
|
+
function listDirSafe(dir) {
|
|
28527
|
+
if (!fs47.existsSync(dir))
|
|
28528
|
+
return [];
|
|
28529
|
+
return fs47.readdirSync(dir);
|
|
28530
|
+
}
|
|
28531
|
+
function resolveSkillsDir(opts) {
|
|
28532
|
+
const { sourceId, baseDir } = opts;
|
|
28533
|
+
const out = [];
|
|
28534
|
+
for (const name of listDirSafe(baseDir)) {
|
|
28535
|
+
const subdir = path52.join(baseDir, name);
|
|
28536
|
+
if (!fs47.statSync(subdir).isDirectory())
|
|
28537
|
+
continue;
|
|
28538
|
+
if (!fs47.existsSync(path52.join(subdir, "SKILL.md")))
|
|
28539
|
+
continue;
|
|
28540
|
+
out.push({ kind: "skill", sourceId, sourcePath: subdir, deployBasename: name });
|
|
28541
|
+
const subagentsDir = path52.join(subdir, "references", "agents");
|
|
28542
|
+
if (fs47.existsSync(subagentsDir) && fs47.statSync(subagentsDir).isDirectory()) {
|
|
28543
|
+
for (const f of listDirSafe(subagentsDir)) {
|
|
28544
|
+
if (!f.endsWith(".md"))
|
|
28545
|
+
continue;
|
|
28546
|
+
out.push({
|
|
28547
|
+
kind: "subagent",
|
|
28548
|
+
sourceId,
|
|
28549
|
+
sourcePath: path52.join(subagentsDir, f),
|
|
28550
|
+
deployBasename: f,
|
|
28551
|
+
parentSkill: name
|
|
28552
|
+
});
|
|
28553
|
+
}
|
|
28554
|
+
}
|
|
28555
|
+
}
|
|
28556
|
+
return out;
|
|
28557
|
+
}
|
|
28558
|
+
function resolveAgentsDir(opts) {
|
|
28559
|
+
const { sourceId, baseDir } = opts;
|
|
28560
|
+
const out = [];
|
|
28561
|
+
for (const name of listDirSafe(baseDir)) {
|
|
28562
|
+
const full = path52.join(baseDir, name);
|
|
28563
|
+
const stat = fs47.statSync(full);
|
|
28564
|
+
if (stat.isFile() && name.endsWith(".md")) {
|
|
28565
|
+
out.push({ kind: "agent", sourceId, sourcePath: full, deployBasename: name });
|
|
28566
|
+
} else if (stat.isDirectory()) {
|
|
28567
|
+
for (const f of listDirSafe(full)) {
|
|
28568
|
+
if (!f.endsWith(".md"))
|
|
28569
|
+
continue;
|
|
28570
|
+
out.push({
|
|
28571
|
+
kind: "agent",
|
|
28572
|
+
sourceId,
|
|
28573
|
+
sourcePath: path52.join(full, f),
|
|
28574
|
+
deployBasename: `${name}-${f}`
|
|
28575
|
+
});
|
|
28576
|
+
}
|
|
28577
|
+
}
|
|
28578
|
+
}
|
|
28579
|
+
return out;
|
|
28580
|
+
}
|
|
28581
|
+
function resolveScriptsDir(opts) {
|
|
28582
|
+
const { sourceId, baseDir } = opts;
|
|
28583
|
+
const out = [];
|
|
28584
|
+
for (const name of listDirSafe(baseDir)) {
|
|
28585
|
+
const full = path52.join(baseDir, name);
|
|
28586
|
+
const stat = fs47.statSync(full);
|
|
28587
|
+
if (stat.isFile() && name.endsWith(".sh")) {
|
|
28588
|
+
out.push({ kind: "script", sourceId, sourcePath: full, deployBasename: name });
|
|
28589
|
+
} else if (stat.isDirectory()) {
|
|
28590
|
+
out.push({ kind: "script", sourceId, sourcePath: full, deployBasename: name });
|
|
28591
|
+
}
|
|
28592
|
+
}
|
|
28593
|
+
return out;
|
|
28594
|
+
}
|
|
28595
|
+
function resolveRulesDir(opts) {
|
|
28596
|
+
const { sourceId, baseDir } = opts;
|
|
28597
|
+
const out = [];
|
|
28598
|
+
for (const name of listDirSafe(baseDir)) {
|
|
28599
|
+
if (!name.endsWith(".md"))
|
|
28600
|
+
continue;
|
|
28601
|
+
const full = path52.join(baseDir, name);
|
|
28602
|
+
if (!fs47.statSync(full).isFile())
|
|
28603
|
+
continue;
|
|
28604
|
+
out.push({ kind: "rule", sourceId, sourcePath: full, deployBasename: name });
|
|
28605
|
+
}
|
|
28606
|
+
return out;
|
|
28607
|
+
}
|
|
28608
|
+
function resolveJsonDir(opts) {
|
|
28609
|
+
const { sourceId, baseDir, kind } = opts;
|
|
28610
|
+
const out = [];
|
|
28611
|
+
for (const name of listDirSafe(baseDir)) {
|
|
28612
|
+
if (!name.endsWith(".json"))
|
|
28613
|
+
continue;
|
|
28614
|
+
const full = path52.join(baseDir, name);
|
|
28615
|
+
if (!fs47.statSync(full).isFile())
|
|
28616
|
+
continue;
|
|
28617
|
+
out.push({ kind, sourceId, sourcePath: full, deployBasename: name });
|
|
28618
|
+
}
|
|
28619
|
+
return out;
|
|
28620
|
+
}
|
|
28621
|
+
function resolveSourceArtifacts(opts) {
|
|
28622
|
+
const { sourceId, clonePath, atlasUser } = opts;
|
|
28623
|
+
const subscription = resolveSubscriptions({ clonePath, atlasUser });
|
|
28624
|
+
const artifacts = [];
|
|
28625
|
+
for (const cat of subscription.categories) {
|
|
28626
|
+
const catDir = path52.join(clonePath, "shared", cat);
|
|
28627
|
+
if (!fs47.existsSync(catDir))
|
|
28628
|
+
continue;
|
|
28629
|
+
artifacts.push(...resolveSkillsDir({ sourceId, baseDir: path52.join(catDir, "skills") }));
|
|
28630
|
+
artifacts.push(...resolveAgentsDir({ sourceId, baseDir: path52.join(catDir, "agents") }));
|
|
28631
|
+
artifacts.push(...resolveScriptsDir({ sourceId, baseDir: path52.join(catDir, "scripts") }));
|
|
28632
|
+
artifacts.push(...resolveRulesDir({ sourceId, baseDir: path52.join(catDir, "rules") }));
|
|
28633
|
+
artifacts.push(...resolveJsonDir({ sourceId, baseDir: path52.join(catDir, "hooks"), kind: "hook" }));
|
|
28634
|
+
artifacts.push(...resolveJsonDir({ sourceId, baseDir: path52.join(catDir, "permissions"), kind: "permission" }));
|
|
28635
|
+
}
|
|
28636
|
+
if (atlasUser) {
|
|
28637
|
+
const memberRoot = path52.join(clonePath, "members", atlasUser);
|
|
28638
|
+
if (fs47.existsSync(memberRoot)) {
|
|
28639
|
+
artifacts.push(...resolveSkillsDir({ sourceId, baseDir: path52.join(memberRoot, "skills") }));
|
|
28640
|
+
artifacts.push(...resolveAgentsDir({ sourceId, baseDir: path52.join(memberRoot, "agents") }));
|
|
28641
|
+
artifacts.push(...resolveScriptsDir({ sourceId, baseDir: path52.join(memberRoot, "scripts") }));
|
|
28642
|
+
artifacts.push(...resolveRulesDir({ sourceId, baseDir: path52.join(memberRoot, "rules") }));
|
|
28643
|
+
artifacts.push(...resolveJsonDir({ sourceId, baseDir: path52.join(memberRoot, "hooks"), kind: "hook" }));
|
|
28644
|
+
artifacts.push(...resolveJsonDir({ sourceId, baseDir: path52.join(memberRoot, "permissions"), kind: "permission" }));
|
|
28645
|
+
}
|
|
28646
|
+
}
|
|
28647
|
+
return { subscription, artifacts };
|
|
28648
|
+
}
|
|
28649
|
+
|
|
28650
|
+
// ../core/dist/skill-sync/symlink-deployer.js
|
|
28651
|
+
import * as fs48 from "node:fs";
|
|
28652
|
+
import * as os28 from "node:os";
|
|
28653
|
+
import * as path53 from "node:path";
|
|
28654
|
+
function claudeDir() {
|
|
28655
|
+
const override = process.env["OLAM_CLAUDE_DIR"];
|
|
28656
|
+
if (override && override.length > 0)
|
|
28657
|
+
return override;
|
|
28658
|
+
return path53.join(os28.homedir(), ".claude");
|
|
28659
|
+
}
|
|
28660
|
+
var BUCKETS = ["commands", "agents", "skills", "scripts", "rules"];
|
|
28661
|
+
function bucketFor(kind) {
|
|
28662
|
+
switch (kind) {
|
|
28663
|
+
case "skill":
|
|
28664
|
+
return "skills";
|
|
28665
|
+
case "agent":
|
|
28666
|
+
return "agents";
|
|
28667
|
+
case "subagent":
|
|
28668
|
+
return "agents";
|
|
28669
|
+
case "script":
|
|
28670
|
+
return "scripts";
|
|
28671
|
+
case "rule":
|
|
28672
|
+
return "rules";
|
|
28673
|
+
case "hook":
|
|
28674
|
+
case "permission":
|
|
28675
|
+
return void 0;
|
|
28676
|
+
}
|
|
28677
|
+
}
|
|
28678
|
+
function cleanManagedSymlinks(claude) {
|
|
28679
|
+
for (const bucket of BUCKETS) {
|
|
28680
|
+
const dir = path53.join(claude, bucket);
|
|
28681
|
+
if (!fs48.existsSync(dir))
|
|
28682
|
+
continue;
|
|
28683
|
+
for (const name of fs48.readdirSync(dir)) {
|
|
28684
|
+
const p = path53.join(dir, name);
|
|
28685
|
+
try {
|
|
28686
|
+
const stat = fs48.lstatSync(p);
|
|
28687
|
+
if (stat.isSymbolicLink()) {
|
|
28688
|
+
fs48.unlinkSync(p);
|
|
28689
|
+
}
|
|
28690
|
+
} catch {
|
|
28691
|
+
}
|
|
28692
|
+
}
|
|
28693
|
+
}
|
|
28694
|
+
}
|
|
28695
|
+
function shadowBackup(link) {
|
|
28696
|
+
const epoch = Math.floor(Date.now() / 1e3);
|
|
28697
|
+
const backup2 = `${link}.shadow-backup-${epoch}`;
|
|
28698
|
+
fs48.renameSync(link, backup2);
|
|
28699
|
+
return backup2;
|
|
28700
|
+
}
|
|
28701
|
+
function linkIfNeeded(target, link) {
|
|
28702
|
+
try {
|
|
28703
|
+
const existing = fs48.readlinkSync(link);
|
|
28704
|
+
if (existing === target)
|
|
28705
|
+
return { created: false };
|
|
28706
|
+
} catch {
|
|
28707
|
+
}
|
|
28708
|
+
let isLink = false;
|
|
28709
|
+
try {
|
|
28710
|
+
isLink = fs48.lstatSync(link).isSymbolicLink();
|
|
28711
|
+
} catch {
|
|
28712
|
+
}
|
|
28713
|
+
let backup2;
|
|
28714
|
+
if (isLink) {
|
|
28715
|
+
fs48.unlinkSync(link);
|
|
28716
|
+
} else if (fs48.existsSync(link)) {
|
|
28717
|
+
backup2 = shadowBackup(link);
|
|
28718
|
+
}
|
|
28719
|
+
fs48.symlinkSync(target, link);
|
|
28720
|
+
return { created: true, shadowBackup: backup2 };
|
|
28721
|
+
}
|
|
28722
|
+
function deployArtifacts(artifacts) {
|
|
28723
|
+
const claude = claudeDir();
|
|
28724
|
+
for (const bucket of BUCKETS) {
|
|
28725
|
+
fs48.mkdirSync(path53.join(claude, bucket), { recursive: true });
|
|
28726
|
+
}
|
|
28727
|
+
cleanManagedSymlinks(claude);
|
|
28728
|
+
const result = { linked: 0, shadowBackups: [], subagentCollisions: [] };
|
|
28729
|
+
const seenAgentBasenames = /* @__PURE__ */ new Map();
|
|
28730
|
+
const collisionLosers = /* @__PURE__ */ new Map();
|
|
28731
|
+
for (const artifact of artifacts) {
|
|
28732
|
+
const bucket = bucketFor(artifact.kind);
|
|
28733
|
+
if (!bucket)
|
|
28734
|
+
continue;
|
|
28735
|
+
if (artifact.kind === "agent" || artifact.kind === "subagent") {
|
|
28736
|
+
const prior = seenAgentBasenames.get(artifact.deployBasename);
|
|
28737
|
+
if (prior) {
|
|
28738
|
+
const losers = collisionLosers.get(artifact.deployBasename) ?? [];
|
|
28739
|
+
losers.push(artifact.sourcePath);
|
|
28740
|
+
collisionLosers.set(artifact.deployBasename, losers);
|
|
28741
|
+
continue;
|
|
28742
|
+
}
|
|
28743
|
+
seenAgentBasenames.set(artifact.deployBasename, artifact.sourcePath);
|
|
28744
|
+
}
|
|
28745
|
+
const linkPath = path53.join(claude, bucket, artifact.deployBasename);
|
|
28746
|
+
const { created, shadowBackup: backup2 } = linkIfNeeded(artifact.sourcePath, linkPath);
|
|
28747
|
+
if (created)
|
|
28748
|
+
result.linked += 1;
|
|
28749
|
+
if (backup2)
|
|
28750
|
+
result.shadowBackups.push(backup2);
|
|
28751
|
+
}
|
|
28752
|
+
for (const [name, losers] of collisionLosers.entries()) {
|
|
28753
|
+
const winner = seenAgentBasenames.get(name);
|
|
28754
|
+
result.subagentCollisions.push({ name, winner, losers });
|
|
28755
|
+
}
|
|
28756
|
+
return result;
|
|
28757
|
+
}
|
|
28758
|
+
|
|
28759
|
+
// ../core/dist/skill-sync/settings-merger.js
|
|
28760
|
+
import * as fs49 from "node:fs";
|
|
28761
|
+
import * as os29 from "node:os";
|
|
28762
|
+
import * as path54 from "node:path";
|
|
28763
|
+
var OLAM_SKILLS_MARKER = "_olamSkillsManaged";
|
|
28764
|
+
var BACKUP_RETENTION = 30;
|
|
28765
|
+
function claudeSettingsPath() {
|
|
28766
|
+
const override = process.env["OLAM_CLAUDE_SETTINGS_PATH"];
|
|
28767
|
+
if (override && override.length > 0)
|
|
28768
|
+
return override;
|
|
28769
|
+
return path54.join(claudeDirInternal(), "settings.json");
|
|
28770
|
+
}
|
|
28771
|
+
function claudeDirInternal() {
|
|
28772
|
+
const override = process.env["OLAM_CLAUDE_DIR"];
|
|
28773
|
+
if (override && override.length > 0)
|
|
28774
|
+
return override;
|
|
28775
|
+
return path54.join(os29.homedir(), ".claude");
|
|
28776
|
+
}
|
|
28777
|
+
function settingsBackupDir() {
|
|
28778
|
+
const override = process.env["OLAM_SETTINGS_BACKUP_DIR"];
|
|
28779
|
+
if (override && override.length > 0)
|
|
28780
|
+
return override;
|
|
28781
|
+
return path54.join(os29.homedir(), ".olam", "state", "settings-backups");
|
|
28782
|
+
}
|
|
28783
|
+
function dedupeByMatcher(entries) {
|
|
28784
|
+
const map = /* @__PURE__ */ new Map();
|
|
28785
|
+
for (const e of entries) {
|
|
28786
|
+
map.set(e.matcher ?? "", e);
|
|
28787
|
+
}
|
|
28788
|
+
return [...map.values()];
|
|
28789
|
+
}
|
|
28790
|
+
function tagOlam(entry) {
|
|
28791
|
+
return { ...entry, [OLAM_SKILLS_MARKER]: true };
|
|
28792
|
+
}
|
|
28793
|
+
function readJson(file) {
|
|
28794
|
+
return JSON.parse(fs49.readFileSync(file, "utf-8"));
|
|
28795
|
+
}
|
|
28796
|
+
function rotateBackups(backupDir) {
|
|
28797
|
+
if (!fs49.existsSync(backupDir))
|
|
28798
|
+
return;
|
|
28799
|
+
const files = fs49.readdirSync(backupDir).filter((f) => f.endsWith(".json")).map((f) => ({ name: f, full: path54.join(backupDir, f), mtime: fs49.statSync(path54.join(backupDir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
|
|
28800
|
+
for (const f of files.slice(BACKUP_RETENTION)) {
|
|
28801
|
+
try {
|
|
28802
|
+
fs49.unlinkSync(f.full);
|
|
28803
|
+
} catch {
|
|
28804
|
+
}
|
|
28805
|
+
}
|
|
28806
|
+
}
|
|
28807
|
+
function backupSettings() {
|
|
28808
|
+
const src = claudeSettingsPath();
|
|
28809
|
+
if (!fs49.existsSync(src))
|
|
28810
|
+
return void 0;
|
|
28811
|
+
const dir = settingsBackupDir();
|
|
28812
|
+
fs49.mkdirSync(dir, { recursive: true });
|
|
28813
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
28814
|
+
const dest = path54.join(dir, `settings-${stamp}.json`);
|
|
28815
|
+
fs49.copyFileSync(src, dest);
|
|
28816
|
+
rotateBackups(dir);
|
|
28817
|
+
return dest;
|
|
28818
|
+
}
|
|
28819
|
+
function mergeSettings(input) {
|
|
28820
|
+
const settingsPath = claudeSettingsPath();
|
|
28821
|
+
const backupPath = backupSettings();
|
|
28822
|
+
let base = {};
|
|
28823
|
+
if (fs49.existsSync(settingsPath)) {
|
|
28824
|
+
try {
|
|
28825
|
+
base = readJson(settingsPath);
|
|
28826
|
+
} catch {
|
|
28827
|
+
base = {};
|
|
28828
|
+
}
|
|
28829
|
+
}
|
|
28830
|
+
const olamHooksByCategory = /* @__PURE__ */ new Map();
|
|
28831
|
+
for (const file of input.hookFiles) {
|
|
28832
|
+
let parsed;
|
|
28833
|
+
try {
|
|
28834
|
+
parsed = readJson(file);
|
|
28835
|
+
} catch {
|
|
28836
|
+
continue;
|
|
28837
|
+
}
|
|
28838
|
+
if (!parsed.hooks || typeof parsed.hooks !== "object")
|
|
28839
|
+
continue;
|
|
28840
|
+
for (const [category, entries] of Object.entries(parsed.hooks)) {
|
|
28841
|
+
if (!Array.isArray(entries))
|
|
28842
|
+
continue;
|
|
28843
|
+
const list = olamHooksByCategory.get(category) ?? [];
|
|
28844
|
+
list.push(...entries.map(tagOlam));
|
|
28845
|
+
olamHooksByCategory.set(category, list);
|
|
28846
|
+
}
|
|
28847
|
+
}
|
|
28848
|
+
const mergedHooks = {};
|
|
28849
|
+
const existingHooks = base.hooks && typeof base.hooks === "object" ? base.hooks : {};
|
|
28850
|
+
const categories = /* @__PURE__ */ new Set([
|
|
28851
|
+
...Object.keys(existingHooks),
|
|
28852
|
+
...olamHooksByCategory.keys()
|
|
28853
|
+
]);
|
|
28854
|
+
let hooksAdded = 0;
|
|
28855
|
+
for (const cat of categories) {
|
|
28856
|
+
const existing = Array.isArray(existingHooks[cat]) ? existingHooks[cat] : [];
|
|
28857
|
+
const preserved = existing.filter((e) => !e[OLAM_SKILLS_MARKER]);
|
|
28858
|
+
const olam = olamHooksByCategory.get(cat) ?? [];
|
|
28859
|
+
const combined = [...preserved, ...olam];
|
|
28860
|
+
mergedHooks[cat] = dedupeByMatcher(combined);
|
|
28861
|
+
hooksAdded += olam.length;
|
|
28862
|
+
}
|
|
28863
|
+
const permSet = /* @__PURE__ */ new Set();
|
|
28864
|
+
for (const file of input.permissionFiles) {
|
|
28865
|
+
let parsed;
|
|
28866
|
+
try {
|
|
28867
|
+
parsed = readJson(file);
|
|
28868
|
+
} catch {
|
|
28869
|
+
continue;
|
|
28870
|
+
}
|
|
28871
|
+
const allow = parsed.permissions?.allow;
|
|
28872
|
+
if (Array.isArray(allow)) {
|
|
28873
|
+
for (const p of allow)
|
|
28874
|
+
if (typeof p === "string")
|
|
28875
|
+
permSet.add(p);
|
|
28876
|
+
}
|
|
28877
|
+
}
|
|
28878
|
+
const next = {
|
|
28879
|
+
...base,
|
|
28880
|
+
hooks: mergedHooks,
|
|
28881
|
+
permissions: {
|
|
28882
|
+
...base.permissions ?? {},
|
|
28883
|
+
...input.permissionFiles.length > 0 ? { allow: [...permSet] } : {}
|
|
28884
|
+
}
|
|
28885
|
+
};
|
|
28886
|
+
fs49.mkdirSync(path54.dirname(settingsPath), { recursive: true });
|
|
28887
|
+
const tmp = `${settingsPath}.tmp-${process.pid}`;
|
|
28888
|
+
fs49.writeFileSync(tmp, JSON.stringify(next, null, 2) + "\n", { mode: 420 });
|
|
28889
|
+
fs49.renameSync(tmp, settingsPath);
|
|
28890
|
+
return { backupPath, hooksAdded, permissionsCount: permSet.size };
|
|
28891
|
+
}
|
|
28892
|
+
|
|
28893
|
+
// ../core/dist/skill-sync/engine.js
|
|
28894
|
+
import * as fs50 from "node:fs";
|
|
28895
|
+
import * as os30 from "node:os";
|
|
28896
|
+
import * as path55 from "node:path";
|
|
28897
|
+
function resolveAtlasUser(override) {
|
|
28898
|
+
if (override)
|
|
28899
|
+
return override;
|
|
28900
|
+
const claudeDir2 = process.env["OLAM_CLAUDE_DIR"] || path55.join(os30.homedir(), ".claude");
|
|
28901
|
+
const f = path55.join(claudeDir2, ".atlas-user");
|
|
28902
|
+
if (fs50.existsSync(f)) {
|
|
28903
|
+
return fs50.readFileSync(f, "utf-8").trim() || void 0;
|
|
28904
|
+
}
|
|
28905
|
+
return void 0;
|
|
28906
|
+
}
|
|
28907
|
+
async function syncSkills(opts = {}) {
|
|
28908
|
+
const atlasUser = resolveAtlasUser(opts.atlasUser);
|
|
28909
|
+
const sources = listSkillSources();
|
|
28910
|
+
const allArtifacts = [];
|
|
28911
|
+
const perSource = [];
|
|
28912
|
+
for (const source of sources) {
|
|
28913
|
+
const clonePath = skillSourceClonePath(source.id);
|
|
28914
|
+
if (!fs50.existsSync(clonePath))
|
|
28915
|
+
continue;
|
|
28916
|
+
const { artifacts, subscription } = resolveSourceArtifacts({
|
|
28917
|
+
sourceId: source.id,
|
|
28918
|
+
clonePath,
|
|
28919
|
+
atlasUser
|
|
28920
|
+
});
|
|
28921
|
+
allArtifacts.push(...artifacts);
|
|
28922
|
+
perSource.push({
|
|
28923
|
+
sourceId: source.id,
|
|
28924
|
+
name: source.name,
|
|
28925
|
+
artifactCount: artifacts.length,
|
|
28926
|
+
categories: subscription.categories,
|
|
28927
|
+
fromSubscriptionsFile: subscription.fromSubscriptionsFile
|
|
28928
|
+
});
|
|
28929
|
+
}
|
|
28930
|
+
const hookFiles = allArtifacts.filter((a) => a.kind === "hook").map((a) => a.sourcePath);
|
|
28931
|
+
const permissionFiles = allArtifacts.filter((a) => a.kind === "permission").map((a) => a.sourcePath);
|
|
28932
|
+
const summary = {
|
|
28933
|
+
sourceCount: sources.length,
|
|
28934
|
+
artifactCount: allArtifacts.length,
|
|
28935
|
+
hookFileCount: hookFiles.length,
|
|
28936
|
+
permissionFileCount: permissionFiles.length,
|
|
28937
|
+
deploy: void 0,
|
|
28938
|
+
merge: void 0,
|
|
28939
|
+
perSource
|
|
28940
|
+
};
|
|
28941
|
+
if (opts.dryRun)
|
|
28942
|
+
return summary;
|
|
28943
|
+
summary.deploy = deployArtifacts(allArtifacts);
|
|
28944
|
+
summary.merge = mergeSettings({ hookFiles, permissionFiles });
|
|
28945
|
+
return summary;
|
|
28946
|
+
}
|
|
28947
|
+
|
|
28948
|
+
// src/commands/config.ts
|
|
28949
|
+
init_store2();
|
|
28950
|
+
var _require4 = createRequire4(import.meta.url);
|
|
28951
|
+
var { parse: parseWithMap } = _require4("json-source-map");
|
|
28952
|
+
function registerConfig(program2) {
|
|
28953
|
+
const config = program2.command("config").description("Manage global olam configuration");
|
|
28954
|
+
config.command("validate [path]").description("Validate ~/.olam/config.json (or a custom path) against the schema").action((filePath) => {
|
|
28955
|
+
const resolvedPath = filePath ?? globalConfigPath();
|
|
28956
|
+
if (!fs51.existsSync(resolvedPath)) {
|
|
28957
|
+
process.stderr.write(`config file not found: ${resolvedPath}
|
|
28958
|
+
`);
|
|
28959
|
+
process.exit(1);
|
|
28960
|
+
}
|
|
28961
|
+
let raw;
|
|
28962
|
+
try {
|
|
28963
|
+
raw = fs51.readFileSync(resolvedPath, "utf-8");
|
|
28964
|
+
} catch (err) {
|
|
28965
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
28966
|
+
process.stderr.write(`cannot read ${resolvedPath}: ${msg}
|
|
28967
|
+
`);
|
|
28968
|
+
process.exit(1);
|
|
28969
|
+
}
|
|
28970
|
+
let parsed;
|
|
28971
|
+
let pointers;
|
|
28972
|
+
try {
|
|
28973
|
+
const result2 = parseWithMap(raw);
|
|
28974
|
+
parsed = result2.data;
|
|
28975
|
+
pointers = result2.pointers;
|
|
28976
|
+
} catch (err) {
|
|
28977
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
28978
|
+
process.stderr.write(`${resolvedPath}: invalid JSON \u2014 ${msg}
|
|
28979
|
+
`);
|
|
28980
|
+
process.exit(1);
|
|
28981
|
+
}
|
|
28982
|
+
const result = GlobalConfigSchema.safeParse(parsed);
|
|
28983
|
+
if (result.success) {
|
|
28984
|
+
process.exit(0);
|
|
28985
|
+
}
|
|
28986
|
+
for (const issue of result.error.issues) {
|
|
28987
|
+
const pointer = "/" + issue.path.join("/");
|
|
28988
|
+
const entry = pointers[pointer];
|
|
28989
|
+
const rawLine = entry?.value?.line ?? entry?.key?.line ?? -1;
|
|
28990
|
+
const lineStr = rawLine >= 0 ? `Line ${rawLine + 1}: ` : "";
|
|
28991
|
+
process.stderr.write(`${lineStr}${pointer}: ${issue.message}
|
|
28992
|
+
`);
|
|
28993
|
+
}
|
|
28994
|
+
process.exit(1);
|
|
28995
|
+
});
|
|
28996
|
+
}
|
|
28997
|
+
|
|
28998
|
+
// src/commands/repos.ts
|
|
28999
|
+
import pc25 from "picocolors";
|
|
29000
|
+
init_output();
|
|
29001
|
+
function asMessage(err) {
|
|
29002
|
+
return err instanceof Error ? err.message : String(err);
|
|
29003
|
+
}
|
|
29004
|
+
function registerRepos(program2) {
|
|
29005
|
+
const repos = program2.command("repos").description("Manage the global repo registry");
|
|
29006
|
+
repos.command("list").description("List all registered repos").action(() => {
|
|
29007
|
+
const all = listRepos();
|
|
29008
|
+
if (all.length === 0) {
|
|
29009
|
+
console.log(pc25.dim("0 repo(s) registered. Add one with: olam repos add --name <n> --path <p>"));
|
|
29010
|
+
return;
|
|
29011
|
+
}
|
|
29012
|
+
printHeader(`${all.length} repo(s)`);
|
|
29013
|
+
for (const r of all) {
|
|
29014
|
+
const when = new Date(r.addedAt).toISOString().slice(0, 10);
|
|
29015
|
+
console.log(
|
|
29016
|
+
` ${pc25.bold(r.name.padEnd(24))} ${r.path.padEnd(48)} ${pc25.dim(when)}`
|
|
29017
|
+
);
|
|
29018
|
+
}
|
|
29019
|
+
});
|
|
29020
|
+
repos.command("add").description("Register a local repo path").requiredOption("--name <name>", "Repo name (lowercase, digits, dash)").requiredOption("--path <path>", "Absolute or ~/... path to the local repo").option("--description <desc>", "Optional human-readable description").option("--default-branch <branch>", "Default branch name (e.g. main)").action((opts) => {
|
|
29021
|
+
try {
|
|
29022
|
+
const entry = addRepo({
|
|
29023
|
+
name: opts.name,
|
|
29024
|
+
path: opts.path,
|
|
29025
|
+
description: opts.description,
|
|
29026
|
+
defaultBranch: opts.defaultBranch
|
|
29027
|
+
});
|
|
29028
|
+
printSuccess(`registered repo "${entry.name}" at ${entry.path}`);
|
|
29029
|
+
} catch (err) {
|
|
28076
29030
|
printError(asMessage(err));
|
|
28077
29031
|
process.exitCode = 1;
|
|
28078
29032
|
}
|
|
@@ -28183,74 +29137,309 @@ function registerRunbooks(program2) {
|
|
|
28183
29137
|
removeRunbook(name);
|
|
28184
29138
|
printSuccess(`removed runbook "${name}"`);
|
|
28185
29139
|
} catch (err) {
|
|
28186
|
-
printError(asMessage2(err));
|
|
29140
|
+
printError(asMessage2(err));
|
|
29141
|
+
process.exitCode = 1;
|
|
29142
|
+
}
|
|
29143
|
+
});
|
|
29144
|
+
runbooks.command("apply").description("Create a world from a runbook (delegates to olam create)").argument("<name>", "Runbook name").option("--name <world>", "World name override").option("--task <task>", "Initial task to dispatch").action((name, opts) => {
|
|
29145
|
+
try {
|
|
29146
|
+
const rb = getRunbook(name);
|
|
29147
|
+
const { conflicts } = validateRunbookPorts(rb);
|
|
29148
|
+
if (conflicts.length > 0) {
|
|
29149
|
+
printError(`port conflicts detected for runbook "${name}":`);
|
|
29150
|
+
for (const c of conflicts) {
|
|
29151
|
+
console.error(formatConflict(c));
|
|
29152
|
+
}
|
|
29153
|
+
console.error(
|
|
29154
|
+
`
|
|
29155
|
+
${conflicts.length} port conflict(s). Stop the conflicting processes or update portMap in runbook "${name}".`
|
|
29156
|
+
);
|
|
29157
|
+
process.exitCode = 1;
|
|
29158
|
+
return;
|
|
29159
|
+
}
|
|
29160
|
+
console.log(
|
|
29161
|
+
pc26.dim(
|
|
29162
|
+
`olam runbooks apply: port validation passed for "${name}" (repos: ${rb.repos.join(", ")}).`
|
|
29163
|
+
)
|
|
29164
|
+
);
|
|
29165
|
+
console.log(
|
|
29166
|
+
pc26.dim(
|
|
29167
|
+
`Hint: use "olam create --repos ${rb.repos.join(" ")}${opts.task ? ` --task "${opts.task}"` : ""}${opts.name ? ` --name ${opts.name}` : ""}" to create the world.`
|
|
29168
|
+
)
|
|
29169
|
+
);
|
|
29170
|
+
console.log(
|
|
29171
|
+
pc26.yellow('Phase D will wire --runbook directly. For now, use "olam create --repos ..." above.')
|
|
29172
|
+
);
|
|
29173
|
+
} catch (err) {
|
|
29174
|
+
printError(asMessage2(err));
|
|
29175
|
+
process.exitCode = 1;
|
|
29176
|
+
}
|
|
29177
|
+
});
|
|
29178
|
+
runbooks.command("check-ports").description("Check if runbook ports are available").argument("<name>", "Runbook name").action((name) => {
|
|
29179
|
+
try {
|
|
29180
|
+
const rb = getRunbook(name);
|
|
29181
|
+
const { conflicts } = validateRunbookPorts(rb);
|
|
29182
|
+
if (!rb.portMap || Object.keys(rb.portMap).length === 0) {
|
|
29183
|
+
console.log(pc26.dim(`runbook "${name}" declares no ports.`));
|
|
29184
|
+
return;
|
|
29185
|
+
}
|
|
29186
|
+
for (const [repoName, svcMap] of Object.entries(rb.portMap ?? {})) {
|
|
29187
|
+
for (const [svcName, port2] of Object.entries(svcMap)) {
|
|
29188
|
+
const conflict = conflicts.find((c) => c.port === port2);
|
|
29189
|
+
if (conflict) {
|
|
29190
|
+
console.log(formatConflict(conflict));
|
|
29191
|
+
} else {
|
|
29192
|
+
console.log(` ${pc26.green("\u2713")} ${repoName}.${svcName}:${port2} \u2014 free`);
|
|
29193
|
+
}
|
|
29194
|
+
}
|
|
29195
|
+
}
|
|
29196
|
+
if (conflicts.length > 0) {
|
|
29197
|
+
console.error(
|
|
29198
|
+
`
|
|
29199
|
+
${conflicts.length} port conflict(s). Stop the conflicting processes or update portMap in runbook "${name}".`
|
|
29200
|
+
);
|
|
29201
|
+
process.exitCode = 1;
|
|
29202
|
+
} else {
|
|
29203
|
+
console.log(pc26.green("\n\u2713 all ports free"));
|
|
29204
|
+
}
|
|
29205
|
+
} catch (err) {
|
|
29206
|
+
printError(asMessage2(err));
|
|
29207
|
+
process.exitCode = 1;
|
|
29208
|
+
}
|
|
29209
|
+
});
|
|
29210
|
+
}
|
|
29211
|
+
|
|
29212
|
+
// src/commands/skills-source.ts
|
|
29213
|
+
import pc27 from "picocolors";
|
|
29214
|
+
init_output();
|
|
29215
|
+
function asMessage3(err) {
|
|
29216
|
+
return err instanceof Error ? err.message : String(err);
|
|
29217
|
+
}
|
|
29218
|
+
function registerSkillsSource(program2) {
|
|
29219
|
+
const skills = program2.command("skills").description("Manage skill sources and synchronization");
|
|
29220
|
+
const source = skills.command("source").description("Manage registered skill sources");
|
|
29221
|
+
source.command("list").description("List all registered skill sources").action(() => {
|
|
29222
|
+
const all = listSkillSources();
|
|
29223
|
+
if (all.length === 0) {
|
|
29224
|
+
console.log(
|
|
29225
|
+
pc27.dim("0 skill source(s) registered. Add one with: olam skills source add --name <n> --git-url <url>")
|
|
29226
|
+
);
|
|
29227
|
+
return;
|
|
29228
|
+
}
|
|
29229
|
+
printHeader(`${all.length} skill source(s)`);
|
|
29230
|
+
for (const s of all) {
|
|
29231
|
+
const when = new Date(s.addedAt).toISOString().slice(0, 10);
|
|
29232
|
+
const sha = s.lastPulledSha ? s.lastPulledSha.slice(0, 8) : pc27.dim("(unpulled)");
|
|
29233
|
+
console.log(
|
|
29234
|
+
` ${pc27.bold(s.id.padEnd(14))} ${s.name.padEnd(24)} ${s.branch.padEnd(12)} ${sha.padEnd(12)} ${pc27.dim(when)} ${pc27.dim(s.gitUrl)}`
|
|
29235
|
+
);
|
|
29236
|
+
}
|
|
29237
|
+
});
|
|
29238
|
+
source.command("add").description("Register and clone a skill source").requiredOption("--name <name>", "Display name (lowercase, digits, dash)").requiredOption("--git-url <url>", "git URL (https://, git@, ssh://, file://, or absolute path)").option("--branch <branch>", "Branch to track", "main").action((opts) => {
|
|
29239
|
+
let entry;
|
|
29240
|
+
try {
|
|
29241
|
+
entry = addSkillSource({ name: opts.name, gitUrl: opts.gitUrl, branch: opts.branch });
|
|
29242
|
+
} catch (err) {
|
|
29243
|
+
printError(asMessage3(err));
|
|
29244
|
+
process.exitCode = 1;
|
|
29245
|
+
return;
|
|
29246
|
+
}
|
|
29247
|
+
try {
|
|
29248
|
+
const result = cloneSkillSource({
|
|
29249
|
+
gitUrl: entry.gitUrl,
|
|
29250
|
+
branch: entry.branch,
|
|
29251
|
+
id: entry.id
|
|
29252
|
+
});
|
|
29253
|
+
updateSkillSource(entry.id, { lastPulledSha: result.headSha });
|
|
29254
|
+
printSuccess(
|
|
29255
|
+
`registered skill source "${entry.name}" (${entry.id}) at ${result.clonePath} @ ${result.headSha.slice(0, 8)}`
|
|
29256
|
+
);
|
|
29257
|
+
} catch (err) {
|
|
29258
|
+
try {
|
|
29259
|
+
removeSkillSource(entry.id);
|
|
29260
|
+
} catch {
|
|
29261
|
+
}
|
|
29262
|
+
printError(asMessage3(err));
|
|
29263
|
+
process.exitCode = 1;
|
|
29264
|
+
}
|
|
29265
|
+
});
|
|
29266
|
+
source.command("remove").description("Remove a registered skill source (deletes its clone)").argument("<id>", 'Skill source id (from "olam skills source list")').action((id) => {
|
|
29267
|
+
try {
|
|
29268
|
+
removeSkillSourceClone(id);
|
|
29269
|
+
removeSkillSource(id);
|
|
29270
|
+
printSuccess(`removed skill source "${id}"`);
|
|
29271
|
+
} catch (err) {
|
|
29272
|
+
printError(asMessage3(err));
|
|
29273
|
+
process.exitCode = 1;
|
|
29274
|
+
}
|
|
29275
|
+
});
|
|
29276
|
+
source.command("pull").description("Fetch + reset the clone to upstream HEAD").argument("<id>", "Skill source id").action((id) => {
|
|
29277
|
+
try {
|
|
29278
|
+
const entry = getSkillSource(id);
|
|
29279
|
+
if (!entry) {
|
|
29280
|
+
printError(`skill source "${id}" is not registered`);
|
|
29281
|
+
process.exitCode = 1;
|
|
29282
|
+
return;
|
|
29283
|
+
}
|
|
29284
|
+
const result = pullSkillSource({
|
|
29285
|
+
gitUrl: entry.gitUrl,
|
|
29286
|
+
branch: entry.branch,
|
|
29287
|
+
id: entry.id
|
|
29288
|
+
});
|
|
29289
|
+
updateSkillSource(entry.id, { lastPulledSha: result.headSha });
|
|
29290
|
+
printSuccess(`pulled "${entry.name}" \u2192 ${result.headSha.slice(0, 8)}`);
|
|
29291
|
+
} catch (err) {
|
|
29292
|
+
printError(asMessage3(err));
|
|
29293
|
+
process.exitCode = 1;
|
|
29294
|
+
}
|
|
29295
|
+
});
|
|
29296
|
+
source.command("show").description("Show details for a single skill source").argument("<id>", "Skill source id").action((id) => {
|
|
29297
|
+
const entry = getSkillSource(id);
|
|
29298
|
+
if (!entry) {
|
|
29299
|
+
printError(`skill source "${id}" is not registered`);
|
|
29300
|
+
process.exitCode = 1;
|
|
29301
|
+
return;
|
|
29302
|
+
}
|
|
29303
|
+
const when = new Date(entry.addedAt).toISOString();
|
|
29304
|
+
console.log(`${pc27.bold("id:")} ${entry.id}`);
|
|
29305
|
+
console.log(`${pc27.bold("name:")} ${entry.name}`);
|
|
29306
|
+
console.log(`${pc27.bold("gitUrl:")} ${entry.gitUrl}`);
|
|
29307
|
+
console.log(`${pc27.bold("branch:")} ${entry.branch}`);
|
|
29308
|
+
console.log(`${pc27.bold("addedAt:")} ${when}`);
|
|
29309
|
+
console.log(`${pc27.bold("lastPulledSha:")} ${entry.lastPulledSha ?? pc27.dim("(unpulled)")}`);
|
|
29310
|
+
console.log(`${pc27.bold("clonePath:")} ${skillSourceClonePath(entry.id)}`);
|
|
29311
|
+
});
|
|
29312
|
+
}
|
|
29313
|
+
|
|
29314
|
+
// src/commands/skills.ts
|
|
29315
|
+
import * as fs52 from "node:fs";
|
|
29316
|
+
import * as path56 from "node:path";
|
|
29317
|
+
import pc28 from "picocolors";
|
|
29318
|
+
init_output();
|
|
29319
|
+
function asMessage4(err) {
|
|
29320
|
+
return err instanceof Error ? err.message : String(err);
|
|
29321
|
+
}
|
|
29322
|
+
function listDeployed() {
|
|
29323
|
+
const dir = claudeDir();
|
|
29324
|
+
const sources = listSkillSources();
|
|
29325
|
+
const sourcePaths = new Map(
|
|
29326
|
+
sources.map((s) => [skillSourceClonePath(s.id), s.id])
|
|
29327
|
+
);
|
|
29328
|
+
const entries = [];
|
|
29329
|
+
for (const bucket of ["skills", "agents", "scripts", "rules", "commands"]) {
|
|
29330
|
+
const bucketDir = path56.join(dir, bucket);
|
|
29331
|
+
if (!fs52.existsSync(bucketDir)) continue;
|
|
29332
|
+
for (const name of fs52.readdirSync(bucketDir)) {
|
|
29333
|
+
const full = path56.join(bucketDir, name);
|
|
29334
|
+
try {
|
|
29335
|
+
const stat = fs52.lstatSync(full);
|
|
29336
|
+
if (!stat.isSymbolicLink()) continue;
|
|
29337
|
+
const target = fs52.readlinkSync(full);
|
|
29338
|
+
let sourceId;
|
|
29339
|
+
for (const [clonePath, id] of sourcePaths.entries()) {
|
|
29340
|
+
if (target.startsWith(clonePath)) {
|
|
29341
|
+
sourceId = id;
|
|
29342
|
+
break;
|
|
29343
|
+
}
|
|
29344
|
+
}
|
|
29345
|
+
entries.push({ bucket, name, target, sourceId });
|
|
29346
|
+
} catch {
|
|
29347
|
+
}
|
|
29348
|
+
}
|
|
29349
|
+
}
|
|
29350
|
+
return entries;
|
|
29351
|
+
}
|
|
29352
|
+
function printSyncSummary(summary, dryRun) {
|
|
29353
|
+
printHeader(dryRun ? "dry-run summary" : "sync summary");
|
|
29354
|
+
console.log(` sources: ${summary.sourceCount}`);
|
|
29355
|
+
console.log(` artifacts: ${summary.artifactCount}`);
|
|
29356
|
+
console.log(` hook files: ${summary.hookFileCount}`);
|
|
29357
|
+
console.log(` permission files:${summary.permissionFileCount}`);
|
|
29358
|
+
if (summary.deploy) {
|
|
29359
|
+
console.log(` symlinks made: ${summary.deploy.linked}`);
|
|
29360
|
+
if (summary.deploy.shadowBackups.length > 0) {
|
|
29361
|
+
console.log(` ${pc28.yellow("shadow backups:")} ${summary.deploy.shadowBackups.length}`);
|
|
29362
|
+
for (const p of summary.deploy.shadowBackups) console.log(` ${pc28.dim(p)}`);
|
|
29363
|
+
}
|
|
29364
|
+
if (summary.deploy.subagentCollisions.length > 0) {
|
|
29365
|
+
console.log(` ${pc28.yellow("agent name collisions:")} ${summary.deploy.subagentCollisions.length}`);
|
|
29366
|
+
for (const c of summary.deploy.subagentCollisions) {
|
|
29367
|
+
console.log(` ${pc28.bold(c.name)} \u2192 winner: ${pc28.dim(c.winner)}`);
|
|
29368
|
+
for (const l of c.losers) console.log(` ${pc28.dim("(skipped)")} ${l}`);
|
|
29369
|
+
}
|
|
29370
|
+
}
|
|
29371
|
+
}
|
|
29372
|
+
if (summary.merge) {
|
|
29373
|
+
console.log(` hooks added: ${summary.merge.hooksAdded}`);
|
|
29374
|
+
console.log(` permissions: ${summary.merge.permissionsCount}`);
|
|
29375
|
+
if (summary.merge.backupPath) {
|
|
29376
|
+
console.log(` settings backup: ${pc28.dim(summary.merge.backupPath)}`);
|
|
29377
|
+
}
|
|
29378
|
+
}
|
|
29379
|
+
console.log();
|
|
29380
|
+
for (const s of summary.perSource) {
|
|
29381
|
+
const sub = s.fromSubscriptionsFile ? "(subscribed)" : "(all categories)";
|
|
29382
|
+
console.log(` ${pc28.bold(s.name.padEnd(24))} ${s.artifactCount} artifacts \xB7 ${s.categories.join(", ") || "(none)"} ${pc28.dim(sub)}`);
|
|
29383
|
+
}
|
|
29384
|
+
}
|
|
29385
|
+
function registerSkills(program2) {
|
|
29386
|
+
const skills = program2.commands.find((c) => c.name() === "skills") ?? program2.command("skills").description("Manage skill sources and synchronization");
|
|
29387
|
+
skills.command("sync").description("Sync registered skill sources to ~/.claude/").option("--dry-run", "Resolve artifacts but do not deploy or merge").option("--atlas-user <user>", "Override atlas-user (defaults to ~/.claude/.atlas-user)").action(async (opts) => {
|
|
29388
|
+
try {
|
|
29389
|
+
const summary = await syncSkills({ dryRun: opts.dryRun, atlasUser: opts.atlasUser });
|
|
29390
|
+
printSyncSummary(summary, !!opts.dryRun);
|
|
29391
|
+
if (!opts.dryRun) {
|
|
29392
|
+
printSuccess(`synced ${summary.sourceCount} source(s), ${summary.artifactCount} artifact(s)`);
|
|
29393
|
+
}
|
|
29394
|
+
} catch (err) {
|
|
29395
|
+
printError(asMessage4(err));
|
|
28187
29396
|
process.exitCode = 1;
|
|
28188
29397
|
}
|
|
28189
29398
|
});
|
|
28190
|
-
|
|
29399
|
+
skills.command("diff").description("Show what `olam skills sync` would deploy (no on-disk changes)").option("--atlas-user <user>", "Override atlas-user").action(async (opts) => {
|
|
28191
29400
|
try {
|
|
28192
|
-
const
|
|
28193
|
-
|
|
28194
|
-
if (conflicts.length > 0) {
|
|
28195
|
-
printError(`port conflicts detected for runbook "${name}":`);
|
|
28196
|
-
for (const c of conflicts) {
|
|
28197
|
-
console.error(formatConflict(c));
|
|
28198
|
-
}
|
|
28199
|
-
console.error(
|
|
28200
|
-
`
|
|
28201
|
-
${conflicts.length} port conflict(s). Stop the conflicting processes or update portMap in runbook "${name}".`
|
|
28202
|
-
);
|
|
28203
|
-
process.exitCode = 1;
|
|
28204
|
-
return;
|
|
28205
|
-
}
|
|
28206
|
-
console.log(
|
|
28207
|
-
pc26.dim(
|
|
28208
|
-
`olam runbooks apply: port validation passed for "${name}" (repos: ${rb.repos.join(", ")}).`
|
|
28209
|
-
)
|
|
28210
|
-
);
|
|
28211
|
-
console.log(
|
|
28212
|
-
pc26.dim(
|
|
28213
|
-
`Hint: use "olam create --repos ${rb.repos.join(" ")}${opts.task ? ` --task "${opts.task}"` : ""}${opts.name ? ` --name ${opts.name}` : ""}" to create the world.`
|
|
28214
|
-
)
|
|
28215
|
-
);
|
|
28216
|
-
console.log(
|
|
28217
|
-
pc26.yellow('Phase D will wire --runbook directly. For now, use "olam create --repos ..." above.')
|
|
28218
|
-
);
|
|
29401
|
+
const summary = await syncSkills({ dryRun: true, atlasUser: opts.atlasUser });
|
|
29402
|
+
printSyncSummary(summary, true);
|
|
28219
29403
|
} catch (err) {
|
|
28220
|
-
printError(
|
|
29404
|
+
printError(asMessage4(err));
|
|
28221
29405
|
process.exitCode = 1;
|
|
28222
29406
|
}
|
|
28223
29407
|
});
|
|
28224
|
-
|
|
28225
|
-
|
|
28226
|
-
|
|
28227
|
-
|
|
28228
|
-
|
|
28229
|
-
|
|
28230
|
-
|
|
28231
|
-
|
|
28232
|
-
|
|
28233
|
-
|
|
28234
|
-
|
|
28235
|
-
|
|
28236
|
-
|
|
28237
|
-
|
|
28238
|
-
|
|
28239
|
-
|
|
28240
|
-
}
|
|
28241
|
-
|
|
28242
|
-
if (conflicts.length > 0) {
|
|
28243
|
-
console.error(
|
|
28244
|
-
`
|
|
28245
|
-
${conflicts.length} port conflict(s). Stop the conflicting processes or update portMap in runbook "${name}".`
|
|
28246
|
-
);
|
|
28247
|
-
process.exitCode = 1;
|
|
28248
|
-
} else {
|
|
28249
|
-
console.log(pc26.green("\n\u2713 all ports free"));
|
|
29408
|
+
skills.command("list").description("List artifacts currently deployed to ~/.claude/").action(() => {
|
|
29409
|
+
const entries = listDeployed();
|
|
29410
|
+
if (entries.length === 0) {
|
|
29411
|
+
console.log(pc28.dim('0 deployed artifact(s). Run "olam skills sync" first.'));
|
|
29412
|
+
return;
|
|
29413
|
+
}
|
|
29414
|
+
const byBucket = /* @__PURE__ */ new Map();
|
|
29415
|
+
for (const e of entries) {
|
|
29416
|
+
const list = byBucket.get(e.bucket) ?? [];
|
|
29417
|
+
list.push(e);
|
|
29418
|
+
byBucket.set(e.bucket, list);
|
|
29419
|
+
}
|
|
29420
|
+
printHeader(`${entries.length} deployed artifact(s)`);
|
|
29421
|
+
for (const [bucket, list] of byBucket.entries()) {
|
|
29422
|
+
console.log(` ${pc28.bold(bucket)} (${list.length})`);
|
|
29423
|
+
for (const e of list) {
|
|
29424
|
+
const src = e.sourceId ? `[${e.sourceId}]` : pc28.yellow("[unknown]");
|
|
29425
|
+
console.log(` ${e.name.padEnd(40)} ${pc28.dim(src)} \u2192 ${pc28.dim(e.target)}`);
|
|
28250
29426
|
}
|
|
28251
|
-
}
|
|
28252
|
-
|
|
29427
|
+
}
|
|
29428
|
+
});
|
|
29429
|
+
skills.command("show").description("Show details for a deployed artifact (by name)").argument("<name>", "Artifact name (e.g. plan-hard, codex-second-opinion.md)").action((name) => {
|
|
29430
|
+
const entries = listDeployed();
|
|
29431
|
+
const matches2 = entries.filter((e) => e.name === name);
|
|
29432
|
+
if (matches2.length === 0) {
|
|
29433
|
+
printError(`no deployed artifact matches "${name}"`);
|
|
28253
29434
|
process.exitCode = 1;
|
|
29435
|
+
return;
|
|
29436
|
+
}
|
|
29437
|
+
for (const e of matches2) {
|
|
29438
|
+
console.log(`${pc28.bold("bucket:")} ${e.bucket}`);
|
|
29439
|
+
console.log(`${pc28.bold("name:")} ${e.name}`);
|
|
29440
|
+
console.log(`${pc28.bold("target:")} ${e.target}`);
|
|
29441
|
+
console.log(`${pc28.bold("source:")} ${e.sourceId ?? pc28.dim("(unknown \u2014 not from a registered olam source)")}`);
|
|
29442
|
+
console.log();
|
|
28254
29443
|
}
|
|
28255
29444
|
});
|
|
28256
29445
|
}
|
|
@@ -28329,17 +29518,17 @@ function registerWorldUpgrade(program2) {
|
|
|
28329
29518
|
|
|
28330
29519
|
// src/commands/mcp/serve.ts
|
|
28331
29520
|
init_output();
|
|
28332
|
-
import { existsSync as
|
|
28333
|
-
import { dirname as
|
|
29521
|
+
import { existsSync as existsSync58 } from "node:fs";
|
|
29522
|
+
import { dirname as dirname28, resolve as resolve13 } from "node:path";
|
|
28334
29523
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
28335
|
-
var here =
|
|
29524
|
+
var here = dirname28(fileURLToPath5(import.meta.url));
|
|
28336
29525
|
var BUNDLE_PATH_CANDIDATES = [
|
|
28337
29526
|
// bundled (`dist/index.js` after bundle-cli.mjs) — sibling
|
|
28338
29527
|
resolve13(here, "mcp-server.js"),
|
|
28339
29528
|
// dev / tsc-only (`dist/commands/mcp/serve.js`) — up two levels
|
|
28340
29529
|
resolve13(here, "..", "..", "mcp-server.js")
|
|
28341
29530
|
];
|
|
28342
|
-
function resolveBundlePath(candidates2 = BUNDLE_PATH_CANDIDATES, exists =
|
|
29531
|
+
function resolveBundlePath(candidates2 = BUNDLE_PATH_CANDIDATES, exists = existsSync58) {
|
|
28343
29532
|
return candidates2.find(exists) ?? null;
|
|
28344
29533
|
}
|
|
28345
29534
|
var MISSING_BUNDLE_REMEDY = "olam mcp server bundle missing. Searched: " + BUNDLE_PATH_CANDIDATES.join(", ") + ". For local dev, run: node packages/cli/scripts/bundle-mcp-server.mjs. A fresh `npm install -g @pleri/olam-cli@latest` should always include the bundle (see prepublishOnly in packages/cli/package.json); file an issue if it does not.";
|
|
@@ -28496,8 +29685,8 @@ var SECRET = process.env["OLAM_MCP_AUTH_SECRET"] ?? "";
|
|
|
28496
29685
|
function authHeaders() {
|
|
28497
29686
|
return SECRET ? { "X-Olam-Mcp-Secret": SECRET } : {};
|
|
28498
29687
|
}
|
|
28499
|
-
async function apiFetch(
|
|
28500
|
-
const res = await fetch(`${BASE_URL}${
|
|
29688
|
+
async function apiFetch(path66, init = {}) {
|
|
29689
|
+
const res = await fetch(`${BASE_URL}${path66}`, {
|
|
28501
29690
|
...init,
|
|
28502
29691
|
headers: {
|
|
28503
29692
|
"Content-Type": "application/json",
|
|
@@ -28649,7 +29838,7 @@ function registerMcpAdd(cmd) {
|
|
|
28649
29838
|
|
|
28650
29839
|
// src/commands/mcp/list.ts
|
|
28651
29840
|
init_output();
|
|
28652
|
-
import
|
|
29841
|
+
import pc29 from "picocolors";
|
|
28653
29842
|
function registerMcpList(cmd) {
|
|
28654
29843
|
cmd.command("list").description("List all registered MCP credentials").action(async () => {
|
|
28655
29844
|
const client = getMcpAuthClient();
|
|
@@ -28664,8 +29853,8 @@ function registerMcpList(cmd) {
|
|
|
28664
29853
|
}
|
|
28665
29854
|
const mcps = data.mcps ?? [];
|
|
28666
29855
|
if (mcps.length === 0) {
|
|
28667
|
-
console.log(
|
|
28668
|
-
console.log(
|
|
29856
|
+
console.log(pc29.dim("No MCP credentials registered."));
|
|
29857
|
+
console.log(pc29.dim("Add one with: olam mcp add <service>"));
|
|
28669
29858
|
return;
|
|
28670
29859
|
}
|
|
28671
29860
|
const [c0, c1, c2, c3] = [16, 20, 10, 24];
|
|
@@ -28676,12 +29865,12 @@ function registerMcpList(cmd) {
|
|
|
28676
29865
|
"ENV VAR".padEnd(c3),
|
|
28677
29866
|
"STATUS"
|
|
28678
29867
|
].join(" ");
|
|
28679
|
-
console.log(
|
|
28680
|
-
console.log(
|
|
29868
|
+
console.log(pc29.dim(header));
|
|
29869
|
+
console.log(pc29.dim("\u2500".repeat(header.length)));
|
|
28681
29870
|
for (const m of mcps) {
|
|
28682
|
-
const status2 = !m.validated ?
|
|
29871
|
+
const status2 = !m.validated ? pc29.yellow("unvalidated") : m.expired ? pc29.red("expired") : pc29.green("active");
|
|
28683
29872
|
const row = [
|
|
28684
|
-
|
|
29873
|
+
pc29.cyan(m.service.padEnd(c0)),
|
|
28685
29874
|
m.label.padEnd(c1).slice(0, c1),
|
|
28686
29875
|
m.type.padEnd(c2),
|
|
28687
29876
|
(m.envName ?? "\u2014").padEnd(c3).slice(0, c3),
|
|
@@ -28709,13 +29898,13 @@ function registerMcpRemove(cmd) {
|
|
|
28709
29898
|
|
|
28710
29899
|
// src/commands/mcp/status.ts
|
|
28711
29900
|
init_output();
|
|
28712
|
-
import
|
|
29901
|
+
import pc30 from "picocolors";
|
|
28713
29902
|
function formatExpiry(expiresAt) {
|
|
28714
29903
|
if (!expiresAt) return "\u2014";
|
|
28715
29904
|
const ms = expiresAt - Date.now();
|
|
28716
|
-
if (ms <= 0) return
|
|
29905
|
+
if (ms <= 0) return pc30.red("expired");
|
|
28717
29906
|
const hours = ms / (1e3 * 60 * 60);
|
|
28718
|
-
if (hours < 1) return
|
|
29907
|
+
if (hours < 1) return pc30.yellow(`${Math.ceil(ms / 6e4)}m`);
|
|
28719
29908
|
return `${hours.toFixed(1)}h`;
|
|
28720
29909
|
}
|
|
28721
29910
|
function registerMcpStatus(cmd) {
|
|
@@ -28732,14 +29921,14 @@ function registerMcpStatus(cmd) {
|
|
|
28732
29921
|
const mcps = data.mcps ?? [];
|
|
28733
29922
|
printHeader(`MCP Credentials (${mcps.length})`);
|
|
28734
29923
|
if (mcps.length === 0) {
|
|
28735
|
-
console.log(
|
|
29924
|
+
console.log(pc30.dim("No credentials registered."));
|
|
28736
29925
|
return;
|
|
28737
29926
|
}
|
|
28738
29927
|
for (const m of mcps) {
|
|
28739
|
-
const stateColor = !m.validated ?
|
|
29928
|
+
const stateColor = !m.validated ? pc30.yellow : m.expired ? pc30.red : pc30.green;
|
|
28740
29929
|
const stateLabel = !m.validated ? "unvalidated" : m.expired ? "expired" : "active";
|
|
28741
29930
|
console.log(`
|
|
28742
|
-
${
|
|
29931
|
+
${pc30.cyan(m.service)} ${stateColor(`[${stateLabel}]`)}`);
|
|
28743
29932
|
console.log(` label: ${m.label}`);
|
|
28744
29933
|
console.log(` type: ${m.type}`);
|
|
28745
29934
|
if (m.envName) console.log(` env var: ${m.envName}`);
|
|
@@ -28754,15 +29943,15 @@ function registerMcpStatus(cmd) {
|
|
|
28754
29943
|
// src/commands/mcp/import.ts
|
|
28755
29944
|
init_output();
|
|
28756
29945
|
import * as readline3 from "node:readline";
|
|
28757
|
-
import
|
|
29946
|
+
import pc31 from "picocolors";
|
|
28758
29947
|
|
|
28759
29948
|
// src/commands/mcp/import-discovery.ts
|
|
28760
|
-
import * as
|
|
28761
|
-
import * as
|
|
28762
|
-
import * as
|
|
29949
|
+
import * as fs53 from "node:fs";
|
|
29950
|
+
import * as os31 from "node:os";
|
|
29951
|
+
import * as path57 from "node:path";
|
|
28763
29952
|
function readJsonFile(filePath) {
|
|
28764
29953
|
try {
|
|
28765
|
-
const raw =
|
|
29954
|
+
const raw = fs53.readFileSync(filePath, "utf-8");
|
|
28766
29955
|
return JSON.parse(raw);
|
|
28767
29956
|
} catch {
|
|
28768
29957
|
return null;
|
|
@@ -28791,24 +29980,24 @@ function extractMcpServers(obj, source, sourceLabel) {
|
|
|
28791
29980
|
}
|
|
28792
29981
|
function getClaudeDesktopPath() {
|
|
28793
29982
|
if (process.platform === "darwin") {
|
|
28794
|
-
return
|
|
29983
|
+
return path57.join(os31.homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
28795
29984
|
}
|
|
28796
29985
|
if (process.platform === "win32") {
|
|
28797
|
-
const appData = process.env["APPDATA"] ??
|
|
28798
|
-
return
|
|
29986
|
+
const appData = process.env["APPDATA"] ?? path57.join(os31.homedir(), "AppData", "Roaming");
|
|
29987
|
+
return path57.join(appData, "Claude", "claude_desktop_config.json");
|
|
28799
29988
|
}
|
|
28800
|
-
return
|
|
29989
|
+
return path57.join(os31.homedir(), ".config", "Claude", "claude_desktop_config.json");
|
|
28801
29990
|
}
|
|
28802
29991
|
function getOlamRepoPaths() {
|
|
28803
29992
|
const configPaths = [
|
|
28804
|
-
|
|
28805
|
-
|
|
29993
|
+
path57.join(os31.homedir(), ".olam", "config.yaml"),
|
|
29994
|
+
path57.join(process.cwd(), ".olam", "config.yaml")
|
|
28806
29995
|
];
|
|
28807
29996
|
const paths = [];
|
|
28808
29997
|
for (const configPath of configPaths) {
|
|
28809
|
-
if (!
|
|
29998
|
+
if (!fs53.existsSync(configPath)) continue;
|
|
28810
29999
|
try {
|
|
28811
|
-
const raw =
|
|
30000
|
+
const raw = fs53.readFileSync(configPath, "utf-8");
|
|
28812
30001
|
const repoMatches = [...raw.matchAll(/path:\s*["']?([^\s"'\n]+)/g)];
|
|
28813
30002
|
for (const m of repoMatches) {
|
|
28814
30003
|
if (m[1]) paths.push(m[1]);
|
|
@@ -28824,7 +30013,7 @@ async function discoverMcpSources(repoPaths) {
|
|
|
28824
30013
|
const sources = [];
|
|
28825
30014
|
const sourceDefs = [
|
|
28826
30015
|
{
|
|
28827
|
-
path:
|
|
30016
|
+
path: path57.join(os31.homedir(), ".claude.json"),
|
|
28828
30017
|
label: "Claude Code (~/.claude.json)"
|
|
28829
30018
|
},
|
|
28830
30019
|
{
|
|
@@ -28832,19 +30021,19 @@ async function discoverMcpSources(repoPaths) {
|
|
|
28832
30021
|
label: "Claude Desktop"
|
|
28833
30022
|
},
|
|
28834
30023
|
{
|
|
28835
|
-
path:
|
|
30024
|
+
path: path57.join(os31.homedir(), ".cursor", "mcp.json"),
|
|
28836
30025
|
label: "Cursor (~/.cursor/mcp.json)"
|
|
28837
30026
|
},
|
|
28838
30027
|
{
|
|
28839
|
-
path:
|
|
30028
|
+
path: path57.join(os31.homedir(), ".codeium", "windsurf", "mcp_config.json"),
|
|
28840
30029
|
label: "Windsurf (~/.codeium/windsurf/mcp_config.json)"
|
|
28841
30030
|
}
|
|
28842
30031
|
];
|
|
28843
30032
|
const resolvedRepoPaths = repoPaths ?? getOlamRepoPaths();
|
|
28844
30033
|
for (const repoPath of resolvedRepoPaths) {
|
|
28845
30034
|
sourceDefs.push({
|
|
28846
|
-
path:
|
|
28847
|
-
label: `.mcp.json (${
|
|
30035
|
+
path: path57.join(repoPath, ".mcp.json"),
|
|
30036
|
+
label: `.mcp.json (${path57.basename(repoPath)})`
|
|
28848
30037
|
});
|
|
28849
30038
|
}
|
|
28850
30039
|
const reads = await Promise.all(
|
|
@@ -28924,13 +30113,13 @@ async function validateMcpEntry(entry) {
|
|
|
28924
30113
|
// src/commands/mcp/import.ts
|
|
28925
30114
|
async function multiSelectPicker(entries) {
|
|
28926
30115
|
if (entries.length === 0) return [];
|
|
28927
|
-
console.log("\n" +
|
|
30116
|
+
console.log("\n" + pc31.bold("Discovered MCP servers:"));
|
|
28928
30117
|
entries.forEach((e, i) => {
|
|
28929
30118
|
console.log(
|
|
28930
|
-
` ${
|
|
30119
|
+
` ${pc31.dim(`[${i + 1}]`)} ${pc31.cyan(e.name.padEnd(20))} ${pc31.dim(e.sourceLabel)}`
|
|
28931
30120
|
);
|
|
28932
30121
|
});
|
|
28933
|
-
console.log("\n" +
|
|
30122
|
+
console.log("\n" + pc31.dim('Enter numbers to import (e.g. 1,2,3 or "all" or Enter to skip):'));
|
|
28934
30123
|
const answer = await new Promise((resolve15) => {
|
|
28935
30124
|
const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
|
|
28936
30125
|
rl.question("> ", (ans) => {
|
|
@@ -28957,7 +30146,7 @@ function registerMcpImport(cmd) {
|
|
|
28957
30146
|
const repoPaths = opts.repoPaths ? opts.repoPaths.split(",").map((s) => s.trim()) : void 0;
|
|
28958
30147
|
const { entries, sources, durationMs } = await discoverMcpSources(repoPaths);
|
|
28959
30148
|
if (entries.length === 0) {
|
|
28960
|
-
console.log(
|
|
30149
|
+
console.log(pc31.dim("No MCP servers found in any source path."));
|
|
28961
30150
|
return;
|
|
28962
30151
|
}
|
|
28963
30152
|
printInfo("Sources", sources.length > 0 ? sources.join(", ") : "none");
|
|
@@ -28970,15 +30159,15 @@ function registerMcpImport(cmd) {
|
|
|
28970
30159
|
candidates2 = filtered;
|
|
28971
30160
|
}
|
|
28972
30161
|
if (skippedCount > 0) {
|
|
28973
|
-
console.log(
|
|
30162
|
+
console.log(pc31.dim(`skipped: ${skippedCount} already registered`));
|
|
28974
30163
|
}
|
|
28975
30164
|
if (candidates2.length === 0) {
|
|
28976
|
-
console.log(
|
|
30165
|
+
console.log(pc31.dim("Nothing new to import. Use --reimport to force."));
|
|
28977
30166
|
return;
|
|
28978
30167
|
}
|
|
28979
30168
|
const selected = await multiSelectPicker(candidates2);
|
|
28980
30169
|
if (selected.length === 0) {
|
|
28981
|
-
console.log(
|
|
30170
|
+
console.log(pc31.dim("No servers selected."));
|
|
28982
30171
|
return;
|
|
28983
30172
|
}
|
|
28984
30173
|
console.log(`
|
|
@@ -28989,16 +30178,16 @@ Importing ${selected.length} server(s)\u2026`);
|
|
|
28989
30178
|
let validated = false;
|
|
28990
30179
|
let validationReason = "skipped";
|
|
28991
30180
|
if (opts.validate !== false) {
|
|
28992
|
-
process.stdout.write(` ${
|
|
30181
|
+
process.stdout.write(` ${pc31.dim("\u2192")} ${entry.name} validating\u2026 `);
|
|
28993
30182
|
const vr = await validateMcpEntry(entry);
|
|
28994
30183
|
validated = vr.validated;
|
|
28995
30184
|
validationReason = vr.reason;
|
|
28996
30185
|
process.stdout.write(
|
|
28997
|
-
validated ?
|
|
30186
|
+
validated ? pc31.green("ok\n") : pc31.yellow(`unvalidated (${vr.reason})
|
|
28998
30187
|
`)
|
|
28999
30188
|
);
|
|
29000
30189
|
} else {
|
|
29001
|
-
console.log(` ${
|
|
30190
|
+
console.log(` ${pc31.dim("\u2192")} ${entry.name} ${pc31.dim("(validation skipped)")}`);
|
|
29002
30191
|
}
|
|
29003
30192
|
if (validated) validatedCount++;
|
|
29004
30193
|
const result = await client.staticAdd({
|
|
@@ -29013,21 +30202,21 @@ Importing ${selected.length} server(s)\u2026`);
|
|
|
29013
30202
|
}
|
|
29014
30203
|
}
|
|
29015
30204
|
console.log("");
|
|
29016
|
-
console.log(
|
|
30205
|
+
console.log(pc31.green(`\u2713 Imported ${importedCount}/${selected.length}`));
|
|
29017
30206
|
if (validatedCount > 0) {
|
|
29018
|
-
console.log(
|
|
30207
|
+
console.log(pc31.dim(` ${validatedCount} validated, ${importedCount - validatedCount} unvalidated`));
|
|
29019
30208
|
}
|
|
29020
30209
|
});
|
|
29021
30210
|
}
|
|
29022
30211
|
|
|
29023
30212
|
// src/commands/mcp/revoke.ts
|
|
29024
30213
|
init_output();
|
|
29025
|
-
import
|
|
30214
|
+
import pc32 from "picocolors";
|
|
29026
30215
|
function registerMcpRevoke(cmd) {
|
|
29027
30216
|
cmd.command("revoke <user> <world> <service>").description("Revoke a user's MCP service entitlement (multi-tenant mode only)").action(async (user, world, service) => {
|
|
29028
30217
|
const multiTenant = (process.env["OLAM_MCP_MULTI_TENANT"] ?? "").toLowerCase() === "true";
|
|
29029
30218
|
if (!multiTenant) {
|
|
29030
|
-
console.warn(
|
|
30219
|
+
console.warn(pc32.yellow("\u26A0 revocation only meaningful in multi_tenant mode \u2014 no-op"));
|
|
29031
30220
|
return;
|
|
29032
30221
|
}
|
|
29033
30222
|
const baseUrl = process.env["OLAM_MCP_AUTH_SERVICE_URL"] ?? "http://127.0.0.1:9998";
|
|
@@ -29056,8 +30245,8 @@ function registerMcpRevoke(cmd) {
|
|
|
29056
30245
|
process.exitCode = 1;
|
|
29057
30246
|
return;
|
|
29058
30247
|
}
|
|
29059
|
-
console.log(
|
|
29060
|
-
console.log(
|
|
30248
|
+
console.log(pc32.green(`\u2713 Revoked: ${user} / ${world} / ${service}`));
|
|
30249
|
+
console.log(pc32.dim(" Next fetch-creds call returns 403; token cleared on next merge."));
|
|
29061
30250
|
});
|
|
29062
30251
|
}
|
|
29063
30252
|
|
|
@@ -29079,34 +30268,34 @@ function registerMcp(program2) {
|
|
|
29079
30268
|
// src/commands/memory/start.ts
|
|
29080
30269
|
init_output();
|
|
29081
30270
|
import { spawn as spawn8 } from "node:child_process";
|
|
29082
|
-
import { existsSync as
|
|
29083
|
-
import { join as
|
|
30271
|
+
import { existsSync as existsSync60, mkdirSync as mkdirSync34, openSync as openSync3, readFileSync as readFileSync41, writeFileSync as writeFileSync25 } from "node:fs";
|
|
30272
|
+
import { join as join58 } from "node:path";
|
|
29084
30273
|
import { pathToFileURL } from "node:url";
|
|
29085
30274
|
|
|
29086
30275
|
// src/commands/memory/_paths.ts
|
|
29087
|
-
import { homedir as
|
|
29088
|
-
import { join as
|
|
30276
|
+
import { homedir as homedir34 } from "node:os";
|
|
30277
|
+
import { join as join57, dirname as dirname29 } from "node:path";
|
|
29089
30278
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
29090
|
-
var OLAM_HOME =
|
|
29091
|
-
var OLAM_BIN_DIR =
|
|
29092
|
-
var III_BINARY_PATH =
|
|
29093
|
-
var MEMORY_PID_PATH =
|
|
29094
|
-
var MEMORY_LOG_PATH =
|
|
29095
|
-
var MEMORY_DATA_DIR =
|
|
30279
|
+
var OLAM_HOME = join57(homedir34(), ".olam");
|
|
30280
|
+
var OLAM_BIN_DIR = join57(OLAM_HOME, "bin");
|
|
30281
|
+
var III_BINARY_PATH = join57(OLAM_BIN_DIR, "iii");
|
|
30282
|
+
var MEMORY_PID_PATH = join57(OLAM_HOME, "memory.pid");
|
|
30283
|
+
var MEMORY_LOG_PATH = join57(OLAM_HOME, "memory-service.log");
|
|
30284
|
+
var MEMORY_DATA_DIR = join57(OLAM_HOME, "memory-data");
|
|
29096
30285
|
var MEMORY_REST_PORT = 3111;
|
|
29097
30286
|
var MEMORY_LIVEZ_URL = `http://localhost:${MEMORY_REST_PORT}/agentmemory/livez`;
|
|
29098
|
-
var here2 =
|
|
30287
|
+
var here2 = dirname29(fileURLToPath6(import.meta.url));
|
|
29099
30288
|
var candidates = [
|
|
29100
30289
|
// 1. Workspace dev (built): packages/cli/dist/commands/memory/_paths.js → packages/cli → packages/memory-service
|
|
29101
|
-
|
|
30290
|
+
join57(here2, "..", "..", "..", "..", "memory-service"),
|
|
29102
30291
|
// 2a. Workspace bundled: packages/cli/dist/index.js → packages/cli → packages/memory-service
|
|
29103
|
-
|
|
30292
|
+
join57(here2, "..", "..", "memory-service"),
|
|
29104
30293
|
// 2b. Published tarball: <prefix>/node_modules/@pleri/olam-cli/dist/index.js
|
|
29105
30294
|
// → <prefix>/node_modules/@pleri/olam-cli/memory-service-bundle
|
|
29106
30295
|
// (copied at publish time by bundle-cli.mjs)
|
|
29107
|
-
|
|
30296
|
+
join57(here2, "..", "memory-service-bundle"),
|
|
29108
30297
|
// 3. CWD fallback
|
|
29109
|
-
|
|
30298
|
+
join57(process.cwd(), "packages", "memory-service")
|
|
29110
30299
|
];
|
|
29111
30300
|
var MEMORY_SERVICE_CANDIDATES = candidates;
|
|
29112
30301
|
|
|
@@ -29115,7 +30304,7 @@ var READINESS_TIMEOUT_MS = 3e4;
|
|
|
29115
30304
|
var READINESS_POLL_MS = 500;
|
|
29116
30305
|
function resolveMemoryServiceDir() {
|
|
29117
30306
|
for (const c of MEMORY_SERVICE_CANDIDATES) {
|
|
29118
|
-
if (
|
|
30307
|
+
if (existsSync60(c)) return c;
|
|
29119
30308
|
}
|
|
29120
30309
|
throw new Error(
|
|
29121
30310
|
`Could not find packages/memory-service/. Searched: ${MEMORY_SERVICE_CANDIDATES.join(", ")}. If running from a published @pleri/olam-cli tarball, this is a packaging bug \u2014 please file an issue.`
|
|
@@ -29123,12 +30312,12 @@ function resolveMemoryServiceDir() {
|
|
|
29123
30312
|
}
|
|
29124
30313
|
function resolveAgentMemoryBin(serviceDir) {
|
|
29125
30314
|
const candidates2 = [
|
|
29126
|
-
|
|
29127
|
-
|
|
29128
|
-
|
|
30315
|
+
join58(serviceDir, "node_modules", ".bin", "agentmemory"),
|
|
30316
|
+
join58(serviceDir, "..", "..", "node_modules", ".bin", "agentmemory"),
|
|
30317
|
+
join58(serviceDir, "..", "..", "..", "node_modules", ".bin", "agentmemory")
|
|
29129
30318
|
];
|
|
29130
30319
|
for (const c of candidates2) {
|
|
29131
|
-
if (
|
|
30320
|
+
if (existsSync60(c)) return c;
|
|
29132
30321
|
}
|
|
29133
30322
|
throw new Error(
|
|
29134
30323
|
`Could not find agentmemory bin. Searched: ${candidates2.join(", ")}. Run 'npm install' from the repo root.`
|
|
@@ -29143,8 +30332,8 @@ function isProcessAlive(pid) {
|
|
|
29143
30332
|
}
|
|
29144
30333
|
}
|
|
29145
30334
|
function readPidFromFile() {
|
|
29146
|
-
if (!
|
|
29147
|
-
const raw =
|
|
30335
|
+
if (!existsSync60(MEMORY_PID_PATH)) return null;
|
|
30336
|
+
const raw = readFileSync41(MEMORY_PID_PATH, "utf8").trim();
|
|
29148
30337
|
const pid = parseInt(raw, 10);
|
|
29149
30338
|
if (!Number.isFinite(pid) || pid <= 0) return null;
|
|
29150
30339
|
return pid;
|
|
@@ -29171,7 +30360,7 @@ async function waitForReady(secret) {
|
|
|
29171
30360
|
return false;
|
|
29172
30361
|
}
|
|
29173
30362
|
async function autoEnsureIiiBinary(serviceDir) {
|
|
29174
|
-
const helperPath =
|
|
30363
|
+
const helperPath = join58(serviceDir, "scripts", "ensure-iii-engine.mjs");
|
|
29175
30364
|
const mod = await import(pathToFileURL(helperPath).href);
|
|
29176
30365
|
const result = await mod.ensureIiiEngine();
|
|
29177
30366
|
if (!result.ok) {
|
|
@@ -29187,7 +30376,7 @@ async function runMemoryStart() {
|
|
|
29187
30376
|
printError(err instanceof Error ? err.message : String(err));
|
|
29188
30377
|
return 1;
|
|
29189
30378
|
}
|
|
29190
|
-
if (!
|
|
30379
|
+
if (!existsSync60(III_BINARY_PATH)) {
|
|
29191
30380
|
printInfo("iii binary", `missing at ${III_BINARY_PATH} \u2014 auto-fetching v0.11.2`);
|
|
29192
30381
|
try {
|
|
29193
30382
|
await autoEnsureIiiBinary(serviceDir);
|
|
@@ -29215,8 +30404,8 @@ async function runMemoryStart() {
|
|
|
29215
30404
|
);
|
|
29216
30405
|
return 1;
|
|
29217
30406
|
}
|
|
29218
|
-
|
|
29219
|
-
|
|
30407
|
+
mkdirSync34(OLAM_HOME, { recursive: true });
|
|
30408
|
+
mkdirSync34(MEMORY_DATA_DIR, { recursive: true });
|
|
29220
30409
|
const logFd = openSync3(MEMORY_LOG_PATH, "a");
|
|
29221
30410
|
const child = spawn8(agentmemoryBin, [], {
|
|
29222
30411
|
cwd: OLAM_HOME,
|
|
@@ -29239,7 +30428,7 @@ async function runMemoryStart() {
|
|
|
29239
30428
|
printError("spawn returned no pid (process failed to start)");
|
|
29240
30429
|
return 1;
|
|
29241
30430
|
}
|
|
29242
|
-
|
|
30431
|
+
writeFileSync25(MEMORY_PID_PATH, `${child.pid}
|
|
29243
30432
|
`, { mode: 420 });
|
|
29244
30433
|
printInfo("pid", `${child.pid}`);
|
|
29245
30434
|
printInfo("readiness", `polling ${MEMORY_LIVEZ_URL}`);
|
|
@@ -29262,7 +30451,7 @@ function registerMemoryStart(cmd) {
|
|
|
29262
30451
|
|
|
29263
30452
|
// src/commands/memory/stop.ts
|
|
29264
30453
|
init_output();
|
|
29265
|
-
import { existsSync as
|
|
30454
|
+
import { existsSync as existsSync61, readFileSync as readFileSync42, unlinkSync as unlinkSync11 } from "node:fs";
|
|
29266
30455
|
var SIGTERM_GRACE_MS = 1e4;
|
|
29267
30456
|
var POLL_MS = 250;
|
|
29268
30457
|
function isAlive(pid) {
|
|
@@ -29275,19 +30464,19 @@ function isAlive(pid) {
|
|
|
29275
30464
|
}
|
|
29276
30465
|
async function runMemoryStop() {
|
|
29277
30466
|
printHeader("olam memory stop");
|
|
29278
|
-
if (!
|
|
30467
|
+
if (!existsSync61(MEMORY_PID_PATH)) {
|
|
29279
30468
|
printSuccess("no pidfile present (nothing to stop)");
|
|
29280
30469
|
return 0;
|
|
29281
30470
|
}
|
|
29282
|
-
const pid = parseInt(
|
|
30471
|
+
const pid = parseInt(readFileSync42(MEMORY_PID_PATH, "utf8").trim(), 10);
|
|
29283
30472
|
if (!Number.isFinite(pid) || pid <= 0) {
|
|
29284
30473
|
printWarning(`pidfile contained invalid value; removing`);
|
|
29285
|
-
|
|
30474
|
+
unlinkSync11(MEMORY_PID_PATH);
|
|
29286
30475
|
return 0;
|
|
29287
30476
|
}
|
|
29288
30477
|
if (!isAlive(pid)) {
|
|
29289
30478
|
printSuccess(`pid ${pid} is not running (stale pidfile); cleaned up`);
|
|
29290
|
-
|
|
30479
|
+
unlinkSync11(MEMORY_PID_PATH);
|
|
29291
30480
|
return 0;
|
|
29292
30481
|
}
|
|
29293
30482
|
printInfo("pid", `${pid}`);
|
|
@@ -29310,8 +30499,8 @@ async function runMemoryStop() {
|
|
|
29310
30499
|
}
|
|
29311
30500
|
await new Promise((r) => setTimeout(r, 500));
|
|
29312
30501
|
}
|
|
29313
|
-
if (
|
|
29314
|
-
|
|
30502
|
+
if (existsSync61(MEMORY_PID_PATH)) {
|
|
30503
|
+
unlinkSync11(MEMORY_PID_PATH);
|
|
29315
30504
|
}
|
|
29316
30505
|
printSuccess(`stopped (pid ${pid})`);
|
|
29317
30506
|
return 0;
|
|
@@ -29325,7 +30514,7 @@ function registerMemoryStop(cmd) {
|
|
|
29325
30514
|
|
|
29326
30515
|
// src/commands/memory/status.ts
|
|
29327
30516
|
init_output();
|
|
29328
|
-
import { existsSync as
|
|
30517
|
+
import { existsSync as existsSync62, readFileSync as readFileSync43 } from "node:fs";
|
|
29329
30518
|
function isAlive2(pid) {
|
|
29330
30519
|
try {
|
|
29331
30520
|
process.kill(pid, 0);
|
|
@@ -29349,8 +30538,8 @@ async function probe(secret) {
|
|
|
29349
30538
|
}
|
|
29350
30539
|
async function collectMemoryStatus() {
|
|
29351
30540
|
let pid = null;
|
|
29352
|
-
if (
|
|
29353
|
-
const raw =
|
|
30541
|
+
if (existsSync62(MEMORY_PID_PATH)) {
|
|
30542
|
+
const raw = readFileSync43(MEMORY_PID_PATH, "utf8").trim();
|
|
29354
30543
|
const parsed = parseInt(raw, 10);
|
|
29355
30544
|
pid = Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
|
29356
30545
|
}
|
|
@@ -29362,7 +30551,7 @@ async function collectMemoryStatus() {
|
|
|
29362
30551
|
alive,
|
|
29363
30552
|
livez,
|
|
29364
30553
|
secretSet: hasMemorySecret(),
|
|
29365
|
-
iiiBinary:
|
|
30554
|
+
iiiBinary: existsSync62(III_BINARY_PATH) ? III_BINARY_PATH : null,
|
|
29366
30555
|
port: MEMORY_REST_PORT
|
|
29367
30556
|
};
|
|
29368
30557
|
}
|
|
@@ -29405,10 +30594,10 @@ function registerMemoryStatus(cmd) {
|
|
|
29405
30594
|
|
|
29406
30595
|
// src/commands/memory/logs.ts
|
|
29407
30596
|
init_output();
|
|
29408
|
-
import { existsSync as
|
|
30597
|
+
import { existsSync as existsSync63 } from "node:fs";
|
|
29409
30598
|
import { spawn as spawn9 } from "node:child_process";
|
|
29410
30599
|
async function runMemoryLogs(opts) {
|
|
29411
|
-
if (!
|
|
30600
|
+
if (!existsSync63(MEMORY_LOG_PATH)) {
|
|
29412
30601
|
printWarning(`no log at ${MEMORY_LOG_PATH} (start the service first via 'olam memory start')`);
|
|
29413
30602
|
return 1;
|
|
29414
30603
|
}
|
|
@@ -29434,7 +30623,7 @@ function registerMemoryLogs(cmd) {
|
|
|
29434
30623
|
}
|
|
29435
30624
|
|
|
29436
30625
|
// src/commands/memory/secret.ts
|
|
29437
|
-
import { existsSync as
|
|
30626
|
+
import { existsSync as existsSync64 } from "node:fs";
|
|
29438
30627
|
init_output();
|
|
29439
30628
|
async function runMemorySecretShow() {
|
|
29440
30629
|
if (!hasMemorySecret()) {
|
|
@@ -29449,7 +30638,7 @@ async function runMemorySecretShow() {
|
|
|
29449
30638
|
}
|
|
29450
30639
|
async function runMemorySecretRotate() {
|
|
29451
30640
|
printHeader("olam memory secret rotate");
|
|
29452
|
-
const wasRunning =
|
|
30641
|
+
const wasRunning = existsSync64(MEMORY_PID_PATH);
|
|
29453
30642
|
if (wasRunning) {
|
|
29454
30643
|
printInfo("current state", "service running; will restart with new secret");
|
|
29455
30644
|
const stopRc = await runMemoryStop();
|
|
@@ -29631,14 +30820,14 @@ function registerMemoryUninstall(cmd) {
|
|
|
29631
30820
|
// src/commands/memory/mode.ts
|
|
29632
30821
|
init_schema2();
|
|
29633
30822
|
init_output();
|
|
29634
|
-
import { existsSync as
|
|
29635
|
-
import { join as
|
|
30823
|
+
import { existsSync as existsSync65, readFileSync as readFileSync44, writeFileSync as writeFileSync26 } from "node:fs";
|
|
30824
|
+
import { join as join59 } from "node:path";
|
|
29636
30825
|
import * as readline4 from "node:readline/promises";
|
|
29637
30826
|
import { parse as parseYaml6, stringify as stringifyYaml6 } from "yaml";
|
|
29638
30827
|
var CONFIG_REL = ".olam/config.yaml";
|
|
29639
30828
|
function locateConfig(cwd) {
|
|
29640
|
-
const absPath =
|
|
29641
|
-
if (!
|
|
30829
|
+
const absPath = join59(cwd, CONFIG_REL);
|
|
30830
|
+
if (!existsSync65(absPath)) {
|
|
29642
30831
|
throw new Error(
|
|
29643
30832
|
`No ${CONFIG_REL} at ${cwd}. Run \`olam init\` in your workspace root first.`
|
|
29644
30833
|
);
|
|
@@ -29646,7 +30835,7 @@ function locateConfig(cwd) {
|
|
|
29646
30835
|
return { absPath };
|
|
29647
30836
|
}
|
|
29648
30837
|
function readConfigYaml(absPath) {
|
|
29649
|
-
const raw =
|
|
30838
|
+
const raw = readFileSync44(absPath, "utf-8");
|
|
29650
30839
|
const parsed = parseYaml6(raw) ?? {};
|
|
29651
30840
|
if (typeof parsed !== "object" || parsed === null) {
|
|
29652
30841
|
throw new Error(`${absPath} is not a YAML object`);
|
|
@@ -29655,7 +30844,7 @@ function readConfigYaml(absPath) {
|
|
|
29655
30844
|
}
|
|
29656
30845
|
function writeConfigYaml(absPath, parsed) {
|
|
29657
30846
|
const out = stringifyYaml6(parsed, { aliasDuplicateObjects: false });
|
|
29658
|
-
|
|
30847
|
+
writeFileSync26(absPath, out, "utf-8");
|
|
29659
30848
|
}
|
|
29660
30849
|
async function defaultPromptText(question) {
|
|
29661
30850
|
const rl = readline4.createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -29771,10 +30960,371 @@ function registerMemoryMode(cmd) {
|
|
|
29771
30960
|
});
|
|
29772
30961
|
}
|
|
29773
30962
|
|
|
30963
|
+
// src/commands/memory/bridge.ts
|
|
30964
|
+
init_output();
|
|
30965
|
+
import { spawn as spawn10 } from "node:child_process";
|
|
30966
|
+
import { existsSync as existsSync66 } from "node:fs";
|
|
30967
|
+
import { join as join60 } from "node:path";
|
|
30968
|
+
var DEFAULT_PORT2 = 8788;
|
|
30969
|
+
function resolveMemoryServiceDir2() {
|
|
30970
|
+
for (const c of MEMORY_SERVICE_CANDIDATES) {
|
|
30971
|
+
if (existsSync66(c)) return c;
|
|
30972
|
+
}
|
|
30973
|
+
throw new Error(
|
|
30974
|
+
`Could not find packages/memory-service/. Searched: ${MEMORY_SERVICE_CANDIDATES.join(", ")}. If running from a published @pleri/olam-cli tarball, this is a packaging bug \u2014 please file an issue.`
|
|
30975
|
+
);
|
|
30976
|
+
}
|
|
30977
|
+
function resolveLocalBridgeScript(serviceDir) {
|
|
30978
|
+
const path66 = join60(serviceDir, "scripts", "local-bridge-server.mjs");
|
|
30979
|
+
if (!existsSync66(path66)) {
|
|
30980
|
+
throw new Error(
|
|
30981
|
+
`Could not find local-bridge-server.mjs at ${path66}. Verify packages/memory-service ships the scripts/ directory.`
|
|
30982
|
+
);
|
|
30983
|
+
}
|
|
30984
|
+
return path66;
|
|
30985
|
+
}
|
|
30986
|
+
function validateBridgeOpts(opts) {
|
|
30987
|
+
const local = opts.local ?? false;
|
|
30988
|
+
if (!local) {
|
|
30989
|
+
throw new Error(
|
|
30990
|
+
`'olam memory bridge serve' currently requires --local. Remote-bridge orchestration is reserved for future flags.`
|
|
30991
|
+
);
|
|
30992
|
+
}
|
|
30993
|
+
const port2 = opts.port ?? DEFAULT_PORT2;
|
|
30994
|
+
if (!Number.isInteger(port2) || port2 < 0 || port2 > 65535) {
|
|
30995
|
+
throw new Error(`--port must be an integer 0..65535 (got: ${opts.port})`);
|
|
30996
|
+
}
|
|
30997
|
+
const result = {
|
|
30998
|
+
port: port2,
|
|
30999
|
+
local
|
|
31000
|
+
};
|
|
31001
|
+
if (typeof opts.persistTo === "string" && opts.persistTo.length > 0) {
|
|
31002
|
+
result.persistTo = opts.persistTo;
|
|
31003
|
+
}
|
|
31004
|
+
return result;
|
|
31005
|
+
}
|
|
31006
|
+
async function runBridgeServe(opts, deps = {}) {
|
|
31007
|
+
printHeader("olam memory bridge serve --local");
|
|
31008
|
+
const validated = validateBridgeOpts(opts);
|
|
31009
|
+
printInfo("port", String(validated.port));
|
|
31010
|
+
if (validated.persistTo) {
|
|
31011
|
+
printInfo("persist-to", validated.persistTo);
|
|
31012
|
+
} else {
|
|
31013
|
+
printInfo("persist-to", "(in-memory \u2014 DO state lost on restart)");
|
|
31014
|
+
}
|
|
31015
|
+
const serviceDir = (deps.resolveServiceDir ?? resolveMemoryServiceDir2)();
|
|
31016
|
+
const scriptPath = resolveLocalBridgeScript(serviceDir);
|
|
31017
|
+
printInfo("entry", scriptPath);
|
|
31018
|
+
const secret = (deps.ensureSecret ?? ensureMemorySecret)();
|
|
31019
|
+
const args = ["--port", String(validated.port)];
|
|
31020
|
+
if (validated.persistTo) {
|
|
31021
|
+
args.push("--persist-to", validated.persistTo);
|
|
31022
|
+
}
|
|
31023
|
+
const env = {
|
|
31024
|
+
...process.env,
|
|
31025
|
+
AGENTMEMORY_SECRET: secret
|
|
31026
|
+
};
|
|
31027
|
+
const spawner = deps.spawnChild ?? ((command, spawnArgs, spawnEnv) => spawn10(command, [...spawnArgs], {
|
|
31028
|
+
env: spawnEnv,
|
|
31029
|
+
stdio: "inherit"
|
|
31030
|
+
}));
|
|
31031
|
+
const child = spawner(process.execPath, [scriptPath, ...args], env);
|
|
31032
|
+
return new Promise((resolve15) => {
|
|
31033
|
+
let resolved = false;
|
|
31034
|
+
const finish = (code) => {
|
|
31035
|
+
if (resolved) return;
|
|
31036
|
+
resolved = true;
|
|
31037
|
+
resolve15(code);
|
|
31038
|
+
};
|
|
31039
|
+
const forward = (signal) => () => {
|
|
31040
|
+
if (!child.killed) {
|
|
31041
|
+
try {
|
|
31042
|
+
child.kill(signal);
|
|
31043
|
+
} catch {
|
|
31044
|
+
}
|
|
31045
|
+
}
|
|
31046
|
+
};
|
|
31047
|
+
process.on("SIGTERM", forward("SIGTERM"));
|
|
31048
|
+
process.on("SIGINT", forward("SIGINT"));
|
|
31049
|
+
child.on("error", (err) => {
|
|
31050
|
+
printError(`failed to spawn local-bridge-server.mjs: ${err.message}`);
|
|
31051
|
+
finish(1);
|
|
31052
|
+
});
|
|
31053
|
+
child.on("exit", (code, signal) => {
|
|
31054
|
+
if (signal) {
|
|
31055
|
+
finish(0);
|
|
31056
|
+
} else {
|
|
31057
|
+
finish(code ?? 1);
|
|
31058
|
+
}
|
|
31059
|
+
});
|
|
31060
|
+
});
|
|
31061
|
+
}
|
|
31062
|
+
function registerMemoryBridge(cmd) {
|
|
31063
|
+
const bridge = cmd.command("bridge").description("Local bridge service (Miniflare-embedded) for the agent-memory engine");
|
|
31064
|
+
bridge.command("serve").description(
|
|
31065
|
+
"Run the agent-memory bridge as a foreground Node process (suitable for systemd/launchd)"
|
|
31066
|
+
).option("--local", "Use the embedded Miniflare runtime (currently required)").option("--port <n>", "TCP port to bind on", (v) => parseInt(v, 10), DEFAULT_PORT2).option("--persist-to <path>", "DO SQLite persistence directory").action(async (opts) => {
|
|
31067
|
+
try {
|
|
31068
|
+
const rc = await runBridgeServe(opts);
|
|
31069
|
+
if (rc !== 0) process.exitCode = rc;
|
|
31070
|
+
} catch (err) {
|
|
31071
|
+
printError(err instanceof Error ? err.message : String(err));
|
|
31072
|
+
process.exitCode = 1;
|
|
31073
|
+
}
|
|
31074
|
+
});
|
|
31075
|
+
}
|
|
31076
|
+
|
|
31077
|
+
// src/commands/memory/reclassify.ts
|
|
31078
|
+
init_output();
|
|
31079
|
+
var DEFAULT_BRIDGE_URL = "https://olam-agent-memory.ernestcodes.workers.dev";
|
|
31080
|
+
var RECLASSIFY_TIMEOUT_MS = 1e4;
|
|
31081
|
+
function validateReclassifyOptions(opts) {
|
|
31082
|
+
const hasHash = typeof opts.contentHash === "string" && opts.contentHash.length > 0;
|
|
31083
|
+
const hasVersion = typeof opts.sinceVersion === "string" && opts.sinceVersion.length > 0;
|
|
31084
|
+
if (!hasHash && !hasVersion) {
|
|
31085
|
+
return "must specify one of --content-hash or --since-version";
|
|
31086
|
+
}
|
|
31087
|
+
if (hasHash && hasVersion) {
|
|
31088
|
+
return "--content-hash and --since-version are mutually exclusive";
|
|
31089
|
+
}
|
|
31090
|
+
if (hasHash && !/^sha256:[0-9a-f]{64}$/.test(opts.contentHash)) {
|
|
31091
|
+
return "--content-hash must be sha256:<64hex>";
|
|
31092
|
+
}
|
|
31093
|
+
return null;
|
|
31094
|
+
}
|
|
31095
|
+
function resolveBridgeUrl(opts) {
|
|
31096
|
+
return opts.bridgeUrl ?? process.env["AGENTMEMORY_BRIDGE_URL"] ?? DEFAULT_BRIDGE_URL;
|
|
31097
|
+
}
|
|
31098
|
+
function resolveBearer(opts) {
|
|
31099
|
+
const fromOpt = opts.bearer;
|
|
31100
|
+
if (fromOpt && fromOpt.length > 0) return fromOpt;
|
|
31101
|
+
const fromEnv = process.env["AGENTMEMORY_BRIDGE_SECRET"];
|
|
31102
|
+
if (fromEnv && fromEnv.length > 0) return fromEnv;
|
|
31103
|
+
return null;
|
|
31104
|
+
}
|
|
31105
|
+
async function runReclassify(opts, fetchImpl = fetch) {
|
|
31106
|
+
const validationError = validateReclassifyOptions(opts);
|
|
31107
|
+
if (validationError) {
|
|
31108
|
+
return { ok: false, busted: 0, mode: opts.contentHash ? "hash" : "version", error: validationError };
|
|
31109
|
+
}
|
|
31110
|
+
if (opts.dryRun) {
|
|
31111
|
+
return {
|
|
31112
|
+
ok: true,
|
|
31113
|
+
busted: 0,
|
|
31114
|
+
mode: "dry-run"
|
|
31115
|
+
};
|
|
31116
|
+
}
|
|
31117
|
+
const bridgeUrl = resolveBridgeUrl(opts);
|
|
31118
|
+
const bearer = resolveBearer(opts);
|
|
31119
|
+
if (!bearer) {
|
|
31120
|
+
return {
|
|
31121
|
+
ok: false,
|
|
31122
|
+
busted: 0,
|
|
31123
|
+
mode: opts.contentHash ? "hash" : "version",
|
|
31124
|
+
error: "AGENTMEMORY_BRIDGE_SECRET not set (or --bearer not passed)"
|
|
31125
|
+
};
|
|
31126
|
+
}
|
|
31127
|
+
const body = opts.contentHash ? { content_hash: opts.contentHash } : { since_version: opts.sinceVersion };
|
|
31128
|
+
try {
|
|
31129
|
+
const res = await fetchImpl(`${bridgeUrl}/classify/cache-bust`, {
|
|
31130
|
+
method: "POST",
|
|
31131
|
+
headers: {
|
|
31132
|
+
authorization: `Bearer ${bearer}`,
|
|
31133
|
+
"content-type": "application/json"
|
|
31134
|
+
},
|
|
31135
|
+
body: JSON.stringify(body),
|
|
31136
|
+
signal: AbortSignal.timeout(RECLASSIFY_TIMEOUT_MS)
|
|
31137
|
+
});
|
|
31138
|
+
if (!res.ok) {
|
|
31139
|
+
const detail = await res.text().catch(() => "");
|
|
31140
|
+
return {
|
|
31141
|
+
ok: false,
|
|
31142
|
+
busted: 0,
|
|
31143
|
+
mode: opts.contentHash ? "hash" : "version",
|
|
31144
|
+
error: `bridge returned ${res.status} ${res.statusText}${detail ? `: ${detail.slice(0, 200)}` : ""}`
|
|
31145
|
+
};
|
|
31146
|
+
}
|
|
31147
|
+
const payload = await res.json();
|
|
31148
|
+
return {
|
|
31149
|
+
ok: true,
|
|
31150
|
+
busted: payload.busted,
|
|
31151
|
+
mode: payload.mode,
|
|
31152
|
+
memoryId: payload.memoryId
|
|
31153
|
+
};
|
|
31154
|
+
} catch (err) {
|
|
31155
|
+
return {
|
|
31156
|
+
ok: false,
|
|
31157
|
+
busted: 0,
|
|
31158
|
+
mode: opts.contentHash ? "hash" : "version",
|
|
31159
|
+
error: err instanceof Error ? err.message : String(err)
|
|
31160
|
+
};
|
|
31161
|
+
}
|
|
31162
|
+
}
|
|
31163
|
+
async function runReclassifyCli(opts) {
|
|
31164
|
+
const result = await runReclassify(opts);
|
|
31165
|
+
if (opts.json) {
|
|
31166
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
31167
|
+
return result.ok ? 0 : 1;
|
|
31168
|
+
}
|
|
31169
|
+
printHeader("olam memory reclassify");
|
|
31170
|
+
if (opts.contentHash) {
|
|
31171
|
+
printInfo("content-hash", opts.contentHash);
|
|
31172
|
+
}
|
|
31173
|
+
if (opts.sinceVersion) {
|
|
31174
|
+
printInfo("since-version", opts.sinceVersion);
|
|
31175
|
+
}
|
|
31176
|
+
printInfo("mode", result.mode);
|
|
31177
|
+
if (opts.dryRun) {
|
|
31178
|
+
printInfo("dry-run", "no HTTP call made");
|
|
31179
|
+
return 0;
|
|
31180
|
+
}
|
|
31181
|
+
if (!result.ok) {
|
|
31182
|
+
printError(`reclassify failed: ${result.error ?? "(unknown error)"}`);
|
|
31183
|
+
return 1;
|
|
31184
|
+
}
|
|
31185
|
+
if (result.busted === 0) {
|
|
31186
|
+
printWarning(`no rows busted (hash not in cache OR no rows matched the version)`);
|
|
31187
|
+
} else {
|
|
31188
|
+
printInfo("busted", `${result.busted}`);
|
|
31189
|
+
if (result.memoryId) {
|
|
31190
|
+
printInfo("memory-id", result.memoryId);
|
|
31191
|
+
}
|
|
31192
|
+
}
|
|
31193
|
+
return 0;
|
|
31194
|
+
}
|
|
31195
|
+
function registerMemoryReclassify(cmd) {
|
|
31196
|
+
cmd.command("reclassify").description(
|
|
31197
|
+
"Bust the bridge classifier cache (single entry by content hash OR bulk by classifier_version) so the next batch re-runs the LLM"
|
|
31198
|
+
).option("--content-hash <hash>", "Bust one entry by sha256:<64hex> content hash").option("--since-version <version>", 'Bulk bust by classifier_version (e.g. "haiku-v1+prompt-v1")').option("--dry-run", "Show what would be busted without calling the bridge", false).option("--bridge-url <url>", "Override AGENTMEMORY_BRIDGE_URL").option("--bearer <token>", "Override AGENTMEMORY_BRIDGE_SECRET").option("--json", "Machine-readable JSON output", false).action(async (opts) => {
|
|
31199
|
+
const rc = await runReclassifyCli(opts);
|
|
31200
|
+
if (rc !== 0) process.exitCode = rc;
|
|
31201
|
+
});
|
|
31202
|
+
}
|
|
31203
|
+
|
|
31204
|
+
// src/commands/memory/stats.ts
|
|
31205
|
+
init_output();
|
|
31206
|
+
var DEFAULT_BRIDGE_URL2 = "https://olam-agent-memory.ernestcodes.workers.dev";
|
|
31207
|
+
var STATS_TIMEOUT_MS = 1e4;
|
|
31208
|
+
function computeCacheErrorRate(counters) {
|
|
31209
|
+
if (counters.writes_observed <= 0) return 0;
|
|
31210
|
+
const busted = counters.cache_busted_hash + counters.cache_busted_version;
|
|
31211
|
+
return busted / counters.writes_observed;
|
|
31212
|
+
}
|
|
31213
|
+
function resolveBridgeUrl2(opts) {
|
|
31214
|
+
return opts.bridgeUrl ?? process.env["AGENTMEMORY_BRIDGE_URL"] ?? DEFAULT_BRIDGE_URL2;
|
|
31215
|
+
}
|
|
31216
|
+
function resolveBearer2(opts) {
|
|
31217
|
+
const fromOpt = opts.bearer;
|
|
31218
|
+
if (fromOpt && fromOpt.length > 0) return fromOpt;
|
|
31219
|
+
const fromEnv = process.env["AGENTMEMORY_BRIDGE_SECRET"];
|
|
31220
|
+
if (fromEnv && fromEnv.length > 0) return fromEnv;
|
|
31221
|
+
return null;
|
|
31222
|
+
}
|
|
31223
|
+
function intOrZero(v) {
|
|
31224
|
+
if (typeof v !== "number" || !Number.isFinite(v)) return 0;
|
|
31225
|
+
return Math.trunc(v);
|
|
31226
|
+
}
|
|
31227
|
+
function extractCounters(stats) {
|
|
31228
|
+
return {
|
|
31229
|
+
writes_observed: intOrZero(stats.classifier_writes_observed),
|
|
31230
|
+
writes_deduped_hash: intOrZero(stats.classifier_writes_deduped_hash),
|
|
31231
|
+
writes_deduped_semantic: intOrZero(stats.classifier_writes_deduped_semantic),
|
|
31232
|
+
recalls_observed: intOrZero(stats.classifier_recalls_observed),
|
|
31233
|
+
cache_busted_hash: intOrZero(stats.classifier_cache_busted_hash),
|
|
31234
|
+
cache_busted_version: intOrZero(stats.classifier_cache_busted_version)
|
|
31235
|
+
};
|
|
31236
|
+
}
|
|
31237
|
+
async function runStats(opts, fetchImpl = fetch) {
|
|
31238
|
+
const bridgeUrl = resolveBridgeUrl2(opts);
|
|
31239
|
+
const bearer = resolveBearer2(opts);
|
|
31240
|
+
if (!bearer) {
|
|
31241
|
+
return {
|
|
31242
|
+
ok: false,
|
|
31243
|
+
error: "AGENTMEMORY_BRIDGE_SECRET not set (or --bearer not passed)"
|
|
31244
|
+
};
|
|
31245
|
+
}
|
|
31246
|
+
try {
|
|
31247
|
+
const res = await fetchImpl(`${bridgeUrl}/bridge/stats`, {
|
|
31248
|
+
method: "GET",
|
|
31249
|
+
headers: {
|
|
31250
|
+
authorization: `Bearer ${bearer}`
|
|
31251
|
+
},
|
|
31252
|
+
signal: AbortSignal.timeout(STATS_TIMEOUT_MS)
|
|
31253
|
+
});
|
|
31254
|
+
if (!res.ok) {
|
|
31255
|
+
const detail = await res.text().catch(() => "");
|
|
31256
|
+
return {
|
|
31257
|
+
ok: false,
|
|
31258
|
+
error: `bridge returned ${res.status} ${res.statusText}${detail ? `: ${detail.slice(0, 200)}` : ""}`
|
|
31259
|
+
};
|
|
31260
|
+
}
|
|
31261
|
+
const payload = await res.json();
|
|
31262
|
+
if (!payload || typeof payload !== "object" || !payload.stats || typeof payload.stats !== "object") {
|
|
31263
|
+
return {
|
|
31264
|
+
ok: false,
|
|
31265
|
+
error: "bridge response missing stats envelope"
|
|
31266
|
+
};
|
|
31267
|
+
}
|
|
31268
|
+
const counters = extractCounters(payload.stats);
|
|
31269
|
+
const cacheErrorRate = computeCacheErrorRate(counters);
|
|
31270
|
+
return {
|
|
31271
|
+
ok: true,
|
|
31272
|
+
counters,
|
|
31273
|
+
cacheErrorRate,
|
|
31274
|
+
raw: payload
|
|
31275
|
+
};
|
|
31276
|
+
} catch (err) {
|
|
31277
|
+
return {
|
|
31278
|
+
ok: false,
|
|
31279
|
+
error: err instanceof Error ? err.message : String(err)
|
|
31280
|
+
};
|
|
31281
|
+
}
|
|
31282
|
+
}
|
|
31283
|
+
function formatPct(numerator, denominator) {
|
|
31284
|
+
if (denominator <= 0) return "0.0%";
|
|
31285
|
+
return `${(numerator / denominator * 100).toFixed(1)}%`;
|
|
31286
|
+
}
|
|
31287
|
+
async function runStatsCli(opts) {
|
|
31288
|
+
const result = await runStats(opts);
|
|
31289
|
+
if (opts.json) {
|
|
31290
|
+
const out = result.ok && result.raw !== void 0 ? result.raw : result;
|
|
31291
|
+
process.stdout.write(JSON.stringify(out, null, 2) + "\n");
|
|
31292
|
+
return result.ok ? 0 : 1;
|
|
31293
|
+
}
|
|
31294
|
+
printHeader("olam memory stats \u2014 bridge counters");
|
|
31295
|
+
if (!result.ok) {
|
|
31296
|
+
printError(`stats failed: ${result.error ?? "(unknown error)"}`);
|
|
31297
|
+
return 1;
|
|
31298
|
+
}
|
|
31299
|
+
const c = result.counters;
|
|
31300
|
+
printInfo("writes_observed", String(c.writes_observed));
|
|
31301
|
+
printInfo(
|
|
31302
|
+
"writes_deduped_hash",
|
|
31303
|
+
`${c.writes_deduped_hash} (${formatPct(c.writes_deduped_hash, c.writes_observed)})`
|
|
31304
|
+
);
|
|
31305
|
+
printInfo(
|
|
31306
|
+
"writes_deduped_semantic",
|
|
31307
|
+
`${c.writes_deduped_semantic} (${formatPct(c.writes_deduped_semantic, c.writes_observed)})`
|
|
31308
|
+
);
|
|
31309
|
+
printInfo("recalls_observed", String(c.recalls_observed));
|
|
31310
|
+
printInfo("cache_busted_hash", String(c.cache_busted_hash));
|
|
31311
|
+
printInfo("cache_busted_version", String(c.cache_busted_version));
|
|
31312
|
+
printInfo("cache_error_rate", `${(result.cacheErrorRate * 100).toFixed(1)}%`);
|
|
31313
|
+
return 0;
|
|
31314
|
+
}
|
|
31315
|
+
function registerMemoryStats(cmd) {
|
|
31316
|
+
cmd.command("stats").description(
|
|
31317
|
+
"Fetch /bridge/stats from the agent-memory bridge and format classifier counters (writes_observed, dedup rates, recalls, cache busts, derived cache_error_rate)"
|
|
31318
|
+
).option("--bridge-url <url>", "Override AGENTMEMORY_BRIDGE_URL").option("--bearer <token>", "Override AGENTMEMORY_BRIDGE_SECRET").option("--json", "Machine-readable JSON output (raw bridge envelope)", false).action(async (opts) => {
|
|
31319
|
+
const rc = await runStatsCli(opts);
|
|
31320
|
+
if (rc !== 0) process.exitCode = rc;
|
|
31321
|
+
});
|
|
31322
|
+
}
|
|
31323
|
+
|
|
29774
31324
|
// src/commands/memory/index.ts
|
|
29775
31325
|
function registerMemory(program2) {
|
|
29776
31326
|
const memory = program2.command("memory").description(
|
|
29777
|
-
"Host-process agent-memory service for the olam fleet (start, stop, status, logs, secret, install, uninstall)"
|
|
31327
|
+
"Host-process agent-memory service for the olam fleet (start, stop, status, logs, secret, install, uninstall, bridge, reclassify, stats)"
|
|
29778
31328
|
);
|
|
29779
31329
|
registerMemoryStart(memory);
|
|
29780
31330
|
registerMemoryStop(memory);
|
|
@@ -29784,14 +31334,17 @@ function registerMemory(program2) {
|
|
|
29784
31334
|
registerMemoryInstall(memory);
|
|
29785
31335
|
registerMemoryUninstall(memory);
|
|
29786
31336
|
registerMemoryMode(memory);
|
|
31337
|
+
registerMemoryBridge(memory);
|
|
31338
|
+
registerMemoryReclassify(memory);
|
|
31339
|
+
registerMemoryStats(memory);
|
|
29787
31340
|
}
|
|
29788
31341
|
|
|
29789
31342
|
// src/commands/kg-build.ts
|
|
29790
31343
|
init_storage_paths();
|
|
29791
31344
|
init_workspace_name();
|
|
29792
|
-
import * as
|
|
29793
|
-
import * as
|
|
29794
|
-
import * as
|
|
31345
|
+
import * as fs59 from "node:fs";
|
|
31346
|
+
import * as os34 from "node:os";
|
|
31347
|
+
import * as path63 from "node:path";
|
|
29795
31348
|
|
|
29796
31349
|
// ../core/dist/kg/kg-service-client.js
|
|
29797
31350
|
var KG_SERVICE_PORT_DEFAULT = 9997;
|
|
@@ -29802,8 +31355,8 @@ function port() {
|
|
|
29802
31355
|
const n = Number.parseInt(env, 10);
|
|
29803
31356
|
return Number.isFinite(n) && n > 0 ? n : KG_SERVICE_PORT_DEFAULT;
|
|
29804
31357
|
}
|
|
29805
|
-
function url(
|
|
29806
|
-
return `http://127.0.0.1:${port()}${
|
|
31358
|
+
function url(path66) {
|
|
31359
|
+
return `http://127.0.0.1:${port()}${path66}`;
|
|
29807
31360
|
}
|
|
29808
31361
|
function kgServiceHealthUrl() {
|
|
29809
31362
|
return url("/health");
|
|
@@ -29881,40 +31434,40 @@ init_output();
|
|
|
29881
31434
|
// src/commands/kg-status.ts
|
|
29882
31435
|
init_storage_paths();
|
|
29883
31436
|
init_workspace_name();
|
|
29884
|
-
import
|
|
29885
|
-
import { homedir as
|
|
29886
|
-
import
|
|
31437
|
+
import fs54 from "node:fs";
|
|
31438
|
+
import { homedir as homedir35 } from "node:os";
|
|
31439
|
+
import path58 from "node:path";
|
|
29887
31440
|
init_output();
|
|
29888
31441
|
function olamHome4() {
|
|
29889
|
-
return process.env.OLAM_HOME ??
|
|
31442
|
+
return process.env.OLAM_HOME ?? path58.join(homedir35(), ".olam");
|
|
29890
31443
|
}
|
|
29891
31444
|
function kgRoot2() {
|
|
29892
|
-
return
|
|
31445
|
+
return path58.join(olamHome4(), "kg");
|
|
29893
31446
|
}
|
|
29894
31447
|
function worldsRoot2() {
|
|
29895
|
-
return
|
|
31448
|
+
return path58.join(olamHome4(), "worlds");
|
|
29896
31449
|
}
|
|
29897
31450
|
function dirSizeBytes2(dir) {
|
|
29898
|
-
if (!
|
|
31451
|
+
if (!fs54.existsSync(dir)) return 0;
|
|
29899
31452
|
let total = 0;
|
|
29900
31453
|
const stack = [dir];
|
|
29901
31454
|
while (stack.length > 0) {
|
|
29902
31455
|
const cur = stack.pop();
|
|
29903
31456
|
let entries;
|
|
29904
31457
|
try {
|
|
29905
|
-
entries =
|
|
31458
|
+
entries = fs54.readdirSync(cur, { withFileTypes: true });
|
|
29906
31459
|
} catch {
|
|
29907
31460
|
continue;
|
|
29908
31461
|
}
|
|
29909
31462
|
for (const entry of entries) {
|
|
29910
|
-
const full =
|
|
31463
|
+
const full = path58.join(cur, entry.name);
|
|
29911
31464
|
if (entry.isSymbolicLink()) continue;
|
|
29912
31465
|
if (entry.isDirectory()) {
|
|
29913
31466
|
stack.push(full);
|
|
29914
31467
|
continue;
|
|
29915
31468
|
}
|
|
29916
31469
|
try {
|
|
29917
|
-
total +=
|
|
31470
|
+
total += fs54.statSync(full).size;
|
|
29918
31471
|
} catch {
|
|
29919
31472
|
}
|
|
29920
31473
|
}
|
|
@@ -29928,10 +31481,10 @@ function formatBytes5(n) {
|
|
|
29928
31481
|
return `${(n / 1024 / 1024 / 1024).toFixed(2)} GB`;
|
|
29929
31482
|
}
|
|
29930
31483
|
function readFreshness(workspace) {
|
|
29931
|
-
const file =
|
|
29932
|
-
if (!
|
|
31484
|
+
const file = path58.join(kgPristinePath(workspace), "freshness.json");
|
|
31485
|
+
if (!fs54.existsSync(file)) return null;
|
|
29933
31486
|
try {
|
|
29934
|
-
const raw = JSON.parse(
|
|
31487
|
+
const raw = JSON.parse(fs54.readFileSync(file, "utf-8"));
|
|
29935
31488
|
if (raw && typeof raw === "object") return raw;
|
|
29936
31489
|
return null;
|
|
29937
31490
|
} catch {
|
|
@@ -29939,10 +31492,10 @@ function readFreshness(workspace) {
|
|
|
29939
31492
|
}
|
|
29940
31493
|
}
|
|
29941
31494
|
function readOverlayNodeCount(graphifyOutDir) {
|
|
29942
|
-
const graphPath =
|
|
29943
|
-
if (!
|
|
31495
|
+
const graphPath = path58.join(graphifyOutDir, "graph.json");
|
|
31496
|
+
if (!fs54.existsSync(graphPath)) return null;
|
|
29944
31497
|
try {
|
|
29945
|
-
const raw = JSON.parse(
|
|
31498
|
+
const raw = JSON.parse(fs54.readFileSync(graphPath, "utf-8"));
|
|
29946
31499
|
if (raw && typeof raw === "object") {
|
|
29947
31500
|
const nodes = raw.nodes;
|
|
29948
31501
|
if (Array.isArray(nodes)) return nodes.length;
|
|
@@ -29954,28 +31507,28 @@ function readOverlayNodeCount(graphifyOutDir) {
|
|
|
29954
31507
|
}
|
|
29955
31508
|
function listOverlays() {
|
|
29956
31509
|
const root = worldsRoot2();
|
|
29957
|
-
if (!
|
|
31510
|
+
if (!fs54.existsSync(root)) return [];
|
|
29958
31511
|
const records = [];
|
|
29959
31512
|
let worldDirs;
|
|
29960
31513
|
try {
|
|
29961
|
-
worldDirs =
|
|
31514
|
+
worldDirs = fs54.readdirSync(root, { withFileTypes: true });
|
|
29962
31515
|
} catch {
|
|
29963
31516
|
return [];
|
|
29964
31517
|
}
|
|
29965
31518
|
for (const worldEntry of worldDirs) {
|
|
29966
31519
|
if (!worldEntry.isDirectory()) continue;
|
|
29967
31520
|
const worldId = worldEntry.name;
|
|
29968
|
-
const worldDir =
|
|
31521
|
+
const worldDir = path58.join(root, worldId);
|
|
29969
31522
|
let cloneDirs;
|
|
29970
31523
|
try {
|
|
29971
|
-
cloneDirs =
|
|
31524
|
+
cloneDirs = fs54.readdirSync(worldDir, { withFileTypes: true });
|
|
29972
31525
|
} catch {
|
|
29973
31526
|
continue;
|
|
29974
31527
|
}
|
|
29975
31528
|
for (const cloneEntry of cloneDirs) {
|
|
29976
31529
|
if (!cloneEntry.isDirectory()) continue;
|
|
29977
|
-
const graphifyOut =
|
|
29978
|
-
if (!
|
|
31530
|
+
const graphifyOut = path58.join(worldDir, cloneEntry.name, "graphify-out");
|
|
31531
|
+
if (!fs54.existsSync(graphifyOut)) continue;
|
|
29979
31532
|
records.push({
|
|
29980
31533
|
world_id: worldId,
|
|
29981
31534
|
clone_dir: cloneEntry.name,
|
|
@@ -29989,11 +31542,11 @@ function listOverlays() {
|
|
|
29989
31542
|
}
|
|
29990
31543
|
function listPristines(overlays) {
|
|
29991
31544
|
const root = kgRoot2();
|
|
29992
|
-
if (!
|
|
31545
|
+
if (!fs54.existsSync(root)) return [];
|
|
29993
31546
|
const records = [];
|
|
29994
31547
|
let entries;
|
|
29995
31548
|
try {
|
|
29996
|
-
entries =
|
|
31549
|
+
entries = fs54.readdirSync(root, { withFileTypes: true });
|
|
29997
31550
|
} catch {
|
|
29998
31551
|
return [];
|
|
29999
31552
|
}
|
|
@@ -30006,7 +31559,7 @@ function listPristines(overlays) {
|
|
|
30006
31559
|
continue;
|
|
30007
31560
|
}
|
|
30008
31561
|
const fresh = readFreshness(workspace);
|
|
30009
|
-
const graphifyOut =
|
|
31562
|
+
const graphifyOut = path58.join(kgPristinePath(workspace), "graphify-out");
|
|
30010
31563
|
const size = dirSizeBytes2(graphifyOut);
|
|
30011
31564
|
const worldCount = overlays.filter((o) => o.clone_dir === workspace).length;
|
|
30012
31565
|
records.push({
|
|
@@ -30141,11 +31694,11 @@ function registerKgStatusCommand(kg) {
|
|
|
30141
31694
|
init_storage_paths();
|
|
30142
31695
|
init_workspace_name();
|
|
30143
31696
|
init_output();
|
|
30144
|
-
import { spawn as
|
|
30145
|
-
import
|
|
30146
|
-
import
|
|
31697
|
+
import { spawn as spawn11 } from "node:child_process";
|
|
31698
|
+
import fs55 from "node:fs";
|
|
31699
|
+
import path59 from "node:path";
|
|
30147
31700
|
function pidFilePath(workspace) {
|
|
30148
|
-
return
|
|
31701
|
+
return path59.join(kgPristinePath(workspace), ".watch.pid");
|
|
30149
31702
|
}
|
|
30150
31703
|
function isPidAlive3(pid) {
|
|
30151
31704
|
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
@@ -30160,39 +31713,39 @@ function isPidAlive3(pid) {
|
|
|
30160
31713
|
}
|
|
30161
31714
|
function readAndClassifyPid(workspace) {
|
|
30162
31715
|
const file = pidFilePath(workspace);
|
|
30163
|
-
if (!
|
|
31716
|
+
if (!fs55.existsSync(file)) return { status: "no-pidfile", pid: null };
|
|
30164
31717
|
let pid;
|
|
30165
31718
|
try {
|
|
30166
|
-
const raw =
|
|
31719
|
+
const raw = fs55.readFileSync(file, "utf-8").trim();
|
|
30167
31720
|
pid = Number.parseInt(raw, 10);
|
|
30168
31721
|
} catch {
|
|
30169
|
-
|
|
31722
|
+
fs55.rmSync(file, { force: true });
|
|
30170
31723
|
return { status: "stale-reclaimed", pid: null };
|
|
30171
31724
|
}
|
|
30172
31725
|
if (!Number.isInteger(pid) || pid <= 0) {
|
|
30173
|
-
|
|
31726
|
+
fs55.rmSync(file, { force: true });
|
|
30174
31727
|
return { status: "stale-reclaimed", pid: null };
|
|
30175
31728
|
}
|
|
30176
31729
|
if (isPidAlive3(pid)) return { status: "active", pid };
|
|
30177
|
-
|
|
31730
|
+
fs55.rmSync(file, { force: true });
|
|
30178
31731
|
return { status: "stale-reclaimed", pid: null };
|
|
30179
31732
|
}
|
|
30180
31733
|
function writePidFile(workspace, pid) {
|
|
30181
31734
|
const file = pidFilePath(workspace);
|
|
30182
|
-
const dir =
|
|
30183
|
-
|
|
30184
|
-
|
|
31735
|
+
const dir = path59.dirname(file);
|
|
31736
|
+
fs55.mkdirSync(dir, { recursive: true });
|
|
31737
|
+
fs55.writeFileSync(file, String(pid), { encoding: "utf-8" });
|
|
30185
31738
|
}
|
|
30186
31739
|
function removePidFile(workspace) {
|
|
30187
31740
|
const file = pidFilePath(workspace);
|
|
30188
31741
|
try {
|
|
30189
|
-
|
|
31742
|
+
fs55.rmSync(file, { force: true });
|
|
30190
31743
|
} catch {
|
|
30191
31744
|
}
|
|
30192
31745
|
}
|
|
30193
31746
|
async function runKgWatch(workspaceArg, opts, deps = {}) {
|
|
30194
31747
|
const cwd = deps.cwd ?? opts.cwd ?? process.cwd();
|
|
30195
|
-
const name = workspaceArg ??
|
|
31748
|
+
const name = workspaceArg ?? path59.basename(cwd).toLowerCase();
|
|
30196
31749
|
try {
|
|
30197
31750
|
validateWorkspaceName(name);
|
|
30198
31751
|
} catch (err) {
|
|
@@ -30200,7 +31753,7 @@ async function runKgWatch(workspaceArg, opts, deps = {}) {
|
|
|
30200
31753
|
return { exitCode: 1, pidWritten: false };
|
|
30201
31754
|
}
|
|
30202
31755
|
const pristinePath = kgPristinePath(name);
|
|
30203
|
-
const graphPath =
|
|
31756
|
+
const graphPath = path59.join(pristinePath, "graphify-out", "graph.json");
|
|
30204
31757
|
const pidState = readAndClassifyPid(name);
|
|
30205
31758
|
if (pidState.status === "active") {
|
|
30206
31759
|
printError(
|
|
@@ -30211,7 +31764,7 @@ async function runKgWatch(workspaceArg, opts, deps = {}) {
|
|
|
30211
31764
|
if (pidState.status === "stale-reclaimed") {
|
|
30212
31765
|
printInfo("stale-pid", `reclaimed dead PID file at ${pidFilePath(name)}`);
|
|
30213
31766
|
}
|
|
30214
|
-
const spawnFn = deps.spawnImpl ??
|
|
31767
|
+
const spawnFn = deps.spawnImpl ?? spawn11;
|
|
30215
31768
|
const child = spawnFn(
|
|
30216
31769
|
"graphify",
|
|
30217
31770
|
[cwd, "--watch", "--update", "--graph", graphPath],
|
|
@@ -30263,7 +31816,7 @@ function registerKgWatchCommand(kg) {
|
|
|
30263
31816
|
}
|
|
30264
31817
|
|
|
30265
31818
|
// src/commands/kg-classify.ts
|
|
30266
|
-
import
|
|
31819
|
+
import pc33 from "picocolors";
|
|
30267
31820
|
init_output();
|
|
30268
31821
|
function registerKgClassifyCommand(kg) {
|
|
30269
31822
|
kg.command("classify <question>").description("Route a question to kg | grep | both via the 4-layer classifier (kg-service required)").option("--workspace <name>", "Scope the L2 probe gate to a specific workspace graph").option("--json", "Emit raw JSON instead of the formatted summary").action(async (question, opts) => {
|
|
@@ -30273,7 +31826,7 @@ function registerKgClassifyCommand(kg) {
|
|
|
30273
31826
|
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
30274
31827
|
return;
|
|
30275
31828
|
}
|
|
30276
|
-
const routeStr = result.route === "kg" ?
|
|
31829
|
+
const routeStr = result.route === "kg" ? pc33.cyan("kg") : result.route === "grep" ? pc33.magenta("grep") : pc33.yellow("both");
|
|
30277
31830
|
printInfo("route", `${routeStr} (layer ${result.layer}, took ${result.took_ms}ms)`);
|
|
30278
31831
|
printInfo("confidence", String(result.confidence));
|
|
30279
31832
|
printInfo("reason", result.reason);
|
|
@@ -30295,7 +31848,7 @@ function registerKgClassifyCommand(kg) {
|
|
|
30295
31848
|
}
|
|
30296
31849
|
|
|
30297
31850
|
// src/commands/kg-doctor.ts
|
|
30298
|
-
import
|
|
31851
|
+
import pc34 from "picocolors";
|
|
30299
31852
|
init_output();
|
|
30300
31853
|
async function runProbes() {
|
|
30301
31854
|
const results = [];
|
|
@@ -30414,12 +31967,12 @@ function registerKgDoctorCommand(kg) {
|
|
|
30414
31967
|
} else {
|
|
30415
31968
|
printHeader("kg-service doctor");
|
|
30416
31969
|
for (const p of probes) {
|
|
30417
|
-
const badge = p.status === "ok" ?
|
|
31970
|
+
const badge = p.status === "ok" ? pc34.green("\u2713") : p.status === "warn" ? pc34.yellow("\u2298") : pc34.red("\u2717");
|
|
30418
31971
|
const label = `${badge} ${p.name}`;
|
|
30419
31972
|
const detail = p.detail ?? "";
|
|
30420
31973
|
printInfo(label, detail);
|
|
30421
31974
|
if (p.remedy) {
|
|
30422
|
-
process.stderr.write(` ${
|
|
31975
|
+
process.stderr.write(` ${pc34.dim("remedy:")} ${p.remedy}
|
|
30423
31976
|
`);
|
|
30424
31977
|
}
|
|
30425
31978
|
}
|
|
@@ -30437,14 +31990,14 @@ function registerKgDoctorCommand(kg) {
|
|
|
30437
31990
|
}
|
|
30438
31991
|
|
|
30439
31992
|
// src/commands/kg-install-hook.ts
|
|
30440
|
-
import * as
|
|
30441
|
-
import * as
|
|
30442
|
-
import * as
|
|
31993
|
+
import * as fs57 from "node:fs";
|
|
31994
|
+
import * as path61 from "node:path";
|
|
31995
|
+
import * as os32 from "node:os";
|
|
30443
31996
|
|
|
30444
31997
|
// ../core/dist/world/merge-settings.js
|
|
30445
|
-
import * as
|
|
30446
|
-
import * as
|
|
30447
|
-
import * as
|
|
31998
|
+
import * as fs56 from "node:fs";
|
|
31999
|
+
import * as path60 from "node:path";
|
|
32000
|
+
import * as crypto8 from "node:crypto";
|
|
30448
32001
|
function mergeHomeSettingsJson(filePath, options) {
|
|
30449
32002
|
let settings;
|
|
30450
32003
|
try {
|
|
@@ -30503,10 +32056,10 @@ function mergeHomeSettingsJson(filePath, options) {
|
|
|
30503
32056
|
return { status: "installed", message: `settings.json updated at ${filePath}` };
|
|
30504
32057
|
}
|
|
30505
32058
|
function readSettings(filePath) {
|
|
30506
|
-
if (!
|
|
32059
|
+
if (!fs56.existsSync(filePath)) {
|
|
30507
32060
|
return {};
|
|
30508
32061
|
}
|
|
30509
|
-
const raw =
|
|
32062
|
+
const raw = fs56.readFileSync(filePath, "utf-8");
|
|
30510
32063
|
if (!raw.trim())
|
|
30511
32064
|
return {};
|
|
30512
32065
|
return JSON.parse(raw);
|
|
@@ -30527,13 +32080,13 @@ function isHookSentinelPresent(matchers, sentinel) {
|
|
|
30527
32080
|
return false;
|
|
30528
32081
|
}
|
|
30529
32082
|
function atomicWriteJson(filePath, data) {
|
|
30530
|
-
const dir =
|
|
30531
|
-
|
|
30532
|
-
const rand =
|
|
32083
|
+
const dir = path60.dirname(filePath);
|
|
32084
|
+
fs56.mkdirSync(dir, { recursive: true });
|
|
32085
|
+
const rand = crypto8.randomBytes(6).toString("hex");
|
|
30533
32086
|
const tmp = `${filePath}.tmp.${process.pid}.${rand}`;
|
|
30534
32087
|
const json = JSON.stringify(data, null, 2) + "\n";
|
|
30535
|
-
|
|
30536
|
-
|
|
32088
|
+
fs56.writeFileSync(tmp, json, { mode: 420 });
|
|
32089
|
+
fs56.renameSync(tmp, filePath);
|
|
30537
32090
|
}
|
|
30538
32091
|
|
|
30539
32092
|
// ../core/dist/kg/hook-template.js
|
|
@@ -30560,11 +32113,11 @@ try:
|
|
|
30560
32113
|
sys.stderr.write(f"\\x1b[32m\\u2713 KG hit\\x1b[0m \\"{q}\\" \\u2192 L{layer}/{route} \\u00b7 {nodes} nodes \\u00b7 ~{saved_k}k tokens saved\\n")
|
|
30561
32114
|
print(json.dumps({"hookSpecificOutput":{"hookEventName":"PreToolUse","additionalContext":f"[kg-classifier L{layer}|{route}] {label[:160]}"}}))
|
|
30562
32115
|
except Exception: pass' 2>/dev/null`;
|
|
32116
|
+
const curlPost = `RESP=$(curl -s --max-time 1 -X POST -H 'Content-Type: application/json' -d "{\\"q\\":\\"$(echo \\"$CMD\\" | head -c 200 | tr '\\"' ' ')\\"}" ${url2} 2>/dev/null)`;
|
|
30563
32117
|
return [
|
|
30564
32118
|
`KG_SENTINEL=${KG_HOOK_SENTINEL}`,
|
|
30565
32119
|
`CMD=$(${extractCmd})`,
|
|
30566
|
-
`case "$CMD" in *grep*|*rg\\ *|*ripgrep*|*find\\ *|*fd\\ *|*ack\\ *|*ag\\ *)`,
|
|
30567
|
-
`RESP=$(curl -s --max-time 1 -X POST -H 'Content-Type: application/json' -d "{\\"q\\":\\"$(echo \\"$CMD\\" | head -c 200 | tr '\\"' ' ')\\"}" ${url2} 2>/dev/null)`,
|
|
32120
|
+
`case "$CMD" in *grep*|*rg\\ *|*ripgrep*|*find\\ *|*fd\\ *|*ack\\ *|*ag\\ *) ${curlPost}`,
|
|
30568
32121
|
`KG_QUERY="$(echo \\"$CMD\\" | head -c 60 | tr '\\"' ' ')" echo "$RESP" | ${emitContext}`,
|
|
30569
32122
|
`;; esac`
|
|
30570
32123
|
].join("; ");
|
|
@@ -30585,15 +32138,15 @@ function buildHookMatcherEntry(opts) {
|
|
|
30585
32138
|
init_output();
|
|
30586
32139
|
function settingsPathFor(scope) {
|
|
30587
32140
|
if (scope === "user") {
|
|
30588
|
-
return
|
|
32141
|
+
return path61.join(os32.homedir(), ".claude", "settings.json");
|
|
30589
32142
|
}
|
|
30590
|
-
return
|
|
32143
|
+
return path61.join(process.cwd(), ".claude", "settings.json");
|
|
30591
32144
|
}
|
|
30592
32145
|
function backup(filePath) {
|
|
30593
|
-
if (!
|
|
32146
|
+
if (!fs57.existsSync(filePath)) return null;
|
|
30594
32147
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
30595
32148
|
const backupPath = `${filePath}.olam-bak.${ts}`;
|
|
30596
|
-
|
|
32149
|
+
fs57.copyFileSync(filePath, backupPath);
|
|
30597
32150
|
return backupPath;
|
|
30598
32151
|
}
|
|
30599
32152
|
function registerKgInstallHookCommand(kg) {
|
|
@@ -30601,9 +32154,9 @@ function registerKgInstallHookCommand(kg) {
|
|
|
30601
32154
|
const scope = opts.scope === "user" ? "user" : "project";
|
|
30602
32155
|
const filePath = settingsPathFor(scope);
|
|
30603
32156
|
try {
|
|
30604
|
-
|
|
32157
|
+
fs57.mkdirSync(path61.dirname(filePath), { recursive: true });
|
|
30605
32158
|
} catch (err) {
|
|
30606
|
-
printError(`could not create ${
|
|
32159
|
+
printError(`could not create ${path61.dirname(filePath)}: ${err instanceof Error ? err.message : String(err)}`);
|
|
30607
32160
|
process.exitCode = 1;
|
|
30608
32161
|
return;
|
|
30609
32162
|
}
|
|
@@ -30627,7 +32180,7 @@ function registerKgInstallHookCommand(kg) {
|
|
|
30627
32180
|
printInfo("kg-service hook", `already installed at ${filePath}`);
|
|
30628
32181
|
if (backupPath) {
|
|
30629
32182
|
try {
|
|
30630
|
-
|
|
32183
|
+
fs57.unlinkSync(backupPath);
|
|
30631
32184
|
} catch {
|
|
30632
32185
|
}
|
|
30633
32186
|
}
|
|
@@ -30645,15 +32198,15 @@ function registerKgInstallHookCommand(kg) {
|
|
|
30645
32198
|
}
|
|
30646
32199
|
|
|
30647
32200
|
// src/commands/kg-uninstall-hook.ts
|
|
30648
|
-
import * as
|
|
30649
|
-
import * as
|
|
30650
|
-
import * as
|
|
32201
|
+
import * as fs58 from "node:fs";
|
|
32202
|
+
import * as path62 from "node:path";
|
|
32203
|
+
import * as os33 from "node:os";
|
|
30651
32204
|
init_output();
|
|
30652
32205
|
function settingsPathFor2(scope) {
|
|
30653
32206
|
if (scope === "user") {
|
|
30654
|
-
return
|
|
32207
|
+
return path62.join(os33.homedir(), ".claude", "settings.json");
|
|
30655
32208
|
}
|
|
30656
|
-
return
|
|
32209
|
+
return path62.join(process.cwd(), ".claude", "settings.json");
|
|
30657
32210
|
}
|
|
30658
32211
|
function dropSentinel(matchers) {
|
|
30659
32212
|
let changed = false;
|
|
@@ -30683,13 +32236,13 @@ function registerKgUninstallHookCommand(kg) {
|
|
|
30683
32236
|
kg.command("uninstall-hook").description("Remove kg-service PreToolUse hook from .claude/settings.json (sentinel-matched; surgical)").option("--scope <scope>", "project (default) or user", "project").action((opts) => {
|
|
30684
32237
|
const scope = opts.scope === "user" ? "user" : "project";
|
|
30685
32238
|
const filePath = settingsPathFor2(scope);
|
|
30686
|
-
if (!
|
|
32239
|
+
if (!fs58.existsSync(filePath)) {
|
|
30687
32240
|
printInfo("kg-service hook", `no settings.json at ${filePath} \u2014 nothing to remove`);
|
|
30688
32241
|
return;
|
|
30689
32242
|
}
|
|
30690
32243
|
let settings;
|
|
30691
32244
|
try {
|
|
30692
|
-
const raw =
|
|
32245
|
+
const raw = fs58.readFileSync(filePath, "utf-8");
|
|
30693
32246
|
settings = raw.trim() ? JSON.parse(raw) : {};
|
|
30694
32247
|
} catch (err) {
|
|
30695
32248
|
printError(`could not parse ${filePath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -30709,7 +32262,7 @@ function registerKgUninstallHookCommand(kg) {
|
|
|
30709
32262
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
30710
32263
|
const backupPath = `${filePath}.olam-bak.${ts}`;
|
|
30711
32264
|
try {
|
|
30712
|
-
|
|
32265
|
+
fs58.copyFileSync(filePath, backupPath);
|
|
30713
32266
|
} catch {
|
|
30714
32267
|
}
|
|
30715
32268
|
const next = {
|
|
@@ -30728,7 +32281,7 @@ function registerKgUninstallHookCommand(kg) {
|
|
|
30728
32281
|
}
|
|
30729
32282
|
}
|
|
30730
32283
|
try {
|
|
30731
|
-
|
|
32284
|
+
fs58.writeFileSync(filePath, JSON.stringify(next, null, 2) + "\n");
|
|
30732
32285
|
printSuccess(`kg-service hook removed from ${filePath}`);
|
|
30733
32286
|
printInfo("backup", backupPath);
|
|
30734
32287
|
} catch (err) {
|
|
@@ -30802,20 +32355,20 @@ function registerKgSavingsCommand(kg) {
|
|
|
30802
32355
|
// src/commands/kg-build.ts
|
|
30803
32356
|
function resolveWorkspace(arg) {
|
|
30804
32357
|
const cwd = process.cwd();
|
|
30805
|
-
const name = arg ??
|
|
32358
|
+
const name = arg ?? path63.basename(cwd).toLowerCase();
|
|
30806
32359
|
validateWorkspaceName(name);
|
|
30807
32360
|
return { name, sourcePath: cwd };
|
|
30808
32361
|
}
|
|
30809
32362
|
function toContainerPath(hostPath) {
|
|
30810
|
-
const home =
|
|
30811
|
-
const resolved =
|
|
30812
|
-
if (!resolved.startsWith(home +
|
|
32363
|
+
const home = os34.homedir();
|
|
32364
|
+
const resolved = path63.resolve(hostPath);
|
|
32365
|
+
if (!resolved.startsWith(home + path63.sep) && resolved !== home) {
|
|
30813
32366
|
throw new Error(
|
|
30814
32367
|
`source path "${resolved}" is not under $HOME (${home}). kg-service can only build repos that live under your home dir (it bind-mounts $HOME:/host-home:ro at start). Move the repo or set OLAM_HOME if you need a different root.`
|
|
30815
32368
|
);
|
|
30816
32369
|
}
|
|
30817
|
-
const rel =
|
|
30818
|
-
return rel === "" ? "/host-home" :
|
|
32370
|
+
const rel = path63.relative(home, resolved);
|
|
32371
|
+
return rel === "" ? "/host-home" : path63.posix.join("/host-home", rel.split(path63.sep).join("/"));
|
|
30819
32372
|
}
|
|
30820
32373
|
async function runKgBuild(workspaceArg, options = {}) {
|
|
30821
32374
|
let workspace;
|
|
@@ -30833,7 +32386,7 @@ async function runKgBuild(workspaceArg, options = {}) {
|
|
|
30833
32386
|
return { exitCode: 2 };
|
|
30834
32387
|
}
|
|
30835
32388
|
const outDir = kgPristinePath(workspace.name);
|
|
30836
|
-
|
|
32389
|
+
fs59.mkdirSync(outDir, { recursive: true });
|
|
30837
32390
|
const human = !options.json;
|
|
30838
32391
|
if (human) {
|
|
30839
32392
|
printInfo("kg build", `workspace=${workspace.name} source=${workspace.sourcePath}`);
|
|
@@ -30866,12 +32419,12 @@ async function runKgBuild(workspaceArg, options = {}) {
|
|
|
30866
32419
|
workspace: workspace.name,
|
|
30867
32420
|
graphify_path: "container"
|
|
30868
32421
|
};
|
|
30869
|
-
|
|
30870
|
-
|
|
32422
|
+
fs59.writeFileSync(
|
|
32423
|
+
path63.join(outDir, "freshness.json"),
|
|
30871
32424
|
JSON.stringify(freshness, null, 2) + "\n",
|
|
30872
32425
|
"utf-8"
|
|
30873
32426
|
);
|
|
30874
|
-
const finalOut =
|
|
32427
|
+
const finalOut = path63.join(outDir, "graphify-out");
|
|
30875
32428
|
if (options.json) {
|
|
30876
32429
|
process.stdout.write(JSON.stringify(freshness) + "\n");
|
|
30877
32430
|
} else {
|
|
@@ -31148,17 +32701,17 @@ init_manager();
|
|
|
31148
32701
|
init_context();
|
|
31149
32702
|
init_output();
|
|
31150
32703
|
import { spawnSync as defaultSpawnSync } from "node:child_process";
|
|
31151
|
-
import * as
|
|
31152
|
-
import * as
|
|
31153
|
-
import * as
|
|
32704
|
+
import * as fs60 from "node:fs";
|
|
32705
|
+
import * as os35 from "node:os";
|
|
32706
|
+
import * as path64 from "node:path";
|
|
31154
32707
|
function devboxContainerName(worldId) {
|
|
31155
32708
|
return `olam-${worldId}-devbox`;
|
|
31156
32709
|
}
|
|
31157
32710
|
function olamHomeDir() {
|
|
31158
|
-
return process.env["OLAM_HOME"] ??
|
|
32711
|
+
return process.env["OLAM_HOME"] ?? path64.join(os35.homedir(), ".olam");
|
|
31159
32712
|
}
|
|
31160
|
-
function defaultRestartContainer(name,
|
|
31161
|
-
const r =
|
|
32713
|
+
function defaultRestartContainer(name, spawn12 = defaultSpawnSync) {
|
|
32714
|
+
const r = spawn12("docker", ["restart", "--time", "30", name], {
|
|
31162
32715
|
encoding: "utf-8",
|
|
31163
32716
|
stdio: ["ignore", "pipe", "pipe"]
|
|
31164
32717
|
});
|
|
@@ -31168,8 +32721,8 @@ function defaultRestartContainer(name, spawn11 = defaultSpawnSync) {
|
|
|
31168
32721
|
};
|
|
31169
32722
|
}
|
|
31170
32723
|
function defaultAppendAuditLog(homeDir, line) {
|
|
31171
|
-
|
|
31172
|
-
|
|
32724
|
+
fs60.mkdirSync(homeDir, { recursive: true });
|
|
32725
|
+
fs60.appendFileSync(path64.join(homeDir, "usage.log"), line.endsWith("\n") ? line : line + "\n", {
|
|
31173
32726
|
encoding: "utf-8"
|
|
31174
32727
|
});
|
|
31175
32728
|
}
|
|
@@ -31214,19 +32767,19 @@ async function doRekey(worldId, deps) {
|
|
|
31214
32767
|
);
|
|
31215
32768
|
const rotatedAt = deps.now().toISOString();
|
|
31216
32769
|
const homeDir = deps.olamHomeDir();
|
|
31217
|
-
const worldDir =
|
|
31218
|
-
|
|
31219
|
-
const credentialsPath =
|
|
32770
|
+
const worldDir = path64.join(homeDir, "worlds", worldId);
|
|
32771
|
+
fs60.mkdirSync(worldDir, { recursive: true });
|
|
32772
|
+
const credentialsPath = path64.join(worldDir, "credentials.json");
|
|
31220
32773
|
const payload = {
|
|
31221
32774
|
worldRoleName,
|
|
31222
32775
|
password,
|
|
31223
32776
|
rotatedAt
|
|
31224
32777
|
};
|
|
31225
|
-
|
|
32778
|
+
fs60.writeFileSync(credentialsPath, JSON.stringify(payload, null, 2) + "\n", {
|
|
31226
32779
|
encoding: "utf-8",
|
|
31227
32780
|
mode: 384
|
|
31228
32781
|
});
|
|
31229
|
-
|
|
32782
|
+
fs60.chmodSync(credentialsPath, 384);
|
|
31230
32783
|
const restart = deps.restartContainer(devboxContainerName(worldId));
|
|
31231
32784
|
deps.appendAuditLog(`${rotatedAt} ${worldId} rekey`);
|
|
31232
32785
|
if (!restart.ok) {
|
|
@@ -31290,18 +32843,18 @@ function registerRekey(program2) {
|
|
|
31290
32843
|
}
|
|
31291
32844
|
|
|
31292
32845
|
// src/pleri-config.ts
|
|
31293
|
-
import * as
|
|
31294
|
-
import * as
|
|
32846
|
+
import * as fs61 from "node:fs";
|
|
32847
|
+
import * as path65 from "node:path";
|
|
31295
32848
|
function isPleriConfigured(configDir = process.env.OLAM_CONFIG_DIR ?? ".olam") {
|
|
31296
32849
|
if (process.env.PLERI_BASE_URL) {
|
|
31297
32850
|
return true;
|
|
31298
32851
|
}
|
|
31299
|
-
const configPath =
|
|
31300
|
-
if (!
|
|
32852
|
+
const configPath = path65.join(configDir, "config.yaml");
|
|
32853
|
+
if (!fs61.existsSync(configPath)) {
|
|
31301
32854
|
return false;
|
|
31302
32855
|
}
|
|
31303
32856
|
try {
|
|
31304
|
-
const contents =
|
|
32857
|
+
const contents = fs61.readFileSync(configPath, "utf8");
|
|
31305
32858
|
return /^[^#\n]*\bpleri:/m.test(contents);
|
|
31306
32859
|
} catch {
|
|
31307
32860
|
return false;
|
|
@@ -31366,4 +32919,6 @@ registerKg(program);
|
|
|
31366
32919
|
registerConfig(program);
|
|
31367
32920
|
registerRepos(program);
|
|
31368
32921
|
registerRunbooks(program);
|
|
32922
|
+
registerSkillsSource(program);
|
|
32923
|
+
registerSkills(program);
|
|
31369
32924
|
program.parse();
|