dsh-env-manager-plugin 0.0.0-stage → 0.1.0
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/LICENSE +21 -0
- package/README.md +249 -2
- package/client.js +2581 -0
- package/cordis.patch.yml +4 -0
- package/docs/00-project/current-state.md +246 -0
- package/docs/00-project/glossary.md +215 -0
- package/docs/00-project/project-brief.md +114 -0
- package/docs/00-project/roadmap.md +89 -0
- package/docs/01-architecture/DSH_Environment_Service_Memory_Skill_Brainstorm.md +493 -0
- package/docs/01-architecture/api-verification.md +416 -0
- package/docs/01-architecture/architecture-overview.md +222 -0
- package/docs/01-architecture/data-model.md +930 -0
- package/docs/01-architecture/decisions/ADR-0001-plugin-as-bundle.md +29 -0
- package/docs/01-architecture/decisions/ADR-0002-file-based-storage.md +25 -0
- package/docs/01-architecture/decisions/ADR-0003-skill-router-over-search.md +25 -0
- package/docs/01-architecture/decisions/ADR-0004-typescript-host-js-client.md +31 -0
- package/docs/01-architecture/decisions/ADR-0005-layered-rule-resolution.md +36 -0
- package/docs/01-architecture/decisions/ADR-0006-credentials-as-references.md +30 -0
- package/docs/01-architecture/decisions/ADR-0007-session-scoped-default-environment.md +28 -0
- package/docs/01-architecture/decisions/ADR-0008-memory-bridge-not-second-store.md +31 -0
- package/docs/01-architecture/decisions/ADR-0009-client-host-http-transport.md +87 -0
- package/docs/01-architecture/decisions/ADR-0010-environment-plane-model.md +87 -0
- package/docs/01-architecture/decisions/ADR-0011-k8s-node-execution-model.md +92 -0
- package/docs/01-architecture/design.md +666 -0
- package/docs/01-architecture/detailed-design.md +961 -0
- package/docs/01-architecture/implementation-plan.md +323 -0
- package/docs/01-architecture/module-boundaries.md +306 -0
- package/docs/01-architecture/security-model.md +227 -0
- package/docs/01-architecture/storage-contract.md +224 -0
- package/docs/01-architecture/tool-contract.md +438 -0
- package/docs/01-architecture/ui-design.md +587 -0
- package/docs/02-harness/agent-behavior.md +36 -0
- package/docs/02-harness/change-snapshot-policy.md +29 -0
- package/docs/02-harness/client-ui-integration.md +110 -0
- package/docs/02-harness/context-policy.md +43 -0
- package/docs/02-harness/harness-standard.md +91 -0
- package/docs/02-harness/task-execution-policy.md +26 -0
- package/docs/02-harness/verification-policy.md +97 -0
- package/docs/03-openspec/archive-rule.md +25 -0
- package/docs/03-openspec/bugfix-rule.md +30 -0
- package/docs/03-openspec/design-rule.md +33 -0
- package/docs/03-openspec/openspec-usage-guide.md +31 -0
- package/docs/03-openspec/proposal-rule.md +70 -0
- package/docs/03-openspec/spec-rule.md +33 -0
- package/docs/03-openspec/tasks-rule.md +31 -0
- package/docs/04-development/coding-style.md +67 -0
- package/docs/04-development/dependency-policy.md +43 -0
- package/docs/04-development/development-guide.md +85 -0
- package/docs/04-development/directory-convention.md +105 -0
- package/docs/04-development/local-run-guide.md +55 -0
- package/docs/04-development/testing-guide.md +102 -0
- package/docs/05-domain/deployment-template.md +76 -0
- package/docs/05-domain/environment-template.md +165 -0
- package/docs/05-domain/operation-binding-template.md +55 -0
- package/docs/05-domain/operation-record-template.md +55 -0
- package/docs/05-domain/prompt-template.md +90 -0
- package/docs/05-domain/rule-template.md +73 -0
- package/docs/05-domain/service-template.md +39 -0
- package/docs/06-operations/deployment-guide.md +54 -0
- package/docs/06-operations/release.md +188 -0
- package/docs/06-operations/security-policy.md +53 -0
- package/docs/06-operations/troubleshooting.md +91 -0
- package/docs/07-templates/adr-template.md +21 -0
- package/docs/07-templates/bugfix-template.md +11 -0
- package/docs/07-templates/case-template.md +15 -0
- package/docs/07-templates/design-template.md +29 -0
- package/docs/07-templates/proposal-template.md +19 -0
- package/docs/07-templates/spec-template.md +15 -0
- package/docs/07-templates/tasks-template.md +12 -0
- package/docs/99-archive/README.md +13 -0
- package/docs/README.md +142 -0
- package/icon.svg +4 -0
- package/index.js +8 -0
- package/lib/app/app.d.ts +67 -0
- package/lib/app/app.js +494 -0
- package/lib/app/app.js.map +1 -0
- package/lib/config.d.ts +55 -0
- package/lib/config.js +42 -0
- package/lib/config.js.map +1 -0
- package/lib/domain/constants.d.ts +21 -0
- package/lib/domain/constants.js +59 -0
- package/lib/domain/constants.js.map +1 -0
- package/lib/domain/errors.d.ts +20 -0
- package/lib/domain/errors.js +22 -0
- package/lib/domain/errors.js.map +1 -0
- package/lib/domain/schemas.d.ts +28 -0
- package/lib/domain/schemas.js +445 -0
- package/lib/domain/schemas.js.map +1 -0
- package/lib/domain/types.d.ts +412 -0
- package/lib/domain/types.js +2 -0
- package/lib/domain/types.js.map +1 -0
- package/lib/index.d.ts +18 -0
- package/lib/index.js +89 -0
- package/lib/index.js.map +1 -0
- package/lib/logger.d.ts +8 -0
- package/lib/logger.js +13 -0
- package/lib/logger.js.map +1 -0
- package/lib/memory/bridge.d.ts +18 -0
- package/lib/memory/bridge.js +13 -0
- package/lib/memory/bridge.js.map +1 -0
- package/lib/registry/integrity.d.ts +10 -0
- package/lib/registry/integrity.js +204 -0
- package/lib/registry/integrity.js.map +1 -0
- package/lib/registry/registry.d.ts +14 -0
- package/lib/registry/registry.js +182 -0
- package/lib/registry/registry.js.map +1 -0
- package/lib/resolver/merge.d.ts +23 -0
- package/lib/resolver/merge.js +90 -0
- package/lib/resolver/merge.js.map +1 -0
- package/lib/resolver/resolve.d.ts +18 -0
- package/lib/resolver/resolve.js +288 -0
- package/lib/resolver/resolve.js.map +1 -0
- package/lib/resolver/template.d.ts +10 -0
- package/lib/resolver/template.js +42 -0
- package/lib/resolver/template.js.map +1 -0
- package/lib/router/params.d.ts +3 -0
- package/lib/router/params.js +83 -0
- package/lib/router/params.js.map +1 -0
- package/lib/router/router.d.ts +11 -0
- package/lib/router/router.js +204 -0
- package/lib/router/router.js.map +1 -0
- package/lib/session/session.d.ts +16 -0
- package/lib/session/session.js +64 -0
- package/lib/session/session.js.map +1 -0
- package/lib/skill.d.ts +17 -0
- package/lib/skill.js +56 -0
- package/lib/skill.js.map +1 -0
- package/lib/storage/atomic.d.ts +10 -0
- package/lib/storage/atomic.js +107 -0
- package/lib/storage/atomic.js.map +1 -0
- package/lib/storage/paths.d.ts +10 -0
- package/lib/storage/paths.js +50 -0
- package/lib/storage/paths.js.map +1 -0
- package/lib/storage/records.d.ts +16 -0
- package/lib/storage/records.js +112 -0
- package/lib/storage/records.js.map +1 -0
- package/lib/storage/sessions.d.ts +17 -0
- package/lib/storage/sessions.js +65 -0
- package/lib/storage/sessions.js.map +1 -0
- package/lib/tools/tools.d.ts +14 -0
- package/lib/tools/tools.js +472 -0
- package/lib/tools/tools.js.map +1 -0
- package/lib/transport/http.d.ts +3 -0
- package/lib/transport/http.js +217 -0
- package/lib/transport/http.js.map +1 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +97 -4
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Dependency Policy
|
|
2
|
+
|
|
3
|
+
## Runtime dependencies
|
|
4
|
+
|
|
5
|
+
Keep the set minimal and justified:
|
|
6
|
+
|
|
7
|
+
| Package | Why |
|
|
8
|
+
|---|---|
|
|
9
|
+
| `zod` | schema validation for records and tool inputs |
|
|
10
|
+
| `yaml` | YAML parse/serialize with typed results |
|
|
11
|
+
| `@deepseek-ai/schemastery` | `Config` schema for the Host plugin |
|
|
12
|
+
|
|
13
|
+
Anything else needs a proposal that explains why the standard library and the three
|
|
14
|
+
packages above are insufficient.
|
|
15
|
+
|
|
16
|
+
DSH-provided packages (`@deepseek-ai/dsh-tools`, `@deepseek-ai/dsh-llm`, `@deepseek-ai/cordis`) are
|
|
17
|
+
peers, supplied by the profile; never bundle them.
|
|
18
|
+
|
|
19
|
+
## Development dependencies
|
|
20
|
+
|
|
21
|
+
| Package | Why |
|
|
22
|
+
|---|---|
|
|
23
|
+
| `typescript` | Host build |
|
|
24
|
+
| `vitest` | tests |
|
|
25
|
+
|
|
26
|
+
Optional, with justification: `@types/node`, a linter, a formatter.
|
|
27
|
+
|
|
28
|
+
## Rules
|
|
29
|
+
|
|
30
|
+
1. No dependency that duplicates DSH-provided services (fs, credentials, approval, storage).
|
|
31
|
+
2. No database driver.
|
|
32
|
+
3. No client bundler unless a new ADR accepts it.
|
|
33
|
+
4. No native modules unless there is no alternative.
|
|
34
|
+
5. Check the license before adding.
|
|
35
|
+
6. Prefer a small maintained package over a large framework.
|
|
36
|
+
|
|
37
|
+
## Adding a dependency
|
|
38
|
+
|
|
39
|
+
1. Write an OpenSpec proposal or a task entry.
|
|
40
|
+
2. State the capability gap.
|
|
41
|
+
3. State alternatives considered.
|
|
42
|
+
4. Add it to `package.json` and the lockfile.
|
|
43
|
+
5. Mention it in `docs/01-architecture/design.md` §15.4 if it changes the build.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Development Guide
|
|
2
|
+
|
|
3
|
+
> Status: this guide describes the V1 target. The repository currently ships the JavaScript skeleton; `src/`, `lib/`, `tsconfig.json` and the npm scripts are added during V1 implementation. See `docs/00-project/current-state.md`.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- Node.js matching the DSH runtime
|
|
8
|
+
- npm
|
|
9
|
+
|
|
10
|
+
## First Run
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install
|
|
14
|
+
npm run build
|
|
15
|
+
npm test
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Project Layout
|
|
19
|
+
|
|
20
|
+
```text
|
|
21
|
+
dsh-env-manager/
|
|
22
|
+
├── index.js # Host entry shim over lib/index.js
|
|
23
|
+
├── client.js # Client module (plain JS)
|
|
24
|
+
├── cordis.patch.yml # Cordis patch
|
|
25
|
+
├── package.json # Bundle manifest
|
|
26
|
+
├── tsconfig.json # Host build config
|
|
27
|
+
├── src/ # TypeScript host sources
|
|
28
|
+
├── lib/ # Build output (git-ignored)
|
|
29
|
+
├── locale/ # Display metadata
|
|
30
|
+
├── docs/ # Project documentation
|
|
31
|
+
├── data/ # Runtime registry data (git-ignored)
|
|
32
|
+
└── tests/ # Vitest tests
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Build
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm run build # tsc -p tsconfig.json
|
|
39
|
+
npm run build:watch # optional watch mode
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The Host entry `index.js` re-exports `lib/index.js`. Do not edit `lib/` by hand.
|
|
43
|
+
|
|
44
|
+
## Test
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npm test # vitest run
|
|
48
|
+
npm run test:watch # vitest
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Install in a DSH Profile
|
|
52
|
+
|
|
53
|
+
In a DSH session, call the `plugin_manager` tool with `action: install_bundle` and the absolute repository path as `target`. Restart DSH if the bundle was already installed.
|
|
54
|
+
|
|
55
|
+
Do not use `dsh plugin add` for bundle selection; that forwards to pnpm only.
|
|
56
|
+
|
|
57
|
+
## Development Loop
|
|
58
|
+
|
|
59
|
+
1. Read the relevant docs (`docs/README.md` decides which).
|
|
60
|
+
2. Change `src/` or `client.js`.
|
|
61
|
+
3. Build and test.
|
|
62
|
+
4. Reinstall the bundle to see Host/Client changes.
|
|
63
|
+
5. Update `docs/00-project/current-state.md`.
|
|
64
|
+
|
|
65
|
+
## Client Changes
|
|
66
|
+
|
|
67
|
+
`client.js` is plain JavaScript and needs no build. The Host serves it as the bundle's `./client` export.
|
|
68
|
+
|
|
69
|
+
Keep the module-loader wrapper intact:
|
|
70
|
+
|
|
71
|
+
```js
|
|
72
|
+
window.__ModuleLoader__.load({ id: 'dsh-env-manager', factory(require) { ... } })
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Data Directory
|
|
76
|
+
|
|
77
|
+
Do not commit `data/`. It is created on first run at the resolved `dataDir`; the startup log reports the path.
|
|
78
|
+
|
|
79
|
+
## Updating Docs
|
|
80
|
+
|
|
81
|
+
After every meaningful change update:
|
|
82
|
+
|
|
83
|
+
- `docs/00-project/current-state.md`
|
|
84
|
+
- the matching architecture document when behavior or schemas changed
|
|
85
|
+
- OpenSpec artifacts when used
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Directory Convention
|
|
2
|
+
|
|
3
|
+
> Status: target layout for V1. Directories marked as V1 are created during implementation; see `docs/00-project/current-state.md`.
|
|
4
|
+
|
|
5
|
+
## Root
|
|
6
|
+
|
|
7
|
+
| Path | Purpose |
|
|
8
|
+
|---|---|
|
|
9
|
+
| `index.js` | Host entry shim; re-exports `lib/index.js` |
|
|
10
|
+
| `client.js` | Client module (plain JS, module-loader format) |
|
|
11
|
+
| `cordis.patch.yml` | Cordis bundle patch |
|
|
12
|
+
| `package.json` | Bundle manifest, scripts, dependencies |
|
|
13
|
+
| `tsconfig.json` | Host build configuration |
|
|
14
|
+
| `lib/` | Build output; git-ignored |
|
|
15
|
+
| `src/` | TypeScript Host sources |
|
|
16
|
+
| `tests/` | Vitest tests and fixtures |
|
|
17
|
+
| `data/` | Runtime registry data; git-ignored |
|
|
18
|
+
| `locale/` | `en.json`, `zh.json` |
|
|
19
|
+
| `docs/` | Project documentation |
|
|
20
|
+
| `icon.svg` | Plugin icon |
|
|
21
|
+
|
|
22
|
+
## `src/`
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
src/
|
|
26
|
+
├── index.ts # apply(): wiring, tools, HTTP route
|
|
27
|
+
├── config.ts # schemastery Config
|
|
28
|
+
├── logger.ts # ctx logger wrapper
|
|
29
|
+
├── domain/
|
|
30
|
+
│ ├── types.ts # interfaces and enums
|
|
31
|
+
│ ├── schemas.ts # Zod schemas, parseRecord
|
|
32
|
+
│ ├── constants.ts # operation ids, limits, patterns
|
|
33
|
+
│ └── errors.ts # EnvManagerError, codes
|
|
34
|
+
├── storage/
|
|
35
|
+
│ ├── paths.ts # dataDir resolution, id/path guards
|
|
36
|
+
│ ├── atomic.ts # temp+fsync+rename write, backups
|
|
37
|
+
│ ├── records.ts # read/write/delete one record
|
|
38
|
+
│ ├── sessions.ts # session state files
|
|
39
|
+
│ └── layout.ts # directory creation, tmp cleanup
|
|
40
|
+
├── registry/
|
|
41
|
+
│ ├── registry.ts # load, snapshot, lookup, reload
|
|
42
|
+
│ ├── aliases.ts # alias index
|
|
43
|
+
│ └── integrity.ts # referential integrity diagnostics
|
|
44
|
+
├── resolver/
|
|
45
|
+
│ ├── template.ts # ${...} parsing and substitution
|
|
46
|
+
│ ├── graph.ts # reference graph, cycles
|
|
47
|
+
│ ├── layers.ts # build ordered layers
|
|
48
|
+
│ ├── merge.ts # field-level merge
|
|
49
|
+
│ ├── context.ts # ServiceContext assembly
|
|
50
|
+
│ └── provenance.ts # provenance construction
|
|
51
|
+
├── router/
|
|
52
|
+
│ ├── match.ts # match level classification
|
|
53
|
+
│ ├── router.ts # selection, conflicts
|
|
54
|
+
│ ├── skills.ts # SkillDirectory adapter
|
|
55
|
+
│ ├── policy.ts # approval and production rules
|
|
56
|
+
│ └── parameters.ts # parameter mapping
|
|
57
|
+
├── session/
|
|
58
|
+
│ └── session-context.ts
|
|
59
|
+
├── app/
|
|
60
|
+
│ ├── app.ts # use cases shared by tools and HTTP
|
|
61
|
+
│ └── actor.ts # 'agent' | 'ui' policy
|
|
62
|
+
├── tools/
|
|
63
|
+
│ ├── define.ts # shared schemas and render helpers
|
|
64
|
+
│ ├── approval.ts # ctx.approval wrapper
|
|
65
|
+
│ ├── read.ts # env_list/get/find/get_context
|
|
66
|
+
│ ├── resolve.ts # env_resolve/resolve_operation
|
|
67
|
+
│ ├── write.ts # env_save/delete/set_context
|
|
68
|
+
│ └── validate.ts # env_validate
|
|
69
|
+
├── transport/
|
|
70
|
+
│ ├── route.ts # webServer registration, body handling
|
|
71
|
+
│ ├── auth.ts # header, origin, size checks
|
|
72
|
+
│ ├── ops.ts # op table → app calls
|
|
73
|
+
│ └── envelope.ts # { ok, data } / { ok:false, error }
|
|
74
|
+
└── memory-bridge/
|
|
75
|
+
├── contract.ts
|
|
76
|
+
└── null-adapter.ts
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`logger.ts` sits at the root because every module may log; it must not import them.
|
|
80
|
+
|
|
81
|
+
## `tests/`
|
|
82
|
+
|
|
83
|
+
```text
|
|
84
|
+
tests/
|
|
85
|
+
├── domain/
|
|
86
|
+
├── storage/
|
|
87
|
+
├── registry/
|
|
88
|
+
├── resolver/
|
|
89
|
+
├── router/
|
|
90
|
+
├── session/
|
|
91
|
+
├── app/
|
|
92
|
+
├── tools/
|
|
93
|
+
├── http/
|
|
94
|
+
└── fixtures/
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
- One test file per module: `<module>.test.ts`.
|
|
98
|
+
- Fixtures are small YAML files with obviously fake values.
|
|
99
|
+
- Never commit real hostnames, IPs, URLs or credentials.
|
|
100
|
+
|
|
101
|
+
## Adding a module
|
|
102
|
+
|
|
103
|
+
1. It must fit an existing boundary in `module-boundaries.md`.
|
|
104
|
+
2. A new boundary needs an ADR.
|
|
105
|
+
3. Update the tree in this document and in `AGENTS.md`.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Local Run Guide
|
|
2
|
+
|
|
3
|
+
## Build
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install
|
|
7
|
+
npm run build
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## Install in a DSH Profile
|
|
11
|
+
|
|
12
|
+
In a DSH session, call the `plugin_manager` tool:
|
|
13
|
+
|
|
14
|
+
```text
|
|
15
|
+
action: install_bundle
|
|
16
|
+
target: <absolute path to this repository>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Restart DSH if the bundle was already installed before this change.
|
|
20
|
+
|
|
21
|
+
## Verify Installation
|
|
22
|
+
|
|
23
|
+
Ask `plugin_manager` for `action: list_bundles`, then confirm the bundle appears.
|
|
24
|
+
|
|
25
|
+
Then check that the tools are available: ask the Agent to call `env_list`; it should return an empty list rather than an unknown-tool error.
|
|
26
|
+
|
|
27
|
+
## Where the data lives
|
|
28
|
+
|
|
29
|
+
The Host reports the resolved `dataDir` once at startup. On first run the directories are created:
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
<dataDir>/environments services deployments rules operations sessions
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Put a sample environment there or use `env_save`.
|
|
36
|
+
|
|
37
|
+
## Development loop
|
|
38
|
+
|
|
39
|
+
1. Edit `src/`.
|
|
40
|
+
2. `npm run build`.
|
|
41
|
+
3. Reinstall the bundle.
|
|
42
|
+
4. Restart DSH (Host code changes need a fresh module generation).
|
|
43
|
+
5. Call the tool again.
|
|
44
|
+
|
|
45
|
+
`client.js` changes need the same reinstall; they do not require a build.
|
|
46
|
+
|
|
47
|
+
## Uninstall
|
|
48
|
+
|
|
49
|
+
Call `plugin_manager` with `action: remove_bundle` and `target: dsh-env-manager-plugin`.
|
|
50
|
+
|
|
51
|
+
Registry data under `dataDir` is not removed automatically. Back it up before deleting the profile.
|
|
52
|
+
|
|
53
|
+
## Troubleshooting
|
|
54
|
+
|
|
55
|
+
See `06-operations/troubleshooting.md`.
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Testing Guide
|
|
2
|
+
|
|
3
|
+
## Framework
|
|
4
|
+
|
|
5
|
+
Vitest, with TypeScript sources tested directly (no build required for tests).
|
|
6
|
+
|
|
7
|
+
## Commands
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm test # run once
|
|
11
|
+
npm run test:watch # watch mode
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Test Organization
|
|
15
|
+
|
|
16
|
+
```text
|
|
17
|
+
tests/
|
|
18
|
+
├── domain/schemas.test.ts
|
|
19
|
+
├── storage/records.test.ts
|
|
20
|
+
├── storage/atomic.test.ts
|
|
21
|
+
├── registry/registry.test.ts
|
|
22
|
+
├── resolver/template.test.ts
|
|
23
|
+
├── resolver/layers.test.ts
|
|
24
|
+
├── resolver/context.test.ts
|
|
25
|
+
├── router/match.test.ts
|
|
26
|
+
├── router/router.test.ts
|
|
27
|
+
├── session/session-context.test.ts
|
|
28
|
+
├── app/app.test.ts
|
|
29
|
+
├── tools/read.test.ts
|
|
30
|
+
├── tools/write.test.ts
|
|
31
|
+
└── http/route.test.ts
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## What to test
|
|
35
|
+
|
|
36
|
+
### `domain`
|
|
37
|
+
- Valid record passes; unknown field fails.
|
|
38
|
+
- Id pattern and `schemaVersion` enforcement.
|
|
39
|
+
- Discriminated unions reject mismatched `runtimeMode`/`location.kind`.
|
|
40
|
+
|
|
41
|
+
### `storage`
|
|
42
|
+
- Atomic write leaves no temp file.
|
|
43
|
+
- Read tolerates missing directory and invalid YAML.
|
|
44
|
+
- Backup pruning keeps at most 5.
|
|
45
|
+
- Concurrent writes serialize in-process.
|
|
46
|
+
|
|
47
|
+
### `registry`
|
|
48
|
+
- Load, lookup by id and alias.
|
|
49
|
+
- Duplicate id detection.
|
|
50
|
+
- Referential integrity: missing environment/service/host/cluster/rule profile.
|
|
51
|
+
- Invalid file is skipped, not deleted.
|
|
52
|
+
|
|
53
|
+
### `resolver`
|
|
54
|
+
- Layer precedence: service < environment < deployment.
|
|
55
|
+
- Field-level merge keeps unrelated fields.
|
|
56
|
+
- Provenance: `definedBy`, `inputs`, `shadowed` for the worked example in design §6.9.
|
|
57
|
+
- Undefined variable, cycle, path traversal, type mismatch.
|
|
58
|
+
- Only the runtime's log source and class location are resolved.
|
|
59
|
+
|
|
60
|
+
### `router`
|
|
61
|
+
- Each match level, in isolation and in combination.
|
|
62
|
+
- Highest level wins; lower levels ignored.
|
|
63
|
+
- Same-level conflict lists all candidates and selects none.
|
|
64
|
+
- Missing skill, source mismatch, provider mismatch.
|
|
65
|
+
- Unknown/incomplete skill registry degrades to `'unknown'` plus warning.
|
|
66
|
+
- Production denial and `requiresApproval` defaults.
|
|
67
|
+
- Parameter mapping, including failure on unresolved reference.
|
|
68
|
+
|
|
69
|
+
### `session`
|
|
70
|
+
- Set, get, clear.
|
|
71
|
+
- Two sessions do not interfere.
|
|
72
|
+
- Corrupt file is treated as "no default" with `persisted: false`.
|
|
73
|
+
|
|
74
|
+
### `app`
|
|
75
|
+
- Save→get round trip; `created` flag.
|
|
76
|
+
- Delete refused when referenced (`RECORD_IN_USE`).
|
|
77
|
+
- Dry-run writes nothing.
|
|
78
|
+
- UI actor cannot write production by default.
|
|
79
|
+
- `find` matches id, name and alias.
|
|
80
|
+
|
|
81
|
+
### `tools`
|
|
82
|
+
- Success value matches the declared `output.schema`.
|
|
83
|
+
- Failures throw `EnvManagerError` with the expected code.
|
|
84
|
+
- No credential value ever appears in output (assert on serialized JSON).
|
|
85
|
+
- Invalid id error code.
|
|
86
|
+
- `env_validate` does not write.
|
|
87
|
+
- Approval denial produces `POLICY_DENIED` and no file change.
|
|
88
|
+
|
|
89
|
+
### `http`
|
|
90
|
+
- Missing header → 403; foreign origin → 403; GET → 405; oversize → 413.
|
|
91
|
+
- Unknown op → 400; not found → 404; conflict → 409; production UI write → 403.
|
|
92
|
+
- Success envelope is `{ ok: true, data }` and never leaks a stack.
|
|
93
|
+
|
|
94
|
+
## Fixtures
|
|
95
|
+
|
|
96
|
+
- Under `tests/fixtures/`, one directory per scenario.
|
|
97
|
+
- Fake values only: `test-a`, `10.0.0.x`, `example.internal`, `cred-test-*`.
|
|
98
|
+
- A fixture that needs a secret-like string must use an obvious placeholder and must not be a real credential.
|
|
99
|
+
|
|
100
|
+
## Coverage expectations
|
|
101
|
+
|
|
102
|
+
No numeric gate in V1. The resolver, router and storage modules are expected to be near-complete because they are pure and cheap to test. The Client UI is verified manually.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Deployment Binding Template
|
|
2
|
+
|
|
3
|
+
Copy into `<dataDir>/deployments/<id>.yaml`.
|
|
4
|
+
|
|
5
|
+
A service may have several bindings in one environment. Use distinct ids and a `name` to tell them apart.
|
|
6
|
+
|
|
7
|
+
## Kubernetes
|
|
8
|
+
|
|
9
|
+
```yaml
|
|
10
|
+
id: deploy-001
|
|
11
|
+
schemaVersion: 1
|
|
12
|
+
name: aicoservice-test-a
|
|
13
|
+
environmentId: test-a
|
|
14
|
+
serviceId: aicoservice
|
|
15
|
+
runtimeMode: kubernetes
|
|
16
|
+
location:
|
|
17
|
+
kind: kubernetes
|
|
18
|
+
clusterRef: k8s-01
|
|
19
|
+
namespace: dv-sop
|
|
20
|
+
workload:
|
|
21
|
+
kind: Deployment
|
|
22
|
+
name: aicoservice
|
|
23
|
+
container: aicoservice
|
|
24
|
+
variables:
|
|
25
|
+
replicaCount: 2
|
|
26
|
+
ruleOverrides:
|
|
27
|
+
logSources:
|
|
28
|
+
kubernetes:
|
|
29
|
+
kind: pod-logs
|
|
30
|
+
namespace: dv-sop
|
|
31
|
+
workload: aicoservice
|
|
32
|
+
createdAt: 2026-11-01T03:00:00.000Z
|
|
33
|
+
updatedAt: 2026-11-01T03:00:00.000Z
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Native
|
|
37
|
+
|
|
38
|
+
```yaml
|
|
39
|
+
id: deploy-002
|
|
40
|
+
schemaVersion: 1
|
|
41
|
+
environmentId: test-a
|
|
42
|
+
serviceId: aicoservice
|
|
43
|
+
runtimeMode: native
|
|
44
|
+
location:
|
|
45
|
+
kind: native
|
|
46
|
+
hostRef: host-001
|
|
47
|
+
installDir: /opt/company/services/aicoservice
|
|
48
|
+
serviceName: aicoservice.service
|
|
49
|
+
createdAt: 2026-11-01T03:00:00.000Z
|
|
50
|
+
updatedAt: 2026-11-01T03:00:00.000Z
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Docker
|
|
54
|
+
|
|
55
|
+
```yaml
|
|
56
|
+
id: deploy-003
|
|
57
|
+
schemaVersion: 1
|
|
58
|
+
environmentId: test-a
|
|
59
|
+
serviceId: aicoservice
|
|
60
|
+
runtimeMode: docker
|
|
61
|
+
location:
|
|
62
|
+
kind: docker
|
|
63
|
+
hostRef: host-001
|
|
64
|
+
containerName: aicoservice
|
|
65
|
+
image: registry.example.internal/team/aicoservice:latest
|
|
66
|
+
createdAt: 2026-11-01T03:00:00.000Z
|
|
67
|
+
updatedAt: 2026-11-01T03:00:00.000Z
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Notes
|
|
71
|
+
|
|
72
|
+
- `runtimeMode` must equal `location.kind`.
|
|
73
|
+
- `hostRef` and `clusterRef` resolve inside the referenced environment only.
|
|
74
|
+
- `variables` are deployment-level user variables; they win over environment-level variables for that field.
|
|
75
|
+
- `ruleOverrides` is field-level: overriding `logSources` does not erase `paths`.
|
|
76
|
+
- If a service has several bindings and the caller omits `deploymentId`, the resolver fails with `DEPLOYMENT_AMBIGUOUS` rather than guessing.
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# Environment Template
|
|
2
|
+
|
|
3
|
+
Copy into `<dataDir>/environments/<id>.yaml` and replace the example values.
|
|
4
|
+
|
|
5
|
+
```yaml
|
|
6
|
+
id: test-a
|
|
7
|
+
schemaVersion: 1
|
|
8
|
+
name: Test Environment A
|
|
9
|
+
aliases: [ta, test-a]
|
|
10
|
+
category: test # dev | test | staging | production | custom
|
|
11
|
+
description: Main test environment for aicoservice
|
|
12
|
+
|
|
13
|
+
roles:
|
|
14
|
+
- id: management
|
|
15
|
+
name: Management Plane
|
|
16
|
+
description: Admin plane and control entry points
|
|
17
|
+
- id: business
|
|
18
|
+
name: Business Plane
|
|
19
|
+
description: Business traffic and workloads
|
|
20
|
+
- id: database
|
|
21
|
+
name: Database
|
|
22
|
+
description: Database tier (primary/replica/sharding detail is Phase 2)
|
|
23
|
+
- id: extension
|
|
24
|
+
name: Expansion / other
|
|
25
|
+
description: Expansion nodes and unclassified workloads
|
|
26
|
+
|
|
27
|
+
hosts:
|
|
28
|
+
- id: host-001
|
|
29
|
+
name: test-a-node-01
|
|
30
|
+
kind: vm
|
|
31
|
+
os: ubuntu-22.04
|
|
32
|
+
platform: Linux / x86_64
|
|
33
|
+
ip: 10.0.1.10
|
|
34
|
+
sshPort: 22
|
|
35
|
+
roles: [management, business] # co-location: one host, two roles
|
|
36
|
+
access: [ssh]
|
|
37
|
+
adminUser: root
|
|
38
|
+
isActive: true
|
|
39
|
+
credentialRef: cred-test-a-root
|
|
40
|
+
accounts:
|
|
41
|
+
- id: root
|
|
42
|
+
username: root
|
|
43
|
+
purpose: system administration
|
|
44
|
+
protocol: ssh
|
|
45
|
+
port: 22
|
|
46
|
+
authType: private-key
|
|
47
|
+
credentialRef: cred-test-a-root
|
|
48
|
+
lastLoginAt: 2026-10-31T08:12:00.000Z
|
|
49
|
+
lastLoginFrom: 10.0.8.21
|
|
50
|
+
- id: console
|
|
51
|
+
username: ops
|
|
52
|
+
purpose: business web console
|
|
53
|
+
protocol: web
|
|
54
|
+
loginUrl: https://test-a.example.internal/login
|
|
55
|
+
authType: password
|
|
56
|
+
credentialRef: env-manager/test-a/console
|
|
57
|
+
- id: db-01
|
|
58
|
+
name: test-a-db-01
|
|
59
|
+
kind: physical
|
|
60
|
+
platform: Linux / x86_64
|
|
61
|
+
ip: 10.0.1.30
|
|
62
|
+
sshPort: 22
|
|
63
|
+
roles: [database]
|
|
64
|
+
access: [ssh]
|
|
65
|
+
adminUser: root
|
|
66
|
+
isActive: true
|
|
67
|
+
note: primary placeholder; topology is Phase 2
|
|
68
|
+
- id: k8s-aio-01
|
|
69
|
+
name: test-a-k8s-aio-01
|
|
70
|
+
kind: k8s-all-in-one # control plane + worker on one machine
|
|
71
|
+
platform: Linux / x86_64
|
|
72
|
+
ip: 10.0.1.20
|
|
73
|
+
sshPort: 22
|
|
74
|
+
roles: [management, business]
|
|
75
|
+
access: [ssh, kubectl, docker]
|
|
76
|
+
clusterRef: k8s-01
|
|
77
|
+
adminUser: root
|
|
78
|
+
isActive: true
|
|
79
|
+
note: kubeconfig stays behind the cluster credentialRef
|
|
80
|
+
- id: pod-host-01
|
|
81
|
+
name: test-a-pod-host-01
|
|
82
|
+
kind: pod-host # machine running Pods; operate with docker exec
|
|
83
|
+
platform: Linux / x86_64
|
|
84
|
+
ip: 10.0.1.21
|
|
85
|
+
sshPort: 22
|
|
86
|
+
roles: [business]
|
|
87
|
+
access: [ssh, docker]
|
|
88
|
+
clusterRef: k8s-01
|
|
89
|
+
adminUser: root
|
|
90
|
+
isActive: true
|
|
91
|
+
note: Pods are workloads; this record is the host, not an individual Pod
|
|
92
|
+
|
|
93
|
+
clusters:
|
|
94
|
+
- id: k8s-01
|
|
95
|
+
name: test-a-k8s
|
|
96
|
+
kind: kubernetes
|
|
97
|
+
topology: all-in-one # all-in-one | separate | external
|
|
98
|
+
apiEndpoint: https://10.0.1.20:6443
|
|
99
|
+
namespaces: [dv-sop]
|
|
100
|
+
credentialRef: cred-test-a-kubeconfig
|
|
101
|
+
|
|
102
|
+
endpoints:
|
|
103
|
+
- id: ep-ssh-01
|
|
104
|
+
kind: ssh
|
|
105
|
+
hostRef: host-001
|
|
106
|
+
port: 22
|
|
107
|
+
credentialRef: cred-test-a-root
|
|
108
|
+
- id: ep-web-01
|
|
109
|
+
kind: web
|
|
110
|
+
url: https://test-a.example.internal
|
|
111
|
+
|
|
112
|
+
loginRecords:
|
|
113
|
+
- id: login-0001
|
|
114
|
+
at: 2026-10-31T08:12:00.000Z
|
|
115
|
+
username: root
|
|
116
|
+
hostRef: host-001
|
|
117
|
+
accountId: root
|
|
118
|
+
protocol: ssh
|
|
119
|
+
sourceIp: 10.0.8.21
|
|
120
|
+
result: success # success | failure
|
|
121
|
+
note: jump-host session
|
|
122
|
+
|
|
123
|
+
createdAt: 2026-11-01T03:00:00.000Z
|
|
124
|
+
updatedAt: 2026-11-01T03:00:00.000Z
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Notes
|
|
128
|
+
|
|
129
|
+
- `id` must match the file name.
|
|
130
|
+
- One environment is one network and one configuration page. Its management, business,
|
|
131
|
+
database and expansion planes are role views over the same `hosts[]`, not separate
|
|
132
|
+
environments. The UI shows a node in every plane it serves.
|
|
133
|
+
- `roles` must contain at least one entry, and every `hosts[].roles` value must reference a
|
|
134
|
+
declared `roles[].id`. A host must declare at least one role. The standard ids are
|
|
135
|
+
`management`, `business`, `database` and `extension`; custom ids are allowed.
|
|
136
|
+
- A host that serves two planes is one entry with two roles; do not duplicate it.
|
|
137
|
+
- `hosts[].kind` is one of `vm`, `physical`, `k8s-all-in-one`, `k8s-worker` or `pod-host`;
|
|
138
|
+
missing means `vm`. `k8s-all-in-one` means the control plane and worker share a machine.
|
|
139
|
+
`pod-host` means the machine runs Pods and is operated with `docker exec`; it is still one
|
|
140
|
+
host, not one entry per Pod.
|
|
141
|
+
- `hosts[].access` records the command surfaces present on the machine: `ssh`, `kubectl`,
|
|
142
|
+
`docker`. It is metadata, not an authorization grant. The form pre-fills defaults; the
|
|
143
|
+
values in YAML are authoritative.
|
|
144
|
+
- `k8s-all-in-one` and `k8s-worker` require `clusterRef` to an existing cluster.
|
|
145
|
+
`pod-host` may omit it for a Docker-only machine outside the cluster. Any `clusterRef`
|
|
146
|
+
must resolve within this environment.
|
|
147
|
+
- `clusters[].topology` is `all-in-one`, `separate` or `external`; it is descriptive and
|
|
148
|
+
optional. Per-host `kind` is authoritative.
|
|
149
|
+
- `endpoints[].hostRef` / `clusterRef` must resolve within this environment.
|
|
150
|
+
- `credentialRef` is a reference only; never put a value here. The same rule applies to
|
|
151
|
+
every `accounts[].credentialRef`.
|
|
152
|
+
- `category: production` triggers the approval gate on management writes.
|
|
153
|
+
- `accounts[]` is capped at 50 per host; `loginRecords[]` is capped at 200 per
|
|
154
|
+
environment. Login records may reference a host and an account on that host.
|
|
155
|
+
- `accounts[].loginUrl` is the web console entry URL for a management or business login;
|
|
156
|
+
`username` is the login name. It must be an absolute `http(s)` URL. The password is never
|
|
157
|
+
stored here: the UI writes it to the DSH credential service and keeps only `credentialRef`.
|
|
158
|
+
- `loginRecords` are audit context, not a replacement for a PAM/SIEM system. Keep only
|
|
159
|
+
the fields needed by DSH; do not copy session recordings or credentials into YAML.
|
|
160
|
+
- The panel's batch import writes one node per line as
|
|
161
|
+
`IP,name,roles,port,user`; use `|` or `/` between roles. It creates generic `vm` nodes
|
|
162
|
+
with `access: [ssh]`, never accepts passwords, and leaves Kubernetes/Pod-host
|
|
163
|
+
classification to the node form or YAML.
|
|
164
|
+
- Database primary/replica/sharding topology is not modeled in Phase 1. Use the
|
|
165
|
+
`database` role plus a node note; a later schema may add topology metadata.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Operation Binding Template
|
|
2
|
+
|
|
3
|
+
Copy into `<dataDir>/operations/<id>.yaml`.
|
|
4
|
+
|
|
5
|
+
```yaml
|
|
6
|
+
id: binding-001
|
|
7
|
+
schemaVersion: 1
|
|
8
|
+
operation: redeploy # standard id or custom id
|
|
9
|
+
match:
|
|
10
|
+
environmentId: test-a
|
|
11
|
+
serviceId: aicoservice
|
|
12
|
+
skill:
|
|
13
|
+
name: redeploy-k8s-service
|
|
14
|
+
expectedSource: null # optional SkillSummary.source constraint
|
|
15
|
+
expectedProvider: null # optional SkillSummary.provider constraint
|
|
16
|
+
parameters:
|
|
17
|
+
namespace: ${deployment.location.namespace}
|
|
18
|
+
workload: ${deployment.location.workload.name}
|
|
19
|
+
cluster: ${deployment.location.clusterRef}
|
|
20
|
+
policy:
|
|
21
|
+
requiresApproval: true
|
|
22
|
+
allowProduction: false
|
|
23
|
+
enabled: true
|
|
24
|
+
createdAt: 2026-11-01T03:00:00.000Z
|
|
25
|
+
updatedAt: 2026-11-01T03:00:00.000Z
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Match levels
|
|
29
|
+
|
|
30
|
+
| Level | Selectors present |
|
|
31
|
+
|---|---|
|
|
32
|
+
| `deployment` | `deploymentId` |
|
|
33
|
+
| `environment-service` | `environmentId` + `serviceId` |
|
|
34
|
+
| `service` | `serviceId` |
|
|
35
|
+
| `category-mode` | `serviceCategory` + `deploymentMode` |
|
|
36
|
+
| `global` | none (empty `match: {}`) |
|
|
37
|
+
|
|
38
|
+
Every declared selector must match. The highest eligible level wins. Two eligible bindings at the same level are a conflict, never a coin flip.
|
|
39
|
+
|
|
40
|
+
A `match` must be exactly one of these five shapes. For example `environmentId` alone, or `environmentId` + `deploymentMode`, is invalid (`RECORD_INVALID`).
|
|
41
|
+
|
|
42
|
+
## Standard operations
|
|
43
|
+
|
|
44
|
+
```text
|
|
45
|
+
install, deploy, redeploy, replace, restart, upgrade, rollback, uninstall,
|
|
46
|
+
collect-logs, health-check, inspect, upload, download
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Custom operations are allowed but must be bound explicitly.
|
|
50
|
+
|
|
51
|
+
## Policy
|
|
52
|
+
|
|
53
|
+
- Write operations default to `requiresApproval: true`.
|
|
54
|
+
- `allowProduction` defaults to `false`.
|
|
55
|
+
- A binding is discovery information. It does not execute anything.
|