@remnic/core 9.66.10 → 9.66.11
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/access-admin-ops-surface.js +2 -1
- package/dist/access-authorization-probe.js +2 -1
- package/dist/access-boundary.js +2 -1
- package/dist/access-cli.js +8 -7
- package/dist/access-cli.js.map +1 -1
- package/dist/access-extraction-force-flush.js +2 -1
- package/dist/access-http-query.js +2 -1
- package/dist/access-http.js +2 -1
- package/dist/access-identity-continuity-surface.js +2 -1
- package/dist/access-lcm-surface.js +2 -1
- package/dist/access-mcp.js +2 -1
- package/dist/access-namespace-preflight.js +2 -1
- package/dist/access-observe-write-surface.js +2 -1
- package/dist/access-operations-batch.js +2 -1
- package/dist/access-operations.js +2 -1
- package/dist/access-recall-concurrency.js +2 -1
- package/dist/access-recall-response.js +2 -1
- package/dist/access-recall-surface.js +2 -1
- package/dist/access-service.js +2 -1
- package/dist/access-surface-catalog.js +2 -1
- package/dist/access-surface-catalog.js.map +1 -1
- package/dist/causal-consolidation.js +3 -2
- package/dist/causal-consolidation.js.map +1 -1
- package/dist/chunk-3UXOZBHV.js +20 -0
- package/dist/chunk-3UXOZBHV.js.map +1 -0
- package/dist/{chunk-YYDOUOYM.js → chunk-6H7JWYTC.js} +16 -11
- package/dist/chunk-6H7JWYTC.js.map +1 -0
- package/dist/{chunk-KWVXCRFZ.js → chunk-BCM6ZAJ3.js} +4 -4
- package/dist/{chunk-TXCLDIXJ.js → chunk-K6PWPC7O.js} +2 -2
- package/dist/{chunk-M2BY5IZL.js → chunk-PVV42HM4.js} +5 -2
- package/dist/chunk-PVV42HM4.js.map +1 -0
- package/dist/{chunk-VSQG4AXL.js → chunk-PWVRMD37.js} +1 -1
- package/dist/{chunk-NR36NW24.js → chunk-UWOA5L6N.js} +40 -43
- package/dist/{chunk-NR36NW24.js.map → chunk-UWOA5L6N.js.map} +1 -1
- package/dist/{chunk-ZPZLGN4Y.js → chunk-XZ7QLOTJ.js} +4 -20
- package/dist/chunk-XZ7QLOTJ.js.map +1 -0
- package/dist/cli.js +5 -4
- package/dist/coding/export-okf-codegraph.js +1 -1
- package/dist/compounding/engine.js +1 -1
- package/dist/connectors/codex-materialize-runner.js +2 -1
- package/dist/connectors/index.js +2 -1
- package/dist/external-wiki-access.js +2 -1
- package/dist/extraction.js +2 -1
- package/dist/index.js +8 -7
- package/dist/orchestrator.js +8 -7
- package/dist/semantic-consolidation.js +3 -2
- package/dist/source-agent-qualifier.js +2 -1
- package/dist/support-passport/index.js +2 -1
- package/dist/transfer/export-okf.d.ts +8 -0
- package/dist/transfer/export-okf.js +3 -2
- package/package.json +2 -2
- package/src/cli/okf-commands.ts +2 -5
- package/src/okf/render.ts +11 -2
- package/src/transfer/export-okf.test.ts +77 -1
- package/src/transfer/export-okf.ts +25 -9
- package/dist/chunk-M2BY5IZL.js.map +0 -1
- package/dist/chunk-YYDOUOYM.js.map +0 -1
- package/dist/chunk-ZPZLGN4Y.js.map +0 -1
- /package/dist/{chunk-KWVXCRFZ.js.map → chunk-BCM6ZAJ3.js.map} +0 -0
- /package/dist/{chunk-TXCLDIXJ.js.map → chunk-K6PWPC7O.js.map} +0 -0
- /package/dist/{chunk-VSQG4AXL.js.map → chunk-PWVRMD37.js.map} +0 -0
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
resolveNamespaceChildRoot
|
|
3
|
+
} from "./chunk-3UXOZBHV.js";
|
|
1
4
|
import {
|
|
2
5
|
hasCodexMaterializeSentinel,
|
|
3
6
|
materializeForNamespace
|
|
@@ -23,24 +26,6 @@ import {
|
|
|
23
26
|
|
|
24
27
|
// src/connectors/codex-materialize-runner.ts
|
|
25
28
|
import { existsSync } from "fs";
|
|
26
|
-
|
|
27
|
-
// src/namespaces/path.ts
|
|
28
|
-
import path from "path";
|
|
29
|
-
function assertNamespacePathInsideRoot(root, candidate, namespace, label = "namespace path") {
|
|
30
|
-
const relative = path.relative(root, candidate);
|
|
31
|
-
if (relative === "" || !relative.startsWith("..") && !path.isAbsolute(relative)) {
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
throw new Error(`invalid ${label}: ${namespace}`);
|
|
35
|
-
}
|
|
36
|
-
function resolveNamespaceChildRoot(memoryDir, namespace, label = "namespace path") {
|
|
37
|
-
const namespaceRoot = path.resolve(memoryDir, "namespaces");
|
|
38
|
-
const candidate = path.resolve(namespaceRoot, namespace);
|
|
39
|
-
assertNamespacePathInsideRoot(namespaceRoot, candidate, namespace, label);
|
|
40
|
-
return candidate;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// src/connectors/codex-materialize-runner.ts
|
|
44
29
|
async function runCodexMaterialize(options) {
|
|
45
30
|
const cfg = options.config;
|
|
46
31
|
if (!cfg.codexMaterializeMemories) {
|
|
@@ -146,8 +131,7 @@ function resolveNamespaceDir(memoryDir, namespace, cfg) {
|
|
|
146
131
|
}
|
|
147
132
|
|
|
148
133
|
export {
|
|
149
|
-
resolveNamespaceChildRoot,
|
|
150
134
|
runCodexMaterialize,
|
|
151
135
|
runPostConsolidationMaterialize
|
|
152
136
|
};
|
|
153
|
-
//# sourceMappingURL=chunk-
|
|
137
|
+
//# sourceMappingURL=chunk-XZ7QLOTJ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/connectors/codex-materialize-runner.ts"],"sourcesContent":["/**\n * codex-materialize-runner.ts — Thin I/O bridge for the Codex materializer.\n *\n * The pure rendering logic lives in {@link ./codex-materialize.js}. This file\n * is the place callers (consolidation hooks, CLI, session-end hook) go when\n * they want the whole \"load memories from storage → render → write\" flow.\n *\n * Kept deliberately small so #378 never has to reach into orchestrator.ts /\n * importance.ts — the two files Wave 1 agents are editing concurrently.\n */\n\nimport { existsSync } from \"node:fs\";\n\nimport { log } from \"../logger.js\";\nimport { resolveNamespaceChildRoot } from \"../namespaces/path.js\";\nimport { isSafeRouteNamespace } from \"../routing/engine.js\";\nimport { projectProceduresToSkills } from \"../procedural/skill-projection.js\";\nimport { BUILTIN_SKILLS } from \"../skills-registry.js\";\nimport { StorageManager } from \"../storage.js\";\nimport { excludeSupportPassportPrivateMemories } from \"../support-passport/card-projection.js\";\nimport type { PluginConfig, MemoryFile } from \"../types.js\";\nimport {\n hasCodexMaterializeSentinel,\n materializeForNamespace,\n type MaterializeResult,\n type RolloutSummaryInput,\n} from \"./codex-materialize.js\";\n\n/** Options accepted by the shared post-consolidation materialize helper. */\nexport interface PostConsolidationMaterializeOptions {\n config: PluginConfig;\n namespace?: string;\n memories?: MemoryFile[];\n memoryDir?: string;\n codexHome?: string;\n rolloutSummaries?: RolloutSummaryInput[];\n now?: Date;\n}\n\n/** Options accepted by the runner. */\nexport interface RunMaterializeOptions {\n /** Remnic config — we only read the `codexMaterialize*` fields. */\n config: PluginConfig;\n /** Namespace to materialize. Overrides the config's `codexMaterializeNamespace`. */\n namespace?: string;\n /** Override the memory directory (defaults to `config.memoryDir`). */\n memoryDir?: string;\n /** Override `<codex_home>` (useful for tests). */\n codexHome?: string;\n /** Optional pre-loaded memories (bypasses disk read — used in tests). */\n memories?: MemoryFile[];\n /** Optional rollout summaries supplied by the caller. */\n rolloutSummaries?: RolloutSummaryInput[];\n /** Current time injection for deterministic runs. */\n now?: Date;\n /** Reason string — logged for observability. */\n reason?: \"consolidation\" | \"session_end\" | \"manual\" | \"cli\";\n}\n\n/**\n * Run the Codex materialization end-to-end. Returns `null` when the feature\n * is disabled in config or when the user hasn't opted in via the sentinel.\n * Never throws for \"expected\" skips; only throws on schema validation or I/O\n * errors that callers actually need to surface.\n */\nexport async function runCodexMaterialize(\n options: RunMaterializeOptions,\n): Promise<MaterializeResult | null> {\n const cfg = options.config;\n if (!cfg.codexMaterializeMemories) {\n log.debug(`[codex-materialize] skipped — codexMaterializeMemories=false`);\n return null;\n }\n\n // Per-trigger gate: session-end runs must honor codexMaterializeOnSessionEnd.\n // The Codex Stop hook (remnic-codex-hook.cjs, session-end event) passes\n // reason=\"session_end\"; when the user has turned off the session-end trigger\n // we short-circuit here without touching disk.\n if (options.reason === \"session_end\" && cfg.codexMaterializeOnSessionEnd === false) {\n log.debug(\n `[codex-materialize] skipped — session-end disabled via codexMaterializeOnSessionEnd=false`,\n );\n return null;\n }\n\n const namespace = resolveNamespace(options.namespace, cfg);\n const memoryDir = options.memoryDir ?? cfg.memoryDir;\n const codexHome = options.codexHome ?? cfg.codex?.codexHome ?? undefined;\n if (!memoryDir) {\n log.warn(`[codex-materialize] skipped — no memoryDir available`);\n return null;\n }\n\n let memories: MemoryFile[];\n if (options.memories) {\n memories = options.memories;\n } else {\n if (!hasCodexMaterializeSentinel(codexHome)) {\n return materializeForNamespace(namespace, {\n memories: [],\n codexHome,\n maxSummaryTokens: cfg.codexMaterializeMaxSummaryTokens,\n rolloutRetentionDays: cfg.codexMaterializeRolloutRetentionDays,\n rolloutSummaries: options.rolloutSummaries,\n now: options.now,\n });\n }\n const nsDir = resolveNamespaceDir(memoryDir, namespace, cfg);\n const storage = new StorageManager(nsDir);\n memories = await storage.readAllMemories();\n }\n memories = excludeSupportPassportPrivateMemories(memories);\n\n // Skill projection (issue #2369). Gated on `procedural.enabled` AND\n // `procedural.skillProjection.enabled`; `maxSkills: 0` disables it too.\n // Passing `undefined` leaves `skills/` untouched, so a disabled gate never\n // deletes a folder the user curated.\n const skillProjection = cfg.procedural?.skillProjection;\n const skills =\n cfg.procedural?.enabled === true && skillProjection?.enabled === true\n ? projectProceduresToSkills(memories, {\n maxSkills: skillProjection.maxSkills,\n reservedSlugs: BUILTIN_SKILLS.map((skill) => skill.slug),\n })\n : undefined;\n\n // Intentionally NOT catching here: per the JSDoc contract above,\n // schema-validation and I/O errors from `materializeForNamespace` must\n // surface to callers so they can exit non-zero (CLI) or log + recover\n // (consolidation post-hook, which has its own narrower try/catch).\n // Catching everything would make a broken MEMORY.md render look like a\n // successful skip, and invisible failures are strictly worse than loud\n // ones for a feature that writes to `~/.codex/memories`.\n const result = materializeForNamespace(namespace, {\n memories,\n codexHome,\n maxSummaryTokens: cfg.codexMaterializeMaxSummaryTokens,\n rolloutRetentionDays: cfg.codexMaterializeRolloutRetentionDays,\n rolloutSummaries: options.rolloutSummaries,\n skills,\n now: options.now,\n });\n if (options.reason) {\n log.debug(\n `[codex-materialize] ran reason=${options.reason} wrote=${result.wrote} files=${result.filesWritten.length}`,\n );\n }\n return result;\n}\n\n/**\n * Shared helper for post-consolidation materialize hooks.\n *\n * `materializeAfterSemanticConsolidation` and `materializeAfterCausalConsolidation`\n * used to be two nearly-identical copies of this logic; keeping the actual\n * body here means any future guard/logging change happens in one place.\n *\n * The only per-caller knob is `logPrefix`, which is used to tag the\n * non-fatal warning emitted when the materializer throws.\n */\nexport async function runPostConsolidationMaterialize(\n logPrefix: string,\n options: PostConsolidationMaterializeOptions,\n): Promise<MaterializeResult | null> {\n if (!options.config.codexMaterializeMemories) return null;\n if (!options.config.codexMaterializeOnConsolidation) return null;\n try {\n return await runCodexMaterialize({\n config: options.config,\n namespace: options.namespace,\n memories: options.memories,\n memoryDir: options.memoryDir,\n codexHome: options.codexHome,\n rolloutSummaries: options.rolloutSummaries,\n now: options.now,\n reason: \"consolidation\",\n });\n } catch (error) {\n log.warn(\n `${logPrefix} Codex materialize post-hook failed (non-fatal): ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n return null;\n }\n}\n\nfunction resolveNamespace(override: string | undefined, cfg: PluginConfig): string {\n const requested = (override ?? cfg.codexMaterializeNamespace ?? \"auto\").trim();\n const defaultNamespace = (cfg.defaultNamespace ?? \"\").trim();\n const namespace =\n requested.length === 0 || requested === \"auto\"\n ? (defaultNamespace.length > 0 ? defaultNamespace : \"default\")\n : requested;\n if (!isSafeRouteNamespace(namespace)) {\n throw new Error(`invalid materialize namespace: ${namespace}`);\n }\n return namespace;\n}\n\n/**\n * Resolve the on-disk storage root for a namespace, matching\n * `NamespaceStorageRouter` in `packages/remnic-core/src/namespaces/storage.ts`.\n *\n * Contract:\n * - When namespaces are disabled, every namespace maps to `memoryDir` itself.\n * - When namespaces are enabled, non-default namespaces always live under\n * `memoryDir/namespaces/<namespace>`.\n * - The default namespace prefers `memoryDir/namespaces/<defaultNamespace>`\n * when that directory already exists (migrated install); otherwise it\n * falls back to the legacy `memoryDir` root so materialization does not\n * silently switch directories out from under an existing install.\n */\nfunction resolveNamespaceDir(\n memoryDir: string,\n namespace: string,\n cfg: PluginConfig,\n): string {\n if (!cfg.namespacesEnabled) return memoryDir;\n\n const configuredDefaultNamespace = (cfg.defaultNamespace ?? \"\").trim();\n const defaultNamespace =\n configuredDefaultNamespace.length > 0 ? configuredDefaultNamespace : \"default\";\n const ns = (namespace || defaultNamespace).trim();\n if (!isSafeRouteNamespace(ns)) {\n throw new Error(`invalid materialize namespace: ${ns}`);\n }\n const namespacedRoot = resolveNamespaceChildRoot(memoryDir, ns, \"materialize namespace path\");\n\n if (ns === defaultNamespace) {\n return existsSync(namespacedRoot) ? namespacedRoot : memoryDir;\n }\n return namespacedRoot;\n}\n"],"mappings":"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","names":[]}
|
package/dist/cli.js
CHANGED
|
@@ -91,14 +91,14 @@ import {
|
|
|
91
91
|
runWorkProductStatusCliCommand,
|
|
92
92
|
runWorkProjectCliCommand,
|
|
93
93
|
runWorkTaskCliCommand
|
|
94
|
-
} from "./chunk-
|
|
94
|
+
} from "./chunk-UWOA5L6N.js";
|
|
95
95
|
import "./chunk-MMACM5G2.js";
|
|
96
96
|
import "./chunk-54BGIBCR.js";
|
|
97
97
|
import "./chunk-H4CKS5B2.js";
|
|
98
98
|
import "./chunk-FXZTL7F7.js";
|
|
99
99
|
import "./chunk-PYHOAXTI.js";
|
|
100
100
|
import "./chunk-UP6MOYCB.js";
|
|
101
|
-
import "./chunk-
|
|
101
|
+
import "./chunk-6H7JWYTC.js";
|
|
102
102
|
import "./chunk-BV2TIZ4F.js";
|
|
103
103
|
import "./chunk-Z734BLO3.js";
|
|
104
104
|
import "./chunk-HDDRVXX4.js";
|
|
@@ -120,7 +120,7 @@ import "./chunk-YLBE6PLR.js";
|
|
|
120
120
|
import "./chunk-CDKHKZL5.js";
|
|
121
121
|
import "./chunk-HSSERO3E.js";
|
|
122
122
|
import "./chunk-NOUJNHWG.js";
|
|
123
|
-
import "./chunk-
|
|
123
|
+
import "./chunk-PVV42HM4.js";
|
|
124
124
|
import "./chunk-HDQUQBPW.js";
|
|
125
125
|
import "./chunk-6FC3IZ26.js";
|
|
126
126
|
import "./chunk-KWLDB64F.js";
|
|
@@ -271,7 +271,8 @@ import "./chunk-D3AUQXTJ.js";
|
|
|
271
271
|
import "./chunk-7MOYAT4Y.js";
|
|
272
272
|
import "./chunk-LTWFU6VJ.js";
|
|
273
273
|
import "./chunk-UFGFMWAD.js";
|
|
274
|
-
import "./chunk-
|
|
274
|
+
import "./chunk-XZ7QLOTJ.js";
|
|
275
|
+
import "./chunk-3UXOZBHV.js";
|
|
275
276
|
import "./chunk-ZXBTTMGZ.js";
|
|
276
277
|
import "./chunk-YMOPYG3K.js";
|
|
277
278
|
import "./chunk-2LSZVONP.js";
|
package/dist/connectors/index.js
CHANGED
|
@@ -47,7 +47,8 @@ import "./chunk-D3AUQXTJ.js";
|
|
|
47
47
|
import "./chunk-7MOYAT4Y.js";
|
|
48
48
|
import "./chunk-LTWFU6VJ.js";
|
|
49
49
|
import "./chunk-UFGFMWAD.js";
|
|
50
|
-
import "./chunk-
|
|
50
|
+
import "./chunk-XZ7QLOTJ.js";
|
|
51
|
+
import "./chunk-3UXOZBHV.js";
|
|
51
52
|
import "./chunk-ZXBTTMGZ.js";
|
|
52
53
|
import "./chunk-YMOPYG3K.js";
|
|
53
54
|
import "./chunk-2LSZVONP.js";
|
package/dist/extraction.js
CHANGED
|
@@ -30,7 +30,8 @@ import "./chunk-NLE6TSLG.js";
|
|
|
30
30
|
import "./chunk-IVKIODHO.js";
|
|
31
31
|
import "./chunk-WKDFJXW5.js";
|
|
32
32
|
import "./chunk-JUXOTY6J.js";
|
|
33
|
-
import "./chunk-
|
|
33
|
+
import "./chunk-XZ7QLOTJ.js";
|
|
34
|
+
import "./chunk-3UXOZBHV.js";
|
|
34
35
|
import "./chunk-ZXBTTMGZ.js";
|
|
35
36
|
import "./chunk-4PHT7KE2.js";
|
|
36
37
|
import "./chunk-UHVXE24Y.js";
|
package/dist/index.js
CHANGED
|
@@ -172,7 +172,7 @@ import {
|
|
|
172
172
|
weeklySnapshotPath,
|
|
173
173
|
writeManifest,
|
|
174
174
|
writeMeetingEpisodeMemory
|
|
175
|
-
} from "./chunk-
|
|
175
|
+
} from "./chunk-BCM6ZAJ3.js";
|
|
176
176
|
import {
|
|
177
177
|
WEARABLE_SOURCE_PREFIX,
|
|
178
178
|
buildExtractionTurns,
|
|
@@ -271,7 +271,7 @@ import "./chunk-UXPUUL3G.js";
|
|
|
271
271
|
import "./chunk-V5OCT34X.js";
|
|
272
272
|
import "./chunk-TECVW3JP.js";
|
|
273
273
|
import "./chunk-C5JF6EZ7.js";
|
|
274
|
-
import "./chunk-
|
|
274
|
+
import "./chunk-PWVRMD37.js";
|
|
275
275
|
import "./chunk-VI637ILW.js";
|
|
276
276
|
import "./chunk-SEF4IOFE.js";
|
|
277
277
|
import {
|
|
@@ -498,14 +498,14 @@ import {
|
|
|
498
498
|
runOkfCliCommand,
|
|
499
499
|
runWearablesCliCommand,
|
|
500
500
|
screenCandidateFact
|
|
501
|
-
} from "./chunk-
|
|
501
|
+
} from "./chunk-UWOA5L6N.js";
|
|
502
502
|
import "./chunk-MMACM5G2.js";
|
|
503
503
|
import "./chunk-54BGIBCR.js";
|
|
504
504
|
import "./chunk-H4CKS5B2.js";
|
|
505
505
|
import "./chunk-FXZTL7F7.js";
|
|
506
506
|
import "./chunk-PYHOAXTI.js";
|
|
507
507
|
import "./chunk-UP6MOYCB.js";
|
|
508
|
-
import "./chunk-
|
|
508
|
+
import "./chunk-6H7JWYTC.js";
|
|
509
509
|
import "./chunk-BV2TIZ4F.js";
|
|
510
510
|
import "./chunk-Z734BLO3.js";
|
|
511
511
|
import "./chunk-HDDRVXX4.js";
|
|
@@ -540,7 +540,7 @@ import {
|
|
|
540
540
|
parseIsoTimestamp,
|
|
541
541
|
validateImportTurn
|
|
542
542
|
} from "./chunk-NOUJNHWG.js";
|
|
543
|
-
import "./chunk-
|
|
543
|
+
import "./chunk-PVV42HM4.js";
|
|
544
544
|
import "./chunk-HDQUQBPW.js";
|
|
545
545
|
import "./chunk-6FC3IZ26.js";
|
|
546
546
|
import "./chunk-KWLDB64F.js";
|
|
@@ -622,7 +622,7 @@ import {
|
|
|
622
622
|
} from "./chunk-3I4FUAVJ.js";
|
|
623
623
|
import {
|
|
624
624
|
buildExtensionsBlockForConsolidation
|
|
625
|
-
} from "./chunk-
|
|
625
|
+
} from "./chunk-K6PWPC7O.js";
|
|
626
626
|
import {
|
|
627
627
|
REMNIC_EXTENSIONS_TOTAL_TOKEN_LIMIT,
|
|
628
628
|
discoverMemoryExtensions,
|
|
@@ -1064,7 +1064,8 @@ import {
|
|
|
1064
1064
|
import {
|
|
1065
1065
|
runCodexMaterialize,
|
|
1066
1066
|
runPostConsolidationMaterialize
|
|
1067
|
-
} from "./chunk-
|
|
1067
|
+
} from "./chunk-XZ7QLOTJ.js";
|
|
1068
|
+
import "./chunk-3UXOZBHV.js";
|
|
1068
1069
|
import {
|
|
1069
1070
|
MATERIALIZE_VERSION,
|
|
1070
1071
|
SENTINEL_FILE,
|
package/dist/orchestrator.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
blendGraphExpandedRecallScore,
|
|
4
4
|
graphPathRelativeToStorage,
|
|
5
5
|
mergeGraphExpandedResults
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-BCM6ZAJ3.js";
|
|
7
7
|
import "./chunk-3TSU662L.js";
|
|
8
8
|
import "./chunk-JNZSNJ43.js";
|
|
9
9
|
import "./chunk-I74SUMNI.js";
|
|
@@ -29,7 +29,7 @@ import "./chunk-UXPUUL3G.js";
|
|
|
29
29
|
import "./chunk-V5OCT34X.js";
|
|
30
30
|
import "./chunk-TECVW3JP.js";
|
|
31
31
|
import "./chunk-C5JF6EZ7.js";
|
|
32
|
-
import "./chunk-
|
|
32
|
+
import "./chunk-PWVRMD37.js";
|
|
33
33
|
import "./chunk-VI637ILW.js";
|
|
34
34
|
import "./chunk-SEF4IOFE.js";
|
|
35
35
|
import "./chunk-2OXBS2LV.js";
|
|
@@ -108,14 +108,14 @@ import "./chunk-VY47VI2T.js";
|
|
|
108
108
|
import "./chunk-PYIR4SSB.js";
|
|
109
109
|
import "./chunk-DFEODULB.js";
|
|
110
110
|
import "./chunk-EG4FHU5C.js";
|
|
111
|
-
import "./chunk-
|
|
111
|
+
import "./chunk-UWOA5L6N.js";
|
|
112
112
|
import "./chunk-MMACM5G2.js";
|
|
113
113
|
import "./chunk-54BGIBCR.js";
|
|
114
114
|
import "./chunk-H4CKS5B2.js";
|
|
115
115
|
import "./chunk-FXZTL7F7.js";
|
|
116
116
|
import "./chunk-PYHOAXTI.js";
|
|
117
117
|
import "./chunk-UP6MOYCB.js";
|
|
118
|
-
import "./chunk-
|
|
118
|
+
import "./chunk-6H7JWYTC.js";
|
|
119
119
|
import "./chunk-BV2TIZ4F.js";
|
|
120
120
|
import "./chunk-Z734BLO3.js";
|
|
121
121
|
import "./chunk-HDDRVXX4.js";
|
|
@@ -137,7 +137,7 @@ import "./chunk-YLBE6PLR.js";
|
|
|
137
137
|
import "./chunk-CDKHKZL5.js";
|
|
138
138
|
import "./chunk-HSSERO3E.js";
|
|
139
139
|
import "./chunk-NOUJNHWG.js";
|
|
140
|
-
import "./chunk-
|
|
140
|
+
import "./chunk-PVV42HM4.js";
|
|
141
141
|
import "./chunk-HDQUQBPW.js";
|
|
142
142
|
import "./chunk-6FC3IZ26.js";
|
|
143
143
|
import "./chunk-KWLDB64F.js";
|
|
@@ -177,7 +177,7 @@ import "./chunk-OKTXM5H4.js";
|
|
|
177
177
|
import "./chunk-DUAJKRR2.js";
|
|
178
178
|
import "./chunk-NOB2T2FC.js";
|
|
179
179
|
import "./chunk-3I4FUAVJ.js";
|
|
180
|
-
import "./chunk-
|
|
180
|
+
import "./chunk-K6PWPC7O.js";
|
|
181
181
|
import "./chunk-5JMAGU26.js";
|
|
182
182
|
import "./chunk-YBPYIAA5.js";
|
|
183
183
|
import "./chunk-HK6KYIF6.js";
|
|
@@ -309,7 +309,8 @@ import "./chunk-D3AUQXTJ.js";
|
|
|
309
309
|
import "./chunk-7MOYAT4Y.js";
|
|
310
310
|
import "./chunk-LTWFU6VJ.js";
|
|
311
311
|
import "./chunk-UFGFMWAD.js";
|
|
312
|
-
import "./chunk-
|
|
312
|
+
import "./chunk-XZ7QLOTJ.js";
|
|
313
|
+
import "./chunk-3UXOZBHV.js";
|
|
313
314
|
import "./chunk-ZXBTTMGZ.js";
|
|
314
315
|
import "./chunk-YMOPYG3K.js";
|
|
315
316
|
import "./chunk-2LSZVONP.js";
|
|
@@ -7,11 +7,12 @@ import {
|
|
|
7
7
|
materializeAfterSemanticConsolidation,
|
|
8
8
|
parseConsolidationResponse,
|
|
9
9
|
parseOperatorAwareConsolidationResponse
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-K6PWPC7O.js";
|
|
11
11
|
import {
|
|
12
12
|
resolveExtensionsRoot
|
|
13
13
|
} from "./chunk-5JMAGU26.js";
|
|
14
|
-
import "./chunk-
|
|
14
|
+
import "./chunk-XZ7QLOTJ.js";
|
|
15
|
+
import "./chunk-3UXOZBHV.js";
|
|
15
16
|
import "./chunk-ZXBTTMGZ.js";
|
|
16
17
|
import "./chunk-4PHT7KE2.js";
|
|
17
18
|
import "./chunk-UHVXE24Y.js";
|
|
@@ -11,7 +11,8 @@ import "./chunk-VEWZZM3H.js";
|
|
|
11
11
|
import "./chunk-J3JEW5CT.js";
|
|
12
12
|
import "./chunk-5TBO7CDR.js";
|
|
13
13
|
import "./chunk-EMDLM3LW.js";
|
|
14
|
-
import "./chunk-
|
|
14
|
+
import "./chunk-XZ7QLOTJ.js";
|
|
15
|
+
import "./chunk-3UXOZBHV.js";
|
|
15
16
|
import "./chunk-ZXBTTMGZ.js";
|
|
16
17
|
import "./chunk-4PHT7KE2.js";
|
|
17
18
|
import "./chunk-UHVXE24Y.js";
|
|
@@ -100,7 +100,8 @@ import "../chunk-D3AUQXTJ.js";
|
|
|
100
100
|
import "../chunk-7MOYAT4Y.js";
|
|
101
101
|
import "../chunk-LTWFU6VJ.js";
|
|
102
102
|
import "../chunk-UFGFMWAD.js";
|
|
103
|
-
import "../chunk-
|
|
103
|
+
import "../chunk-XZ7QLOTJ.js";
|
|
104
|
+
import "../chunk-3UXOZBHV.js";
|
|
104
105
|
import "../chunk-ZXBTTMGZ.js";
|
|
105
106
|
import "../chunk-YMOPYG3K.js";
|
|
106
107
|
import "../chunk-2LSZVONP.js";
|
|
@@ -17,6 +17,14 @@ declare const OKF_LOG_TRUNCATION_MARKER = "<!-- okf-log-truncated -->";
|
|
|
17
17
|
declare const DEFAULT_OKF_LOG_MAX_ENTRIES = 500;
|
|
18
18
|
interface ExportOkfOptions {
|
|
19
19
|
memoryDir: string;
|
|
20
|
+
/**
|
|
21
|
+
* Optional namespace segment. Resolved here, once, through the shared
|
|
22
|
+
* containment guard: both CLI entry points used to join the raw operator
|
|
23
|
+
* value onto `memoryDir/namespaces`, so `--namespace ../../..` escaped the
|
|
24
|
+
* namespace root and exported an arbitrary tree (rule 9 — one resolver, not
|
|
25
|
+
* a per-caller guard).
|
|
26
|
+
*/
|
|
27
|
+
namespace?: string;
|
|
20
28
|
outDir: string;
|
|
21
29
|
includeStatus?: readonly string[];
|
|
22
30
|
includeCategories?: readonly string[];
|
|
@@ -3,11 +3,12 @@ import {
|
|
|
3
3
|
OKF_LOG_TRUNCATION_MARKER,
|
|
4
4
|
exportOkfBundle,
|
|
5
5
|
parseIncludeStatus
|
|
6
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-6H7JWYTC.js";
|
|
7
7
|
import {
|
|
8
8
|
OKF_EXPORT_VERSION
|
|
9
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-PVV42HM4.js";
|
|
10
10
|
import "../chunk-D24OXEPB.js";
|
|
11
|
+
import "../chunk-3UXOZBHV.js";
|
|
11
12
|
import "../chunk-4PHT7KE2.js";
|
|
12
13
|
import "../chunk-UHVXE24Y.js";
|
|
13
14
|
import "../chunk-4ZWCQJNM.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remnic/core",
|
|
3
|
-
"version": "9.66.
|
|
3
|
+
"version": "9.66.11",
|
|
4
4
|
"description": "Framework-agnostic Remnic memory engine — orchestrator, storage, extraction, search, trust zones",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -3217,7 +3217,7 @@
|
|
|
3217
3217
|
"core"
|
|
3218
3218
|
],
|
|
3219
3219
|
"peerDependencies": {
|
|
3220
|
-
"@remnic/coding-graph": "^9.66.
|
|
3220
|
+
"@remnic/coding-graph": "^9.66.11"
|
|
3221
3221
|
},
|
|
3222
3222
|
"peerDependenciesMeta": {
|
|
3223
3223
|
"@remnic/coding-graph": {
|
package/src/cli/okf-commands.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
1
|
import type { Orchestrator } from "../orchestrator.js";
|
|
3
2
|
import type { CliCommand } from "../cli.js";
|
|
4
3
|
import { runOkfCliCommand } from "../okf/cli.js";
|
|
@@ -44,11 +43,9 @@ export function registerExportOkfCommand(exportCmd: CliCommand, orchestrator: Or
|
|
|
44
43
|
if (!out) throw new Error("Missing --out");
|
|
45
44
|
const includeStatus = parseIncludeStatus(options.includeStatus);
|
|
46
45
|
const namespace = options.namespace ? String(options.namespace) : "";
|
|
47
|
-
const memoryDir = namespace
|
|
48
|
-
? path.join(orchestrator.config.memoryDir, "namespaces", namespace)
|
|
49
|
-
: orchestrator.config.memoryDir;
|
|
50
46
|
const result = await exportOkfBundle({
|
|
51
|
-
memoryDir,
|
|
47
|
+
memoryDir: orchestrator.config.memoryDir,
|
|
48
|
+
namespace,
|
|
52
49
|
outDir: out,
|
|
53
50
|
includeStatus,
|
|
54
51
|
includeCategories: options.includeCategories ? String(options.includeCategories).split(",") : undefined,
|
package/src/okf/render.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* bundles are byte-compatible in convention (rule 38 — deterministic
|
|
10
10
|
* rendering discipline; rule 9 — stable seam over shared mechanics).
|
|
11
11
|
*/
|
|
12
|
+
import { randomUUID } from "node:crypto";
|
|
12
13
|
import { lstatSync, mkdirSync, readdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
13
14
|
import path from "node:path";
|
|
14
15
|
|
|
@@ -76,6 +77,7 @@ export function publishBundle(staging: string, outDir: string, force: boolean):
|
|
|
76
77
|
try {
|
|
77
78
|
const stat = lstatSync(outDir);
|
|
78
79
|
if (stat.isSymbolicLink()) throw new Error(`--out must not be a symlink: ${outDir}`);
|
|
80
|
+
if (!stat.isDirectory()) throw new Error(`--out exists and is not a directory: ${outDir}`);
|
|
79
81
|
exists = true;
|
|
80
82
|
const entries = readdirSync(outDir).filter((name) => name !== "." && name !== "..");
|
|
81
83
|
if (entries.length > 0 && !force) {
|
|
@@ -87,7 +89,12 @@ export function publishBundle(staging: string, outDir: string, force: boolean):
|
|
|
87
89
|
}
|
|
88
90
|
mkdirSync(path.dirname(outDir), { recursive: true });
|
|
89
91
|
if (exists && force) {
|
|
90
|
-
|
|
92
|
+
// A fixed backup name is not reusable: an earlier crash between the two
|
|
93
|
+
// renames, or an unrelated directory a user created, leaves the path
|
|
94
|
+
// occupied and every later --force export fails on the rename. Each
|
|
95
|
+
// attempt takes its own name and removes it on both the success and the
|
|
96
|
+
// rollback path.
|
|
97
|
+
const backup = `${outDir}.okf-prev-${randomUUID().slice(0, 8)}`;
|
|
91
98
|
try {
|
|
92
99
|
renameSync(outDir, backup);
|
|
93
100
|
} catch {
|
|
@@ -100,8 +107,10 @@ export function publishBundle(staging: string, outDir: string, force: boolean):
|
|
|
100
107
|
} catch (err) {
|
|
101
108
|
try {
|
|
102
109
|
renameSync(backup, outDir);
|
|
110
|
+
rmSync(staging, { recursive: true, force: true });
|
|
103
111
|
} catch {
|
|
104
|
-
// Keep backup
|
|
112
|
+
// Keep the backup when the restore itself fails: it is the only
|
|
113
|
+
// surviving copy of the operator's previous bundle.
|
|
105
114
|
}
|
|
106
115
|
throw err;
|
|
107
116
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
|
-
import { mkdtemp, readFile, rm, writeFile, symlink } from "node:fs/promises";
|
|
2
|
+
import { mkdir, mkdtemp, readFile, rm, writeFile, symlink } from "node:fs/promises";
|
|
3
3
|
import { existsSync, lstatSync, readdirSync } from "node:fs";
|
|
4
4
|
import os from "node:os";
|
|
5
5
|
import path from "node:path";
|
|
@@ -162,3 +162,79 @@ function collectFiles(root: string): string[] {
|
|
|
162
162
|
walk(root);
|
|
163
163
|
return out.sort();
|
|
164
164
|
}
|
|
165
|
+
|
|
166
|
+
test("--namespace cannot escape the namespace root", async () => {
|
|
167
|
+
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-okf-ns-"));
|
|
168
|
+
const outDir = path.join(await mkdtemp(path.join(os.tmpdir(), "remnic-okf-nsout-")), "bundle");
|
|
169
|
+
try {
|
|
170
|
+
await seedStore(memoryDir);
|
|
171
|
+
// A raw path.join on the operator value used to resolve outside
|
|
172
|
+
// <memoryDir>/namespaces and export an arbitrary tree.
|
|
173
|
+
for (const namespace of ["../../..", "../sibling", path.resolve(memoryDir)]) {
|
|
174
|
+
await assert.rejects(
|
|
175
|
+
() => exportOkfBundle({ memoryDir, namespace, outDir }),
|
|
176
|
+
/invalid --namespace/,
|
|
177
|
+
`namespace ${namespace} must be rejected`,
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
assert.equal(existsSync(outDir), false, "a rejected namespace must not create the bundle");
|
|
181
|
+
} finally {
|
|
182
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
test("a plain --namespace still exports from the namespace subtree", async () => {
|
|
187
|
+
const root = await mkdtemp(path.join(os.tmpdir(), "remnic-okf-nsok-"));
|
|
188
|
+
const outDir = path.join(root, "bundle");
|
|
189
|
+
try {
|
|
190
|
+
await seedStore(path.join(root, "namespaces", "team-a"));
|
|
191
|
+
const result = await exportOkfBundle({ memoryDir: root, namespace: "team-a", outDir });
|
|
192
|
+
assert.ok(result.exported > 0, "namespaced export must find its own memories");
|
|
193
|
+
assert.ok(existsSync(path.join(outDir, "index.md")));
|
|
194
|
+
} finally {
|
|
195
|
+
await rm(root, { recursive: true, force: true });
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test("a leftover backup directory does not block a forced export", async () => {
|
|
200
|
+
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-okf-bk-"));
|
|
201
|
+
const root = await mkdtemp(path.join(os.tmpdir(), "remnic-okf-bkout-"));
|
|
202
|
+
const outDir = path.join(root, "bundle");
|
|
203
|
+
try {
|
|
204
|
+
await seedStore(memoryDir);
|
|
205
|
+
await exportOkfBundle({ memoryDir, outDir });
|
|
206
|
+
// Simulate a crash between the two publish renames, or any directory a
|
|
207
|
+
// user happens to own at the old fixed backup path.
|
|
208
|
+
const stale = `${outDir}.okf-prev`;
|
|
209
|
+
await mkdir(stale, { recursive: true });
|
|
210
|
+
await writeFile(path.join(stale, "keep.md"), "stale\n", "utf8");
|
|
211
|
+
|
|
212
|
+
const again = await exportOkfBundle({ memoryDir, outDir, force: true });
|
|
213
|
+
assert.ok(again.exported > 0);
|
|
214
|
+
assert.ok(existsSync(path.join(outDir, "index.md")), "forced export must republish");
|
|
215
|
+
assert.equal(existsSync(path.join(stale, "keep.md")), true, "an unrelated directory is left alone");
|
|
216
|
+
const leftovers = readdirSync(root).filter((name) => name.startsWith("bundle.okf-prev-"));
|
|
217
|
+
assert.deepEqual(leftovers, [], "each attempt removes its own backup");
|
|
218
|
+
} finally {
|
|
219
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
220
|
+
await rm(root, { recursive: true, force: true });
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
test("--out that is a file is rejected before anything is written", async () => {
|
|
225
|
+
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-okf-file-"));
|
|
226
|
+
const root = await mkdtemp(path.join(os.tmpdir(), "remnic-okf-fileout-"));
|
|
227
|
+
const outDir = path.join(root, "bundle");
|
|
228
|
+
try {
|
|
229
|
+
await seedStore(memoryDir);
|
|
230
|
+
await writeFile(outDir, "not a directory\n", "utf8");
|
|
231
|
+
await assert.rejects(
|
|
232
|
+
() => exportOkfBundle({ memoryDir, outDir, force: true }),
|
|
233
|
+
/--out exists and is not a directory/,
|
|
234
|
+
);
|
|
235
|
+
assert.equal(await readFile(outDir, "utf8"), "not a directory\n", "the file is left untouched");
|
|
236
|
+
} finally {
|
|
237
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
238
|
+
await rm(root, { recursive: true, force: true });
|
|
239
|
+
}
|
|
240
|
+
});
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { rmSync } from "node:fs";
|
|
1
|
+
import { mkdirSync, rmSync } from "node:fs";
|
|
2
2
|
import { mkdtemp } from "node:fs/promises";
|
|
3
|
-
import os from "node:os";
|
|
4
3
|
import path from "node:path";
|
|
5
4
|
|
|
6
5
|
import { parseOaiMemCitation } from "../citations.js";
|
|
@@ -8,6 +7,7 @@ import { inferMemoryStatus } from "../memory-lifecycle-ledger-utils.js";
|
|
|
8
7
|
import { normalizeProjectionPreview } from "../memory-projection-format.js";
|
|
9
8
|
import { lintOkfDir } from "../okf/lint.js";
|
|
10
9
|
import { okfTypeForMemory, OKF_PROFILE_TYPE } from "../okf/type-mapping.js";
|
|
10
|
+
import { resolveNamespaceChildRoot } from "../namespaces/path.js";
|
|
11
11
|
import {
|
|
12
12
|
OKF_EXPORT_VERSION,
|
|
13
13
|
publishBundle,
|
|
@@ -38,6 +38,14 @@ const MEMORY_STATUSES: readonly MemoryStatus[] = [
|
|
|
38
38
|
|
|
39
39
|
export interface ExportOkfOptions {
|
|
40
40
|
memoryDir: string;
|
|
41
|
+
/**
|
|
42
|
+
* Optional namespace segment. Resolved here, once, through the shared
|
|
43
|
+
* containment guard: both CLI entry points used to join the raw operator
|
|
44
|
+
* value onto `memoryDir/namespaces`, so `--namespace ../../..` escaped the
|
|
45
|
+
* namespace root and exported an arbitrary tree (rule 9 — one resolver, not
|
|
46
|
+
* a per-caller guard).
|
|
47
|
+
*/
|
|
48
|
+
namespace?: string;
|
|
41
49
|
outDir: string;
|
|
42
50
|
includeStatus?: readonly string[];
|
|
43
51
|
includeCategories?: readonly string[];
|
|
@@ -71,15 +79,19 @@ export function parseIncludeStatus(raw: unknown): MemoryStatus[] {
|
|
|
71
79
|
export async function exportOkfBundle(opts: ExportOkfOptions): Promise<ExportOkfResult> {
|
|
72
80
|
const outDir = path.resolve(opts.outDir);
|
|
73
81
|
rejectSymlinkPath(outDir);
|
|
82
|
+
const namespace = (opts.namespace ?? "").trim();
|
|
83
|
+
const memoryDir = namespace
|
|
84
|
+
? resolveNamespaceChildRoot(opts.memoryDir, namespace, "--namespace")
|
|
85
|
+
: opts.memoryDir;
|
|
74
86
|
const includeStatus = new Set(parseIncludeStatus(opts.includeStatus));
|
|
75
87
|
const includeCategories = opts.includeCategories?.length ? new Set(opts.includeCategories) : null;
|
|
76
88
|
const excludeTags = new Set(opts.excludeTags ?? []);
|
|
77
|
-
const storage = new StorageManager(
|
|
89
|
+
const storage = new StorageManager(memoryDir);
|
|
78
90
|
const memories = await storage.readAllMemories();
|
|
79
91
|
const included: MemoryFile[] = [];
|
|
80
92
|
let excluded = 0;
|
|
81
93
|
for (const memory of memories) {
|
|
82
|
-
const rel = toRel(memory.path,
|
|
94
|
+
const rel = toRel(memory.path, memoryDir);
|
|
83
95
|
if (!opts.includeWearables && rel.startsWith("wearables/")) {
|
|
84
96
|
excluded += 1;
|
|
85
97
|
continue;
|
|
@@ -99,17 +111,21 @@ export async function exportOkfBundle(opts: ExportOkfOptions): Promise<ExportOkf
|
|
|
99
111
|
}
|
|
100
112
|
included.push(memory);
|
|
101
113
|
}
|
|
102
|
-
included.sort((a, b) => toRel(a.path,
|
|
114
|
+
included.sort((a, b) => toRel(a.path, memoryDir).localeCompare(toRel(b.path, memoryDir)));
|
|
103
115
|
|
|
104
|
-
|
|
116
|
+
// Stage inside the destination's parent, not the OS temp dir: publishBundle
|
|
117
|
+
// finishes with a rename, and a rename across filesystems fails with EXDEV
|
|
118
|
+
// whenever --out lives on a different mount than /tmp.
|
|
119
|
+
mkdirSync(path.dirname(outDir), { recursive: true });
|
|
120
|
+
const staging = await mkdtemp(path.join(path.dirname(outDir), ".remnic-okf-export-"));
|
|
105
121
|
const byCategory: Record<string, number> = {};
|
|
106
122
|
const idToRel = new Map<string, string>();
|
|
107
123
|
for (const memory of included) {
|
|
108
|
-
const rel = toRel(memory.path,
|
|
124
|
+
const rel = toRel(memory.path, memoryDir);
|
|
109
125
|
idToRel.set(memory.frontmatter.id, rel);
|
|
110
126
|
}
|
|
111
127
|
for (const memory of included) {
|
|
112
|
-
const rel = toRel(memory.path,
|
|
128
|
+
const rel = toRel(memory.path, memoryDir);
|
|
113
129
|
const rendered = renderMemory(memory, rel, idToRel);
|
|
114
130
|
writeBundleFile(staging, rel, rendered);
|
|
115
131
|
const category = memory.frontmatter.category;
|
|
@@ -140,7 +156,7 @@ export async function exportOkfBundle(opts: ExportOkfOptions): Promise<ExportOkf
|
|
|
140
156
|
writeBundleFile(staging, "log.md", renderLog(events, idToRel, opts.logMaxEntries ?? DEFAULT_OKF_LOG_MAX_ENTRIES));
|
|
141
157
|
}
|
|
142
158
|
|
|
143
|
-
writeIndexes(staging, included,
|
|
159
|
+
writeIndexes(staging, included, memoryDir);
|
|
144
160
|
const lint = lintOkfDir(staging);
|
|
145
161
|
const blocking = lint.findings.filter((f) => f.code !== "skipped_encrypted" && f.code !== "reserved_basename");
|
|
146
162
|
if (blocking.length > 0) {
|