@pleri/olam-cli 0.1.195 → 0.1.198
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/README.md +52 -0
- package/dist/ask/knowledge-pack.generated.d.ts.map +1 -1
- package/dist/ask/knowledge-pack.generated.js +12 -8
- package/dist/ask/knowledge-pack.generated.js.map +1 -1
- package/dist/commands/auth-list-json.d.ts +34 -0
- package/dist/commands/auth-list-json.d.ts.map +1 -1
- package/dist/commands/auth-list-json.js +24 -0
- package/dist/commands/auth-list-json.js.map +1 -1
- package/dist/commands/auth-migrate.d.ts +212 -0
- package/dist/commands/auth-migrate.d.ts.map +1 -0
- package/dist/commands/auth-migrate.js +465 -0
- package/dist/commands/auth-migrate.js.map +1 -0
- package/dist/commands/auth.d.ts.map +1 -1
- package/dist/commands/auth.js +239 -184
- package/dist/commands/auth.js.map +1 -1
- package/dist/commands/bootstrap.d.ts +4 -0
- package/dist/commands/bootstrap.d.ts.map +1 -1
- package/dist/commands/bootstrap.js +6 -0
- package/dist/commands/bootstrap.js.map +1 -1
- package/dist/commands/dispatch.d.ts.map +1 -1
- package/dist/commands/dispatch.js +11 -1
- package/dist/commands/dispatch.js.map +1 -1
- package/dist/commands/doctor.d.ts +33 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +299 -12
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/kg-mirror.d.ts +18 -2
- package/dist/commands/kg-mirror.d.ts.map +1 -1
- package/dist/commands/kg-mirror.js +78 -3
- package/dist/commands/kg-mirror.js.map +1 -1
- package/dist/commands/mcp/complete.d.ts +36 -0
- package/dist/commands/mcp/complete.d.ts.map +1 -0
- package/dist/commands/mcp/complete.js +66 -0
- package/dist/commands/mcp/complete.js.map +1 -0
- package/dist/commands/mcp/index.d.ts +1 -1
- package/dist/commands/mcp/index.d.ts.map +1 -1
- package/dist/commands/mcp/index.js +3 -1
- package/dist/commands/mcp/index.js.map +1 -1
- package/dist/commands/memory/bridge.d.ts +1 -1
- package/dist/commands/memory/bridge.d.ts.map +1 -1
- package/dist/commands/memory/bridge.js +2 -6
- package/dist/commands/memory/bridge.js.map +1 -1
- package/dist/commands/memory/secret.d.ts.map +1 -1
- package/dist/commands/memory/secret.js +4 -3
- package/dist/commands/memory/secret.js.map +1 -1
- package/dist/commands/observe.d.ts +3 -3
- package/dist/commands/observe.d.ts.map +1 -1
- package/dist/commands/observe.js +11 -8
- package/dist/commands/observe.js.map +1 -1
- package/dist/commands/runbooks.d.ts.map +1 -1
- package/dist/commands/runbooks.js +77 -10
- package/dist/commands/runbooks.js.map +1 -1
- package/dist/commands/services-tls.d.ts.map +1 -1
- package/dist/commands/services-tls.js +65 -10
- package/dist/commands/services-tls.js.map +1 -1
- package/dist/commands/services.d.ts +35 -1
- package/dist/commands/services.d.ts.map +1 -1
- package/dist/commands/services.js +153 -32
- package/dist/commands/services.js.map +1 -1
- package/dist/commands/setup-phase-8-kg-hook.d.ts +48 -0
- package/dist/commands/setup-phase-8-kg-hook.d.ts.map +1 -0
- package/dist/commands/setup-phase-8-kg-hook.js +93 -0
- package/dist/commands/setup-phase-8-kg-hook.js.map +1 -0
- package/dist/commands/setup-phase-9-memory-bridge.d.ts +36 -0
- package/dist/commands/setup-phase-9-memory-bridge.d.ts.map +1 -0
- package/dist/commands/setup-phase-9-memory-bridge.js +59 -0
- package/dist/commands/setup-phase-9-memory-bridge.js.map +1 -0
- package/dist/commands/setup.d.ts +34 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +372 -32
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/skills-source.d.ts.map +1 -1
- package/dist/commands/skills-source.js +70 -1
- package/dist/commands/skills-source.js.map +1 -1
- package/dist/commands/update.d.ts +24 -0
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +53 -0
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/upgrade.d.ts +5 -0
- package/dist/commands/upgrade.d.ts.map +1 -1
- package/dist/commands/upgrade.js +31 -8
- package/dist/commands/upgrade.js.map +1 -1
- package/dist/image-digests.json +8 -8
- package/dist/index.js +4487 -2451
- package/dist/lib/auth-backend.d.ts +168 -0
- package/dist/lib/auth-backend.d.ts.map +1 -0
- package/dist/lib/auth-backend.js +172 -0
- package/dist/lib/auth-backend.js.map +1 -0
- package/dist/lib/auth-list-cache.d.ts +67 -0
- package/dist/lib/auth-list-cache.d.ts.map +1 -0
- package/dist/lib/auth-list-cache.js +84 -0
- package/dist/lib/auth-list-cache.js.map +1 -0
- package/dist/lib/auth-list.d.ts +107 -0
- package/dist/lib/auth-list.d.ts.map +1 -0
- package/dist/lib/auth-list.js +123 -0
- package/dist/lib/auth-list.js.map +1 -0
- package/dist/lib/auth-login.d.ts +92 -0
- package/dist/lib/auth-login.d.ts.map +1 -0
- package/dist/lib/auth-login.js +124 -0
- package/dist/lib/auth-login.js.map +1 -0
- package/dist/lib/auth-mutator-backend.d.ts +54 -0
- package/dist/lib/auth-mutator-backend.d.ts.map +1 -0
- package/dist/lib/auth-mutator-backend.js +62 -0
- package/dist/lib/auth-mutator-backend.js.map +1 -0
- package/dist/lib/auth-remote.d.ts +50 -0
- package/dist/lib/auth-remote.d.ts.map +1 -1
- package/dist/lib/auth-remote.js +84 -2
- package/dist/lib/auth-remote.js.map +1 -1
- package/dist/lib/bootstrap-kubernetes.d.ts +69 -10
- package/dist/lib/bootstrap-kubernetes.d.ts.map +1 -1
- package/dist/lib/bootstrap-kubernetes.js +264 -46
- package/dist/lib/bootstrap-kubernetes.js.map +1 -1
- package/dist/lib/config.d.ts +35 -4
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +82 -11
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/health-probes.d.ts +0 -22
- package/dist/lib/health-probes.d.ts.map +1 -1
- package/dist/lib/health-probes.js +57 -0
- package/dist/lib/health-probes.js.map +1 -1
- package/dist/lib/peripheral-registry.d.ts +11 -0
- package/dist/lib/peripheral-registry.d.ts.map +1 -1
- package/dist/lib/peripheral-registry.js +5 -0
- package/dist/lib/peripheral-registry.js.map +1 -1
- package/dist/lib/plans-client.d.ts.map +1 -1
- package/dist/lib/plans-client.js +6 -3
- package/dist/lib/plans-client.js.map +1 -1
- package/dist/mcp-server.js +138 -6
- package/hermes-bundle/version.json +1 -1
- package/host-cp/k8s/manifests/30-configmap.yaml +4 -0
- package/host-cp/k8s/manifests/50-deployment.yaml +13 -1
- package/host-cp/k8s/manifests/65-tls-secret-template.yaml.tmpl +35 -0
- package/host-cp/k8s/manifests/auth-service/50-deployment.yaml +1 -1
- package/host-cp/k8s/manifests/kg-service/50-deployment.yaml +1 -1
- package/host-cp/k8s/manifests/mcp-auth-service/50-deployment.yaml +1 -1
- package/host-cp/k8s/manifests/memory-service/50-deployment.yaml +1 -1
- package/host-cp/src/dispatch-persister.mjs +157 -0
- package/host-cp/src/pr-nanny.mjs +7 -0
- package/host-cp/src/server.mjs +175 -3
- package/host-cp/src/world-watchdog-pid-lookup.mjs +119 -0
- package/host-cp/src/world-watchdog-probes.mjs +271 -0
- package/host-cp/src/world-watchdog-recovery.mjs +192 -0
- package/host-cp/src/world-watchdog.mjs +313 -0
- package/package.json +1 -1
package/dist/mcp-server.js
CHANGED
|
@@ -11497,7 +11497,14 @@ var init_schema2 = __esm({
|
|
|
11497
11497
|
// a doc-lookup task routes to `cloudflare-isolate` when its worker_url is
|
|
11498
11498
|
// set). Never routes to an unconfigured tier; falls back to `default` with
|
|
11499
11499
|
// an explicit reason. See packages/core/src/dispatch/tier-selection.ts.
|
|
11500
|
-
verifiability_routing: external_exports.boolean().optional().default(false)
|
|
11500
|
+
verifiability_routing: external_exports.boolean().optional().default(false),
|
|
11501
|
+
// Phase B auto-recovery — off by default (backward-compatible with all
|
|
11502
|
+
// existing .olam/config.yaml files that omit this field).
|
|
11503
|
+
// false — detection only; never kills (default)
|
|
11504
|
+
// true — kill + replay when wedge confirmed + rate-limit allows
|
|
11505
|
+
// 'dry-run' — emits breadcrumbs but never kills (confidence mode)
|
|
11506
|
+
// See docs/architecture/world-watchdog.md Recovery section.
|
|
11507
|
+
autoRecover: external_exports.union([external_exports.boolean(), external_exports.literal("dry-run")]).default(false)
|
|
11501
11508
|
});
|
|
11502
11509
|
CostConfigSchema = external_exports.object({
|
|
11503
11510
|
max_per_world_usd: external_exports.number().positive().default(50),
|
|
@@ -11695,7 +11702,10 @@ var init_defaults = __esm({
|
|
|
11695
11702
|
providers: {},
|
|
11696
11703
|
// Verifiability-routed tier selection is opt-in (ADR 022 §Consequences #2);
|
|
11697
11704
|
// the default is OFF so dispatch behaviour is unchanged out of the box.
|
|
11698
|
-
verifiability_routing: false
|
|
11705
|
+
verifiability_routing: false,
|
|
11706
|
+
// World-watchdog auto-recovery is opt-in; default OFF so existing operators
|
|
11707
|
+
// see no behaviour change until they explicitly enable it in config.
|
|
11708
|
+
autoRecover: false
|
|
11699
11709
|
};
|
|
11700
11710
|
DEFAULT_COST = {
|
|
11701
11711
|
max_per_world_usd: 50,
|
|
@@ -11983,7 +11993,17 @@ var init_source_config_schema = __esm({
|
|
|
11983
11993
|
* `SkillSourceSchema.prefixScope`. Operators inherit this when their
|
|
11984
11994
|
* `~/.olam/config.json` has no explicit `prefixScope` override.
|
|
11985
11995
|
*/
|
|
11986
|
-
prefixScope: external_exports.array(external_exports.enum(["skill", "agent"])).optional()
|
|
11996
|
+
prefixScope: external_exports.array(external_exports.enum(["skill", "agent"])).optional(),
|
|
11997
|
+
/**
|
|
11998
|
+
* Source's RECOMMENDED prefixTarget patterns. Same semantics as
|
|
11999
|
+
* operator-side `SkillSourceSchema.prefixTarget`: an array of glob
|
|
12000
|
+
* patterns (only `*` wildcard) that restrict which canonical names are
|
|
12001
|
+
* renamed. Operators inherit this when their `~/.olam/config.json` has
|
|
12002
|
+
* no explicit `prefixTarget` override.
|
|
12003
|
+
*
|
|
12004
|
+
* See: docs/decisions/022-prefix-target-name-patterns.md.
|
|
12005
|
+
*/
|
|
12006
|
+
prefixTarget: external_exports.array(external_exports.string().min(1, "prefixTarget pattern must be a non-empty string")).optional()
|
|
11987
12007
|
}).strict();
|
|
11988
12008
|
SourceConfigSnapshotSchema = SourceConfigSchema;
|
|
11989
12009
|
}
|
|
@@ -12029,6 +12049,29 @@ var init_schema3 = __esm({
|
|
|
12029
12049
|
* NOT applicable when `prefix` is undefined.
|
|
12030
12050
|
*/
|
|
12031
12051
|
prefixScope: external_exports.array(external_exports.enum(["skill", "agent"])).optional(),
|
|
12052
|
+
/**
|
|
12053
|
+
* Restrict prefix renaming to canonical names matching at least one glob
|
|
12054
|
+
* pattern. Patterns support `*` as a wildcard (zero or more chars); no
|
|
12055
|
+
* other metacharacters. Examples:
|
|
12056
|
+
*
|
|
12057
|
+
* `['*']` — rename ALL skills/agents (DEFAULT — today's behavior)
|
|
12058
|
+
* `['10x:*']` — rename only canonicals that start with `10x:`
|
|
12059
|
+
* `['10x:*', 'atl:*']`— match either namespace
|
|
12060
|
+
* `['plan-*']` — match a name-family prefix
|
|
12061
|
+
*
|
|
12062
|
+
* When `prefixTarget` is undefined OR `['*']`, all kind-allowed artifacts are
|
|
12063
|
+
* renamed (identical to the pre-ADR-022 behavior — full backward compat).
|
|
12064
|
+
*
|
|
12065
|
+
* When set, artifacts whose canonical name does NOT match ANY pattern are
|
|
12066
|
+
* deployed WITHOUT a prefix (canonical-only). No error is raised; `sync`
|
|
12067
|
+
* output emits a `[prefix-target: <n> skipped]` warning so operators can
|
|
12068
|
+
* detect misconfigured patterns.
|
|
12069
|
+
*
|
|
12070
|
+
* NOT applicable when `prefix` is undefined.
|
|
12071
|
+
*
|
|
12072
|
+
* See: docs/decisions/022-prefix-target-name-patterns.md.
|
|
12073
|
+
*/
|
|
12074
|
+
prefixTarget: external_exports.array(external_exports.string().min(1, "prefixTarget pattern must be a non-empty string")).optional(),
|
|
12032
12075
|
/**
|
|
12033
12076
|
* Snapshot of the LAST source-config.yaml content this operator saw +
|
|
12034
12077
|
* consented to (ADR-021). Trust-gate state: when the live source-config
|
|
@@ -12706,6 +12749,18 @@ function updateSkillSource(id, patch) {
|
|
|
12706
12749
|
} else if (patch.prefixScope !== void 0) {
|
|
12707
12750
|
working = { ...working, prefixScope: [...patch.prefixScope] };
|
|
12708
12751
|
}
|
|
12752
|
+
if (patch.prefixTarget === null) {
|
|
12753
|
+
const { prefixTarget: _t, ...rest } = working;
|
|
12754
|
+
void _t;
|
|
12755
|
+
working = rest;
|
|
12756
|
+
} else if (patch.prefixTarget !== void 0) {
|
|
12757
|
+
for (const p of patch.prefixTarget) {
|
|
12758
|
+
if (typeof p !== "string" || p.length === 0) {
|
|
12759
|
+
throw new Error(`prefixTarget patterns must be non-empty strings \u2014 got ${JSON.stringify(p)}`);
|
|
12760
|
+
}
|
|
12761
|
+
}
|
|
12762
|
+
working = { ...working, prefixTarget: [...patch.prefixTarget] };
|
|
12763
|
+
}
|
|
12709
12764
|
if (patch.lastSeenSourcePrefix === null) {
|
|
12710
12765
|
const { lastSeenSourcePrefix: _l, ...rest } = working;
|
|
12711
12766
|
void _l;
|
|
@@ -15083,10 +15138,61 @@ function rewriteFrontmatterName(content, rewriter) {
|
|
|
15083
15138
|
const rebuilt = serializeFrontmatter(nextFm) + parsed.body;
|
|
15084
15139
|
return Buffer.from(rebuilt, "utf-8");
|
|
15085
15140
|
}
|
|
15141
|
+
function matchesSinglePattern(candidate, pattern) {
|
|
15142
|
+
const cacheKey2 = `${candidate}\0${pattern}`;
|
|
15143
|
+
const cached2 = _matchCache.get(cacheKey2);
|
|
15144
|
+
if (cached2 !== void 0)
|
|
15145
|
+
return cached2;
|
|
15146
|
+
const result = _matchesSinglePatternRaw(candidate, pattern);
|
|
15147
|
+
_matchCache.set(cacheKey2, result);
|
|
15148
|
+
return result;
|
|
15149
|
+
}
|
|
15150
|
+
function _matchesSinglePatternRaw(candidate, pattern) {
|
|
15151
|
+
if (pattern === "*")
|
|
15152
|
+
return true;
|
|
15153
|
+
if (!pattern.includes("*"))
|
|
15154
|
+
return candidate === pattern;
|
|
15155
|
+
const segments = pattern.split("*");
|
|
15156
|
+
let pos = 0;
|
|
15157
|
+
for (let i = 0; i < segments.length; i++) {
|
|
15158
|
+
const seg = segments[i];
|
|
15159
|
+
if (seg.length === 0)
|
|
15160
|
+
continue;
|
|
15161
|
+
if (i === 0) {
|
|
15162
|
+
if (!candidate.startsWith(seg))
|
|
15163
|
+
return false;
|
|
15164
|
+
pos = seg.length;
|
|
15165
|
+
} else if (i === segments.length - 1) {
|
|
15166
|
+
if (!candidate.endsWith(seg))
|
|
15167
|
+
return false;
|
|
15168
|
+
if (pos > candidate.length - seg.length)
|
|
15169
|
+
return false;
|
|
15170
|
+
} else {
|
|
15171
|
+
const found = candidate.indexOf(seg, pos);
|
|
15172
|
+
if (found === -1)
|
|
15173
|
+
return false;
|
|
15174
|
+
pos = found + seg.length;
|
|
15175
|
+
}
|
|
15176
|
+
}
|
|
15177
|
+
return true;
|
|
15178
|
+
}
|
|
15179
|
+
function matchesPrefixTarget(canonical, patterns) {
|
|
15180
|
+
if (patterns === void 0 || patterns.length === 0)
|
|
15181
|
+
return true;
|
|
15182
|
+
if (patterns.length === 1 && patterns[0] === "*")
|
|
15183
|
+
return true;
|
|
15184
|
+
for (const pattern of patterns) {
|
|
15185
|
+
if (matchesSinglePattern(canonical, pattern))
|
|
15186
|
+
return true;
|
|
15187
|
+
}
|
|
15188
|
+
return false;
|
|
15189
|
+
}
|
|
15190
|
+
var _matchCache;
|
|
15086
15191
|
var init_prefix_rules = __esm({
|
|
15087
15192
|
"../core/dist/skill-sync/prefix-rules.js"() {
|
|
15088
15193
|
"use strict";
|
|
15089
15194
|
init_markdown_merger();
|
|
15195
|
+
_matchCache = /* @__PURE__ */ new Map();
|
|
15090
15196
|
}
|
|
15091
15197
|
});
|
|
15092
15198
|
|
|
@@ -15096,17 +15202,20 @@ import * as path32 from "node:path";
|
|
|
15096
15202
|
function buildSourcePrefixMap(sources) {
|
|
15097
15203
|
const byId = /* @__PURE__ */ new Map();
|
|
15098
15204
|
const scopeById = /* @__PURE__ */ new Map();
|
|
15205
|
+
const targetById = /* @__PURE__ */ new Map();
|
|
15099
15206
|
const prefixes = /* @__PURE__ */ new Set();
|
|
15100
15207
|
for (const s of sources) {
|
|
15101
15208
|
if (s.prefix !== void 0 && s.prefix.length > 0) {
|
|
15102
15209
|
byId.set(s.id, s.prefix);
|
|
15103
15210
|
scopeById.set(s.id, s.prefixScope ?? DEFAULT_SCOPE);
|
|
15211
|
+
targetById.set(s.id, s.prefixTarget);
|
|
15104
15212
|
prefixes.add(s.prefix);
|
|
15105
15213
|
}
|
|
15106
15214
|
}
|
|
15107
15215
|
return {
|
|
15108
15216
|
get: (sourceId) => byId.get(sourceId),
|
|
15109
15217
|
getScope: (sourceId) => scopeById.get(sourceId) ?? DEFAULT_SCOPE,
|
|
15218
|
+
getPrefixTarget: (sourceId) => targetById.get(sourceId),
|
|
15110
15219
|
registeredPrefixes: Array.from(prefixes)
|
|
15111
15220
|
};
|
|
15112
15221
|
}
|
|
@@ -15122,6 +15231,9 @@ function applyPrefixRewrites(baseArtifacts, sourceMap, claudeDir2, dryRun) {
|
|
|
15122
15231
|
if (!scope.includes(artifact.kind))
|
|
15123
15232
|
continue;
|
|
15124
15233
|
const canonical = artifact.deployBasename;
|
|
15234
|
+
const prefixTarget = sourceMap.getPrefixTarget(artifact.sourceId);
|
|
15235
|
+
if (!matchesPrefixTarget(canonical, prefixTarget))
|
|
15236
|
+
continue;
|
|
15125
15237
|
const otherPrefixes = sourceMap.registeredPrefixes.filter((p) => p !== prefix);
|
|
15126
15238
|
const renamed = applyPrefix(canonical, prefix, otherPrefixes);
|
|
15127
15239
|
if (renamed === canonical)
|
|
@@ -15241,18 +15353,23 @@ function errToMsg(err) {
|
|
|
15241
15353
|
function resolveEffectivePrefix(operatorSource, sourceConfig) {
|
|
15242
15354
|
const opPrefix = operatorSource.prefix;
|
|
15243
15355
|
const opScope = operatorSource.prefixScope;
|
|
15356
|
+
const opTarget = operatorSource.prefixTarget;
|
|
15244
15357
|
const srcPrefix = sourceConfig?.prefix;
|
|
15245
15358
|
const srcScope = sourceConfig?.prefixScope;
|
|
15359
|
+
const srcTarget = sourceConfig?.prefixTarget;
|
|
15246
15360
|
const effectivePrefix = opPrefix ?? srcPrefix;
|
|
15247
15361
|
const effectiveScope = opScope ?? srcScope;
|
|
15248
|
-
const
|
|
15249
|
-
const
|
|
15362
|
+
const effectiveTarget = opTarget ?? srcTarget;
|
|
15363
|
+
const operatorHasAny = opPrefix !== void 0 || opScope !== void 0 && opScope.length > 0 || opTarget !== void 0 && opTarget.length > 0;
|
|
15364
|
+
const sourceHasAny = srcPrefix !== void 0 || srcScope !== void 0 && srcScope.length > 0 || srcTarget !== void 0 && srcTarget.length > 0;
|
|
15250
15365
|
const origin = operatorHasAny ? "operator" : sourceHasAny ? "source" : "none";
|
|
15251
15366
|
const result = { origin };
|
|
15252
15367
|
if (effectivePrefix !== void 0)
|
|
15253
15368
|
result.prefix = effectivePrefix;
|
|
15254
15369
|
if (effectiveScope !== void 0)
|
|
15255
15370
|
result.prefixScope = effectiveScope;
|
|
15371
|
+
if (effectiveTarget !== void 0)
|
|
15372
|
+
result.prefixTarget = effectiveTarget;
|
|
15256
15373
|
return result;
|
|
15257
15374
|
}
|
|
15258
15375
|
function sourceConfigsEqual(a, b) {
|
|
@@ -15265,6 +15382,9 @@ function sourceConfigsEqual(a, b) {
|
|
|
15265
15382
|
if (c.prefixScope !== void 0 && c.prefixScope.length > 0) {
|
|
15266
15383
|
out.prefixScope = [...c.prefixScope];
|
|
15267
15384
|
}
|
|
15385
|
+
if (c.prefixTarget !== void 0 && c.prefixTarget.length > 0) {
|
|
15386
|
+
out.prefixTarget = [...c.prefixTarget];
|
|
15387
|
+
}
|
|
15268
15388
|
return out;
|
|
15269
15389
|
};
|
|
15270
15390
|
const na = normalize(a);
|
|
@@ -15279,6 +15399,14 @@ function sourceConfigsEqual(a, b) {
|
|
|
15279
15399
|
if (sa[i] !== sb[i])
|
|
15280
15400
|
return false;
|
|
15281
15401
|
}
|
|
15402
|
+
const ta = na.prefixTarget ?? [];
|
|
15403
|
+
const tb = nb.prefixTarget ?? [];
|
|
15404
|
+
if (ta.length !== tb.length)
|
|
15405
|
+
return false;
|
|
15406
|
+
for (let i = 0; i < ta.length; i++) {
|
|
15407
|
+
if (ta[i] !== tb[i])
|
|
15408
|
+
return false;
|
|
15409
|
+
}
|
|
15282
15410
|
return true;
|
|
15283
15411
|
}
|
|
15284
15412
|
var init_resolve_source_config = __esm({
|
|
@@ -15361,6 +15489,8 @@ async function syncSkills(opts = {}) {
|
|
|
15361
15489
|
entry.prefix = eff.prefix;
|
|
15362
15490
|
if (eff.prefixScope !== void 0)
|
|
15363
15491
|
entry.prefixScope = eff.prefixScope;
|
|
15492
|
+
if (eff.prefixTarget !== void 0)
|
|
15493
|
+
entry.prefixTarget = eff.prefixTarget;
|
|
15364
15494
|
return entry;
|
|
15365
15495
|
});
|
|
15366
15496
|
const sourcePrefixGateOutcomes = [];
|
|
@@ -15419,6 +15549,7 @@ async function syncSkills(opts = {}) {
|
|
|
15419
15549
|
} else {
|
|
15420
15550
|
delete eff.prefix;
|
|
15421
15551
|
delete eff.prefixScope;
|
|
15552
|
+
delete eff.prefixTarget;
|
|
15422
15553
|
eff.origin = "none";
|
|
15423
15554
|
const isNonTty = opts.sourcePrefixPrompt === void 0;
|
|
15424
15555
|
sourcePrefixGateOutcomes.push({
|
|
@@ -37453,10 +37584,11 @@ except Exception: pass' 2>/dev/null`;
|
|
|
37453
37584
|
const authHeader = isCloud ? `-H "Authorization: Bearer \${OLAM_KG_PROXY_BEARER:-}"` : "";
|
|
37454
37585
|
const cloudGuard = isCloud ? `if [ -z "\${OLAM_KG_PROXY_URL:-}" ] || [ -z "\${OLAM_KG_PROXY_BEARER:-}" ]; then exit 0; fi; ` : "";
|
|
37455
37586
|
const curlPost = `RESP=$(curl -s --max-time 1 -X POST -H 'Content-Type: application/json' ${authHeader} -d "{\\"q\\":\\"$(echo \\"$CMD\\" | head -c 200 | tr '\\"' ' ')\\"}" ${resolvedUrl} 2>/dev/null)`;
|
|
37587
|
+
const hostRemoteFallback = opts.flavor === "host" ? `; if [ -z "$RESP" ] && [ -r "$HOME/.olam/kg-proxy-url" ] && [ -r "$HOME/.olam/kg-proxy-bearer" ]; then RESP=$(curl -s --max-time 2 -X POST -H 'Content-Type: application/json' -H "Authorization: Bearer $(cat "$HOME/.olam/kg-proxy-bearer")" -d "{\\"q\\":\\"$(echo \\"$CMD\\" | head -c 200 | tr '\\"' ' ')\\"}" "$(cat "$HOME/.olam/kg-proxy-url")/v1/classify" 2>/dev/null); fi` : "";
|
|
37456
37588
|
return [
|
|
37457
37589
|
`KG_SENTINEL=${KG_HOOK_SENTINEL}`,
|
|
37458
37590
|
`CMD=$(${extractCmd})`,
|
|
37459
|
-
`case "$CMD" in
|
|
37591
|
+
`case "$CMD" in grep\\ *|grep|rg\\ *|ripgrep\\ *|find\\ *|fd\\ *|ack\\ *|ag\\ *) ${cloudGuard}${curlPost}${hostRemoteFallback}`,
|
|
37460
37592
|
`KG_QUERY="$(echo \\"$CMD\\" | head -c 60 | tr '\\"' ' ')" echo "$RESP" | ${emitContext}`,
|
|
37461
37593
|
`;; esac`
|
|
37462
37594
|
].join("; ");
|
|
@@ -51,3 +51,7 @@ data:
|
|
|
51
51
|
OLAM_SECRET_CACHE_TTL_SEC: "300"
|
|
52
52
|
OLAM_PR_POLL_INTERVAL_MS: "300000"
|
|
53
53
|
OLAM_MERGE_GRACE_MS: "600000"
|
|
54
|
+
# World watchdog — periodic probe of each active world's claude PID for the
|
|
55
|
+
# three wedge signals (wchan + CLOSE_WAIT + CPU). Detection-only in Phase A.
|
|
56
|
+
# Set OLAM_WORLD_WATCHDOG_DISABLED=1 in the deployment env to kill-switch.
|
|
57
|
+
OLAM_WORLD_WATCHDOG_TICK_MS: "30000"
|
|
@@ -118,7 +118,7 @@ spec:
|
|
|
118
118
|
# k3d), started by `olam upgrade` Step 0.7 — not inside this Pod.
|
|
119
119
|
containers:
|
|
120
120
|
- name: olam-host-cp
|
|
121
|
-
image: ghcr.io/pleri/olam-host-cp@sha256:
|
|
121
|
+
image: ghcr.io/pleri/olam-host-cp@sha256:c072cebaa1387b6f16e441b55922973bd9c781e80dc867bcfdbed309b0653918
|
|
122
122
|
imagePullPolicy: IfNotPresent
|
|
123
123
|
securityContext:
|
|
124
124
|
runAsNonRoot: true
|
|
@@ -131,6 +131,18 @@ spec:
|
|
|
131
131
|
- name: http
|
|
132
132
|
containerPort: 19000
|
|
133
133
|
protocol: TCP
|
|
134
|
+
env:
|
|
135
|
+
# World watchdog — tick cadence (from ConfigMap default = 30s).
|
|
136
|
+
# Override per-operator to tune probe frequency.
|
|
137
|
+
- name: OLAM_WORLD_WATCHDOG_TICK_MS
|
|
138
|
+
valueFrom:
|
|
139
|
+
configMapKeyRef:
|
|
140
|
+
name: olam-host-cp-env
|
|
141
|
+
key: OLAM_WORLD_WATCHDOG_TICK_MS
|
|
142
|
+
# Set to "1" to disable the world-watchdog entirely (emergency kill switch).
|
|
143
|
+
# Unset by default — watchdog runs in detection-only mode.
|
|
144
|
+
# - name: OLAM_WORLD_WATCHDOG_DISABLED
|
|
145
|
+
# value: "1"
|
|
134
146
|
envFrom:
|
|
135
147
|
- configMapRef:
|
|
136
148
|
name: olam-host-cp-env
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# TLS secret template for olam-host-cp Traefik IngressRoute.
|
|
2
|
+
#
|
|
3
|
+
# DO NOT apply this template directly — the placeholders `__TLS_CRT_BASE64__`
|
|
4
|
+
# and `__TLS_KEY_BASE64__` are substituted at apply time by
|
|
5
|
+
# `olam services tls-install` (packages/cli/src/commands/services-tls.ts),
|
|
6
|
+
# which uses `mkcert` to mint a locally-trusted certificate for the SAN list
|
|
7
|
+
# olam.local 127.0.0.1 ::1
|
|
8
|
+
# and then `kubectl apply -f -` against the rendered manifest.
|
|
9
|
+
#
|
|
10
|
+
# Why a Secret of type kubernetes.io/tls (instead of a plain Opaque secret):
|
|
11
|
+
# Traefik's IngressRoute TLS resolver requires this exact type — it reads
|
|
12
|
+
# tls.crt + tls.key fields by convention. Using Opaque would silently fail
|
|
13
|
+
# the handshake at request time.
|
|
14
|
+
#
|
|
15
|
+
# Why the cert covers SANs (not just CN): modern browsers (Chrome 58+, Brave,
|
|
16
|
+
# Safari, Firefox) ignore the certificate CN entirely and only honour SANs.
|
|
17
|
+
# Without `127.0.0.1` + `::1` in the SAN list, hitting the IP directly fails
|
|
18
|
+
# even though the cert is "valid for olam.local".
|
|
19
|
+
#
|
|
20
|
+
# Renewal: certs minted by mkcert are valid ~2 years and 3 months. The
|
|
21
|
+
# tls-install command checks NotAfter and regenerates when within 30 days
|
|
22
|
+
# of expiry. To force regeneration: `kubectl -n olam delete secret olam-host-cp-tls`
|
|
23
|
+
# and re-run `olam services tls-install`.
|
|
24
|
+
apiVersion: v1
|
|
25
|
+
kind: Secret
|
|
26
|
+
metadata:
|
|
27
|
+
name: olam-host-cp-tls
|
|
28
|
+
namespace: olam
|
|
29
|
+
labels:
|
|
30
|
+
app: olam-host-cp
|
|
31
|
+
olam.io/component: host-stack
|
|
32
|
+
type: kubernetes.io/tls
|
|
33
|
+
data:
|
|
34
|
+
tls.crt: __TLS_CRT_BASE64__
|
|
35
|
+
tls.key: __TLS_KEY_BASE64__
|
|
@@ -70,7 +70,7 @@ spec:
|
|
|
70
70
|
mountPath: /data
|
|
71
71
|
containers:
|
|
72
72
|
- name: olam-auth-service
|
|
73
|
-
image: ghcr.io/pleri/olam-auth@sha256:
|
|
73
|
+
image: ghcr.io/pleri/olam-auth@sha256:8ab49079d37fcc6aff70631526df1034f85d19e9854c7dc0d7789c0bd5a7f209
|
|
74
74
|
imagePullPolicy: IfNotPresent
|
|
75
75
|
securityContext:
|
|
76
76
|
runAsNonRoot: true
|
|
@@ -61,7 +61,7 @@ spec:
|
|
|
61
61
|
mountPath: /data
|
|
62
62
|
containers:
|
|
63
63
|
- name: olam-kg-service
|
|
64
|
-
image: ghcr.io/pleri/olam-kg-service@sha256:
|
|
64
|
+
image: ghcr.io/pleri/olam-kg-service@sha256:318ad6566dc42d4202accd582086086783df2b4f0b7fe11fdbdeab3541489cf4
|
|
65
65
|
imagePullPolicy: IfNotPresent
|
|
66
66
|
securityContext:
|
|
67
67
|
runAsNonRoot: true
|
|
@@ -68,7 +68,7 @@ spec:
|
|
|
68
68
|
mountPath: /data
|
|
69
69
|
containers:
|
|
70
70
|
- name: olam-mcp-auth-service
|
|
71
|
-
image: ghcr.io/pleri/olam-mcp-auth@sha256:
|
|
71
|
+
image: ghcr.io/pleri/olam-mcp-auth@sha256:e59f7a04ae43d29233e29b812f3de22a74ad08bcb5060c4c580d8b6b4653d614
|
|
72
72
|
imagePullPolicy: IfNotPresent
|
|
73
73
|
securityContext:
|
|
74
74
|
runAsNonRoot: true
|
|
@@ -70,7 +70,7 @@ spec:
|
|
|
70
70
|
# bootstrap-placeholder comment + run `npm run refresh:manifest-digests`
|
|
71
71
|
# once ghcr.io/pleri/olam-memory-service has a real published digest.
|
|
72
72
|
# bootstrap-placeholder: pre-publish; refresh after first release
|
|
73
|
-
image: ghcr.io/pleri/olam-memory-service@sha256:
|
|
73
|
+
image: ghcr.io/pleri/olam-memory-service@sha256:c26d027243fe6f82c8cadb41d8174703e171a85a7872d1e9a55c6e2c80482a4d
|
|
74
74
|
imagePullPolicy: IfNotPresent
|
|
75
75
|
securityContext:
|
|
76
76
|
runAsNonRoot: true
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dispatch-persister.mjs — persist the last dispatch for each world.
|
|
3
|
+
*
|
|
4
|
+
* The world watchdog's recovery hook reads this to replay the last
|
|
5
|
+
* unanswered prompt when it auto-recovers a wedged claude process.
|
|
6
|
+
*
|
|
7
|
+
* Contract:
|
|
8
|
+
* persist({ worldId, messageId, prompt, source, statePath?, now? })
|
|
9
|
+
* Atomically writes ~/.olam/worlds/<worldId>/state/last-dispatch.json.
|
|
10
|
+
* Overwrites any previous file — only the LATEST dispatch matters for
|
|
11
|
+
* replay. Atomic write (tmp + fs.rename) prevents partial-write residue
|
|
12
|
+
* from corrupting recovery reads.
|
|
13
|
+
*
|
|
14
|
+
* read({ worldId, statePath? })
|
|
15
|
+
* Returns { messageId, prompt, dispatchedAt, source } or null.
|
|
16
|
+
* null on ENOENT (no dispatch persisted yet) — never throws.
|
|
17
|
+
* null on JSON parse error (logs + skips) — never throws on corrupt file.
|
|
18
|
+
*
|
|
19
|
+
* Multiple worlds are independent: world A and world B have separate files.
|
|
20
|
+
* Multiple concurrent persist() calls for the SAME world are safe — each
|
|
21
|
+
* write is a rename of a tmp file so the worst case is one write winning.
|
|
22
|
+
*
|
|
23
|
+
* @see docs/architecture/world-watchdog.md
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import fs from 'node:fs/promises';
|
|
27
|
+
import path from 'node:path';
|
|
28
|
+
import os from 'node:os';
|
|
29
|
+
|
|
30
|
+
// Default base path under which per-world state directories live.
|
|
31
|
+
const DEFAULT_STATE_BASE = path.join(os.homedir(), '.olam', 'worlds');
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Derive the path to last-dispatch.json for a world.
|
|
35
|
+
*
|
|
36
|
+
* @param {string} worldId
|
|
37
|
+
* @param {string} [stateBase] Override the base directory (for tests).
|
|
38
|
+
* @returns {string}
|
|
39
|
+
*/
|
|
40
|
+
export function lastDispatchPath(worldId, stateBase = DEFAULT_STATE_BASE) {
|
|
41
|
+
return path.join(stateBase, worldId, 'state', 'last-dispatch.json');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Persist the last dispatch for a world.
|
|
46
|
+
*
|
|
47
|
+
* @param {{
|
|
48
|
+
* worldId: string,
|
|
49
|
+
* messageId: string,
|
|
50
|
+
* prompt: string,
|
|
51
|
+
* source: string,
|
|
52
|
+
* statePath?: string,
|
|
53
|
+
* now?: () => number,
|
|
54
|
+
* }} opts
|
|
55
|
+
* @returns {Promise<void>}
|
|
56
|
+
*/
|
|
57
|
+
export async function persist({
|
|
58
|
+
worldId,
|
|
59
|
+
messageId,
|
|
60
|
+
prompt,
|
|
61
|
+
source,
|
|
62
|
+
statePath,
|
|
63
|
+
now = () => Date.now(),
|
|
64
|
+
}) {
|
|
65
|
+
const filePath = statePath ?? lastDispatchPath(worldId);
|
|
66
|
+
const dir = path.dirname(filePath);
|
|
67
|
+
const tmpPath = `${filePath}.tmp`;
|
|
68
|
+
|
|
69
|
+
const record = {
|
|
70
|
+
messageId,
|
|
71
|
+
prompt,
|
|
72
|
+
dispatchedAt: new Date(now()).toISOString(),
|
|
73
|
+
source,
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// Ensure the directory exists.
|
|
77
|
+
await fs.mkdir(dir, { recursive: true });
|
|
78
|
+
|
|
79
|
+
// Atomic write: write to .tmp then rename over the target.
|
|
80
|
+
await fs.writeFile(tmpPath, JSON.stringify(record, null, 2) + '\n', 'utf8');
|
|
81
|
+
await fs.rename(tmpPath, filePath);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Fire-and-forget persist wrapper used at the dispatch call-sites.
|
|
86
|
+
*
|
|
87
|
+
* Centralises the void/.catch boilerplate so the two enrichment sites
|
|
88
|
+
* (pr-nanny + /api/cloud-dispatch) can't drift on future changes.
|
|
89
|
+
* Logs failures via the supplied logSource tag; never throws.
|
|
90
|
+
*
|
|
91
|
+
* @param {{
|
|
92
|
+
* worldId: string,
|
|
93
|
+
* messageId: string,
|
|
94
|
+
* prompt: string,
|
|
95
|
+
* source: string,
|
|
96
|
+
* logSource?: string,
|
|
97
|
+
* statePath?: string,
|
|
98
|
+
* now?: () => number,
|
|
99
|
+
* }} opts
|
|
100
|
+
* @returns {void}
|
|
101
|
+
*/
|
|
102
|
+
export function safePersistLastDispatch(opts) {
|
|
103
|
+
const { logSource = opts.source, ...persistOpts } = opts;
|
|
104
|
+
void persist(persistOpts).catch((err) => {
|
|
105
|
+
console.warn(
|
|
106
|
+
`[${logSource}] persistLastDispatch failed (non-fatal): ${err?.message ?? err}`,
|
|
107
|
+
);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Read the last persisted dispatch for a world.
|
|
113
|
+
*
|
|
114
|
+
* @param {{
|
|
115
|
+
* worldId: string,
|
|
116
|
+
* statePath?: string,
|
|
117
|
+
* }} opts
|
|
118
|
+
* @returns {Promise<{ messageId: string, prompt: string, dispatchedAt: string, source: string } | null>}
|
|
119
|
+
*/
|
|
120
|
+
export async function read({ worldId, statePath }) {
|
|
121
|
+
const filePath = statePath ?? lastDispatchPath(worldId);
|
|
122
|
+
|
|
123
|
+
let raw;
|
|
124
|
+
try {
|
|
125
|
+
raw = await fs.readFile(filePath, 'utf8');
|
|
126
|
+
} catch (err) {
|
|
127
|
+
if (err?.code === 'ENOENT') return null;
|
|
128
|
+
// Other I/O errors (e.g. permissions) — log + return null (fail-soft).
|
|
129
|
+
console.error(`[dispatch-persister] readFile ${filePath}: ${err?.message ?? err}`);
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
const parsed = JSON.parse(raw);
|
|
135
|
+
// Basic shape validation — don't throw on corrupt file.
|
|
136
|
+
if (
|
|
137
|
+
typeof parsed !== 'object' ||
|
|
138
|
+
parsed === null ||
|
|
139
|
+
typeof parsed.messageId !== 'string' ||
|
|
140
|
+
typeof parsed.prompt !== 'string' ||
|
|
141
|
+
typeof parsed.dispatchedAt !== 'string' ||
|
|
142
|
+
typeof parsed.source !== 'string'
|
|
143
|
+
) {
|
|
144
|
+
console.error(`[dispatch-persister] ${filePath}: unexpected shape, skipping`);
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
messageId: parsed.messageId,
|
|
149
|
+
prompt: parsed.prompt,
|
|
150
|
+
dispatchedAt: parsed.dispatchedAt,
|
|
151
|
+
source: parsed.source,
|
|
152
|
+
};
|
|
153
|
+
} catch (err) {
|
|
154
|
+
console.error(`[dispatch-persister] ${filePath}: JSON parse error: ${err?.message ?? err}`);
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
}
|
package/host-cp/src/pr-nanny.mjs
CHANGED
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
import { execFile } from 'node:child_process';
|
|
25
25
|
import { promisify } from 'node:util';
|
|
26
26
|
import { pickNextTier } from './dispatch/tier-escalator.mjs';
|
|
27
|
+
import { safePersistLastDispatch } from './dispatch-persister.mjs';
|
|
27
28
|
|
|
28
29
|
const execFileAsync = promisify(execFile);
|
|
29
30
|
|
|
@@ -251,6 +252,12 @@ export function createPrNanny({
|
|
|
251
252
|
|
|
252
253
|
// Dispatch fix
|
|
253
254
|
try {
|
|
255
|
+
safePersistLastDispatch({
|
|
256
|
+
worldId,
|
|
257
|
+
messageId: `nanny-${worldId}-${Date.now()}`,
|
|
258
|
+
prompt,
|
|
259
|
+
source: 'pr-nanny',
|
|
260
|
+
});
|
|
254
261
|
await dispatchToWorld(worldId, prompt, { tier: tierForThisDispatch });
|
|
255
262
|
const now = new Date().toISOString();
|
|
256
263
|
prStateStore.set(worldId, {
|