opensquid 0.5.441 → 0.5.447
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 +1 -0
- package/dist/functions/arm_scope.d.ts +27 -0
- package/dist/functions/arm_scope.d.ts.map +1 -0
- package/dist/functions/arm_scope.js +52 -0
- package/dist/functions/arm_scope.js.map +1 -0
- package/dist/functions/index.d.ts +1 -0
- package/dist/functions/index.d.ts.map +1 -1
- package/dist/functions/index.js +1 -0
- package/dist/functions/index.js.map +1 -1
- package/dist/runtime/bootstrap.d.ts.map +1 -1
- package/dist/runtime/bootstrap.js +2 -0
- package/dist/runtime/bootstrap.js.map +1 -1
- package/dist/runtime/handoff/render.d.ts +5 -4
- package/dist/runtime/handoff/render.d.ts.map +1 -1
- package/dist/runtime/handoff/render.js +7 -7
- package/dist/runtime/handoff/render.js.map +1 -1
- package/dist/runtime/hooks/active_task_mirror.js +0 -0
- package/dist/runtime/hooks/apply_patch.js +0 -0
- package/dist/runtime/hooks/dispatch.js +0 -0
- package/dist/runtime/hooks/hook_output.js +0 -0
- package/dist/runtime/hooks/memory_reconcile.js +0 -0
- package/dist/runtime/hooks/new_project_detect.js +0 -0
- package/dist/runtime/hooks/profession_resolver.js +0 -0
- package/dist/runtime/hooks/scope_intent.js +0 -0
- package/dist/runtime/hooks/session_id.js +0 -0
- package/dist/runtime/hooks/session_liveness.js +0 -0
- package/dist/runtime/hooks/stop_drive.js +0 -0
- package/dist/runtime/hooks/stop_stream.js +0 -0
- package/dist/runtime/hooks/subagent_guard.js +0 -0
- package/dist/runtime/hooks/transcript.js +0 -0
- package/dist/runtime/hooks/transcript_tasks.js +0 -0
- package/dist/runtime/ralph/orchestrator.d.ts.map +1 -1
- package/dist/runtime/ralph/orchestrator.js +2 -1
- package/dist/runtime/ralph/orchestrator.js.map +1 -1
- package/dist/setup/cli/limits_state.d.ts.map +1 -1
- package/dist/setup/cli/limits_state.js +6 -40
- package/dist/setup/cli/limits_state.js.map +1 -1
- package/dist/setup/cli/pack_walk.d.ts +32 -0
- package/dist/setup/cli/pack_walk.d.ts.map +1 -0
- package/dist/setup/cli/pack_walk.js +76 -0
- package/dist/setup/cli/pack_walk.js.map +1 -0
- package/dist/setup/cli/permissions_state.d.ts.map +1 -1
- package/dist/setup/cli/permissions_state.js +6 -37
- package/dist/setup/cli/permissions_state.js.map +1 -1
- package/dist/setup/cli/triggers_state.d.ts.map +1 -1
- package/dist/setup/cli/triggers_state.js +3 -29
- package/dist/setup/cli/triggers_state.js.map +1 -1
- package/dist/workgraph/events.d.ts.map +1 -1
- package/dist/workgraph/events.js +10 -0
- package/dist/workgraph/events.js.map +1 -1
- package/dist/workgraph/store.d.ts.map +1 -1
- package/dist/workgraph/store.js +5 -0
- package/dist/workgraph/store.js.map +1 -1
- package/dist/workgraph/types.d.ts +2 -1
- package/dist/workgraph/types.d.ts.map +1 -1
- package/docs/ARCHITECTURE.md +268 -0
- package/package.json +5 -3
- package/packs/builtin/coding-flow/skills/entry-and-handoffs/skill.yaml +13 -17
- package/dist/anti-drift/evaluator.d.ts +0 -88
- package/dist/anti-drift/evaluator.d.ts.map +0 -1
- package/dist/anti-drift/evaluator.js +0 -417
- package/dist/anti-drift/evaluator.js.map +0 -1
- package/dist/anti-drift/evaluator.test.js +0 -78
- package/dist/anti-drift/rules.d.ts +0 -80
- package/dist/anti-drift/rules.d.ts.map +0 -1
- package/dist/anti-drift/rules.js +0 -368
- package/dist/anti-drift/rules.js.map +0 -1
- package/dist/anti-drift/rules.test.js +0 -213
- package/dist/anti-drift/state.d.ts +0 -107
- package/dist/anti-drift/state.d.ts.map +0 -1
- package/dist/anti-drift/state.js +0 -177
- package/dist/anti-drift/state.js.map +0 -1
- package/dist/anti-drift/state.test.js +0 -120
- package/dist/chat/adapters/discord.d.ts +0 -41
- package/dist/chat/adapters/discord.d.ts.map +0 -1
- package/dist/chat/adapters/discord.js +0 -176
- package/dist/chat/adapters/discord.js.map +0 -1
- package/dist/chat/adapters/discord.test.js +0 -25
- package/dist/chat/adapters/slack.d.ts +0 -43
- package/dist/chat/adapters/slack.d.ts.map +0 -1
- package/dist/chat/adapters/slack.js +0 -172
- package/dist/chat/adapters/slack.js.map +0 -1
- package/dist/chat/adapters/slack.test.js +0 -30
- package/dist/chat/adapters/telegram.d.ts +0 -148
- package/dist/chat/adapters/telegram.d.ts.map +0 -1
- package/dist/chat/adapters/telegram.js +0 -498
- package/dist/chat/adapters/telegram.js.map +0 -1
- package/dist/chat/adapters/telegram.test.js +0 -94
- package/dist/chat/config.d.ts +0 -98
- package/dist/chat/config.d.ts.map +0 -1
- package/dist/chat/config.js +0 -185
- package/dist/chat/config.js.map +0 -1
- package/dist/chat/daemon/active-project.d.ts +0 -17
- package/dist/chat/daemon/active-project.d.ts.map +0 -1
- package/dist/chat/daemon/active-project.js +0 -23
- package/dist/chat/daemon/active-project.js.map +0 -1
- package/dist/chat/daemon/autospawn.d.ts +0 -40
- package/dist/chat/daemon/autospawn.d.ts.map +0 -1
- package/dist/chat/daemon/autospawn.js +0 -129
- package/dist/chat/daemon/autospawn.js.map +0 -1
- package/dist/chat/daemon/autospawn.test.js +0 -112
- package/dist/chat/daemon/cli.d.ts +0 -18
- package/dist/chat/daemon/cli.d.ts.map +0 -1
- package/dist/chat/daemon/cli.js +0 -71
- package/dist/chat/daemon/cli.js.map +0 -1
- package/dist/chat/daemon/collisions.js +0 -384
- package/dist/chat/daemon/health-check.d.ts +0 -69
- package/dist/chat/daemon/health-check.d.ts.map +0 -1
- package/dist/chat/daemon/health-check.js +0 -112
- package/dist/chat/daemon/health-check.js.map +0 -1
- package/dist/chat/daemon/inbox-read.d.ts +0 -35
- package/dist/chat/daemon/inbox-read.d.ts.map +0 -1
- package/dist/chat/daemon/inbox-read.js +0 -75
- package/dist/chat/daemon/inbox-read.js.map +0 -1
- package/dist/chat/daemon/inbox-read.test.js +0 -97
- package/dist/chat/daemon/inbox.d.ts +0 -63
- package/dist/chat/daemon/inbox.d.ts.map +0 -1
- package/dist/chat/daemon/inbox.js +0 -56
- package/dist/chat/daemon/inbox.js.map +0 -1
- package/dist/chat/daemon/inbox.test.js +0 -110
- package/dist/chat/daemon/lifecycle.d.ts +0 -71
- package/dist/chat/daemon/lifecycle.d.ts.map +0 -1
- package/dist/chat/daemon/lifecycle.js +0 -221
- package/dist/chat/daemon/lifecycle.js.map +0 -1
- package/dist/chat/daemon/lifecycle.test.js +0 -163
- package/dist/chat/daemon/protocol.d.ts +0 -107
- package/dist/chat/daemon/protocol.d.ts.map +0 -1
- package/dist/chat/daemon/protocol.js +0 -54
- package/dist/chat/daemon/protocol.js.map +0 -1
- package/dist/chat/daemon/routing.d.ts +0 -140
- package/dist/chat/daemon/routing.d.ts.map +0 -1
- package/dist/chat/daemon/routing.js +0 -198
- package/dist/chat/daemon/routing.js.map +0 -1
- package/dist/chat/daemon/routing.test.js +0 -259
- package/dist/chat/daemon/rpc-client.d.ts +0 -45
- package/dist/chat/daemon/rpc-client.d.ts.map +0 -1
- package/dist/chat/daemon/rpc-client.js +0 -133
- package/dist/chat/daemon/rpc-client.js.map +0 -1
- package/dist/chat/daemon/rpc-server.d.ts +0 -39
- package/dist/chat/daemon/rpc-server.d.ts.map +0 -1
- package/dist/chat/daemon/rpc-server.js +0 -385
- package/dist/chat/daemon/rpc-server.js.map +0 -1
- package/dist/chat/daemon/rpc.test.js +0 -177
- package/dist/chat/daemon/subscribers.js +0 -257
- package/dist/chat/daemon/worker.d.ts +0 -27
- package/dist/chat/daemon/worker.d.ts.map +0 -1
- package/dist/chat/daemon/worker.js +0 -313
- package/dist/chat/daemon/worker.js.map +0 -1
- package/dist/chat/daemon/workspace-topic.js +0 -324
- package/dist/chat/env-token.d.ts +0 -60
- package/dist/chat/env-token.d.ts.map +0 -1
- package/dist/chat/env-token.js +0 -137
- package/dist/chat/env-token.js.map +0 -1
- package/dist/chat/env-token.test.js +0 -160
- package/dist/chat/factory.d.ts +0 -30
- package/dist/chat/factory.d.ts.map +0 -1
- package/dist/chat/factory.js +0 -50
- package/dist/chat/factory.js.map +0 -1
- package/dist/chat/factory.test.js +0 -55
- package/dist/chat/gateway.d.ts +0 -176
- package/dist/chat/gateway.d.ts.map +0 -1
- package/dist/chat/gateway.js +0 -146
- package/dist/chat/gateway.js.map +0 -1
- package/dist/chat/gateway.test.js +0 -192
- package/dist/claude-md.d.ts +0 -39
- package/dist/claude-md.d.ts.map +0 -1
- package/dist/claude-md.js +0 -113
- package/dist/claude-md.js.map +0 -1
- package/dist/claude-md.test.js +0 -91
- package/dist/codex/activate.d.ts +0 -66
- package/dist/codex/activate.d.ts.map +0 -1
- package/dist/codex/activate.js +0 -329
- package/dist/codex/activate.js.map +0 -1
- package/dist/codex/activate.test.js +0 -229
- package/dist/codex/bundled-default/bundled-default.test.js +0 -161
- package/dist/codex/cli-publish.test.js +0 -133
- package/dist/codex/cli.d.ts +0 -35
- package/dist/codex/cli.d.ts.map +0 -1
- package/dist/codex/cli.js +0 -554
- package/dist/codex/cli.js.map +0 -1
- package/dist/codex/cli.test.js +0 -277
- package/dist/codex/import-skill-md.d.ts +0 -53
- package/dist/codex/import-skill-md.d.ts.map +0 -1
- package/dist/codex/import-skill-md.js +0 -236
- package/dist/codex/import-skill-md.js.map +0 -1
- package/dist/codex/import-skill-md.test.js +0 -225
- package/dist/codex/loader.d.ts +0 -27
- package/dist/codex/loader.d.ts.map +0 -1
- package/dist/codex/loader.js +0 -86
- package/dist/codex/loader.js.map +0 -1
- package/dist/codex/loader.test.js +0 -75
- package/dist/codex/parse.d.ts +0 -28
- package/dist/codex/parse.d.ts.map +0 -1
- package/dist/codex/parse.js +0 -309
- package/dist/codex/parse.js.map +0 -1
- package/dist/codex/parse.test.js +0 -241
- package/dist/codex/store.d.ts +0 -87
- package/dist/codex/store.d.ts.map +0 -1
- package/dist/codex/store.js +0 -205
- package/dist/codex/store.js.map +0 -1
- package/dist/codex/store.test.js +0 -242
- package/dist/codex/types.d.ts +0 -398
- package/dist/codex/types.d.ts.map +0 -1
- package/dist/codex/types.js +0 -21
- package/dist/codex/types.js.map +0 -1
- package/dist/config.d.ts +0 -53
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js +0 -202
- package/dist/config.js.map +0 -1
- package/dist/config.test.js +0 -117
- package/dist/engine/cli.d.ts +0 -14
- package/dist/engine/cli.d.ts.map +0 -1
- package/dist/engine/cli.js +0 -171
- package/dist/engine/cli.js.map +0 -1
- package/dist/engine/client.d.ts +0 -219
- package/dist/engine/client.d.ts.map +0 -1
- package/dist/engine/client.js +0 -312
- package/dist/engine/client.js.map +0 -1
- package/dist/engine/config.d.ts +0 -62
- package/dist/engine/config.d.ts.map +0 -1
- package/dist/engine/config.js +0 -223
- package/dist/engine/config.js.map +0 -1
- package/dist/engine/index.d.ts +0 -17
- package/dist/engine/index.d.ts.map +0 -1
- package/dist/engine/index.js +0 -16
- package/dist/engine/index.js.map +0 -1
- package/dist/engine/resolver.d.ts +0 -62
- package/dist/engine/resolver.d.ts.map +0 -1
- package/dist/engine/resolver.js +0 -103
- package/dist/engine/resolver.js.map +0 -1
- package/dist/engine/singleton.d.ts +0 -95
- package/dist/engine/singleton.d.ts.map +0 -1
- package/dist/engine/singleton.js +0 -325
- package/dist/engine/singleton.js.map +0 -1
- package/dist/engine/types.d.ts +0 -402
- package/dist/engine/types.d.ts.map +0 -1
- package/dist/engine/types.js +0 -22
- package/dist/engine/types.js.map +0 -1
- package/dist/engine-binary-resolver.js +0 -110
- package/dist/engine-binary-resolver.test.js +0 -61
- package/dist/engine-cli.js +0 -60
- package/dist/engine-client.js +0 -301
- package/dist/engine-client.test.js +0 -118
- package/dist/functions/chain_state.d.ts +0 -51
- package/dist/functions/chain_state.d.ts.map +0 -1
- package/dist/functions/chain_state.js +0 -59
- package/dist/functions/chain_state.js.map +0 -1
- package/dist/hooks/drift-catalog.d.ts +0 -68
- package/dist/hooks/drift-catalog.d.ts.map +0 -1
- package/dist/hooks/drift-catalog.js +0 -184
- package/dist/hooks/drift-catalog.js.map +0 -1
- package/dist/hooks/drift-catalog.test.js +0 -154
- package/dist/hooks/drift-patterns.d.ts +0 -110
- package/dist/hooks/drift-patterns.d.ts.map +0 -1
- package/dist/hooks/drift-patterns.js +0 -289
- package/dist/hooks/drift-patterns.js.map +0 -1
- package/dist/hooks/drift-patterns.test.js +0 -325
- package/dist/hooks/engine-vocab-gate.d.ts +0 -108
- package/dist/hooks/engine-vocab-gate.d.ts.map +0 -1
- package/dist/hooks/engine-vocab-gate.js +0 -225
- package/dist/hooks/engine-vocab-gate.js.map +0 -1
- package/dist/hooks/engine-vocab-gate.test.js +0 -170
- package/dist/hooks/heartbeat.d.ts +0 -107
- package/dist/hooks/heartbeat.d.ts.map +0 -1
- package/dist/hooks/heartbeat.js +0 -316
- package/dist/hooks/heartbeat.js.map +0 -1
- package/dist/hooks/heartbeat.test.js +0 -393
- package/dist/hooks/honesty-ledger-session-scope.test.js +0 -100
- package/dist/hooks/honesty-ledger.d.ts +0 -123
- package/dist/hooks/honesty-ledger.d.ts.map +0 -1
- package/dist/hooks/honesty-ledger.js +0 -226
- package/dist/hooks/honesty-ledger.js.map +0 -1
- package/dist/hooks/honesty-ledger.test.js +0 -466
- package/dist/hooks/inline-report-check.d.ts +0 -63
- package/dist/hooks/inline-report-check.d.ts.map +0 -1
- package/dist/hooks/inline-report-check.js +0 -88
- package/dist/hooks/inline-report-check.js.map +0 -1
- package/dist/hooks/inline-report-check.test.js +0 -96
- package/dist/hooks/pre-tool-use.d.ts +0 -62
- package/dist/hooks/pre-tool-use.d.ts.map +0 -1
- package/dist/hooks/pre-tool-use.js +0 -342
- package/dist/hooks/pre-tool-use.js.map +0 -1
- package/dist/hooks/pre-tool-use.test.js +0 -134
- package/dist/hooks/session-end.d.ts +0 -15
- package/dist/hooks/session-end.d.ts.map +0 -1
- package/dist/hooks/session-end.js +0 -60
- package/dist/hooks/session-end.js.map +0 -1
- package/dist/hooks/session-end.test.js +0 -52
- package/dist/hooks/stop.d.ts +0 -35
- package/dist/hooks/stop.d.ts.map +0 -1
- package/dist/hooks/stop.js +0 -136
- package/dist/hooks/stop.js.map +0 -1
- package/dist/hooks/transcript-active-task.test.js +0 -342
- package/dist/hooks/transcript.d.ts +0 -26
- package/dist/hooks/transcript.d.ts.map +0 -1
- package/dist/hooks/transcript.js +0 -266
- package/dist/hooks/transcript.js.map +0 -1
- package/dist/hooks/transcript.test.js +0 -103
- package/dist/hooks/user-prompt-submit.d.ts +0 -74
- package/dist/hooks/user-prompt-submit.d.ts.map +0 -1
- package/dist/hooks/user-prompt-submit.js +0 -256
- package/dist/hooks/user-prompt-submit.js.map +0 -1
- package/dist/hooks/user-prompt-submit.test.js +0 -118
- package/dist/hooks/versioning-gate.d.ts +0 -101
- package/dist/hooks/versioning-gate.d.ts.map +0 -1
- package/dist/hooks/versioning-gate.js +0 -245
- package/dist/hooks/versioning-gate.js.map +0 -1
- package/dist/hooks/versioning-gate.test.js +0 -368
- package/dist/hooks/workflow-gate.d.ts +0 -64
- package/dist/hooks/workflow-gate.d.ts.map +0 -1
- package/dist/hooks/workflow-gate.js +0 -152
- package/dist/hooks/workflow-gate.js.map +0 -1
- package/dist/hooks/workflow-gate.test.js +0 -197
- package/dist/hooks-cli.d.ts +0 -25
- package/dist/hooks-cli.d.ts.map +0 -1
- package/dist/hooks-cli.js +0 -286
- package/dist/hooks-cli.js.map +0 -1
- package/dist/hooks-cli.test.js +0 -148
- package/dist/origin.d.ts +0 -16
- package/dist/origin.d.ts.map +0 -1
- package/dist/origin.js +0 -92
- package/dist/origin.js.map +0 -1
- package/dist/packs/seed_lessons_ingest.d.ts +0 -30
- package/dist/packs/seed_lessons_ingest.d.ts.map +0 -1
- package/dist/packs/seed_lessons_ingest.js +0 -107
- package/dist/packs/seed_lessons_ingest.js.map +0 -1
- package/dist/project-cli.d.ts +0 -7
- package/dist/project-cli.d.ts.map +0 -1
- package/dist/project-cli.js +0 -145
- package/dist/project-cli.js.map +0 -1
- package/dist/project.d.ts +0 -127
- package/dist/project.d.ts.map +0 -1
- package/dist/project.js +0 -281
- package/dist/project.js.map +0 -1
- package/dist/project.test.js +0 -287
- package/dist/rag/backends/loop_engine.d.ts +0 -61
- package/dist/rag/backends/loop_engine.d.ts.map +0 -1
- package/dist/rag/backends/loop_engine.js +0 -160
- package/dist/rag/backends/loop_engine.js.map +0 -1
- package/dist/recall.d.ts +0 -82
- package/dist/recall.d.ts.map +0 -1
- package/dist/recall.js +0 -81
- package/dist/recall.js.map +0 -1
- package/dist/runtime/agent_bridge/autospawn.d.ts +0 -131
- package/dist/runtime/agent_bridge/autospawn.d.ts.map +0 -1
- package/dist/runtime/agent_bridge/autospawn.js +0 -251
- package/dist/runtime/agent_bridge/autospawn.js.map +0 -1
- package/dist/runtime/chain_state.d.ts +0 -124
- package/dist/runtime/chain_state.d.ts.map +0 -1
- package/dist/runtime/chain_state.js +0 -189
- package/dist/runtime/chain_state.js.map +0 -1
- package/dist/runtime/hooks/permission_decision.d.ts +0 -34
- package/dist/runtime/hooks/permission_decision.d.ts.map +0 -1
- package/dist/runtime/hooks/permission_decision.js +0 -39
- package/dist/runtime/hooks/permission_decision.js.map +0 -1
- package/dist/runtime/workflow_fsm.d.ts +0 -21
- package/dist/runtime/workflow_fsm.d.ts.map +0 -1
- package/dist/runtime/workflow_fsm.js +0 -25
- package/dist/runtime/workflow_fsm.js.map +0 -1
- package/dist/runtime/workflow_map.d.ts +0 -26
- package/dist/runtime/workflow_map.d.ts.map +0 -1
- package/dist/runtime/workflow_map.js +0 -38
- package/dist/runtime/workflow_map.js.map +0 -1
- package/dist/scope.d.ts +0 -48
- package/dist/scope.d.ts.map +0 -1
- package/dist/scope.js +0 -111
- package/dist/scope.js.map +0 -1
- package/dist/setup/cli/topic_create_step.d.ts +0 -84
- package/dist/setup/cli/topic_create_step.d.ts.map +0 -1
- package/dist/setup/cli/topic_create_step.js +0 -213
- package/dist/setup/cli/topic_create_step.js.map +0 -1
- package/dist/system-export.d.ts +0 -65
- package/dist/system-export.d.ts.map +0 -1
- package/dist/system-export.js +0 -194
- package/dist/system-export.js.map +0 -1
- package/dist/utterance/classifier.d.ts +0 -53
- package/dist/utterance/classifier.d.ts.map +0 -1
- package/dist/utterance/classifier.js +0 -184
- package/dist/utterance/classifier.js.map +0 -1
- package/dist/utterance/classifier.test.js +0 -147
package/dist/codex/store.js
DELETED
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Codex local storage — filesystem layer for installed codexes.
|
|
3
|
-
*
|
|
4
|
-
* Stores codex YAML + lesson markdown files at <root>/codexes/<id>/.
|
|
5
|
-
* Engine sees none of this; it only stores the resulting lessons that
|
|
6
|
-
* opensquid seeds via `lesson.create` with `authored_by: Pack(<id>)`.
|
|
7
|
-
*
|
|
8
|
-
* Resolution order for the data root:
|
|
9
|
-
* 1. `rootDir` parameter (explicit override; used by tests)
|
|
10
|
-
* 2. `OPENSQUID_HOME` env var
|
|
11
|
-
* 3. `LOOP_HOME` env var (engine-shared)
|
|
12
|
-
* 4. `~/.opensquid/` default
|
|
13
|
-
*/
|
|
14
|
-
import { promises as fs } from "node:fs";
|
|
15
|
-
import * as os from "node:os";
|
|
16
|
-
import * as path from "node:path";
|
|
17
|
-
import { parseCodexYaml } from "./parse.js";
|
|
18
|
-
import { stringify as stringifyYaml } from "yaml";
|
|
19
|
-
export class CodexStoreError extends Error {
|
|
20
|
-
code;
|
|
21
|
-
cause;
|
|
22
|
-
constructor(code, message, cause) {
|
|
23
|
-
super(message);
|
|
24
|
-
this.code = code;
|
|
25
|
-
this.cause = cause;
|
|
26
|
-
this.name = "CodexStoreError";
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
// ---------------------------------------------------------------------
|
|
30
|
-
// Data root resolution
|
|
31
|
-
// ---------------------------------------------------------------------
|
|
32
|
-
/**
|
|
33
|
-
* Resolve the opensquid data root.
|
|
34
|
-
*
|
|
35
|
-
* Order: explicit rootDir → OPENSQUID_HOME → LOOP_HOME → ~/.opensquid
|
|
36
|
-
*/
|
|
37
|
-
export function resolveDataRoot(rootDir) {
|
|
38
|
-
if (rootDir)
|
|
39
|
-
return rootDir;
|
|
40
|
-
if (process.env.OPENSQUID_HOME)
|
|
41
|
-
return process.env.OPENSQUID_HOME;
|
|
42
|
-
if (process.env.LOOP_HOME)
|
|
43
|
-
return process.env.LOOP_HOME;
|
|
44
|
-
return path.join(os.homedir(), ".opensquid");
|
|
45
|
-
}
|
|
46
|
-
/** Directory holding all installed codexes. */
|
|
47
|
-
export function codexesDir(rootDir) {
|
|
48
|
-
return path.join(resolveDataRoot(rootDir), "codexes");
|
|
49
|
-
}
|
|
50
|
-
/** Directory for a specific codex by id. */
|
|
51
|
-
export function codexDir(id, rootDir) {
|
|
52
|
-
return path.join(codexesDir(rootDir), id);
|
|
53
|
-
}
|
|
54
|
-
// ---------------------------------------------------------------------
|
|
55
|
-
// Id validation
|
|
56
|
-
// ---------------------------------------------------------------------
|
|
57
|
-
const CODEX_ID_RE = /^[a-z0-9][a-z0-9._-]{0,127}$/;
|
|
58
|
-
/**
|
|
59
|
-
* Validate a codex id: lowercase alphanumeric + `._-`, max 128 chars,
|
|
60
|
-
* cannot start with `._-`. Prevents path traversal and ambiguous names.
|
|
61
|
-
*/
|
|
62
|
-
export function validateCodexId(id) {
|
|
63
|
-
if (!CODEX_ID_RE.test(id)) {
|
|
64
|
-
throw new CodexStoreError("INVALID_ID", `codex id "${id}" is invalid (must match /^[a-z0-9][a-z0-9._-]{0,127}$/)`);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
// ---------------------------------------------------------------------
|
|
68
|
-
// Filesystem helpers
|
|
69
|
-
// ---------------------------------------------------------------------
|
|
70
|
-
async function ensureDir(dir) {
|
|
71
|
-
await fs.mkdir(dir, { recursive: true });
|
|
72
|
-
}
|
|
73
|
-
async function readIfExists(filePath) {
|
|
74
|
-
try {
|
|
75
|
-
return await fs.readFile(filePath, "utf8");
|
|
76
|
-
}
|
|
77
|
-
catch (err) {
|
|
78
|
-
if (err.code === "ENOENT")
|
|
79
|
-
return null;
|
|
80
|
-
throw new CodexStoreError("READ_FAILED", `read failed: ${filePath}`, err);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
async function pathExists(p) {
|
|
84
|
-
try {
|
|
85
|
-
await fs.access(p);
|
|
86
|
-
return true;
|
|
87
|
-
}
|
|
88
|
-
catch {
|
|
89
|
-
return false;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
// ---------------------------------------------------------------------
|
|
93
|
-
// Public API
|
|
94
|
-
// ---------------------------------------------------------------------
|
|
95
|
-
/**
|
|
96
|
-
* Install a codex into local storage.
|
|
97
|
-
*
|
|
98
|
-
* Writes `codex.yaml` at the canonical path. If `force=false` and a
|
|
99
|
-
* codex with this id is already installed, throws ALREADY_INSTALLED.
|
|
100
|
-
*/
|
|
101
|
-
export async function installCodex(codex, options = {}) {
|
|
102
|
-
validateCodexId(codex.id);
|
|
103
|
-
const dir = codexDir(codex.id, options.rootDir);
|
|
104
|
-
const manifestPath = path.join(dir, "codex.yaml");
|
|
105
|
-
if (!options.force && (await pathExists(manifestPath))) {
|
|
106
|
-
throw new CodexStoreError("ALREADY_INSTALLED", `codex "${codex.id}" is already installed at ${dir} (use force to overwrite)`);
|
|
107
|
-
}
|
|
108
|
-
try {
|
|
109
|
-
await ensureDir(dir);
|
|
110
|
-
const yaml = stringifyYaml(codex);
|
|
111
|
-
await fs.writeFile(manifestPath, yaml, "utf8");
|
|
112
|
-
}
|
|
113
|
-
catch (err) {
|
|
114
|
-
throw new CodexStoreError("WRITE_FAILED", `failed to write codex "${codex.id}"`, err);
|
|
115
|
-
}
|
|
116
|
-
return { id: codex.id, path: dir };
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Load an installed codex by id.
|
|
120
|
-
*
|
|
121
|
-
* Reads + parses `codex.yaml`. Throws NOT_FOUND if not installed, or
|
|
122
|
-
* PARSE_FAILED if the manifest is malformed.
|
|
123
|
-
*/
|
|
124
|
-
export async function getCodex(id, options = {}) {
|
|
125
|
-
validateCodexId(id);
|
|
126
|
-
const manifestPath = path.join(codexDir(id, options.rootDir), "codex.yaml");
|
|
127
|
-
const content = await readIfExists(manifestPath);
|
|
128
|
-
if (content === null) {
|
|
129
|
-
throw new CodexStoreError("NOT_FOUND", `codex "${id}" not installed at ${manifestPath}`);
|
|
130
|
-
}
|
|
131
|
-
try {
|
|
132
|
-
return parseCodexYaml(content);
|
|
133
|
-
}
|
|
134
|
-
catch (err) {
|
|
135
|
-
throw new CodexStoreError("PARSE_FAILED", `codex "${id}" manifest is malformed`, err);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* List all installed codex ids.
|
|
140
|
-
*
|
|
141
|
-
* Scans `<root>/codexes/` for directories that contain a `codex.yaml`.
|
|
142
|
-
* Directories without a manifest are skipped (partial installs, etc.).
|
|
143
|
-
* Returns ids in sorted order.
|
|
144
|
-
*/
|
|
145
|
-
export async function listCodexes(options = {}) {
|
|
146
|
-
const dir = codexesDir(options.rootDir);
|
|
147
|
-
let entries;
|
|
148
|
-
try {
|
|
149
|
-
entries = await fs.readdir(dir, { withFileTypes: true });
|
|
150
|
-
}
|
|
151
|
-
catch (err) {
|
|
152
|
-
if (err.code === "ENOENT")
|
|
153
|
-
return [];
|
|
154
|
-
throw new CodexStoreError("READ_FAILED", `failed to list codexes at ${dir}`, err);
|
|
155
|
-
}
|
|
156
|
-
const ids = [];
|
|
157
|
-
for (const entry of entries) {
|
|
158
|
-
if (!entry.isDirectory())
|
|
159
|
-
continue;
|
|
160
|
-
if (!CODEX_ID_RE.test(entry.name))
|
|
161
|
-
continue;
|
|
162
|
-
const manifest = path.join(dir, entry.name, "codex.yaml");
|
|
163
|
-
if (await pathExists(manifest)) {
|
|
164
|
-
ids.push(entry.name);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
ids.sort();
|
|
168
|
-
return ids;
|
|
169
|
-
}
|
|
170
|
-
/**
|
|
171
|
-
* Remove an installed codex.
|
|
172
|
-
*
|
|
173
|
-
* Deletes the entire `<root>/codexes/<id>/` directory. Returns true if
|
|
174
|
-
* the codex existed and was removed; false if it wasn't installed.
|
|
175
|
-
* Does NOT touch the engine's lesson store — the caller is responsible
|
|
176
|
-
* for retiring the codex's seeded lessons via the engine RPC first.
|
|
177
|
-
*/
|
|
178
|
-
export async function removeCodex(id, options = {}) {
|
|
179
|
-
validateCodexId(id);
|
|
180
|
-
const dir = codexDir(id, options.rootDir);
|
|
181
|
-
if (!(await pathExists(dir)))
|
|
182
|
-
return false;
|
|
183
|
-
try {
|
|
184
|
-
await fs.rm(dir, { recursive: true, force: true });
|
|
185
|
-
return true;
|
|
186
|
-
}
|
|
187
|
-
catch (err) {
|
|
188
|
-
throw new CodexStoreError("WRITE_FAILED", `failed to remove codex "${id}"`, err);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
/**
|
|
192
|
-
* Resolve the path to a content file (lesson body, reference doc) inside
|
|
193
|
-
* a codex. Used by the orchestrator when bank_strategy requires lazy
|
|
194
|
-
* fetching the full body. Does NOT read the file — just returns the path.
|
|
195
|
-
*/
|
|
196
|
-
export function codexContentPath(id, relativePath, options = {}) {
|
|
197
|
-
validateCodexId(id);
|
|
198
|
-
// Refuse paths that try to escape the codex dir.
|
|
199
|
-
const dir = codexDir(id, options.rootDir);
|
|
200
|
-
const resolved = path.resolve(dir, relativePath);
|
|
201
|
-
if (!resolved.startsWith(dir + path.sep) && resolved !== dir) {
|
|
202
|
-
throw new CodexStoreError("INVALID_ID", `relative path "${relativePath}" escapes codex root`);
|
|
203
|
-
}
|
|
204
|
-
return resolved;
|
|
205
|
-
}
|
package/dist/codex/store.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src.legacy/codex/store.ts"],"names":[],"mappings":"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"}
|
package/dist/codex/store.test.js
DELETED
|
@@ -1,242 +0,0 @@
|
|
|
1
|
-
import * as crypto from "node:crypto";
|
|
2
|
-
import { promises as fs } from "node:fs";
|
|
3
|
-
import * as os from "node:os";
|
|
4
|
-
import * as path from "node:path";
|
|
5
|
-
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
6
|
-
import { CodexStoreError, codexContentPath, codexDir, codexesDir, getCodex, installCodex, listCodexes, removeCodex, resolveDataRoot, validateCodexId, } from "./store.js";
|
|
7
|
-
// ---------------------------------------------------------------------
|
|
8
|
-
// Per-test temp directory isolation
|
|
9
|
-
// ---------------------------------------------------------------------
|
|
10
|
-
let tmpRoot;
|
|
11
|
-
beforeEach(async () => {
|
|
12
|
-
tmpRoot = path.join(os.tmpdir(), `opensquid-codex-test-${crypto.randomUUID()}`);
|
|
13
|
-
await fs.mkdir(tmpRoot, { recursive: true });
|
|
14
|
-
});
|
|
15
|
-
afterEach(async () => {
|
|
16
|
-
await fs.rm(tmpRoot, { recursive: true, force: true });
|
|
17
|
-
});
|
|
18
|
-
// Test fixtures
|
|
19
|
-
function focused(id) {
|
|
20
|
-
return {
|
|
21
|
-
id,
|
|
22
|
-
version: "1.0.0",
|
|
23
|
-
foundation: { methodologies: ["tdd"] },
|
|
24
|
-
detected_by: [{ kind: "user_pinned" }],
|
|
25
|
-
activation_scope: "user",
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
// ---------------------------------------------------------------------
|
|
29
|
-
// resolveDataRoot
|
|
30
|
-
// ---------------------------------------------------------------------
|
|
31
|
-
describe("resolveDataRoot", () => {
|
|
32
|
-
const origOpenSquid = process.env.OPENSQUID_HOME;
|
|
33
|
-
const origLoop = process.env.LOOP_HOME;
|
|
34
|
-
afterEach(() => {
|
|
35
|
-
if (origOpenSquid === undefined)
|
|
36
|
-
delete process.env.OPENSQUID_HOME;
|
|
37
|
-
else
|
|
38
|
-
process.env.OPENSQUID_HOME = origOpenSquid;
|
|
39
|
-
if (origLoop === undefined)
|
|
40
|
-
delete process.env.LOOP_HOME;
|
|
41
|
-
else
|
|
42
|
-
process.env.LOOP_HOME = origLoop;
|
|
43
|
-
});
|
|
44
|
-
it("prefers explicit rootDir over everything else", () => {
|
|
45
|
-
process.env.OPENSQUID_HOME = "/from-env";
|
|
46
|
-
expect(resolveDataRoot("/explicit")).toBe("/explicit");
|
|
47
|
-
});
|
|
48
|
-
it("uses OPENSQUID_HOME when no explicit rootDir", () => {
|
|
49
|
-
process.env.OPENSQUID_HOME = "/from-os-home";
|
|
50
|
-
process.env.LOOP_HOME = "/from-loop-home";
|
|
51
|
-
expect(resolveDataRoot()).toBe("/from-os-home");
|
|
52
|
-
});
|
|
53
|
-
it("falls back to LOOP_HOME", () => {
|
|
54
|
-
delete process.env.OPENSQUID_HOME;
|
|
55
|
-
process.env.LOOP_HOME = "/from-loop-home";
|
|
56
|
-
expect(resolveDataRoot()).toBe("/from-loop-home");
|
|
57
|
-
});
|
|
58
|
-
it("defaults to ~/.opensquid when no env set", () => {
|
|
59
|
-
delete process.env.OPENSQUID_HOME;
|
|
60
|
-
delete process.env.LOOP_HOME;
|
|
61
|
-
expect(resolveDataRoot()).toBe(path.join(os.homedir(), ".opensquid"));
|
|
62
|
-
});
|
|
63
|
-
});
|
|
64
|
-
// ---------------------------------------------------------------------
|
|
65
|
-
// validateCodexId
|
|
66
|
-
// ---------------------------------------------------------------------
|
|
67
|
-
describe("validateCodexId", () => {
|
|
68
|
-
it("accepts standard ids", () => {
|
|
69
|
-
expect(() => validateCodexId("react-19")).not.toThrow();
|
|
70
|
-
expect(() => validateCodexId("fullstack-react-atomic")).not.toThrow();
|
|
71
|
-
expect(() => validateCodexId("scanpy.v1")).not.toThrow();
|
|
72
|
-
expect(() => validateCodexId("a")).not.toThrow();
|
|
73
|
-
expect(() => validateCodexId("123tools")).not.toThrow();
|
|
74
|
-
});
|
|
75
|
-
it("rejects path traversal attempts", () => {
|
|
76
|
-
expect(() => validateCodexId("../escape")).toThrow(CodexStoreError);
|
|
77
|
-
expect(() => validateCodexId("a/b")).toThrow(CodexStoreError);
|
|
78
|
-
expect(() => validateCodexId("a\\b")).toThrow(CodexStoreError);
|
|
79
|
-
});
|
|
80
|
-
it("rejects uppercase", () => {
|
|
81
|
-
expect(() => validateCodexId("React-19")).toThrow(CodexStoreError);
|
|
82
|
-
});
|
|
83
|
-
it("rejects empty + dot/dash leading", () => {
|
|
84
|
-
expect(() => validateCodexId("")).toThrow(CodexStoreError);
|
|
85
|
-
expect(() => validateCodexId(".hidden")).toThrow(CodexStoreError);
|
|
86
|
-
expect(() => validateCodexId("-leading")).toThrow(CodexStoreError);
|
|
87
|
-
});
|
|
88
|
-
it("rejects >128 chars", () => {
|
|
89
|
-
expect(() => validateCodexId("a".repeat(129))).toThrow(CodexStoreError);
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
// ---------------------------------------------------------------------
|
|
93
|
-
// installCodex / getCodex round-trip
|
|
94
|
-
// ---------------------------------------------------------------------
|
|
95
|
-
describe("installCodex + getCodex round-trip", () => {
|
|
96
|
-
it("installs and retrieves a focused codex", async () => {
|
|
97
|
-
const c = focused("react-19");
|
|
98
|
-
const res = await installCodex(c, { rootDir: tmpRoot });
|
|
99
|
-
expect(res.id).toBe("react-19");
|
|
100
|
-
expect(res.path).toBe(codexDir("react-19", tmpRoot));
|
|
101
|
-
const loaded = await getCodex("react-19", { rootDir: tmpRoot });
|
|
102
|
-
expect(loaded.id).toBe("react-19");
|
|
103
|
-
expect(loaded.version).toBe("1.0.0");
|
|
104
|
-
});
|
|
105
|
-
it("creates the codex directory + codex.yaml manifest", async () => {
|
|
106
|
-
await installCodex(focused("x"), { rootDir: tmpRoot });
|
|
107
|
-
const manifest = path.join(codexDir("x", tmpRoot), "codex.yaml");
|
|
108
|
-
const stat = await fs.stat(manifest);
|
|
109
|
-
expect(stat.isFile()).toBe(true);
|
|
110
|
-
});
|
|
111
|
-
it("throws ALREADY_INSTALLED on re-install without force", async () => {
|
|
112
|
-
await installCodex(focused("x"), { rootDir: tmpRoot });
|
|
113
|
-
await expect(installCodex(focused("x"), { rootDir: tmpRoot })).rejects.toMatchObject({
|
|
114
|
-
code: "ALREADY_INSTALLED",
|
|
115
|
-
});
|
|
116
|
-
});
|
|
117
|
-
it("overwrites with force=true", async () => {
|
|
118
|
-
const v1 = focused("x");
|
|
119
|
-
v1.version = "1.0.0";
|
|
120
|
-
await installCodex(v1, { rootDir: tmpRoot });
|
|
121
|
-
const v2 = focused("x");
|
|
122
|
-
v2.version = "2.0.0";
|
|
123
|
-
await installCodex(v2, { rootDir: tmpRoot, force: true });
|
|
124
|
-
const loaded = await getCodex("x", { rootDir: tmpRoot });
|
|
125
|
-
expect(loaded.version).toBe("2.0.0");
|
|
126
|
-
});
|
|
127
|
-
it("installs composite codex", async () => {
|
|
128
|
-
const composite = {
|
|
129
|
-
id: "fullstack",
|
|
130
|
-
kind: "composite",
|
|
131
|
-
version: "1.0.0",
|
|
132
|
-
includes: [
|
|
133
|
-
{ id: "react", semver: ">=18" },
|
|
134
|
-
{ id: "tdd", semver: ">=1" },
|
|
135
|
-
],
|
|
136
|
-
};
|
|
137
|
-
await installCodex(composite, { rootDir: tmpRoot });
|
|
138
|
-
const loaded = await getCodex("fullstack", { rootDir: tmpRoot });
|
|
139
|
-
expect(loaded.kind).toBe("composite");
|
|
140
|
-
});
|
|
141
|
-
it("rejects invalid codex id at install", async () => {
|
|
142
|
-
const bad = focused("Bad-Id");
|
|
143
|
-
await expect(installCodex(bad, { rootDir: tmpRoot })).rejects.toMatchObject({
|
|
144
|
-
code: "INVALID_ID",
|
|
145
|
-
});
|
|
146
|
-
});
|
|
147
|
-
});
|
|
148
|
-
// ---------------------------------------------------------------------
|
|
149
|
-
// getCodex error paths
|
|
150
|
-
// ---------------------------------------------------------------------
|
|
151
|
-
describe("getCodex error paths", () => {
|
|
152
|
-
it("throws NOT_FOUND when codex is not installed", async () => {
|
|
153
|
-
await expect(getCodex("ghost", { rootDir: tmpRoot })).rejects.toMatchObject({
|
|
154
|
-
code: "NOT_FOUND",
|
|
155
|
-
});
|
|
156
|
-
});
|
|
157
|
-
it("throws PARSE_FAILED on malformed manifest", async () => {
|
|
158
|
-
const dir = codexDir("malformed", tmpRoot);
|
|
159
|
-
await fs.mkdir(dir, { recursive: true });
|
|
160
|
-
await fs.writeFile(path.join(dir, "codex.yaml"), "id: x\n bad: indentation: here", "utf8");
|
|
161
|
-
await expect(getCodex("malformed", { rootDir: tmpRoot })).rejects.toMatchObject({
|
|
162
|
-
code: "PARSE_FAILED",
|
|
163
|
-
});
|
|
164
|
-
});
|
|
165
|
-
it("throws INVALID_ID for path-escaping ids", async () => {
|
|
166
|
-
await expect(getCodex("../escape", { rootDir: tmpRoot })).rejects.toMatchObject({
|
|
167
|
-
code: "INVALID_ID",
|
|
168
|
-
});
|
|
169
|
-
});
|
|
170
|
-
});
|
|
171
|
-
// ---------------------------------------------------------------------
|
|
172
|
-
// listCodexes
|
|
173
|
-
// ---------------------------------------------------------------------
|
|
174
|
-
describe("listCodexes", () => {
|
|
175
|
-
it("returns [] when codexes/ dir doesn't exist", async () => {
|
|
176
|
-
expect(await listCodexes({ rootDir: tmpRoot })).toEqual([]);
|
|
177
|
-
});
|
|
178
|
-
it("returns sorted list of installed ids", async () => {
|
|
179
|
-
await installCodex(focused("zebra"), { rootDir: tmpRoot });
|
|
180
|
-
await installCodex(focused("apple"), { rootDir: tmpRoot });
|
|
181
|
-
await installCodex(focused("mango"), { rootDir: tmpRoot });
|
|
182
|
-
expect(await listCodexes({ rootDir: tmpRoot })).toEqual(["apple", "mango", "zebra"]);
|
|
183
|
-
});
|
|
184
|
-
it("skips directories without codex.yaml (partial installs)", async () => {
|
|
185
|
-
await installCodex(focused("complete"), { rootDir: tmpRoot });
|
|
186
|
-
// Create a stray directory without a manifest.
|
|
187
|
-
await fs.mkdir(path.join(codexesDir(tmpRoot), "partial"), {
|
|
188
|
-
recursive: true,
|
|
189
|
-
});
|
|
190
|
-
expect(await listCodexes({ rootDir: tmpRoot })).toEqual(["complete"]);
|
|
191
|
-
});
|
|
192
|
-
it("skips directories with invalid id syntax", async () => {
|
|
193
|
-
await installCodex(focused("valid"), { rootDir: tmpRoot });
|
|
194
|
-
await fs.mkdir(path.join(codexesDir(tmpRoot), "Bad-Id"), {
|
|
195
|
-
recursive: true,
|
|
196
|
-
});
|
|
197
|
-
await fs.writeFile(path.join(codexesDir(tmpRoot), "Bad-Id", "codex.yaml"), "id: Bad-Id\nversion: 1.0.0\n", "utf8");
|
|
198
|
-
expect(await listCodexes({ rootDir: tmpRoot })).toEqual(["valid"]);
|
|
199
|
-
});
|
|
200
|
-
});
|
|
201
|
-
// ---------------------------------------------------------------------
|
|
202
|
-
// removeCodex
|
|
203
|
-
// ---------------------------------------------------------------------
|
|
204
|
-
describe("removeCodex", () => {
|
|
205
|
-
it("removes an installed codex, returns true", async () => {
|
|
206
|
-
await installCodex(focused("doomed"), { rootDir: tmpRoot });
|
|
207
|
-
expect(await removeCodex("doomed", { rootDir: tmpRoot })).toBe(true);
|
|
208
|
-
await expect(getCodex("doomed", { rootDir: tmpRoot })).rejects.toMatchObject({
|
|
209
|
-
code: "NOT_FOUND",
|
|
210
|
-
});
|
|
211
|
-
});
|
|
212
|
-
it("returns false when codex is not installed (idempotent)", async () => {
|
|
213
|
-
expect(await removeCodex("ghost", { rootDir: tmpRoot })).toBe(false);
|
|
214
|
-
});
|
|
215
|
-
it("recursively removes lesson bodies + companion files", async () => {
|
|
216
|
-
await installCodex(focused("with-content"), { rootDir: tmpRoot });
|
|
217
|
-
const lessonDir = path.join(codexDir("with-content", tmpRoot), "lessons", "l1");
|
|
218
|
-
await fs.mkdir(lessonDir, { recursive: true });
|
|
219
|
-
await fs.writeFile(path.join(lessonDir, "lesson.md"), "# lesson", "utf8");
|
|
220
|
-
await removeCodex("with-content", { rootDir: tmpRoot });
|
|
221
|
-
await expect(fs.access(lessonDir)).rejects.toThrow();
|
|
222
|
-
});
|
|
223
|
-
});
|
|
224
|
-
// ---------------------------------------------------------------------
|
|
225
|
-
// codexContentPath
|
|
226
|
-
// ---------------------------------------------------------------------
|
|
227
|
-
describe("codexContentPath", () => {
|
|
228
|
-
it("resolves a content path inside the codex", () => {
|
|
229
|
-
const p = codexContentPath("react-19", "lessons/x/lesson.md", {
|
|
230
|
-
rootDir: tmpRoot,
|
|
231
|
-
});
|
|
232
|
-
expect(p).toBe(path.join(codexDir("react-19", tmpRoot), "lessons", "x", "lesson.md"));
|
|
233
|
-
});
|
|
234
|
-
it("rejects path traversal in the relative path", () => {
|
|
235
|
-
expect(() => codexContentPath("react-19", "../../../etc/passwd", {
|
|
236
|
-
rootDir: tmpRoot,
|
|
237
|
-
})).toThrow(CodexStoreError);
|
|
238
|
-
});
|
|
239
|
-
it("rejects invalid codex id", () => {
|
|
240
|
-
expect(() => codexContentPath("../escape", "lesson.md", { rootDir: tmpRoot })).toThrow(CodexStoreError);
|
|
241
|
-
});
|
|
242
|
-
});
|