@pleri/olam-cli 0.1.144 → 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/image-digests.json +7 -7
- package/dist/index.js +2027 -529
- 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 +1246 -351
- package/host-cp/src/agent-runtime-trigger.mjs +74 -4
- package/host-cp/src/engine-identity.mjs +32 -0
- package/host-cp/src/server.mjs +188 -3
- 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
|
|
@@ -25778,7 +25887,7 @@ function registerUpgrade(program2) {
|
|
|
25778
25887
|
init_host_cp();
|
|
25779
25888
|
init_context();
|
|
25780
25889
|
init_output();
|
|
25781
|
-
import * as
|
|
25890
|
+
import * as http4 from "node:http";
|
|
25782
25891
|
import pc19 from "picocolors";
|
|
25783
25892
|
var HOST_CP_PORT3 = 19e3;
|
|
25784
25893
|
function colorLine(line) {
|
|
@@ -25787,25 +25896,16 @@ function colorLine(line) {
|
|
|
25787
25896
|
if (/\bINFO\b/.test(line)) return pc19.dim(line);
|
|
25788
25897
|
return line;
|
|
25789
25898
|
}
|
|
25790
|
-
function
|
|
25791
|
-
const
|
|
25792
|
-
|
|
25793
|
-
|
|
25794
|
-
|
|
25795
|
-
if (
|
|
25796
|
-
|
|
25797
|
-
const parsed = JSON.parse(raw.slice(6));
|
|
25798
|
-
if (!parsed || typeof parsed !== "object") return null;
|
|
25799
|
-
const ev = parsed;
|
|
25800
|
-
if (ev.type === "replay" && Array.isArray(ev.lines)) return ev;
|
|
25801
|
-
if (ev.type === "line" && typeof ev.line === "string") return ev;
|
|
25802
|
-
return null;
|
|
25803
|
-
} catch {
|
|
25804
|
-
return null;
|
|
25805
|
-
}
|
|
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()}`;
|
|
25806
25906
|
}
|
|
25807
25907
|
function registerLogs(program2) {
|
|
25808
|
-
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) => {
|
|
25809
25909
|
const { ctx, error } = await loadContext();
|
|
25810
25910
|
if (!ctx) {
|
|
25811
25911
|
printError(error?.message ?? "Olam is not configured. Run `olam init` first.");
|
|
@@ -25825,10 +25925,11 @@ function registerLogs(program2) {
|
|
|
25825
25925
|
return;
|
|
25826
25926
|
}
|
|
25827
25927
|
const tailLimit = Math.max(1, parseInt(opts.tail, 10) || 200);
|
|
25828
|
-
const
|
|
25829
|
-
|
|
25830
|
-
|
|
25831
|
-
|
|
25928
|
+
const url2 = buildLogsUrl(worldId, {
|
|
25929
|
+
tail: tailLimit,
|
|
25930
|
+
follow: opts.follow,
|
|
25931
|
+
...opts.service ? { service: opts.service } : {}
|
|
25932
|
+
});
|
|
25832
25933
|
let done = false;
|
|
25833
25934
|
let resolveStream;
|
|
25834
25935
|
const streamDone = new Promise((r) => {
|
|
@@ -25840,16 +25941,7 @@ function registerLogs(program2) {
|
|
|
25840
25941
|
resolveStream();
|
|
25841
25942
|
}
|
|
25842
25943
|
};
|
|
25843
|
-
const
|
|
25844
|
-
process.stdout.write(formatLine(line, service, showService) + "\n");
|
|
25845
|
-
lineCount++;
|
|
25846
|
-
if (!opts.follow && lineCount >= tailLimit) {
|
|
25847
|
-
finish();
|
|
25848
|
-
return false;
|
|
25849
|
-
}
|
|
25850
|
-
return true;
|
|
25851
|
-
};
|
|
25852
|
-
const req = http3.get(url2, { headers: { Authorization: `Bearer ${token}` } }, (res) => {
|
|
25944
|
+
const req = http4.get(url2, { headers: { Authorization: `Bearer ${token}` } }, (res) => {
|
|
25853
25945
|
if (res.statusCode !== 200) {
|
|
25854
25946
|
printError(`Log stream returned HTTP ${res.statusCode ?? "unknown"}`);
|
|
25855
25947
|
process.exitCode = 1;
|
|
@@ -25865,21 +25957,14 @@ function registerLogs(program2) {
|
|
|
25865
25957
|
buf = rawLines.pop() ?? "";
|
|
25866
25958
|
for (const raw of rawLines) {
|
|
25867
25959
|
if (done) break;
|
|
25868
|
-
|
|
25869
|
-
|
|
25870
|
-
if (ev.type === "replay") {
|
|
25871
|
-
for (const l of ev.lines) {
|
|
25872
|
-
if (!emit2(l, ev.service)) {
|
|
25873
|
-
req.destroy();
|
|
25874
|
-
break;
|
|
25875
|
-
}
|
|
25876
|
-
}
|
|
25877
|
-
} else if (ev.type === "line") {
|
|
25878
|
-
if (!emit2(ev.line, ev.service)) req.destroy();
|
|
25879
|
-
}
|
|
25960
|
+
if (!raw) continue;
|
|
25961
|
+
process.stdout.write(colorLine(raw) + "\n");
|
|
25880
25962
|
}
|
|
25881
25963
|
});
|
|
25882
|
-
res.on("end", () =>
|
|
25964
|
+
res.on("end", () => {
|
|
25965
|
+
if (buf) process.stdout.write(colorLine(buf) + "\n");
|
|
25966
|
+
finish();
|
|
25967
|
+
});
|
|
25883
25968
|
res.on("error", (err) => {
|
|
25884
25969
|
if (!done) printError(`Stream error: ${err.message}`);
|
|
25885
25970
|
finish();
|
|
@@ -26902,6 +26987,11 @@ var HARD_CAP_BYTES = 5e9;
|
|
|
26902
26987
|
|
|
26903
26988
|
// src/lib/health-probes.ts
|
|
26904
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+$/;
|
|
26905
26995
|
var defaultDockerExec2 = (cmd, args) => {
|
|
26906
26996
|
const r = spawnSync18(cmd, [...args], {
|
|
26907
26997
|
encoding: "utf-8",
|
|
@@ -27125,6 +27215,146 @@ function formatBytes4(n) {
|
|
|
27125
27215
|
if (n < 1024 * 1024 * 1024) return `${(n / 1024 / 1024).toFixed(1)} MB`;
|
|
27126
27216
|
return `${(n / 1024 / 1024 / 1024).toFixed(2)} GB`;
|
|
27127
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
|
+
}
|
|
27128
27358
|
|
|
27129
27359
|
// src/commands/doctor.ts
|
|
27130
27360
|
init_output();
|
|
@@ -27148,27 +27378,56 @@ async function runDoctor(opts, deps = {}) {
|
|
|
27148
27378
|
const fetchImpl = deps.fetchImpl;
|
|
27149
27379
|
const olamHomeOverride = deps.olamHomeOverride;
|
|
27150
27380
|
const registry = deps.registry ?? DEFAULT_REGISTRY;
|
|
27151
|
-
const
|
|
27152
|
-
const
|
|
27153
|
-
|
|
27154
|
-
|
|
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);
|
|
27155
27396
|
}
|
|
27156
27397
|
const imageRefs = buildRequiredImageRefs(registry);
|
|
27157
|
-
const
|
|
27158
|
-
|
|
27398
|
+
const slot2Promise = isK8s ? probeK8sImagePresence(imageRefs, dockerExec) : probeImagePresence(imageRefs, dockerExec);
|
|
27399
|
+
const [imageResult, hostCpResult, authResult, kgResult, engineResult, colimaResult] = await Promise.all([
|
|
27400
|
+
slot2Promise,
|
|
27159
27401
|
probeHostCpHealth(fetchImpl),
|
|
27160
27402
|
probeAuthVault(olamHomeOverride),
|
|
27161
|
-
probeKgStorage(olamHomeOverride)
|
|
27403
|
+
probeKgStorage(olamHomeOverride),
|
|
27404
|
+
probeEngine(fetchImpl),
|
|
27405
|
+
probeColimaVersion(dockerExec)
|
|
27162
27406
|
]);
|
|
27163
27407
|
rows.push(
|
|
27164
27408
|
{ name: "images", result: imageResult },
|
|
27165
27409
|
{ name: "host-cp /health", result: hostCpResult },
|
|
27166
27410
|
{ name: "auth vault", result: authResult },
|
|
27167
|
-
{ name: "KG storage", result: kgResult }
|
|
27411
|
+
{ name: "KG storage", result: kgResult },
|
|
27412
|
+
{ name: "engine", result: engineResult },
|
|
27413
|
+
{ name: "colima version", result: colimaResult }
|
|
27168
27414
|
);
|
|
27415
|
+
return emit(makeReport(rows), opts);
|
|
27416
|
+
}
|
|
27417
|
+
function makeReport(rows) {
|
|
27169
27418
|
const failureCount = rows.filter((r) => !r.result.ok).length;
|
|
27419
|
+
const warnCount = rows.filter((r) => isWarn(r.result)).length;
|
|
27170
27420
|
const summary = failureCount === 0 ? "OK" : "FAILED";
|
|
27171
|
-
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;
|
|
27172
27431
|
}
|
|
27173
27432
|
function emit(report, opts) {
|
|
27174
27433
|
if (opts.json) {
|
|
@@ -27177,12 +27436,19 @@ function emit(report, opts) {
|
|
|
27177
27436
|
{
|
|
27178
27437
|
summary: report.summary,
|
|
27179
27438
|
failureCount: report.failureCount,
|
|
27180
|
-
|
|
27181
|
-
|
|
27182
|
-
|
|
27183
|
-
|
|
27184
|
-
|
|
27185
|
-
|
|
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
|
+
})
|
|
27186
27452
|
},
|
|
27187
27453
|
null,
|
|
27188
27454
|
2
|
|
@@ -27199,23 +27465,28 @@ function renderHuman(report) {
|
|
|
27199
27465
|
const namePad = Math.max(...report.rows.map((r) => r.name.length));
|
|
27200
27466
|
for (const row of report.rows) {
|
|
27201
27467
|
const label = row.name.padEnd(namePad);
|
|
27202
|
-
if (row.result
|
|
27203
|
-
|
|
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}`);
|
|
27204
27474
|
} else {
|
|
27205
|
-
printError(
|
|
27206
|
-
printWarning(`${"".padEnd(namePad)} remedy: ${row.result.remedy}`);
|
|
27475
|
+
printError(`[FAIL] ${label} ${row.result.message}`);
|
|
27476
|
+
printWarning(`${"".padEnd(namePad + 7)} remedy: ${row.result.remedy}`);
|
|
27207
27477
|
}
|
|
27208
27478
|
}
|
|
27209
27479
|
process.stdout.write("\n");
|
|
27210
27480
|
if (report.summary === "OK") {
|
|
27211
|
-
|
|
27481
|
+
const warnNote = report.warnCount > 0 ? ` (${report.warnCount} warn)` : "";
|
|
27482
|
+
printSuccess(`OK \u2014 all ${report.rows.length} probes green${warnNote}`);
|
|
27212
27483
|
} else {
|
|
27213
27484
|
printError(`FAILED \u2014 ${report.failureCount} of ${report.rows.length} probes failed`);
|
|
27214
27485
|
}
|
|
27215
27486
|
}
|
|
27216
27487
|
function registerDoctor(program2) {
|
|
27217
27488
|
program2.command("doctor").description(
|
|
27218
|
-
"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)."
|
|
27219
27490
|
).option("--json", "emit the report as JSON instead of a human-readable table").action(async (opts) => {
|
|
27220
27491
|
const r = await runDoctor(opts);
|
|
27221
27492
|
if (r.exitCode !== 0) process.exitCode = r.exitCode;
|
|
@@ -27970,11 +28241,11 @@ function registerBegin(program2) {
|
|
|
27970
28241
|
}
|
|
27971
28242
|
|
|
27972
28243
|
// src/commands/config.ts
|
|
27973
|
-
import * as
|
|
28244
|
+
import * as fs51 from "node:fs";
|
|
27974
28245
|
import { createRequire as createRequire4 } from "node:module";
|
|
27975
28246
|
|
|
27976
28247
|
// ../core/dist/global-config/index.js
|
|
27977
|
-
|
|
28248
|
+
init_schema4();
|
|
27978
28249
|
init_store2();
|
|
27979
28250
|
|
|
27980
28251
|
// ../core/dist/global-config/repos.js
|
|
@@ -28048,83 +28319,709 @@ init_runbooks();
|
|
|
28048
28319
|
init_port_validator();
|
|
28049
28320
|
init_bridge();
|
|
28050
28321
|
|
|
28051
|
-
//
|
|
28322
|
+
// ../core/dist/skill-sources/index.js
|
|
28323
|
+
init_schema3();
|
|
28324
|
+
|
|
28325
|
+
// ../core/dist/skill-sources/store.js
|
|
28052
28326
|
init_store2();
|
|
28053
|
-
|
|
28054
|
-
|
|
28055
|
-
function
|
|
28056
|
-
|
|
28057
|
-
|
|
28058
|
-
|
|
28059
|
-
|
|
28060
|
-
|
|
28061
|
-
|
|
28062
|
-
|
|
28063
|
-
|
|
28064
|
-
|
|
28065
|
-
|
|
28066
|
-
|
|
28067
|
-
|
|
28068
|
-
|
|
28069
|
-
|
|
28070
|
-
|
|
28071
|
-
|
|
28072
|
-
|
|
28073
|
-
|
|
28074
|
-
|
|
28075
|
-
|
|
28076
|
-
|
|
28077
|
-
|
|
28078
|
-
|
|
28079
|
-
|
|
28080
|
-
|
|
28081
|
-
|
|
28082
|
-
|
|
28083
|
-
|
|
28084
|
-
|
|
28085
|
-
|
|
28086
|
-
|
|
28087
|
-
|
|
28088
|
-
|
|
28089
|
-
|
|
28090
|
-
|
|
28091
|
-
|
|
28092
|
-
const rawLine = entry?.value?.line ?? entry?.key?.line ?? -1;
|
|
28093
|
-
const lineStr = rawLine >= 0 ? `Line ${rawLine + 1}: ` : "";
|
|
28094
|
-
process.stderr.write(`${lineStr}${pointer}: ${issue.message}
|
|
28095
|
-
`);
|
|
28096
|
-
}
|
|
28097
|
-
process.exit(1);
|
|
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);
|
|
28331
|
+
}
|
|
28332
|
+
function listSkillSources() {
|
|
28333
|
+
return readGlobalConfig().skillSources;
|
|
28334
|
+
}
|
|
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)
|
|
28098
28366
|
});
|
|
28099
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
|
+
}
|
|
28100
28392
|
|
|
28101
|
-
//
|
|
28102
|
-
import
|
|
28103
|
-
|
|
28104
|
-
|
|
28105
|
-
|
|
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
|
+
}
|
|
28106
28429
|
}
|
|
28107
|
-
function
|
|
28108
|
-
const
|
|
28109
|
-
|
|
28110
|
-
|
|
28111
|
-
|
|
28112
|
-
|
|
28113
|
-
|
|
28114
|
-
|
|
28115
|
-
|
|
28116
|
-
|
|
28117
|
-
|
|
28118
|
-
console.log(
|
|
28119
|
-
` ${pc25.bold(r.name.padEnd(24))} ${r.path.padEnd(48)} ${pc25.dim(when)}`
|
|
28120
|
-
);
|
|
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 });
|
|
28121
28441
|
}
|
|
28122
|
-
|
|
28123
|
-
|
|
28124
|
-
|
|
28125
|
-
|
|
28126
|
-
|
|
28127
|
-
|
|
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,
|
|
28128
29025
|
description: opts.description,
|
|
28129
29026
|
defaultBranch: opts.defaultBranch
|
|
28130
29027
|
});
|
|
@@ -28240,74 +29137,309 @@ function registerRunbooks(program2) {
|
|
|
28240
29137
|
removeRunbook(name);
|
|
28241
29138
|
printSuccess(`removed runbook "${name}"`);
|
|
28242
29139
|
} catch (err) {
|
|
28243
|
-
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));
|
|
28244
29396
|
process.exitCode = 1;
|
|
28245
29397
|
}
|
|
28246
29398
|
});
|
|
28247
|
-
|
|
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) => {
|
|
28248
29400
|
try {
|
|
28249
|
-
const
|
|
28250
|
-
|
|
28251
|
-
if (conflicts.length > 0) {
|
|
28252
|
-
printError(`port conflicts detected for runbook "${name}":`);
|
|
28253
|
-
for (const c of conflicts) {
|
|
28254
|
-
console.error(formatConflict(c));
|
|
28255
|
-
}
|
|
28256
|
-
console.error(
|
|
28257
|
-
`
|
|
28258
|
-
${conflicts.length} port conflict(s). Stop the conflicting processes or update portMap in runbook "${name}".`
|
|
28259
|
-
);
|
|
28260
|
-
process.exitCode = 1;
|
|
28261
|
-
return;
|
|
28262
|
-
}
|
|
28263
|
-
console.log(
|
|
28264
|
-
pc26.dim(
|
|
28265
|
-
`olam runbooks apply: port validation passed for "${name}" (repos: ${rb.repos.join(", ")}).`
|
|
28266
|
-
)
|
|
28267
|
-
);
|
|
28268
|
-
console.log(
|
|
28269
|
-
pc26.dim(
|
|
28270
|
-
`Hint: use "olam create --repos ${rb.repos.join(" ")}${opts.task ? ` --task "${opts.task}"` : ""}${opts.name ? ` --name ${opts.name}` : ""}" to create the world.`
|
|
28271
|
-
)
|
|
28272
|
-
);
|
|
28273
|
-
console.log(
|
|
28274
|
-
pc26.yellow('Phase D will wire --runbook directly. For now, use "olam create --repos ..." above.')
|
|
28275
|
-
);
|
|
29401
|
+
const summary = await syncSkills({ dryRun: true, atlasUser: opts.atlasUser });
|
|
29402
|
+
printSyncSummary(summary, true);
|
|
28276
29403
|
} catch (err) {
|
|
28277
|
-
printError(
|
|
29404
|
+
printError(asMessage4(err));
|
|
28278
29405
|
process.exitCode = 1;
|
|
28279
29406
|
}
|
|
28280
29407
|
});
|
|
28281
|
-
|
|
28282
|
-
|
|
28283
|
-
|
|
28284
|
-
|
|
28285
|
-
|
|
28286
|
-
|
|
28287
|
-
|
|
28288
|
-
|
|
28289
|
-
|
|
28290
|
-
|
|
28291
|
-
|
|
28292
|
-
|
|
28293
|
-
|
|
28294
|
-
|
|
28295
|
-
|
|
28296
|
-
|
|
28297
|
-
}
|
|
28298
|
-
|
|
28299
|
-
if (conflicts.length > 0) {
|
|
28300
|
-
console.error(
|
|
28301
|
-
`
|
|
28302
|
-
${conflicts.length} port conflict(s). Stop the conflicting processes or update portMap in runbook "${name}".`
|
|
28303
|
-
);
|
|
28304
|
-
process.exitCode = 1;
|
|
28305
|
-
} else {
|
|
28306
|
-
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)}`);
|
|
28307
29426
|
}
|
|
28308
|
-
}
|
|
28309
|
-
|
|
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}"`);
|
|
28310
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();
|
|
28311
29443
|
}
|
|
28312
29444
|
});
|
|
28313
29445
|
}
|
|
@@ -28386,17 +29518,17 @@ function registerWorldUpgrade(program2) {
|
|
|
28386
29518
|
|
|
28387
29519
|
// src/commands/mcp/serve.ts
|
|
28388
29520
|
init_output();
|
|
28389
|
-
import { existsSync as
|
|
28390
|
-
import { dirname as
|
|
29521
|
+
import { existsSync as existsSync58 } from "node:fs";
|
|
29522
|
+
import { dirname as dirname28, resolve as resolve13 } from "node:path";
|
|
28391
29523
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
28392
|
-
var here =
|
|
29524
|
+
var here = dirname28(fileURLToPath5(import.meta.url));
|
|
28393
29525
|
var BUNDLE_PATH_CANDIDATES = [
|
|
28394
29526
|
// bundled (`dist/index.js` after bundle-cli.mjs) — sibling
|
|
28395
29527
|
resolve13(here, "mcp-server.js"),
|
|
28396
29528
|
// dev / tsc-only (`dist/commands/mcp/serve.js`) — up two levels
|
|
28397
29529
|
resolve13(here, "..", "..", "mcp-server.js")
|
|
28398
29530
|
];
|
|
28399
|
-
function resolveBundlePath(candidates2 = BUNDLE_PATH_CANDIDATES, exists =
|
|
29531
|
+
function resolveBundlePath(candidates2 = BUNDLE_PATH_CANDIDATES, exists = existsSync58) {
|
|
28400
29532
|
return candidates2.find(exists) ?? null;
|
|
28401
29533
|
}
|
|
28402
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.";
|
|
@@ -28553,8 +29685,8 @@ var SECRET = process.env["OLAM_MCP_AUTH_SECRET"] ?? "";
|
|
|
28553
29685
|
function authHeaders() {
|
|
28554
29686
|
return SECRET ? { "X-Olam-Mcp-Secret": SECRET } : {};
|
|
28555
29687
|
}
|
|
28556
|
-
async function apiFetch(
|
|
28557
|
-
const res = await fetch(`${BASE_URL}${
|
|
29688
|
+
async function apiFetch(path66, init = {}) {
|
|
29689
|
+
const res = await fetch(`${BASE_URL}${path66}`, {
|
|
28558
29690
|
...init,
|
|
28559
29691
|
headers: {
|
|
28560
29692
|
"Content-Type": "application/json",
|
|
@@ -28706,7 +29838,7 @@ function registerMcpAdd(cmd) {
|
|
|
28706
29838
|
|
|
28707
29839
|
// src/commands/mcp/list.ts
|
|
28708
29840
|
init_output();
|
|
28709
|
-
import
|
|
29841
|
+
import pc29 from "picocolors";
|
|
28710
29842
|
function registerMcpList(cmd) {
|
|
28711
29843
|
cmd.command("list").description("List all registered MCP credentials").action(async () => {
|
|
28712
29844
|
const client = getMcpAuthClient();
|
|
@@ -28721,8 +29853,8 @@ function registerMcpList(cmd) {
|
|
|
28721
29853
|
}
|
|
28722
29854
|
const mcps = data.mcps ?? [];
|
|
28723
29855
|
if (mcps.length === 0) {
|
|
28724
|
-
console.log(
|
|
28725
|
-
console.log(
|
|
29856
|
+
console.log(pc29.dim("No MCP credentials registered."));
|
|
29857
|
+
console.log(pc29.dim("Add one with: olam mcp add <service>"));
|
|
28726
29858
|
return;
|
|
28727
29859
|
}
|
|
28728
29860
|
const [c0, c1, c2, c3] = [16, 20, 10, 24];
|
|
@@ -28733,12 +29865,12 @@ function registerMcpList(cmd) {
|
|
|
28733
29865
|
"ENV VAR".padEnd(c3),
|
|
28734
29866
|
"STATUS"
|
|
28735
29867
|
].join(" ");
|
|
28736
|
-
console.log(
|
|
28737
|
-
console.log(
|
|
29868
|
+
console.log(pc29.dim(header));
|
|
29869
|
+
console.log(pc29.dim("\u2500".repeat(header.length)));
|
|
28738
29870
|
for (const m of mcps) {
|
|
28739
|
-
const status2 = !m.validated ?
|
|
29871
|
+
const status2 = !m.validated ? pc29.yellow("unvalidated") : m.expired ? pc29.red("expired") : pc29.green("active");
|
|
28740
29872
|
const row = [
|
|
28741
|
-
|
|
29873
|
+
pc29.cyan(m.service.padEnd(c0)),
|
|
28742
29874
|
m.label.padEnd(c1).slice(0, c1),
|
|
28743
29875
|
m.type.padEnd(c2),
|
|
28744
29876
|
(m.envName ?? "\u2014").padEnd(c3).slice(0, c3),
|
|
@@ -28766,13 +29898,13 @@ function registerMcpRemove(cmd) {
|
|
|
28766
29898
|
|
|
28767
29899
|
// src/commands/mcp/status.ts
|
|
28768
29900
|
init_output();
|
|
28769
|
-
import
|
|
29901
|
+
import pc30 from "picocolors";
|
|
28770
29902
|
function formatExpiry(expiresAt) {
|
|
28771
29903
|
if (!expiresAt) return "\u2014";
|
|
28772
29904
|
const ms = expiresAt - Date.now();
|
|
28773
|
-
if (ms <= 0) return
|
|
29905
|
+
if (ms <= 0) return pc30.red("expired");
|
|
28774
29906
|
const hours = ms / (1e3 * 60 * 60);
|
|
28775
|
-
if (hours < 1) return
|
|
29907
|
+
if (hours < 1) return pc30.yellow(`${Math.ceil(ms / 6e4)}m`);
|
|
28776
29908
|
return `${hours.toFixed(1)}h`;
|
|
28777
29909
|
}
|
|
28778
29910
|
function registerMcpStatus(cmd) {
|
|
@@ -28789,14 +29921,14 @@ function registerMcpStatus(cmd) {
|
|
|
28789
29921
|
const mcps = data.mcps ?? [];
|
|
28790
29922
|
printHeader(`MCP Credentials (${mcps.length})`);
|
|
28791
29923
|
if (mcps.length === 0) {
|
|
28792
|
-
console.log(
|
|
29924
|
+
console.log(pc30.dim("No credentials registered."));
|
|
28793
29925
|
return;
|
|
28794
29926
|
}
|
|
28795
29927
|
for (const m of mcps) {
|
|
28796
|
-
const stateColor = !m.validated ?
|
|
29928
|
+
const stateColor = !m.validated ? pc30.yellow : m.expired ? pc30.red : pc30.green;
|
|
28797
29929
|
const stateLabel = !m.validated ? "unvalidated" : m.expired ? "expired" : "active";
|
|
28798
29930
|
console.log(`
|
|
28799
|
-
${
|
|
29931
|
+
${pc30.cyan(m.service)} ${stateColor(`[${stateLabel}]`)}`);
|
|
28800
29932
|
console.log(` label: ${m.label}`);
|
|
28801
29933
|
console.log(` type: ${m.type}`);
|
|
28802
29934
|
if (m.envName) console.log(` env var: ${m.envName}`);
|
|
@@ -28811,15 +29943,15 @@ function registerMcpStatus(cmd) {
|
|
|
28811
29943
|
// src/commands/mcp/import.ts
|
|
28812
29944
|
init_output();
|
|
28813
29945
|
import * as readline3 from "node:readline";
|
|
28814
|
-
import
|
|
29946
|
+
import pc31 from "picocolors";
|
|
28815
29947
|
|
|
28816
29948
|
// src/commands/mcp/import-discovery.ts
|
|
28817
|
-
import * as
|
|
28818
|
-
import * as
|
|
28819
|
-
import * as
|
|
29949
|
+
import * as fs53 from "node:fs";
|
|
29950
|
+
import * as os31 from "node:os";
|
|
29951
|
+
import * as path57 from "node:path";
|
|
28820
29952
|
function readJsonFile(filePath) {
|
|
28821
29953
|
try {
|
|
28822
|
-
const raw =
|
|
29954
|
+
const raw = fs53.readFileSync(filePath, "utf-8");
|
|
28823
29955
|
return JSON.parse(raw);
|
|
28824
29956
|
} catch {
|
|
28825
29957
|
return null;
|
|
@@ -28848,24 +29980,24 @@ function extractMcpServers(obj, source, sourceLabel) {
|
|
|
28848
29980
|
}
|
|
28849
29981
|
function getClaudeDesktopPath() {
|
|
28850
29982
|
if (process.platform === "darwin") {
|
|
28851
|
-
return
|
|
29983
|
+
return path57.join(os31.homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
28852
29984
|
}
|
|
28853
29985
|
if (process.platform === "win32") {
|
|
28854
|
-
const appData = process.env["APPDATA"] ??
|
|
28855
|
-
return
|
|
29986
|
+
const appData = process.env["APPDATA"] ?? path57.join(os31.homedir(), "AppData", "Roaming");
|
|
29987
|
+
return path57.join(appData, "Claude", "claude_desktop_config.json");
|
|
28856
29988
|
}
|
|
28857
|
-
return
|
|
29989
|
+
return path57.join(os31.homedir(), ".config", "Claude", "claude_desktop_config.json");
|
|
28858
29990
|
}
|
|
28859
29991
|
function getOlamRepoPaths() {
|
|
28860
29992
|
const configPaths = [
|
|
28861
|
-
|
|
28862
|
-
|
|
29993
|
+
path57.join(os31.homedir(), ".olam", "config.yaml"),
|
|
29994
|
+
path57.join(process.cwd(), ".olam", "config.yaml")
|
|
28863
29995
|
];
|
|
28864
29996
|
const paths = [];
|
|
28865
29997
|
for (const configPath of configPaths) {
|
|
28866
|
-
if (!
|
|
29998
|
+
if (!fs53.existsSync(configPath)) continue;
|
|
28867
29999
|
try {
|
|
28868
|
-
const raw =
|
|
30000
|
+
const raw = fs53.readFileSync(configPath, "utf-8");
|
|
28869
30001
|
const repoMatches = [...raw.matchAll(/path:\s*["']?([^\s"'\n]+)/g)];
|
|
28870
30002
|
for (const m of repoMatches) {
|
|
28871
30003
|
if (m[1]) paths.push(m[1]);
|
|
@@ -28881,7 +30013,7 @@ async function discoverMcpSources(repoPaths) {
|
|
|
28881
30013
|
const sources = [];
|
|
28882
30014
|
const sourceDefs = [
|
|
28883
30015
|
{
|
|
28884
|
-
path:
|
|
30016
|
+
path: path57.join(os31.homedir(), ".claude.json"),
|
|
28885
30017
|
label: "Claude Code (~/.claude.json)"
|
|
28886
30018
|
},
|
|
28887
30019
|
{
|
|
@@ -28889,19 +30021,19 @@ async function discoverMcpSources(repoPaths) {
|
|
|
28889
30021
|
label: "Claude Desktop"
|
|
28890
30022
|
},
|
|
28891
30023
|
{
|
|
28892
|
-
path:
|
|
30024
|
+
path: path57.join(os31.homedir(), ".cursor", "mcp.json"),
|
|
28893
30025
|
label: "Cursor (~/.cursor/mcp.json)"
|
|
28894
30026
|
},
|
|
28895
30027
|
{
|
|
28896
|
-
path:
|
|
30028
|
+
path: path57.join(os31.homedir(), ".codeium", "windsurf", "mcp_config.json"),
|
|
28897
30029
|
label: "Windsurf (~/.codeium/windsurf/mcp_config.json)"
|
|
28898
30030
|
}
|
|
28899
30031
|
];
|
|
28900
30032
|
const resolvedRepoPaths = repoPaths ?? getOlamRepoPaths();
|
|
28901
30033
|
for (const repoPath of resolvedRepoPaths) {
|
|
28902
30034
|
sourceDefs.push({
|
|
28903
|
-
path:
|
|
28904
|
-
label: `.mcp.json (${
|
|
30035
|
+
path: path57.join(repoPath, ".mcp.json"),
|
|
30036
|
+
label: `.mcp.json (${path57.basename(repoPath)})`
|
|
28905
30037
|
});
|
|
28906
30038
|
}
|
|
28907
30039
|
const reads = await Promise.all(
|
|
@@ -28981,13 +30113,13 @@ async function validateMcpEntry(entry) {
|
|
|
28981
30113
|
// src/commands/mcp/import.ts
|
|
28982
30114
|
async function multiSelectPicker(entries) {
|
|
28983
30115
|
if (entries.length === 0) return [];
|
|
28984
|
-
console.log("\n" +
|
|
30116
|
+
console.log("\n" + pc31.bold("Discovered MCP servers:"));
|
|
28985
30117
|
entries.forEach((e, i) => {
|
|
28986
30118
|
console.log(
|
|
28987
|
-
` ${
|
|
30119
|
+
` ${pc31.dim(`[${i + 1}]`)} ${pc31.cyan(e.name.padEnd(20))} ${pc31.dim(e.sourceLabel)}`
|
|
28988
30120
|
);
|
|
28989
30121
|
});
|
|
28990
|
-
console.log("\n" +
|
|
30122
|
+
console.log("\n" + pc31.dim('Enter numbers to import (e.g. 1,2,3 or "all" or Enter to skip):'));
|
|
28991
30123
|
const answer = await new Promise((resolve15) => {
|
|
28992
30124
|
const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
|
|
28993
30125
|
rl.question("> ", (ans) => {
|
|
@@ -29014,7 +30146,7 @@ function registerMcpImport(cmd) {
|
|
|
29014
30146
|
const repoPaths = opts.repoPaths ? opts.repoPaths.split(",").map((s) => s.trim()) : void 0;
|
|
29015
30147
|
const { entries, sources, durationMs } = await discoverMcpSources(repoPaths);
|
|
29016
30148
|
if (entries.length === 0) {
|
|
29017
|
-
console.log(
|
|
30149
|
+
console.log(pc31.dim("No MCP servers found in any source path."));
|
|
29018
30150
|
return;
|
|
29019
30151
|
}
|
|
29020
30152
|
printInfo("Sources", sources.length > 0 ? sources.join(", ") : "none");
|
|
@@ -29027,15 +30159,15 @@ function registerMcpImport(cmd) {
|
|
|
29027
30159
|
candidates2 = filtered;
|
|
29028
30160
|
}
|
|
29029
30161
|
if (skippedCount > 0) {
|
|
29030
|
-
console.log(
|
|
30162
|
+
console.log(pc31.dim(`skipped: ${skippedCount} already registered`));
|
|
29031
30163
|
}
|
|
29032
30164
|
if (candidates2.length === 0) {
|
|
29033
|
-
console.log(
|
|
30165
|
+
console.log(pc31.dim("Nothing new to import. Use --reimport to force."));
|
|
29034
30166
|
return;
|
|
29035
30167
|
}
|
|
29036
30168
|
const selected = await multiSelectPicker(candidates2);
|
|
29037
30169
|
if (selected.length === 0) {
|
|
29038
|
-
console.log(
|
|
30170
|
+
console.log(pc31.dim("No servers selected."));
|
|
29039
30171
|
return;
|
|
29040
30172
|
}
|
|
29041
30173
|
console.log(`
|
|
@@ -29046,16 +30178,16 @@ Importing ${selected.length} server(s)\u2026`);
|
|
|
29046
30178
|
let validated = false;
|
|
29047
30179
|
let validationReason = "skipped";
|
|
29048
30180
|
if (opts.validate !== false) {
|
|
29049
|
-
process.stdout.write(` ${
|
|
30181
|
+
process.stdout.write(` ${pc31.dim("\u2192")} ${entry.name} validating\u2026 `);
|
|
29050
30182
|
const vr = await validateMcpEntry(entry);
|
|
29051
30183
|
validated = vr.validated;
|
|
29052
30184
|
validationReason = vr.reason;
|
|
29053
30185
|
process.stdout.write(
|
|
29054
|
-
validated ?
|
|
30186
|
+
validated ? pc31.green("ok\n") : pc31.yellow(`unvalidated (${vr.reason})
|
|
29055
30187
|
`)
|
|
29056
30188
|
);
|
|
29057
30189
|
} else {
|
|
29058
|
-
console.log(` ${
|
|
30190
|
+
console.log(` ${pc31.dim("\u2192")} ${entry.name} ${pc31.dim("(validation skipped)")}`);
|
|
29059
30191
|
}
|
|
29060
30192
|
if (validated) validatedCount++;
|
|
29061
30193
|
const result = await client.staticAdd({
|
|
@@ -29070,21 +30202,21 @@ Importing ${selected.length} server(s)\u2026`);
|
|
|
29070
30202
|
}
|
|
29071
30203
|
}
|
|
29072
30204
|
console.log("");
|
|
29073
|
-
console.log(
|
|
30205
|
+
console.log(pc31.green(`\u2713 Imported ${importedCount}/${selected.length}`));
|
|
29074
30206
|
if (validatedCount > 0) {
|
|
29075
|
-
console.log(
|
|
30207
|
+
console.log(pc31.dim(` ${validatedCount} validated, ${importedCount - validatedCount} unvalidated`));
|
|
29076
30208
|
}
|
|
29077
30209
|
});
|
|
29078
30210
|
}
|
|
29079
30211
|
|
|
29080
30212
|
// src/commands/mcp/revoke.ts
|
|
29081
30213
|
init_output();
|
|
29082
|
-
import
|
|
30214
|
+
import pc32 from "picocolors";
|
|
29083
30215
|
function registerMcpRevoke(cmd) {
|
|
29084
30216
|
cmd.command("revoke <user> <world> <service>").description("Revoke a user's MCP service entitlement (multi-tenant mode only)").action(async (user, world, service) => {
|
|
29085
30217
|
const multiTenant = (process.env["OLAM_MCP_MULTI_TENANT"] ?? "").toLowerCase() === "true";
|
|
29086
30218
|
if (!multiTenant) {
|
|
29087
|
-
console.warn(
|
|
30219
|
+
console.warn(pc32.yellow("\u26A0 revocation only meaningful in multi_tenant mode \u2014 no-op"));
|
|
29088
30220
|
return;
|
|
29089
30221
|
}
|
|
29090
30222
|
const baseUrl = process.env["OLAM_MCP_AUTH_SERVICE_URL"] ?? "http://127.0.0.1:9998";
|
|
@@ -29113,8 +30245,8 @@ function registerMcpRevoke(cmd) {
|
|
|
29113
30245
|
process.exitCode = 1;
|
|
29114
30246
|
return;
|
|
29115
30247
|
}
|
|
29116
|
-
console.log(
|
|
29117
|
-
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."));
|
|
29118
30250
|
});
|
|
29119
30251
|
}
|
|
29120
30252
|
|
|
@@ -29136,34 +30268,34 @@ function registerMcp(program2) {
|
|
|
29136
30268
|
// src/commands/memory/start.ts
|
|
29137
30269
|
init_output();
|
|
29138
30270
|
import { spawn as spawn8 } from "node:child_process";
|
|
29139
|
-
import { existsSync as
|
|
29140
|
-
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";
|
|
29141
30273
|
import { pathToFileURL } from "node:url";
|
|
29142
30274
|
|
|
29143
30275
|
// src/commands/memory/_paths.ts
|
|
29144
|
-
import { homedir as
|
|
29145
|
-
import { join as
|
|
30276
|
+
import { homedir as homedir34 } from "node:os";
|
|
30277
|
+
import { join as join57, dirname as dirname29 } from "node:path";
|
|
29146
30278
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
29147
|
-
var OLAM_HOME =
|
|
29148
|
-
var OLAM_BIN_DIR =
|
|
29149
|
-
var III_BINARY_PATH =
|
|
29150
|
-
var MEMORY_PID_PATH =
|
|
29151
|
-
var MEMORY_LOG_PATH =
|
|
29152
|
-
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");
|
|
29153
30285
|
var MEMORY_REST_PORT = 3111;
|
|
29154
30286
|
var MEMORY_LIVEZ_URL = `http://localhost:${MEMORY_REST_PORT}/agentmemory/livez`;
|
|
29155
|
-
var here2 =
|
|
30287
|
+
var here2 = dirname29(fileURLToPath6(import.meta.url));
|
|
29156
30288
|
var candidates = [
|
|
29157
30289
|
// 1. Workspace dev (built): packages/cli/dist/commands/memory/_paths.js → packages/cli → packages/memory-service
|
|
29158
|
-
|
|
30290
|
+
join57(here2, "..", "..", "..", "..", "memory-service"),
|
|
29159
30291
|
// 2a. Workspace bundled: packages/cli/dist/index.js → packages/cli → packages/memory-service
|
|
29160
|
-
|
|
30292
|
+
join57(here2, "..", "..", "memory-service"),
|
|
29161
30293
|
// 2b. Published tarball: <prefix>/node_modules/@pleri/olam-cli/dist/index.js
|
|
29162
30294
|
// → <prefix>/node_modules/@pleri/olam-cli/memory-service-bundle
|
|
29163
30295
|
// (copied at publish time by bundle-cli.mjs)
|
|
29164
|
-
|
|
30296
|
+
join57(here2, "..", "memory-service-bundle"),
|
|
29165
30297
|
// 3. CWD fallback
|
|
29166
|
-
|
|
30298
|
+
join57(process.cwd(), "packages", "memory-service")
|
|
29167
30299
|
];
|
|
29168
30300
|
var MEMORY_SERVICE_CANDIDATES = candidates;
|
|
29169
30301
|
|
|
@@ -29172,7 +30304,7 @@ var READINESS_TIMEOUT_MS = 3e4;
|
|
|
29172
30304
|
var READINESS_POLL_MS = 500;
|
|
29173
30305
|
function resolveMemoryServiceDir() {
|
|
29174
30306
|
for (const c of MEMORY_SERVICE_CANDIDATES) {
|
|
29175
|
-
if (
|
|
30307
|
+
if (existsSync60(c)) return c;
|
|
29176
30308
|
}
|
|
29177
30309
|
throw new Error(
|
|
29178
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.`
|
|
@@ -29180,12 +30312,12 @@ function resolveMemoryServiceDir() {
|
|
|
29180
30312
|
}
|
|
29181
30313
|
function resolveAgentMemoryBin(serviceDir) {
|
|
29182
30314
|
const candidates2 = [
|
|
29183
|
-
|
|
29184
|
-
|
|
29185
|
-
|
|
30315
|
+
join58(serviceDir, "node_modules", ".bin", "agentmemory"),
|
|
30316
|
+
join58(serviceDir, "..", "..", "node_modules", ".bin", "agentmemory"),
|
|
30317
|
+
join58(serviceDir, "..", "..", "..", "node_modules", ".bin", "agentmemory")
|
|
29186
30318
|
];
|
|
29187
30319
|
for (const c of candidates2) {
|
|
29188
|
-
if (
|
|
30320
|
+
if (existsSync60(c)) return c;
|
|
29189
30321
|
}
|
|
29190
30322
|
throw new Error(
|
|
29191
30323
|
`Could not find agentmemory bin. Searched: ${candidates2.join(", ")}. Run 'npm install' from the repo root.`
|
|
@@ -29200,8 +30332,8 @@ function isProcessAlive(pid) {
|
|
|
29200
30332
|
}
|
|
29201
30333
|
}
|
|
29202
30334
|
function readPidFromFile() {
|
|
29203
|
-
if (!
|
|
29204
|
-
const raw =
|
|
30335
|
+
if (!existsSync60(MEMORY_PID_PATH)) return null;
|
|
30336
|
+
const raw = readFileSync41(MEMORY_PID_PATH, "utf8").trim();
|
|
29205
30337
|
const pid = parseInt(raw, 10);
|
|
29206
30338
|
if (!Number.isFinite(pid) || pid <= 0) return null;
|
|
29207
30339
|
return pid;
|
|
@@ -29228,7 +30360,7 @@ async function waitForReady(secret) {
|
|
|
29228
30360
|
return false;
|
|
29229
30361
|
}
|
|
29230
30362
|
async function autoEnsureIiiBinary(serviceDir) {
|
|
29231
|
-
const helperPath =
|
|
30363
|
+
const helperPath = join58(serviceDir, "scripts", "ensure-iii-engine.mjs");
|
|
29232
30364
|
const mod = await import(pathToFileURL(helperPath).href);
|
|
29233
30365
|
const result = await mod.ensureIiiEngine();
|
|
29234
30366
|
if (!result.ok) {
|
|
@@ -29244,7 +30376,7 @@ async function runMemoryStart() {
|
|
|
29244
30376
|
printError(err instanceof Error ? err.message : String(err));
|
|
29245
30377
|
return 1;
|
|
29246
30378
|
}
|
|
29247
|
-
if (!
|
|
30379
|
+
if (!existsSync60(III_BINARY_PATH)) {
|
|
29248
30380
|
printInfo("iii binary", `missing at ${III_BINARY_PATH} \u2014 auto-fetching v0.11.2`);
|
|
29249
30381
|
try {
|
|
29250
30382
|
await autoEnsureIiiBinary(serviceDir);
|
|
@@ -29272,8 +30404,8 @@ async function runMemoryStart() {
|
|
|
29272
30404
|
);
|
|
29273
30405
|
return 1;
|
|
29274
30406
|
}
|
|
29275
|
-
|
|
29276
|
-
|
|
30407
|
+
mkdirSync34(OLAM_HOME, { recursive: true });
|
|
30408
|
+
mkdirSync34(MEMORY_DATA_DIR, { recursive: true });
|
|
29277
30409
|
const logFd = openSync3(MEMORY_LOG_PATH, "a");
|
|
29278
30410
|
const child = spawn8(agentmemoryBin, [], {
|
|
29279
30411
|
cwd: OLAM_HOME,
|
|
@@ -29296,7 +30428,7 @@ async function runMemoryStart() {
|
|
|
29296
30428
|
printError("spawn returned no pid (process failed to start)");
|
|
29297
30429
|
return 1;
|
|
29298
30430
|
}
|
|
29299
|
-
|
|
30431
|
+
writeFileSync25(MEMORY_PID_PATH, `${child.pid}
|
|
29300
30432
|
`, { mode: 420 });
|
|
29301
30433
|
printInfo("pid", `${child.pid}`);
|
|
29302
30434
|
printInfo("readiness", `polling ${MEMORY_LIVEZ_URL}`);
|
|
@@ -29319,7 +30451,7 @@ function registerMemoryStart(cmd) {
|
|
|
29319
30451
|
|
|
29320
30452
|
// src/commands/memory/stop.ts
|
|
29321
30453
|
init_output();
|
|
29322
|
-
import { existsSync as
|
|
30454
|
+
import { existsSync as existsSync61, readFileSync as readFileSync42, unlinkSync as unlinkSync11 } from "node:fs";
|
|
29323
30455
|
var SIGTERM_GRACE_MS = 1e4;
|
|
29324
30456
|
var POLL_MS = 250;
|
|
29325
30457
|
function isAlive(pid) {
|
|
@@ -29332,19 +30464,19 @@ function isAlive(pid) {
|
|
|
29332
30464
|
}
|
|
29333
30465
|
async function runMemoryStop() {
|
|
29334
30466
|
printHeader("olam memory stop");
|
|
29335
|
-
if (!
|
|
30467
|
+
if (!existsSync61(MEMORY_PID_PATH)) {
|
|
29336
30468
|
printSuccess("no pidfile present (nothing to stop)");
|
|
29337
30469
|
return 0;
|
|
29338
30470
|
}
|
|
29339
|
-
const pid = parseInt(
|
|
30471
|
+
const pid = parseInt(readFileSync42(MEMORY_PID_PATH, "utf8").trim(), 10);
|
|
29340
30472
|
if (!Number.isFinite(pid) || pid <= 0) {
|
|
29341
30473
|
printWarning(`pidfile contained invalid value; removing`);
|
|
29342
|
-
|
|
30474
|
+
unlinkSync11(MEMORY_PID_PATH);
|
|
29343
30475
|
return 0;
|
|
29344
30476
|
}
|
|
29345
30477
|
if (!isAlive(pid)) {
|
|
29346
30478
|
printSuccess(`pid ${pid} is not running (stale pidfile); cleaned up`);
|
|
29347
|
-
|
|
30479
|
+
unlinkSync11(MEMORY_PID_PATH);
|
|
29348
30480
|
return 0;
|
|
29349
30481
|
}
|
|
29350
30482
|
printInfo("pid", `${pid}`);
|
|
@@ -29367,8 +30499,8 @@ async function runMemoryStop() {
|
|
|
29367
30499
|
}
|
|
29368
30500
|
await new Promise((r) => setTimeout(r, 500));
|
|
29369
30501
|
}
|
|
29370
|
-
if (
|
|
29371
|
-
|
|
30502
|
+
if (existsSync61(MEMORY_PID_PATH)) {
|
|
30503
|
+
unlinkSync11(MEMORY_PID_PATH);
|
|
29372
30504
|
}
|
|
29373
30505
|
printSuccess(`stopped (pid ${pid})`);
|
|
29374
30506
|
return 0;
|
|
@@ -29382,7 +30514,7 @@ function registerMemoryStop(cmd) {
|
|
|
29382
30514
|
|
|
29383
30515
|
// src/commands/memory/status.ts
|
|
29384
30516
|
init_output();
|
|
29385
|
-
import { existsSync as
|
|
30517
|
+
import { existsSync as existsSync62, readFileSync as readFileSync43 } from "node:fs";
|
|
29386
30518
|
function isAlive2(pid) {
|
|
29387
30519
|
try {
|
|
29388
30520
|
process.kill(pid, 0);
|
|
@@ -29406,8 +30538,8 @@ async function probe(secret) {
|
|
|
29406
30538
|
}
|
|
29407
30539
|
async function collectMemoryStatus() {
|
|
29408
30540
|
let pid = null;
|
|
29409
|
-
if (
|
|
29410
|
-
const raw =
|
|
30541
|
+
if (existsSync62(MEMORY_PID_PATH)) {
|
|
30542
|
+
const raw = readFileSync43(MEMORY_PID_PATH, "utf8").trim();
|
|
29411
30543
|
const parsed = parseInt(raw, 10);
|
|
29412
30544
|
pid = Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
|
29413
30545
|
}
|
|
@@ -29419,7 +30551,7 @@ async function collectMemoryStatus() {
|
|
|
29419
30551
|
alive,
|
|
29420
30552
|
livez,
|
|
29421
30553
|
secretSet: hasMemorySecret(),
|
|
29422
|
-
iiiBinary:
|
|
30554
|
+
iiiBinary: existsSync62(III_BINARY_PATH) ? III_BINARY_PATH : null,
|
|
29423
30555
|
port: MEMORY_REST_PORT
|
|
29424
30556
|
};
|
|
29425
30557
|
}
|
|
@@ -29462,10 +30594,10 @@ function registerMemoryStatus(cmd) {
|
|
|
29462
30594
|
|
|
29463
30595
|
// src/commands/memory/logs.ts
|
|
29464
30596
|
init_output();
|
|
29465
|
-
import { existsSync as
|
|
30597
|
+
import { existsSync as existsSync63 } from "node:fs";
|
|
29466
30598
|
import { spawn as spawn9 } from "node:child_process";
|
|
29467
30599
|
async function runMemoryLogs(opts) {
|
|
29468
|
-
if (!
|
|
30600
|
+
if (!existsSync63(MEMORY_LOG_PATH)) {
|
|
29469
30601
|
printWarning(`no log at ${MEMORY_LOG_PATH} (start the service first via 'olam memory start')`);
|
|
29470
30602
|
return 1;
|
|
29471
30603
|
}
|
|
@@ -29491,7 +30623,7 @@ function registerMemoryLogs(cmd) {
|
|
|
29491
30623
|
}
|
|
29492
30624
|
|
|
29493
30625
|
// src/commands/memory/secret.ts
|
|
29494
|
-
import { existsSync as
|
|
30626
|
+
import { existsSync as existsSync64 } from "node:fs";
|
|
29495
30627
|
init_output();
|
|
29496
30628
|
async function runMemorySecretShow() {
|
|
29497
30629
|
if (!hasMemorySecret()) {
|
|
@@ -29506,7 +30638,7 @@ async function runMemorySecretShow() {
|
|
|
29506
30638
|
}
|
|
29507
30639
|
async function runMemorySecretRotate() {
|
|
29508
30640
|
printHeader("olam memory secret rotate");
|
|
29509
|
-
const wasRunning =
|
|
30641
|
+
const wasRunning = existsSync64(MEMORY_PID_PATH);
|
|
29510
30642
|
if (wasRunning) {
|
|
29511
30643
|
printInfo("current state", "service running; will restart with new secret");
|
|
29512
30644
|
const stopRc = await runMemoryStop();
|
|
@@ -29688,14 +30820,14 @@ function registerMemoryUninstall(cmd) {
|
|
|
29688
30820
|
// src/commands/memory/mode.ts
|
|
29689
30821
|
init_schema2();
|
|
29690
30822
|
init_output();
|
|
29691
|
-
import { existsSync as
|
|
29692
|
-
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";
|
|
29693
30825
|
import * as readline4 from "node:readline/promises";
|
|
29694
30826
|
import { parse as parseYaml6, stringify as stringifyYaml6 } from "yaml";
|
|
29695
30827
|
var CONFIG_REL = ".olam/config.yaml";
|
|
29696
30828
|
function locateConfig(cwd) {
|
|
29697
|
-
const absPath =
|
|
29698
|
-
if (!
|
|
30829
|
+
const absPath = join59(cwd, CONFIG_REL);
|
|
30830
|
+
if (!existsSync65(absPath)) {
|
|
29699
30831
|
throw new Error(
|
|
29700
30832
|
`No ${CONFIG_REL} at ${cwd}. Run \`olam init\` in your workspace root first.`
|
|
29701
30833
|
);
|
|
@@ -29703,7 +30835,7 @@ function locateConfig(cwd) {
|
|
|
29703
30835
|
return { absPath };
|
|
29704
30836
|
}
|
|
29705
30837
|
function readConfigYaml(absPath) {
|
|
29706
|
-
const raw =
|
|
30838
|
+
const raw = readFileSync44(absPath, "utf-8");
|
|
29707
30839
|
const parsed = parseYaml6(raw) ?? {};
|
|
29708
30840
|
if (typeof parsed !== "object" || parsed === null) {
|
|
29709
30841
|
throw new Error(`${absPath} is not a YAML object`);
|
|
@@ -29712,7 +30844,7 @@ function readConfigYaml(absPath) {
|
|
|
29712
30844
|
}
|
|
29713
30845
|
function writeConfigYaml(absPath, parsed) {
|
|
29714
30846
|
const out = stringifyYaml6(parsed, { aliasDuplicateObjects: false });
|
|
29715
|
-
|
|
30847
|
+
writeFileSync26(absPath, out, "utf-8");
|
|
29716
30848
|
}
|
|
29717
30849
|
async function defaultPromptText(question) {
|
|
29718
30850
|
const rl = readline4.createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -29828,10 +30960,371 @@ function registerMemoryMode(cmd) {
|
|
|
29828
30960
|
});
|
|
29829
30961
|
}
|
|
29830
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
|
+
|
|
29831
31324
|
// src/commands/memory/index.ts
|
|
29832
31325
|
function registerMemory(program2) {
|
|
29833
31326
|
const memory = program2.command("memory").description(
|
|
29834
|
-
"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)"
|
|
29835
31328
|
);
|
|
29836
31329
|
registerMemoryStart(memory);
|
|
29837
31330
|
registerMemoryStop(memory);
|
|
@@ -29841,14 +31334,17 @@ function registerMemory(program2) {
|
|
|
29841
31334
|
registerMemoryInstall(memory);
|
|
29842
31335
|
registerMemoryUninstall(memory);
|
|
29843
31336
|
registerMemoryMode(memory);
|
|
31337
|
+
registerMemoryBridge(memory);
|
|
31338
|
+
registerMemoryReclassify(memory);
|
|
31339
|
+
registerMemoryStats(memory);
|
|
29844
31340
|
}
|
|
29845
31341
|
|
|
29846
31342
|
// src/commands/kg-build.ts
|
|
29847
31343
|
init_storage_paths();
|
|
29848
31344
|
init_workspace_name();
|
|
29849
|
-
import * as
|
|
29850
|
-
import * as
|
|
29851
|
-
import * as
|
|
31345
|
+
import * as fs59 from "node:fs";
|
|
31346
|
+
import * as os34 from "node:os";
|
|
31347
|
+
import * as path63 from "node:path";
|
|
29852
31348
|
|
|
29853
31349
|
// ../core/dist/kg/kg-service-client.js
|
|
29854
31350
|
var KG_SERVICE_PORT_DEFAULT = 9997;
|
|
@@ -29859,8 +31355,8 @@ function port() {
|
|
|
29859
31355
|
const n = Number.parseInt(env, 10);
|
|
29860
31356
|
return Number.isFinite(n) && n > 0 ? n : KG_SERVICE_PORT_DEFAULT;
|
|
29861
31357
|
}
|
|
29862
|
-
function url(
|
|
29863
|
-
return `http://127.0.0.1:${port()}${
|
|
31358
|
+
function url(path66) {
|
|
31359
|
+
return `http://127.0.0.1:${port()}${path66}`;
|
|
29864
31360
|
}
|
|
29865
31361
|
function kgServiceHealthUrl() {
|
|
29866
31362
|
return url("/health");
|
|
@@ -29938,40 +31434,40 @@ init_output();
|
|
|
29938
31434
|
// src/commands/kg-status.ts
|
|
29939
31435
|
init_storage_paths();
|
|
29940
31436
|
init_workspace_name();
|
|
29941
|
-
import
|
|
29942
|
-
import { homedir as
|
|
29943
|
-
import
|
|
31437
|
+
import fs54 from "node:fs";
|
|
31438
|
+
import { homedir as homedir35 } from "node:os";
|
|
31439
|
+
import path58 from "node:path";
|
|
29944
31440
|
init_output();
|
|
29945
31441
|
function olamHome4() {
|
|
29946
|
-
return process.env.OLAM_HOME ??
|
|
31442
|
+
return process.env.OLAM_HOME ?? path58.join(homedir35(), ".olam");
|
|
29947
31443
|
}
|
|
29948
31444
|
function kgRoot2() {
|
|
29949
|
-
return
|
|
31445
|
+
return path58.join(olamHome4(), "kg");
|
|
29950
31446
|
}
|
|
29951
31447
|
function worldsRoot2() {
|
|
29952
|
-
return
|
|
31448
|
+
return path58.join(olamHome4(), "worlds");
|
|
29953
31449
|
}
|
|
29954
31450
|
function dirSizeBytes2(dir) {
|
|
29955
|
-
if (!
|
|
31451
|
+
if (!fs54.existsSync(dir)) return 0;
|
|
29956
31452
|
let total = 0;
|
|
29957
31453
|
const stack = [dir];
|
|
29958
31454
|
while (stack.length > 0) {
|
|
29959
31455
|
const cur = stack.pop();
|
|
29960
31456
|
let entries;
|
|
29961
31457
|
try {
|
|
29962
|
-
entries =
|
|
31458
|
+
entries = fs54.readdirSync(cur, { withFileTypes: true });
|
|
29963
31459
|
} catch {
|
|
29964
31460
|
continue;
|
|
29965
31461
|
}
|
|
29966
31462
|
for (const entry of entries) {
|
|
29967
|
-
const full =
|
|
31463
|
+
const full = path58.join(cur, entry.name);
|
|
29968
31464
|
if (entry.isSymbolicLink()) continue;
|
|
29969
31465
|
if (entry.isDirectory()) {
|
|
29970
31466
|
stack.push(full);
|
|
29971
31467
|
continue;
|
|
29972
31468
|
}
|
|
29973
31469
|
try {
|
|
29974
|
-
total +=
|
|
31470
|
+
total += fs54.statSync(full).size;
|
|
29975
31471
|
} catch {
|
|
29976
31472
|
}
|
|
29977
31473
|
}
|
|
@@ -29985,10 +31481,10 @@ function formatBytes5(n) {
|
|
|
29985
31481
|
return `${(n / 1024 / 1024 / 1024).toFixed(2)} GB`;
|
|
29986
31482
|
}
|
|
29987
31483
|
function readFreshness(workspace) {
|
|
29988
|
-
const file =
|
|
29989
|
-
if (!
|
|
31484
|
+
const file = path58.join(kgPristinePath(workspace), "freshness.json");
|
|
31485
|
+
if (!fs54.existsSync(file)) return null;
|
|
29990
31486
|
try {
|
|
29991
|
-
const raw = JSON.parse(
|
|
31487
|
+
const raw = JSON.parse(fs54.readFileSync(file, "utf-8"));
|
|
29992
31488
|
if (raw && typeof raw === "object") return raw;
|
|
29993
31489
|
return null;
|
|
29994
31490
|
} catch {
|
|
@@ -29996,10 +31492,10 @@ function readFreshness(workspace) {
|
|
|
29996
31492
|
}
|
|
29997
31493
|
}
|
|
29998
31494
|
function readOverlayNodeCount(graphifyOutDir) {
|
|
29999
|
-
const graphPath =
|
|
30000
|
-
if (!
|
|
31495
|
+
const graphPath = path58.join(graphifyOutDir, "graph.json");
|
|
31496
|
+
if (!fs54.existsSync(graphPath)) return null;
|
|
30001
31497
|
try {
|
|
30002
|
-
const raw = JSON.parse(
|
|
31498
|
+
const raw = JSON.parse(fs54.readFileSync(graphPath, "utf-8"));
|
|
30003
31499
|
if (raw && typeof raw === "object") {
|
|
30004
31500
|
const nodes = raw.nodes;
|
|
30005
31501
|
if (Array.isArray(nodes)) return nodes.length;
|
|
@@ -30011,28 +31507,28 @@ function readOverlayNodeCount(graphifyOutDir) {
|
|
|
30011
31507
|
}
|
|
30012
31508
|
function listOverlays() {
|
|
30013
31509
|
const root = worldsRoot2();
|
|
30014
|
-
if (!
|
|
31510
|
+
if (!fs54.existsSync(root)) return [];
|
|
30015
31511
|
const records = [];
|
|
30016
31512
|
let worldDirs;
|
|
30017
31513
|
try {
|
|
30018
|
-
worldDirs =
|
|
31514
|
+
worldDirs = fs54.readdirSync(root, { withFileTypes: true });
|
|
30019
31515
|
} catch {
|
|
30020
31516
|
return [];
|
|
30021
31517
|
}
|
|
30022
31518
|
for (const worldEntry of worldDirs) {
|
|
30023
31519
|
if (!worldEntry.isDirectory()) continue;
|
|
30024
31520
|
const worldId = worldEntry.name;
|
|
30025
|
-
const worldDir =
|
|
31521
|
+
const worldDir = path58.join(root, worldId);
|
|
30026
31522
|
let cloneDirs;
|
|
30027
31523
|
try {
|
|
30028
|
-
cloneDirs =
|
|
31524
|
+
cloneDirs = fs54.readdirSync(worldDir, { withFileTypes: true });
|
|
30029
31525
|
} catch {
|
|
30030
31526
|
continue;
|
|
30031
31527
|
}
|
|
30032
31528
|
for (const cloneEntry of cloneDirs) {
|
|
30033
31529
|
if (!cloneEntry.isDirectory()) continue;
|
|
30034
|
-
const graphifyOut =
|
|
30035
|
-
if (!
|
|
31530
|
+
const graphifyOut = path58.join(worldDir, cloneEntry.name, "graphify-out");
|
|
31531
|
+
if (!fs54.existsSync(graphifyOut)) continue;
|
|
30036
31532
|
records.push({
|
|
30037
31533
|
world_id: worldId,
|
|
30038
31534
|
clone_dir: cloneEntry.name,
|
|
@@ -30046,11 +31542,11 @@ function listOverlays() {
|
|
|
30046
31542
|
}
|
|
30047
31543
|
function listPristines(overlays) {
|
|
30048
31544
|
const root = kgRoot2();
|
|
30049
|
-
if (!
|
|
31545
|
+
if (!fs54.existsSync(root)) return [];
|
|
30050
31546
|
const records = [];
|
|
30051
31547
|
let entries;
|
|
30052
31548
|
try {
|
|
30053
|
-
entries =
|
|
31549
|
+
entries = fs54.readdirSync(root, { withFileTypes: true });
|
|
30054
31550
|
} catch {
|
|
30055
31551
|
return [];
|
|
30056
31552
|
}
|
|
@@ -30063,7 +31559,7 @@ function listPristines(overlays) {
|
|
|
30063
31559
|
continue;
|
|
30064
31560
|
}
|
|
30065
31561
|
const fresh = readFreshness(workspace);
|
|
30066
|
-
const graphifyOut =
|
|
31562
|
+
const graphifyOut = path58.join(kgPristinePath(workspace), "graphify-out");
|
|
30067
31563
|
const size = dirSizeBytes2(graphifyOut);
|
|
30068
31564
|
const worldCount = overlays.filter((o) => o.clone_dir === workspace).length;
|
|
30069
31565
|
records.push({
|
|
@@ -30198,11 +31694,11 @@ function registerKgStatusCommand(kg) {
|
|
|
30198
31694
|
init_storage_paths();
|
|
30199
31695
|
init_workspace_name();
|
|
30200
31696
|
init_output();
|
|
30201
|
-
import { spawn as
|
|
30202
|
-
import
|
|
30203
|
-
import
|
|
31697
|
+
import { spawn as spawn11 } from "node:child_process";
|
|
31698
|
+
import fs55 from "node:fs";
|
|
31699
|
+
import path59 from "node:path";
|
|
30204
31700
|
function pidFilePath(workspace) {
|
|
30205
|
-
return
|
|
31701
|
+
return path59.join(kgPristinePath(workspace), ".watch.pid");
|
|
30206
31702
|
}
|
|
30207
31703
|
function isPidAlive3(pid) {
|
|
30208
31704
|
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
@@ -30217,39 +31713,39 @@ function isPidAlive3(pid) {
|
|
|
30217
31713
|
}
|
|
30218
31714
|
function readAndClassifyPid(workspace) {
|
|
30219
31715
|
const file = pidFilePath(workspace);
|
|
30220
|
-
if (!
|
|
31716
|
+
if (!fs55.existsSync(file)) return { status: "no-pidfile", pid: null };
|
|
30221
31717
|
let pid;
|
|
30222
31718
|
try {
|
|
30223
|
-
const raw =
|
|
31719
|
+
const raw = fs55.readFileSync(file, "utf-8").trim();
|
|
30224
31720
|
pid = Number.parseInt(raw, 10);
|
|
30225
31721
|
} catch {
|
|
30226
|
-
|
|
31722
|
+
fs55.rmSync(file, { force: true });
|
|
30227
31723
|
return { status: "stale-reclaimed", pid: null };
|
|
30228
31724
|
}
|
|
30229
31725
|
if (!Number.isInteger(pid) || pid <= 0) {
|
|
30230
|
-
|
|
31726
|
+
fs55.rmSync(file, { force: true });
|
|
30231
31727
|
return { status: "stale-reclaimed", pid: null };
|
|
30232
31728
|
}
|
|
30233
31729
|
if (isPidAlive3(pid)) return { status: "active", pid };
|
|
30234
|
-
|
|
31730
|
+
fs55.rmSync(file, { force: true });
|
|
30235
31731
|
return { status: "stale-reclaimed", pid: null };
|
|
30236
31732
|
}
|
|
30237
31733
|
function writePidFile(workspace, pid) {
|
|
30238
31734
|
const file = pidFilePath(workspace);
|
|
30239
|
-
const dir =
|
|
30240
|
-
|
|
30241
|
-
|
|
31735
|
+
const dir = path59.dirname(file);
|
|
31736
|
+
fs55.mkdirSync(dir, { recursive: true });
|
|
31737
|
+
fs55.writeFileSync(file, String(pid), { encoding: "utf-8" });
|
|
30242
31738
|
}
|
|
30243
31739
|
function removePidFile(workspace) {
|
|
30244
31740
|
const file = pidFilePath(workspace);
|
|
30245
31741
|
try {
|
|
30246
|
-
|
|
31742
|
+
fs55.rmSync(file, { force: true });
|
|
30247
31743
|
} catch {
|
|
30248
31744
|
}
|
|
30249
31745
|
}
|
|
30250
31746
|
async function runKgWatch(workspaceArg, opts, deps = {}) {
|
|
30251
31747
|
const cwd = deps.cwd ?? opts.cwd ?? process.cwd();
|
|
30252
|
-
const name = workspaceArg ??
|
|
31748
|
+
const name = workspaceArg ?? path59.basename(cwd).toLowerCase();
|
|
30253
31749
|
try {
|
|
30254
31750
|
validateWorkspaceName(name);
|
|
30255
31751
|
} catch (err) {
|
|
@@ -30257,7 +31753,7 @@ async function runKgWatch(workspaceArg, opts, deps = {}) {
|
|
|
30257
31753
|
return { exitCode: 1, pidWritten: false };
|
|
30258
31754
|
}
|
|
30259
31755
|
const pristinePath = kgPristinePath(name);
|
|
30260
|
-
const graphPath =
|
|
31756
|
+
const graphPath = path59.join(pristinePath, "graphify-out", "graph.json");
|
|
30261
31757
|
const pidState = readAndClassifyPid(name);
|
|
30262
31758
|
if (pidState.status === "active") {
|
|
30263
31759
|
printError(
|
|
@@ -30268,7 +31764,7 @@ async function runKgWatch(workspaceArg, opts, deps = {}) {
|
|
|
30268
31764
|
if (pidState.status === "stale-reclaimed") {
|
|
30269
31765
|
printInfo("stale-pid", `reclaimed dead PID file at ${pidFilePath(name)}`);
|
|
30270
31766
|
}
|
|
30271
|
-
const spawnFn = deps.spawnImpl ??
|
|
31767
|
+
const spawnFn = deps.spawnImpl ?? spawn11;
|
|
30272
31768
|
const child = spawnFn(
|
|
30273
31769
|
"graphify",
|
|
30274
31770
|
[cwd, "--watch", "--update", "--graph", graphPath],
|
|
@@ -30320,7 +31816,7 @@ function registerKgWatchCommand(kg) {
|
|
|
30320
31816
|
}
|
|
30321
31817
|
|
|
30322
31818
|
// src/commands/kg-classify.ts
|
|
30323
|
-
import
|
|
31819
|
+
import pc33 from "picocolors";
|
|
30324
31820
|
init_output();
|
|
30325
31821
|
function registerKgClassifyCommand(kg) {
|
|
30326
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) => {
|
|
@@ -30330,7 +31826,7 @@ function registerKgClassifyCommand(kg) {
|
|
|
30330
31826
|
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
30331
31827
|
return;
|
|
30332
31828
|
}
|
|
30333
|
-
const routeStr = result.route === "kg" ?
|
|
31829
|
+
const routeStr = result.route === "kg" ? pc33.cyan("kg") : result.route === "grep" ? pc33.magenta("grep") : pc33.yellow("both");
|
|
30334
31830
|
printInfo("route", `${routeStr} (layer ${result.layer}, took ${result.took_ms}ms)`);
|
|
30335
31831
|
printInfo("confidence", String(result.confidence));
|
|
30336
31832
|
printInfo("reason", result.reason);
|
|
@@ -30352,7 +31848,7 @@ function registerKgClassifyCommand(kg) {
|
|
|
30352
31848
|
}
|
|
30353
31849
|
|
|
30354
31850
|
// src/commands/kg-doctor.ts
|
|
30355
|
-
import
|
|
31851
|
+
import pc34 from "picocolors";
|
|
30356
31852
|
init_output();
|
|
30357
31853
|
async function runProbes() {
|
|
30358
31854
|
const results = [];
|
|
@@ -30471,12 +31967,12 @@ function registerKgDoctorCommand(kg) {
|
|
|
30471
31967
|
} else {
|
|
30472
31968
|
printHeader("kg-service doctor");
|
|
30473
31969
|
for (const p of probes) {
|
|
30474
|
-
const badge = p.status === "ok" ?
|
|
31970
|
+
const badge = p.status === "ok" ? pc34.green("\u2713") : p.status === "warn" ? pc34.yellow("\u2298") : pc34.red("\u2717");
|
|
30475
31971
|
const label = `${badge} ${p.name}`;
|
|
30476
31972
|
const detail = p.detail ?? "";
|
|
30477
31973
|
printInfo(label, detail);
|
|
30478
31974
|
if (p.remedy) {
|
|
30479
|
-
process.stderr.write(` ${
|
|
31975
|
+
process.stderr.write(` ${pc34.dim("remedy:")} ${p.remedy}
|
|
30480
31976
|
`);
|
|
30481
31977
|
}
|
|
30482
31978
|
}
|
|
@@ -30494,14 +31990,14 @@ function registerKgDoctorCommand(kg) {
|
|
|
30494
31990
|
}
|
|
30495
31991
|
|
|
30496
31992
|
// src/commands/kg-install-hook.ts
|
|
30497
|
-
import * as
|
|
30498
|
-
import * as
|
|
30499
|
-
import * as
|
|
31993
|
+
import * as fs57 from "node:fs";
|
|
31994
|
+
import * as path61 from "node:path";
|
|
31995
|
+
import * as os32 from "node:os";
|
|
30500
31996
|
|
|
30501
31997
|
// ../core/dist/world/merge-settings.js
|
|
30502
|
-
import * as
|
|
30503
|
-
import * as
|
|
30504
|
-
import * as
|
|
31998
|
+
import * as fs56 from "node:fs";
|
|
31999
|
+
import * as path60 from "node:path";
|
|
32000
|
+
import * as crypto8 from "node:crypto";
|
|
30505
32001
|
function mergeHomeSettingsJson(filePath, options) {
|
|
30506
32002
|
let settings;
|
|
30507
32003
|
try {
|
|
@@ -30560,10 +32056,10 @@ function mergeHomeSettingsJson(filePath, options) {
|
|
|
30560
32056
|
return { status: "installed", message: `settings.json updated at ${filePath}` };
|
|
30561
32057
|
}
|
|
30562
32058
|
function readSettings(filePath) {
|
|
30563
|
-
if (!
|
|
32059
|
+
if (!fs56.existsSync(filePath)) {
|
|
30564
32060
|
return {};
|
|
30565
32061
|
}
|
|
30566
|
-
const raw =
|
|
32062
|
+
const raw = fs56.readFileSync(filePath, "utf-8");
|
|
30567
32063
|
if (!raw.trim())
|
|
30568
32064
|
return {};
|
|
30569
32065
|
return JSON.parse(raw);
|
|
@@ -30584,13 +32080,13 @@ function isHookSentinelPresent(matchers, sentinel) {
|
|
|
30584
32080
|
return false;
|
|
30585
32081
|
}
|
|
30586
32082
|
function atomicWriteJson(filePath, data) {
|
|
30587
|
-
const dir =
|
|
30588
|
-
|
|
30589
|
-
const rand =
|
|
32083
|
+
const dir = path60.dirname(filePath);
|
|
32084
|
+
fs56.mkdirSync(dir, { recursive: true });
|
|
32085
|
+
const rand = crypto8.randomBytes(6).toString("hex");
|
|
30590
32086
|
const tmp = `${filePath}.tmp.${process.pid}.${rand}`;
|
|
30591
32087
|
const json = JSON.stringify(data, null, 2) + "\n";
|
|
30592
|
-
|
|
30593
|
-
|
|
32088
|
+
fs56.writeFileSync(tmp, json, { mode: 420 });
|
|
32089
|
+
fs56.renameSync(tmp, filePath);
|
|
30594
32090
|
}
|
|
30595
32091
|
|
|
30596
32092
|
// ../core/dist/kg/hook-template.js
|
|
@@ -30642,15 +32138,15 @@ function buildHookMatcherEntry(opts) {
|
|
|
30642
32138
|
init_output();
|
|
30643
32139
|
function settingsPathFor(scope) {
|
|
30644
32140
|
if (scope === "user") {
|
|
30645
|
-
return
|
|
32141
|
+
return path61.join(os32.homedir(), ".claude", "settings.json");
|
|
30646
32142
|
}
|
|
30647
|
-
return
|
|
32143
|
+
return path61.join(process.cwd(), ".claude", "settings.json");
|
|
30648
32144
|
}
|
|
30649
32145
|
function backup(filePath) {
|
|
30650
|
-
if (!
|
|
32146
|
+
if (!fs57.existsSync(filePath)) return null;
|
|
30651
32147
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
30652
32148
|
const backupPath = `${filePath}.olam-bak.${ts}`;
|
|
30653
|
-
|
|
32149
|
+
fs57.copyFileSync(filePath, backupPath);
|
|
30654
32150
|
return backupPath;
|
|
30655
32151
|
}
|
|
30656
32152
|
function registerKgInstallHookCommand(kg) {
|
|
@@ -30658,9 +32154,9 @@ function registerKgInstallHookCommand(kg) {
|
|
|
30658
32154
|
const scope = opts.scope === "user" ? "user" : "project";
|
|
30659
32155
|
const filePath = settingsPathFor(scope);
|
|
30660
32156
|
try {
|
|
30661
|
-
|
|
32157
|
+
fs57.mkdirSync(path61.dirname(filePath), { recursive: true });
|
|
30662
32158
|
} catch (err) {
|
|
30663
|
-
printError(`could not create ${
|
|
32159
|
+
printError(`could not create ${path61.dirname(filePath)}: ${err instanceof Error ? err.message : String(err)}`);
|
|
30664
32160
|
process.exitCode = 1;
|
|
30665
32161
|
return;
|
|
30666
32162
|
}
|
|
@@ -30684,7 +32180,7 @@ function registerKgInstallHookCommand(kg) {
|
|
|
30684
32180
|
printInfo("kg-service hook", `already installed at ${filePath}`);
|
|
30685
32181
|
if (backupPath) {
|
|
30686
32182
|
try {
|
|
30687
|
-
|
|
32183
|
+
fs57.unlinkSync(backupPath);
|
|
30688
32184
|
} catch {
|
|
30689
32185
|
}
|
|
30690
32186
|
}
|
|
@@ -30702,15 +32198,15 @@ function registerKgInstallHookCommand(kg) {
|
|
|
30702
32198
|
}
|
|
30703
32199
|
|
|
30704
32200
|
// src/commands/kg-uninstall-hook.ts
|
|
30705
|
-
import * as
|
|
30706
|
-
import * as
|
|
30707
|
-
import * as
|
|
32201
|
+
import * as fs58 from "node:fs";
|
|
32202
|
+
import * as path62 from "node:path";
|
|
32203
|
+
import * as os33 from "node:os";
|
|
30708
32204
|
init_output();
|
|
30709
32205
|
function settingsPathFor2(scope) {
|
|
30710
32206
|
if (scope === "user") {
|
|
30711
|
-
return
|
|
32207
|
+
return path62.join(os33.homedir(), ".claude", "settings.json");
|
|
30712
32208
|
}
|
|
30713
|
-
return
|
|
32209
|
+
return path62.join(process.cwd(), ".claude", "settings.json");
|
|
30714
32210
|
}
|
|
30715
32211
|
function dropSentinel(matchers) {
|
|
30716
32212
|
let changed = false;
|
|
@@ -30740,13 +32236,13 @@ function registerKgUninstallHookCommand(kg) {
|
|
|
30740
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) => {
|
|
30741
32237
|
const scope = opts.scope === "user" ? "user" : "project";
|
|
30742
32238
|
const filePath = settingsPathFor2(scope);
|
|
30743
|
-
if (!
|
|
32239
|
+
if (!fs58.existsSync(filePath)) {
|
|
30744
32240
|
printInfo("kg-service hook", `no settings.json at ${filePath} \u2014 nothing to remove`);
|
|
30745
32241
|
return;
|
|
30746
32242
|
}
|
|
30747
32243
|
let settings;
|
|
30748
32244
|
try {
|
|
30749
|
-
const raw =
|
|
32245
|
+
const raw = fs58.readFileSync(filePath, "utf-8");
|
|
30750
32246
|
settings = raw.trim() ? JSON.parse(raw) : {};
|
|
30751
32247
|
} catch (err) {
|
|
30752
32248
|
printError(`could not parse ${filePath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -30766,7 +32262,7 @@ function registerKgUninstallHookCommand(kg) {
|
|
|
30766
32262
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
30767
32263
|
const backupPath = `${filePath}.olam-bak.${ts}`;
|
|
30768
32264
|
try {
|
|
30769
|
-
|
|
32265
|
+
fs58.copyFileSync(filePath, backupPath);
|
|
30770
32266
|
} catch {
|
|
30771
32267
|
}
|
|
30772
32268
|
const next = {
|
|
@@ -30785,7 +32281,7 @@ function registerKgUninstallHookCommand(kg) {
|
|
|
30785
32281
|
}
|
|
30786
32282
|
}
|
|
30787
32283
|
try {
|
|
30788
|
-
|
|
32284
|
+
fs58.writeFileSync(filePath, JSON.stringify(next, null, 2) + "\n");
|
|
30789
32285
|
printSuccess(`kg-service hook removed from ${filePath}`);
|
|
30790
32286
|
printInfo("backup", backupPath);
|
|
30791
32287
|
} catch (err) {
|
|
@@ -30859,20 +32355,20 @@ function registerKgSavingsCommand(kg) {
|
|
|
30859
32355
|
// src/commands/kg-build.ts
|
|
30860
32356
|
function resolveWorkspace(arg) {
|
|
30861
32357
|
const cwd = process.cwd();
|
|
30862
|
-
const name = arg ??
|
|
32358
|
+
const name = arg ?? path63.basename(cwd).toLowerCase();
|
|
30863
32359
|
validateWorkspaceName(name);
|
|
30864
32360
|
return { name, sourcePath: cwd };
|
|
30865
32361
|
}
|
|
30866
32362
|
function toContainerPath(hostPath) {
|
|
30867
|
-
const home =
|
|
30868
|
-
const resolved =
|
|
30869
|
-
if (!resolved.startsWith(home +
|
|
32363
|
+
const home = os34.homedir();
|
|
32364
|
+
const resolved = path63.resolve(hostPath);
|
|
32365
|
+
if (!resolved.startsWith(home + path63.sep) && resolved !== home) {
|
|
30870
32366
|
throw new Error(
|
|
30871
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.`
|
|
30872
32368
|
);
|
|
30873
32369
|
}
|
|
30874
|
-
const rel =
|
|
30875
|
-
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("/"));
|
|
30876
32372
|
}
|
|
30877
32373
|
async function runKgBuild(workspaceArg, options = {}) {
|
|
30878
32374
|
let workspace;
|
|
@@ -30890,7 +32386,7 @@ async function runKgBuild(workspaceArg, options = {}) {
|
|
|
30890
32386
|
return { exitCode: 2 };
|
|
30891
32387
|
}
|
|
30892
32388
|
const outDir = kgPristinePath(workspace.name);
|
|
30893
|
-
|
|
32389
|
+
fs59.mkdirSync(outDir, { recursive: true });
|
|
30894
32390
|
const human = !options.json;
|
|
30895
32391
|
if (human) {
|
|
30896
32392
|
printInfo("kg build", `workspace=${workspace.name} source=${workspace.sourcePath}`);
|
|
@@ -30923,12 +32419,12 @@ async function runKgBuild(workspaceArg, options = {}) {
|
|
|
30923
32419
|
workspace: workspace.name,
|
|
30924
32420
|
graphify_path: "container"
|
|
30925
32421
|
};
|
|
30926
|
-
|
|
30927
|
-
|
|
32422
|
+
fs59.writeFileSync(
|
|
32423
|
+
path63.join(outDir, "freshness.json"),
|
|
30928
32424
|
JSON.stringify(freshness, null, 2) + "\n",
|
|
30929
32425
|
"utf-8"
|
|
30930
32426
|
);
|
|
30931
|
-
const finalOut =
|
|
32427
|
+
const finalOut = path63.join(outDir, "graphify-out");
|
|
30932
32428
|
if (options.json) {
|
|
30933
32429
|
process.stdout.write(JSON.stringify(freshness) + "\n");
|
|
30934
32430
|
} else {
|
|
@@ -31205,17 +32701,17 @@ init_manager();
|
|
|
31205
32701
|
init_context();
|
|
31206
32702
|
init_output();
|
|
31207
32703
|
import { spawnSync as defaultSpawnSync } from "node:child_process";
|
|
31208
|
-
import * as
|
|
31209
|
-
import * as
|
|
31210
|
-
import * as
|
|
32704
|
+
import * as fs60 from "node:fs";
|
|
32705
|
+
import * as os35 from "node:os";
|
|
32706
|
+
import * as path64 from "node:path";
|
|
31211
32707
|
function devboxContainerName(worldId) {
|
|
31212
32708
|
return `olam-${worldId}-devbox`;
|
|
31213
32709
|
}
|
|
31214
32710
|
function olamHomeDir() {
|
|
31215
|
-
return process.env["OLAM_HOME"] ??
|
|
32711
|
+
return process.env["OLAM_HOME"] ?? path64.join(os35.homedir(), ".olam");
|
|
31216
32712
|
}
|
|
31217
|
-
function defaultRestartContainer(name,
|
|
31218
|
-
const r =
|
|
32713
|
+
function defaultRestartContainer(name, spawn12 = defaultSpawnSync) {
|
|
32714
|
+
const r = spawn12("docker", ["restart", "--time", "30", name], {
|
|
31219
32715
|
encoding: "utf-8",
|
|
31220
32716
|
stdio: ["ignore", "pipe", "pipe"]
|
|
31221
32717
|
});
|
|
@@ -31225,8 +32721,8 @@ function defaultRestartContainer(name, spawn11 = defaultSpawnSync) {
|
|
|
31225
32721
|
};
|
|
31226
32722
|
}
|
|
31227
32723
|
function defaultAppendAuditLog(homeDir, line) {
|
|
31228
|
-
|
|
31229
|
-
|
|
32724
|
+
fs60.mkdirSync(homeDir, { recursive: true });
|
|
32725
|
+
fs60.appendFileSync(path64.join(homeDir, "usage.log"), line.endsWith("\n") ? line : line + "\n", {
|
|
31230
32726
|
encoding: "utf-8"
|
|
31231
32727
|
});
|
|
31232
32728
|
}
|
|
@@ -31271,19 +32767,19 @@ async function doRekey(worldId, deps) {
|
|
|
31271
32767
|
);
|
|
31272
32768
|
const rotatedAt = deps.now().toISOString();
|
|
31273
32769
|
const homeDir = deps.olamHomeDir();
|
|
31274
|
-
const worldDir =
|
|
31275
|
-
|
|
31276
|
-
const credentialsPath =
|
|
32770
|
+
const worldDir = path64.join(homeDir, "worlds", worldId);
|
|
32771
|
+
fs60.mkdirSync(worldDir, { recursive: true });
|
|
32772
|
+
const credentialsPath = path64.join(worldDir, "credentials.json");
|
|
31277
32773
|
const payload = {
|
|
31278
32774
|
worldRoleName,
|
|
31279
32775
|
password,
|
|
31280
32776
|
rotatedAt
|
|
31281
32777
|
};
|
|
31282
|
-
|
|
32778
|
+
fs60.writeFileSync(credentialsPath, JSON.stringify(payload, null, 2) + "\n", {
|
|
31283
32779
|
encoding: "utf-8",
|
|
31284
32780
|
mode: 384
|
|
31285
32781
|
});
|
|
31286
|
-
|
|
32782
|
+
fs60.chmodSync(credentialsPath, 384);
|
|
31287
32783
|
const restart = deps.restartContainer(devboxContainerName(worldId));
|
|
31288
32784
|
deps.appendAuditLog(`${rotatedAt} ${worldId} rekey`);
|
|
31289
32785
|
if (!restart.ok) {
|
|
@@ -31347,18 +32843,18 @@ function registerRekey(program2) {
|
|
|
31347
32843
|
}
|
|
31348
32844
|
|
|
31349
32845
|
// src/pleri-config.ts
|
|
31350
|
-
import * as
|
|
31351
|
-
import * as
|
|
32846
|
+
import * as fs61 from "node:fs";
|
|
32847
|
+
import * as path65 from "node:path";
|
|
31352
32848
|
function isPleriConfigured(configDir = process.env.OLAM_CONFIG_DIR ?? ".olam") {
|
|
31353
32849
|
if (process.env.PLERI_BASE_URL) {
|
|
31354
32850
|
return true;
|
|
31355
32851
|
}
|
|
31356
|
-
const configPath =
|
|
31357
|
-
if (!
|
|
32852
|
+
const configPath = path65.join(configDir, "config.yaml");
|
|
32853
|
+
if (!fs61.existsSync(configPath)) {
|
|
31358
32854
|
return false;
|
|
31359
32855
|
}
|
|
31360
32856
|
try {
|
|
31361
|
-
const contents =
|
|
32857
|
+
const contents = fs61.readFileSync(configPath, "utf8");
|
|
31362
32858
|
return /^[^#\n]*\bpleri:/m.test(contents);
|
|
31363
32859
|
} catch {
|
|
31364
32860
|
return false;
|
|
@@ -31423,4 +32919,6 @@ registerKg(program);
|
|
|
31423
32919
|
registerConfig(program);
|
|
31424
32920
|
registerRepos(program);
|
|
31425
32921
|
registerRunbooks(program);
|
|
32922
|
+
registerSkillsSource(program);
|
|
32923
|
+
registerSkills(program);
|
|
31426
32924
|
program.parse();
|