dsh-env-manager-plugin 0.0.0-stage → 0.1.1
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 +273 -3
- package/client.js +2581 -0
- package/cordis.patch.yml +4 -0
- package/docs/00-project/current-state.md +272 -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 +61 -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 +198 -0
- package/docs/06-operations/security-policy.md +53 -0
- package/docs/06-operations/troubleshooting.md +141 -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 +31 -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 +101 -6
- package/scripts/repair-profile.mjs +141 -0
|
@@ -0,0 +1,587 @@
|
|
|
1
|
+
# UI Design (V1)
|
|
2
|
+
|
|
3
|
+
> Status: normative for the Client half. Host behavior is in
|
|
4
|
+
> [`detailed-design.md`](detailed-design.md); transport and authentication are
|
|
5
|
+
> §4 here and §12 there.
|
|
6
|
+
|
|
7
|
+
## 1. Goals
|
|
8
|
+
|
|
9
|
+
The V1 UI makes the registry **inspectable and editable** without asking the agent to
|
|
10
|
+
run a tool, and makes rule resolution **explainable** rather than magical.
|
|
11
|
+
|
|
12
|
+
It is not a CMDB dashboard: no charts, no topology graph, no Skill execution button.
|
|
13
|
+
Bulk import is limited to the node line format below. Every other screen answers one
|
|
14
|
+
question and offers focused write actions.
|
|
15
|
+
|
|
16
|
+
## 2. Constraints
|
|
17
|
+
|
|
18
|
+
- Plain JavaScript `client.js`; no bundler, no JSX, no npm imports (F13).
|
|
19
|
+
- React comes from `require('react')`; elements via `React.createElement`.
|
|
20
|
+
- Only `slots` and `locale` services (F16).
|
|
21
|
+
- All data through `POST /env-manager/api` (F14, ADR-0009).
|
|
22
|
+
- All colors from `--dsw-alias-*` tokens (F17).
|
|
23
|
+
- No credential value is rendered or persisted. Environment-shell creation collects no
|
|
24
|
+
password. The add/edit node form may collect a login password transiently and send it
|
|
25
|
+
once to the Host, which forwards it to `ctx.credentials.set`; the registry keeps only
|
|
26
|
+
`credentialRef`. Editing a node with both login fields empty never touches existing
|
|
27
|
+
accounts or credentials.
|
|
28
|
+
|
|
29
|
+
## 3. Information architecture
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
Sidebar
|
|
33
|
+
└── Environment Manager (id: dsh-env-manager, order 40)
|
|
34
|
+
└── main panel (key: dsh-env-manager)
|
|
35
|
+
├── Environments (default; list, detail, New environment)
|
|
36
|
+
├── Overview (counts, runtime, warnings, getting started, validation)
|
|
37
|
+
├── Records (Services, Deployments, Rule Profiles, Operation Bindings)
|
|
38
|
+
├── Context Preview
|
|
39
|
+
└── Operation Preview
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Sections are a horizontal tab strip inside the panel, not separate sidebar entries. The
|
|
43
|
+
sidebar entry is the only global surface; the panel fills the same region as
|
|
44
|
+
`conversation`. **Environments is the default section** because host and account
|
|
45
|
+
management is the primary job; the panel header also carries a persistent
|
|
46
|
+
`New environment` action. Records is for the remaining configuration kinds and no longer
|
|
47
|
+
offers environment creation.
|
|
48
|
+
|
|
49
|
+
### 3.1 Sidebar slot contract
|
|
50
|
+
|
|
51
|
+
The sidebar is a host-owned list slot, not a free-form navigation area. The entry must
|
|
52
|
+
follow the installed `sidebar.panellist` contract (verified against
|
|
53
|
+
`@deepseek-ai/dsh-client-ui-sidebar` in this runtime). The full reusable contract is in
|
|
54
|
+
[`client-ui-integration.md`](../02-harness/client-ui-integration.md); the project-specific
|
|
55
|
+
rules are:
|
|
56
|
+
|
|
57
|
+
1. Register a list entry on the root context:
|
|
58
|
+
|
|
59
|
+
```js
|
|
60
|
+
ctx.slots.inject('sidebar.panellist', () =>
|
|
61
|
+
ctx.slots.register({
|
|
62
|
+
name: 'sidebar.panellist',
|
|
63
|
+
id: 'dsh-env-manager',
|
|
64
|
+
order: 40,
|
|
65
|
+
label: () => t('title'), // thunk: re-read on every locale projection
|
|
66
|
+
}, SidebarGlyph),
|
|
67
|
+
)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
`name` names the slot for the registration; `id` is the row key and must equal the
|
|
71
|
+
`main` key. `label` is the row text and tooltip; use a thunk so a language switch does
|
|
72
|
+
not require re-registration. Do not register a second entry for panel sections.
|
|
73
|
+
|
|
74
|
+
2. The owner renders the second registration argument inside its own
|
|
75
|
+
`span.panelGlyph` and passes exactly `{ size, active }`:
|
|
76
|
+
- `size` is `16` when the sidebar is wide and `18` in the collapsed rail;
|
|
77
|
+
- `active` is the selected-page state.
|
|
78
|
+
|
|
79
|
+
The glyph must render an inline `<svg>` with `viewBox="0 0 20 20"`,
|
|
80
|
+
`width={size}`, `height={size}`, `fill="none"`, and `stroke="currentColor"`.
|
|
81
|
+
Use `opacity: 0.72` when `active === false`; do not set a fixed pixel size,
|
|
82
|
+
background, border, radius, margin, or padding. The host row already supplies the
|
|
83
|
+
button, hit area, hover, focus, tooltip, and `aria-current`.
|
|
84
|
+
|
|
85
|
+
3. The glyph is decorative: set `aria-hidden="true"` and do not put an `aria-label` or
|
|
86
|
+
tooltip on it. The row button owns the accessible name from `label`.
|
|
87
|
+
|
|
88
|
+
4. Register the page under `main` on the root context with the same key:
|
|
89
|
+
|
|
90
|
+
```js
|
|
91
|
+
ctx.slots.inject('main', () =>
|
|
92
|
+
ctx.slots.register({ name: 'main', key: 'dsh-env-manager', inject: () => ({}) }, Panel),
|
|
93
|
+
)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
A sidebar id without a matching `main` key opens an empty page; a `main` key without
|
|
97
|
+
the sidebar id is unreachable.
|
|
98
|
+
|
|
99
|
+
5. Keep the glyph monochrome. Brand color belongs to `icon.svg` (the plugin-manager
|
|
100
|
+
card), not to the sidebar row; the sidebar uses host `currentColor` so light and dark
|
|
101
|
+
themes stay consistent with shipped icons.
|
|
102
|
+
|
|
103
|
+
The bundle page (`plugins.bundle.config`, keyed `dsh-env-manager`) shows a read-only
|
|
104
|
+
summary: version, `dataDir`, record counts, last load time, warning count. It links to
|
|
105
|
+
the panel's Diagnostics section.
|
|
106
|
+
|
|
107
|
+
## 4. Client ↔ Host transport
|
|
108
|
+
|
|
109
|
+
### 4.1 Request
|
|
110
|
+
|
|
111
|
+
```js
|
|
112
|
+
const API = '/env-manager/api'
|
|
113
|
+
|
|
114
|
+
async function call(op, payload, signal) {
|
|
115
|
+
const res = await fetch(API, {
|
|
116
|
+
method: 'POST',
|
|
117
|
+
headers: { 'content-type': 'application/json', 'x-env-manager-request': '1' },
|
|
118
|
+
body: JSON.stringify(payload === undefined ? { op } : { op, payload }),
|
|
119
|
+
signal,
|
|
120
|
+
})
|
|
121
|
+
const body = await res.json().catch(() => null)
|
|
122
|
+
if (!body) throw { code: 'NETWORK', message: `HTTP ${res.status}` }
|
|
123
|
+
if (!body.ok) throw body.error
|
|
124
|
+
return body.data
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
- One helper, one place that knows the URL and header.
|
|
129
|
+
- `signal` is always passed; every screen creates an `AbortController` on mount and
|
|
130
|
+
aborts it on unmount or section switch.
|
|
131
|
+
- Errors are surfaced as `{ code, message, details }`; the UI never renders a stack.
|
|
132
|
+
|
|
133
|
+
### 4.2 Operations used by the UI
|
|
134
|
+
|
|
135
|
+
`meta`, `record.list`, `record.get`, `record.find`, `record.save`, `record.remove`
|
|
136
|
+
(with `confirm`), `record.validate`, `context.resolve`, `operation.resolve`,
|
|
137
|
+
`diagnostics`. No session-context operation.
|
|
138
|
+
|
|
139
|
+
### 4.3 Failure presentation
|
|
140
|
+
|
|
141
|
+
| HTTP | UI |
|
|
142
|
+
|---|---|
|
|
143
|
+
| 400/422 | inline field banner with `code` and message |
|
|
144
|
+
| 403 | banner: "This action is not allowed by the current policy" + code |
|
|
145
|
+
| 404 | banner + refresh button |
|
|
146
|
+
| 409 | conflict dialog listing the conflicting ids from `details` |
|
|
147
|
+
| 500 | banner: "Unexpected error", `code`, copy button for the message |
|
|
148
|
+
| network | banner with retry |
|
|
149
|
+
|
|
150
|
+
## 5. Layout
|
|
151
|
+
|
|
152
|
+
The implemented V1 panel is a centered content column, not a second navigation
|
|
153
|
+
sidebar:
|
|
154
|
+
|
|
155
|
+
```text
|
|
156
|
+
┌──────────────────────────────────────────────────────────────┐
|
|
157
|
+
│ [glyph] Environment Manager [New environment]│
|
|
158
|
+
│ Environments, services, deployments, routing. │
|
|
159
|
+
│ │
|
|
160
|
+
│ Environments Overview Records Context Operations │
|
|
161
|
+
│ ──────────── │
|
|
162
|
+
│ Environments [Refresh] │
|
|
163
|
+
│ ┌────────────────────┬───────────────────────────────────┐ │
|
|
164
|
+
│ │ Oracle 1 │ Select an environment, │ │
|
|
165
|
+
│ │ test · 1 host ... │ or add a new one. [New environment]│ │
|
|
166
|
+
│ └────────────────────┴───────────────────────────────────┘ │
|
|
167
|
+
└──────────────────────────────────────────────────────────────┘
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
- The root is a plugin-owned `div.dem-root` with `height:100%; overflow:auto`; the
|
|
171
|
+
content column is capped at 1080 px and centered.
|
|
172
|
+
- Title 22 px / 650; subtitle 13 px secondary. The header carries identity **and** the
|
|
173
|
+
persistent `New environment` primary action; each view still supplies its own toolbar.
|
|
174
|
+
- Tabs are a horizontal underline strip (`role="tablist"`), never host `nav` styling.
|
|
175
|
+
Environments / Overview / Records / Context / Operations are views. Environments is
|
|
176
|
+
the default; record kinds are a second, segmented control inside Records.
|
|
177
|
+
- Panel background: `--dsw-alias-bg-base`; cards `--dsw-alias-bg-layer-1`; borders
|
|
178
|
+
`--dsw-alias-border-l1`; secondary text `--dsw-alias-label-secondary`.
|
|
179
|
+
- The primary button is filled with `--dsw-alias-brand-primary` and uses
|
|
180
|
+
`--dsw-alias-bg-base` for its label; this stays legible in both themes because the
|
|
181
|
+
brand color and the base background are a contrast pair.
|
|
182
|
+
- Content scrolls with the root. Minimum practical width is 720 px; below 820 px the
|
|
183
|
+
two-column blocks and the record split stack into one column.
|
|
184
|
+
- Avoid bare `<nav>`/`<section>` layout containers: host global styles can impose
|
|
185
|
+
height on them. Use prefixed class names and plugin-owned elements.
|
|
186
|
+
|
|
187
|
+
## 6. Screens
|
|
188
|
+
|
|
189
|
+
### 6.1 Overview
|
|
190
|
+
|
|
191
|
+
- Five compact stat cards: Environments, Services, Deployments, Rule Profiles,
|
|
192
|
+
Operation Bindings. Each shows the count only; no per-card diagnostics.
|
|
193
|
+
- Two side-by-side cards: Runtime (`dataDir`, total records, version pill) and Warnings
|
|
194
|
+
(count pill, diagnostic list, or an empty state).
|
|
195
|
+
- `Refresh` reloads `meta`; `Validate` calls `validate` and renders a result card with
|
|
196
|
+
a pass/fail pill and diagnostics.
|
|
197
|
+
- When the registry is empty, a "Get started" card shows the registration order
|
|
198
|
+
(environment → service → deployment → operation binding) and opens the guided
|
|
199
|
+
`New environment` form directly.
|
|
200
|
+
|
|
201
|
+
### 6.2 Environments
|
|
202
|
+
|
|
203
|
+
- The default section. A toolbar shows the section title, a one-line purpose and
|
|
204
|
+
`Refresh`; the persistent header action `New environment` is always available.
|
|
205
|
+
- Environments use a master/detail flow: the left card lists environments (name, id,
|
|
206
|
+
`category · hosts · accounts · login records`); selecting one opens the grouped detail
|
|
207
|
+
view (§6.3).
|
|
208
|
+
- `New environment` creates only the **environment shell**: name, optional id, category and
|
|
209
|
+
description. It seeds the four standard planes (`management`, `business`, `database`,
|
|
210
|
+
`extension`) and leaves `hosts`, `clusters`, `endpoints` and `loginRecords` empty. Nodes
|
|
211
|
+
are added inside the environment, from the plane they belong to. The shell form never
|
|
212
|
+
asks for a password.
|
|
213
|
+
- Empty lists show a dashed empty state with `New environment` and `Refresh`.
|
|
214
|
+
- There is no global search across environments in V1; the `record.find` HTTP operation
|
|
215
|
+
remains available to future screens.
|
|
216
|
+
|
|
217
|
+
### 6.2a Records
|
|
218
|
+
|
|
219
|
+
- A kind segmented control for Services, Deployments, Rule Profiles and Operation
|
|
220
|
+
Bindings only. Environments are not listed here.
|
|
221
|
+
- `New record` loads the selected kind's YAML template into the editor; `Refresh`
|
|
222
|
+
reloads the list. This is the advanced path for non-environment records.
|
|
223
|
+
- Empty lists show a dashed empty state with a short hint, not a blank card.
|
|
224
|
+
|
|
225
|
+
### 6.3 Environment detail
|
|
226
|
+
|
|
227
|
+
```text
|
|
228
|
+
← Back to environments
|
|
229
|
+
[glyph] Oracle 1 [Edit YAML] [Delete]
|
|
230
|
+
oracle1 [test] ● 4 hosts · 1 cluster · 5 accounts
|
|
231
|
+
Environment overview | All nodes | Management | Business | Database |
|
|
232
|
+
Expansion / other | Login records | Kubernetes / clusters | Endpoints | YAML
|
|
233
|
+
┌────────────────────────────────────┬──────────────────────────┐
|
|
234
|
+
│ Basic information │ Access summary │
|
|
235
|
+
│ ID / category / name / aliases │ 4 hosts 5 accounts │
|
|
236
|
+
│ description / created / updated │ 1 endpoint 0 failed │
|
|
237
|
+
│ hosts / accounts │ latest login │
|
|
238
|
+
│ Plane distribution │ Roles │
|
|
239
|
+
│ [Management 2] [Business 2] ... │ [management] [business] │
|
|
240
|
+
│ All nodes │ │
|
|
241
|
+
│ [Oracle1] [db-01] [db-02] ... │ │
|
|
242
|
+
└────────────────────────────────────┴──────────────────────────┘
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
- Tabs are `Environment overview`, `All nodes`, one tab per declared role (`Management`,
|
|
246
|
+
`Business`, `Database`, `Expansion / other`), `Login records`, `Kubernetes / clusters`,
|
|
247
|
+
`Endpoints`, `YAML`. The identity row and the back link stay visible above the tabs; tabs
|
|
248
|
+
wrap rather than hide behind a horizontal scrollbar.
|
|
249
|
+
- The environment is **one network and one YAML document**. Plane tabs are filtered views
|
|
250
|
+
over `hosts[]`: the same node appears in every plane it serves, and no record is copied
|
|
251
|
+
per plane. A plane with zero nodes is valid and shows a plane-specific empty state with
|
|
252
|
+
`Add node`.
|
|
253
|
+
- Overview basic information is a two-column key/value grid. `Edit info` opens an inline
|
|
254
|
+
form for name, aliases, category and description; the ID is immutable. Saving sends the
|
|
255
|
+
full structured record through `save` (`record`, not `yaml`) and then re-reads it.
|
|
256
|
+
- Access summary counts hosts, accounts, endpoints, clusters, Kubernetes/Pod nodes and
|
|
257
|
+
failed logins; the latest login time and declared roles sit in the right rail. The role
|
|
258
|
+
card shows node counts per plane and the four standard roles even when the environment
|
|
259
|
+
declares only some of them.
|
|
260
|
+
- `All nodes` and every plane tab share one node view:
|
|
261
|
+
- toolbar: search box (id, name, IP, platform, admin user, role, kind, access
|
|
262
|
+
method, cluster name/reference, account username, purpose or web login URL),
|
|
263
|
+
`Add node`, `Batch import`;
|
|
264
|
+
- one compact card per host: name, active state, a kind badge, command-access badges,
|
|
265
|
+
IP/port, platform, admin user, account count, cluster reference, credential reference
|
|
266
|
+
and role chips;
|
|
267
|
+
- `Edit node` opens the same form prefilled; `Accounts (n)` expands the account table;
|
|
268
|
+
`Add account` opens the account form for that host; `Add web login` opens the same form
|
|
269
|
+
with protocol `web` preselected.
|
|
270
|
+
- `Add node` / `Edit node` form fields: id (auto-slug when blank), name, node kind,
|
|
271
|
+
Kubernetes cluster, IP, SSH port, platform, admin user, credential reference, role
|
|
272
|
+
checkboxes (at least one), command-access checkboxes (`ssh`, `kubectl`, `docker`) and an
|
|
273
|
+
optional login username + password. Changing the kind pre-fills that kind's default
|
|
274
|
+
access set; the operator can override it. `k8s-all-in-one` and `k8s-worker` require an
|
|
275
|
+
existing cluster; `pod-host` may omit one for a Docker-only machine. Username and password
|
|
276
|
+
must be filled together; when present the password is written to `ctx.credentials.set`
|
|
277
|
+
and the account/credential ref is `env-manager/<environmentId>/<hostId>-<username>`.
|
|
278
|
+
Editing a node preserves its existing accounts; filling username + password again adds a
|
|
279
|
+
new password account while leaving the existing accounts and host credential reference
|
|
280
|
+
intact. Opening the form from a plane tab preselects that role.
|
|
281
|
+
- `Add account` / `Edit account` form fields: id (auto-slug when blank, immutable after
|
|
282
|
+
creation), username, purpose, protocol, web login URL, auth type, password, credential
|
|
283
|
+
reference, latest login and source. A web login URL must start with `http://` or
|
|
284
|
+
`https://`; the Host schema also rejects non-http(s) values. It is shown as a link under
|
|
285
|
+
the account username. A non-empty password is
|
|
286
|
+
written to the DSH credential service and the account keeps only the generated
|
|
287
|
+
`credentialRef`; leaving the password blank preserves the existing reference. Accounts
|
|
288
|
+
can be edited in place, so a web URL or password can be changed without adding a
|
|
289
|
+
duplicate account.
|
|
290
|
+
- `Batch import` accepts one node per line:
|
|
291
|
+
`IP,name,roles,port,user`; multiple roles use `|` or `/`, for example
|
|
292
|
+
`10.20.30.53,app-01,business|management,22,root`. Import creates generic `vm` nodes with
|
|
293
|
+
`access: [ssh]` and optional accounts but never passwords; Kubernetes/Pod-host nodes are
|
|
294
|
+
edited after import. The batch parser rejects unknown roles, duplicate IPs, duplicate host
|
|
295
|
+
ids and invalid ports, and reports the first offending line without saving a partial batch.
|
|
296
|
+
- `Login records` sorts newest-first and shows time, result (success/failure), user, host,
|
|
297
|
+
source IP, protocol and note. `Add login record` opens an inline form with host/account
|
|
298
|
+
pickers; picking an account fills its username. A successful login also updates that
|
|
299
|
+
account's `lastLoginAt`/`lastLoginFrom` when it is newer than the stored value. A
|
|
300
|
+
failure renders with the error state color.
|
|
301
|
+
- `Kubernetes / clusters` lists cluster id, name, kind, topology, API endpoint, namespaces,
|
|
302
|
+
attached node count and credential reference. `Add cluster` / `Edit cluster` opens an
|
|
303
|
+
inline form for id (auto-slug when blank, immutable after creation), name, kind
|
|
304
|
+
(`kubernetes` or `docker`), topology (`all-in-one`, `separate`, `external`, or unset),
|
|
305
|
+
API endpoint, comma-separated namespaces, credential reference and note. Node counts are
|
|
306
|
+
derived from `Host.clusterRef`; cluster deletion remains a YAML operation so referenced
|
|
307
|
+
clusters cannot disappear silently.
|
|
308
|
+
- `Endpoints` lists id, kind, target, URL, port, credential reference and note.
|
|
309
|
+
- `YAML` exposes the whole environment document with `Save` and `Delete`; it is the
|
|
310
|
+
escape hatch, not the primary path. Account and login fields never contain secrets.
|
|
311
|
+
- Forms validate required fields locally (name, at least one role, IP format/duplicates,
|
|
312
|
+
port range, username/password pairing and `http(s)` web login URLs) and rely on the Host
|
|
313
|
+
for schema and referential-integrity failures. A Host rejection stays visible above the
|
|
314
|
+
node form and the form remains open; a failed save also removes any credential written
|
|
315
|
+
for that attempt.
|
|
316
|
+
|
|
317
|
+
### 6.4 YAML record editor
|
|
318
|
+
|
|
319
|
+
Services, deployments, rule profiles and operation bindings. Environments are created as
|
|
320
|
+
shells through the dedicated shell form; an existing environment's YAML is editable from
|
|
321
|
+
its detail tab.
|
|
322
|
+
|
|
323
|
+
```text
|
|
324
|
+
test-a [Save] [Delete]
|
|
325
|
+
environment / test-a
|
|
326
|
+
┌─────────────────────────────────────────────────────┐
|
|
327
|
+
│ id: test-a │
|
|
328
|
+
│ schemaVersion: 1 │
|
|
329
|
+
│ ... │
|
|
330
|
+
└─────────────────────────────────────────────────────┘
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
- The textarea is the source of truth for this editor; environment detail forms are the
|
|
334
|
+
structured alternative.
|
|
335
|
+
- `Save` sends the whole YAML through `save`; schema failures are shown in an alert with
|
|
336
|
+
the Host message and code. There is no per-record dry-run button in V1; `Validate`
|
|
337
|
+
on Overview checks the whole registry.
|
|
338
|
+
- `Delete` asks for confirmation, then calls `remove`. The Host keeps a backup.
|
|
339
|
+
- Production records are still protected by the Host write policy; the panel surfaces
|
|
340
|
+
`POLICY_DENIED` instead of retrying.
|
|
341
|
+
- Empty editor state offers `New record`, so the user does not have to go back to the
|
|
342
|
+
toolbar.
|
|
343
|
+
- Keyboard shortcuts (Ctrl/Cmd+S, Esc) and dirty-state prompts are not implemented in
|
|
344
|
+
V1; they remain on the roadmap.
|
|
345
|
+
|
|
346
|
+
Production records show a persistent banner:
|
|
347
|
+
"Production environment. Changes affect a live system." with
|
|
348
|
+
`--dsw-alias-state-warn-primary`.
|
|
349
|
+
|
|
350
|
+
### 6.5 Context Preview
|
|
351
|
+
|
|
352
|
+
Inputs: environment id, service id, deployment id (optional), entered as free text with
|
|
353
|
+
example placeholders (`test-a`, `aicoservice`, `deploy-001`).
|
|
354
|
+
|
|
355
|
+
Output in V1: the resolved context as formatted JSON in a scrollable `<pre>`. The Host
|
|
356
|
+
already returns provenance and structured `paths`/`variables`/`logSources`; a table view
|
|
357
|
+
with expandable provenance is a later refinement, not part of the current panel.
|
|
358
|
+
|
|
359
|
+
If resolution fails, the Host error (code and message) appears in the alert above the
|
|
360
|
+
form.
|
|
361
|
+
|
|
362
|
+
### 6.6 Operation Preview
|
|
363
|
+
|
|
364
|
+
Inputs: operation id, environment id, service id, deployment id (optional).
|
|
365
|
+
|
|
366
|
+
Output in V1: the routing result as formatted JSON, including the selected binding,
|
|
367
|
+
Skill, policy and parameters. Conflicts and missing Skills surface as the Host error
|
|
368
|
+
alert. A per-field policy card and an "also matched" list are later refinements.
|
|
369
|
+
|
|
370
|
+
### 6.7 Validation
|
|
371
|
+
|
|
372
|
+
There is no separate Diagnostics tab in V1. `Validate` on Overview calls the whole
|
|
373
|
+
registry check and renders its result card: pass/fail pill plus diagnostics with
|
|
374
|
+
severity, code and message. `Refresh` reloads `meta` and its warnings.
|
|
375
|
+
|
|
376
|
+
### 6.8 Bundle config page
|
|
377
|
+
|
|
378
|
+
Read-only rows: version, `dataDir`, record counts, warning count, "Open manager".
|
|
379
|
+
|
|
380
|
+
## 7. State and data flow
|
|
381
|
+
|
|
382
|
+
```js
|
|
383
|
+
const [section, setSection] = useState('overview')
|
|
384
|
+
const [kind, setKind] = useState('environment')
|
|
385
|
+
const [items, setItems] = useState([])
|
|
386
|
+
const [selected, setSelected] = useState(null) // record id, not an object
|
|
387
|
+
const [record, setRecord] = useState(null) // parsed environment detail
|
|
388
|
+
const [yaml, setYaml] = useState('')
|
|
389
|
+
const [error, setError] = useState(null)
|
|
390
|
+
const [message, setMessage] = useState(null) // transient save/delete toast
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
- Requests use the shared `call(op, payload)` helper and surface Host errors as `ErrorBox`.
|
|
394
|
+
- After a successful save/delete: refresh the list, re-read the selected record for
|
|
395
|
+
structured saves, and clear detail state when the record is removed.
|
|
396
|
+
- Environment detail forms save with `{ kind: 'environment', record }`; the YAML tab and
|
|
397
|
+
the other record kinds save with `{ kind, yaml }`.
|
|
398
|
+
- No global state, no context provider, no polling. "Refresh" is explicit.
|
|
399
|
+
- A client reload or HMR resets the selected section and open detail; this is accepted in V1.
|
|
400
|
+
|
|
401
|
+
## 8. Internationalization
|
|
402
|
+
|
|
403
|
+
Namespace `envManager`; dictionaries registered for `en` and `zh`.
|
|
404
|
+
|
|
405
|
+
| Key | en | zh |
|
|
406
|
+
|---|---|---|
|
|
407
|
+
| `title` | Environment Manager | 环境管理器 |
|
|
408
|
+
| `section.overview` | Overview | 概览 |
|
|
409
|
+
| `section.environments` | Environments | 环境 |
|
|
410
|
+
| `section.services` | Services | 服务 |
|
|
411
|
+
| `section.deployments` | Deployments | 部署 |
|
|
412
|
+
| `section.rules` | Rule Profiles | 规则档案 |
|
|
413
|
+
| `section.bindings` | Operation Bindings | 操作绑定 |
|
|
414
|
+
| `section.context` | Context Preview | 上下文预览 |
|
|
415
|
+
| `section.operation` | Operation Preview | 操作预览 |
|
|
416
|
+
| `section.diagnostics` | Diagnostics | 诊断 |
|
|
417
|
+
| `action.new` | New | 新建 |
|
|
418
|
+
| `action.save` | Save | 保存 |
|
|
419
|
+
| `action.validate` | Validate | 校验 |
|
|
420
|
+
| `action.delete` | Delete | 删除 |
|
|
421
|
+
| `action.refresh` | Refresh | 刷新 |
|
|
422
|
+
| `action.back` | Back | 返回 |
|
|
423
|
+
| `action.cancel` | Cancel | 取消 |
|
|
424
|
+
| `state.empty` | No records yet | 暂无记录 |
|
|
425
|
+
| `state.loading` | Loading… | 加载中… |
|
|
426
|
+
| `state.dirty` | Unsaved changes | 有未保存的修改 |
|
|
427
|
+
| `state.production` | Production environment. Changes affect a live system. | 生产环境,修改会影响线上系统。 |
|
|
428
|
+
| `confirm.delete.title` | Delete {id}? | 删除 {id}? |
|
|
429
|
+
| `confirm.delete.body` | This cannot be undone. A backup copy is kept. | 此操作不可撤销,但会保留一份备份。 |
|
|
430
|
+
| `confirm.delete.type` | Type {id} to confirm | 输入 {id} 以确认 |
|
|
431
|
+
| `error.policy` | Not allowed by the current policy | 当前策略不允许此操作 |
|
|
432
|
+
| `error.network` | Cannot reach the Host | 无法连接 Host |
|
|
433
|
+
| `error.conflict` | Conflicting bindings: {ids} | 冲突的绑定:{ids} |
|
|
434
|
+
| `context.definedBy` | Defined by | 定义来源 |
|
|
435
|
+
| `context.inputs` | Inputs | 输入 |
|
|
436
|
+
| `context.shadowed` | Shadowed | 被覆盖 |
|
|
437
|
+
| `context.noValue` | Not defined | 未定义 |
|
|
438
|
+
| `operation.requiresApproval` | Requires approval | 需要审批 |
|
|
439
|
+
| `operation.allowProduction` | Allows production | 允许生产 |
|
|
440
|
+
| `skill.available` | Available | 可用 |
|
|
441
|
+
| `skill.unavailable` | Unavailable | 不可用 |
|
|
442
|
+
| `skill.unknown` | Unknown | 未知 |
|
|
443
|
+
|
|
444
|
+
Rules:
|
|
445
|
+
|
|
446
|
+
- `t(key, params)` from `ctx.locale.bind('envManager')`; no string concatenation.
|
|
447
|
+
- Dates render with `Intl.DateTimeFormat` from the active locale id.
|
|
448
|
+
- A missing key falls back to English and logs once.
|
|
449
|
+
|
|
450
|
+
## 9. Styling
|
|
451
|
+
|
|
452
|
+
```js
|
|
453
|
+
const S = {
|
|
454
|
+
panel: { display: 'flex', height: '100%', background: 'var(--dsw-alias-bg-base)' },
|
|
455
|
+
strip: { width: 180, borderRight: '1px solid var(--dsw-alias-border-l1)',
|
|
456
|
+
background: 'var(--dsw-alias-bg-layer-1)', padding: 8 },
|
|
457
|
+
card: { background: 'var(--dsw-alias-bg-layer-1)', border: '1px solid var(--dsw-alias-border-l1)',
|
|
458
|
+
borderRadius: 8, padding: 16 },
|
|
459
|
+
text: { color: 'var(--dsw-alias-label-primary)' },
|
|
460
|
+
muted: { color: 'var(--dsw-alias-label-secondary)' },
|
|
461
|
+
danger: { color: 'var(--dsw-alias-state-error-primary)' },
|
|
462
|
+
warn: { color: 'var(--dsw-alias-state-warn-primary)' },
|
|
463
|
+
ok: { color: 'var(--dsw-alias-state-success-primary)' },
|
|
464
|
+
mono: { fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace', fontSize: 12 },
|
|
465
|
+
}
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
- No inline hex colors except the plugin-manager `icon.svg` brand mark. The sidebar
|
|
469
|
+
glyph uses `currentColor` only, so it follows the host theme.
|
|
470
|
+
- Radius 8, spacing multiples of 4, body 13 px, mono 12 px.
|
|
471
|
+
- Hover uses `--dsw-alias-bg-layer-2`; focus ring uses `--dsw-alias-brand-primary`.
|
|
472
|
+
|
|
473
|
+
## 10. Accessibility
|
|
474
|
+
|
|
475
|
+
- Section strip is a `role="tablist"` with arrow-key navigation.
|
|
476
|
+
- Every icon-only button has `aria-label`.
|
|
477
|
+
- Errors use `role="alert"`; success uses `role="status"`.
|
|
478
|
+
- The YAML textarea has a label and a described-by diagnostics id.
|
|
479
|
+
- Focus returns to the row that opened the editor after Save/Back.
|
|
480
|
+
- No action is reachable only by hover.
|
|
481
|
+
|
|
482
|
+
## 11. Component tree
|
|
483
|
+
|
|
484
|
+
```text
|
|
485
|
+
EnvManagerPanel
|
|
486
|
+
├── PanelHeader (glyph, title, subtitle)
|
|
487
|
+
├── HeaderActions (New environment, persistent)
|
|
488
|
+
├── SectionTabs (Environments / Overview / Records / Context / Operations)
|
|
489
|
+
├── SectionContent
|
|
490
|
+
│ ├── OverviewView (stat cards, runtime, warnings, onboarding)
|
|
491
|
+
│ ├── EnvironmentsView
|
|
492
|
+
│ │ ├── EnvironmentList ── EnvironmentRow
|
|
493
|
+
│ │ ├── EnvironmentDetail
|
|
494
|
+
│ │ │ ├── DetailHeader (back, identity, actions)
|
|
495
|
+
│ │ │ ├── DetailTabs (overview/all nodes/plane tabs/logins/clusters/endpoints/yaml)
|
|
496
|
+
│ │ │ ├── BasicInfo + BasicInfoForm
|
|
497
|
+
│ │ │ ├── AccessSummary + PlaneDistribution + RoleChips
|
|
498
|
+
│ │ │ ├── NodeToolbar (search/add/batch)
|
|
499
|
+
│ │ │ ├── NodeCard ── AccountTable + AccountForm
|
|
500
|
+
│ │ │ ├── NodeForm (kind/cluster/access/multi-role/optional credential)
|
|
501
|
+
│ │ │ ├── BatchNodeForm
|
|
502
|
+
│ │ │ ├── LoginTable + LoginForm
|
|
503
|
+
│ │ │ ├── ClusterTable + ClusterForm
|
|
504
|
+
│ │ │ ├── EndpointTable
|
|
505
|
+
│ │ │ └── YamlEditor
|
|
506
|
+
│ │ └── EnvironmentWizard (shell create: name/id/category/description + default planes)
|
|
507
|
+
│ ├── RecordsView
|
|
508
|
+
│ │ └── YamlRecordEditor (service/deployment/rule/binding)
|
|
509
|
+
│ ├── ContextView ── ResultBlock
|
|
510
|
+
│ └── OperationsView ── ResultBlock
|
|
511
|
+
└── Toast / ErrorBox
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
Each component is a function in `client.js`; shared helpers are `h()`, `t()`, `call()`,
|
|
515
|
+
`formatTime()`, `categoryLabel()`, `authLabel()`, `roleName()`, `countByRole()`,
|
|
516
|
+
`hostKindLabel()`, `hostAccessLabel()`, `topologyLabel()`, `stableSlug()` and the form
|
|
517
|
+
primitives `Field`, `Select` and `TextArea`.
|
|
518
|
+
|
|
519
|
+
## 12. Manual QA checklist
|
|
520
|
+
|
|
521
|
+
- [ ] Sidebar icon appears once, at 16 px wide / 18 px collapsed, in `currentColor`,
|
|
522
|
+
with no filled background, and selects the panel.
|
|
523
|
+
- [ ] The sidebar row shows one label, aligned with the shipped rows, and the active
|
|
524
|
+
row uses the host highlight rather than plugin-provided styling.
|
|
525
|
+
- [ ] Panel opens on **Environments**, not Overview or Records.
|
|
526
|
+
- [ ] The header `New environment` action opens the guided shell form from any tab.
|
|
527
|
+
- [ ] `Records` excludes Environments and keeps Services/Deployments/Rule Profiles/
|
|
528
|
+
Operation Bindings YAML editing.
|
|
529
|
+
- [ ] Panel renders with an empty `dataDir` and with several environments.
|
|
530
|
+
- [ ] `New environment` creates one environment shell with the four standard roles and
|
|
531
|
+
zero hosts; no password is requested and no credential is written.
|
|
532
|
+
- [ ] Environment list summary shows hosts, accounts and login records.
|
|
533
|
+
- [ ] Detail back link returns to the list and keeps the Records tab selected.
|
|
534
|
+
- [ ] Basic info edit round-trips name, aliases, category and description.
|
|
535
|
+
- [ ] Detail tabs wrap without clipping and show Overview, All nodes, each declared
|
|
536
|
+
plane, Login records, Kubernetes / clusters, Endpoints and YAML.
|
|
537
|
+
- [ ] A plane tab shows only nodes with that role; a multi-role node appears in every
|
|
538
|
+
plane it serves without duplicating the host record.
|
|
539
|
+
- [ ] A plane with zero nodes shows its empty state and `Add node` preselects that role.
|
|
540
|
+
- [ ] Add node requires a name and at least one role; optional username + password writes
|
|
541
|
+
a credential reference and never a plaintext secret.
|
|
542
|
+
- [ ] Add node offers VM, physical, K8s all-in-one, K8s worker and Pod host; selecting a
|
|
543
|
+
K8s kind requires a cluster and pre-fills its default access methods.
|
|
544
|
+
- [ ] A Pod host records `ssh` + `docker`, not a Pod name; the node remains one machine.
|
|
545
|
+
- [ ] Edit node preserves existing accounts and can add/remove roles, kind, access and
|
|
546
|
+
cluster reference.
|
|
547
|
+
- [ ] Batch import parses `IP,name,roles,port,user`, supports `|` role separators and
|
|
548
|
+
rejects unknown roles/duplicate IPs without saving a partial batch.
|
|
549
|
+
- [ ] Node search filters by id, name, IP, platform, admin user, role, kind, access method,
|
|
550
|
+
cluster name/reference, account username and web login URL.
|
|
551
|
+
- [ ] An expanded node card shows every account with auth type and last login source.
|
|
552
|
+
- [ ] Add web login stores the URL and username, shows the URL as a link, and keeps only a
|
|
553
|
+
credential reference after the password is written; edit account can update the URL
|
|
554
|
+
or rotate the password.
|
|
555
|
+
- [ ] Kubernetes / clusters lists node counts, topology and API endpoint; add/edit cluster
|
|
556
|
+
round-trips and the new cluster appears in the node form's cluster picker.
|
|
557
|
+
- [ ] Add node / add account / add login record / add cluster persist and re-render without
|
|
558
|
+
a reload.
|
|
559
|
+
- [ ] Login records are ordered newest-first and failures use the error color.
|
|
560
|
+
- [ ] Endpoints show target name plus id and preserve URL/credential reference.
|
|
561
|
+
- [ ] YAML tab saves the same document and clears stale detail state after delete.
|
|
562
|
+
- [ ] Invalid YAML shows a diagnostic and does not write.
|
|
563
|
+
- [ ] Delete asks for confirmation and reports the Host error if removal fails.
|
|
564
|
+
- [ ] Production save is denied when `allowUiProductionWrites` is false.
|
|
565
|
+
- [ ] Context preview returns a structured context.
|
|
566
|
+
- [ ] Operation preview resolves a binding or a clear diagnostic.
|
|
567
|
+
- [ ] Language switch between `en` and `zh` updates every visible string.
|
|
568
|
+
- [ ] Light and dark themes both keep contrast on cards and badges.
|
|
569
|
+
- [ ] Keyboard-only navigation reaches every action.
|
|
570
|
+
- [ ] Reload of the page keeps the selected section out of scope (state resets).
|
|
571
|
+
|
|
572
|
+
## 13. Out of scope for V1
|
|
573
|
+
|
|
574
|
+
- Full CRUD forms for services, deployments, rules and bindings.
|
|
575
|
+
- Deleting individual hosts/accounts/logins from rows; account and node edit exist, deletion
|
|
576
|
+
stays in YAML in V1.
|
|
577
|
+
- Per-Pod or per-container inventory, container ids and restart counts. V1 records the
|
|
578
|
+
Pod-hosting machine as a `pod-host` and leaves Pods in the deployment model.
|
|
579
|
+
- Deleting clusters from a row; cluster deletion stays in YAML so references are deliberate.
|
|
580
|
+
- Database primary/replica/sharding topology; V1 models the database plane as a role and
|
|
581
|
+
keeps topology detail in node notes.
|
|
582
|
+
- YAML syntax highlighting, folding, or diff view.
|
|
583
|
+
- Topology graphs and dependency visualizations.
|
|
584
|
+
- Creating or editing the session default environment.
|
|
585
|
+
- Executing a Skill or showing an operation timeline.
|
|
586
|
+
- Multi-user presence, comments, or audit UI.
|
|
587
|
+
- JumpServer/PAM API synchronization; login data is entered or imported explicitly.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Agent Behavior
|
|
2
|
+
|
|
3
|
+
## Required Behavior
|
|
4
|
+
|
|
5
|
+
Agents working on this project must:
|
|
6
|
+
|
|
7
|
+
- preserve the file-system-first architecture
|
|
8
|
+
- keep changes small and reviewable
|
|
9
|
+
- write tests for core logic
|
|
10
|
+
- update current-state.md after completion
|
|
11
|
+
- avoid introducing infrastructure not requested by the user
|
|
12
|
+
- keep tool functions thin
|
|
13
|
+
- keep business logic in resolver/router modules
|
|
14
|
+
- treat skill references as discovery, not execution authorization
|
|
15
|
+
|
|
16
|
+
## Forbidden Behavior
|
|
17
|
+
|
|
18
|
+
Agents must not:
|
|
19
|
+
|
|
20
|
+
- introduce plain-text credentials into registry files
|
|
21
|
+
- execute arbitrary JavaScript or shell in rule templates
|
|
22
|
+
- auto-apply rule or skill changes without review
|
|
23
|
+
- write hidden durable state outside documented directories
|
|
24
|
+
- silently discard resolver/router failures
|
|
25
|
+
- treat skill reference resolution as execution approval
|
|
26
|
+
- perform unrelated refactoring
|
|
27
|
+
- change storage contracts without documentation
|
|
28
|
+
|
|
29
|
+
## When Unclear
|
|
30
|
+
|
|
31
|
+
If the task is unclear:
|
|
32
|
+
|
|
33
|
+
1. inspect relevant docs
|
|
34
|
+
2. inspect source files
|
|
35
|
+
3. propose a small plan
|
|
36
|
+
4. avoid broad implementation
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Change Snapshot Policy
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Preserve project continuity by documenting what changed and why after each meaningful change.
|
|
6
|
+
|
|
7
|
+
## When to Update
|
|
8
|
+
|
|
9
|
+
Update `docs/00-project/current-state.md` when:
|
|
10
|
+
|
|
11
|
+
- a new feature is implemented
|
|
12
|
+
- a bug is fixed
|
|
13
|
+
- a storage contract changes
|
|
14
|
+
- a module boundary changes
|
|
15
|
+
- a known gap is closed
|
|
16
|
+
|
|
17
|
+
## What to Record
|
|
18
|
+
|
|
19
|
+
- What changed (high-level)
|
|
20
|
+
- Which files were added/removed/modified
|
|
21
|
+
- Any new constraints or dependencies
|
|
22
|
+
- Remaining known gaps
|
|
23
|
+
- Recommended next work
|
|
24
|
+
|
|
25
|
+
## What Not to Record
|
|
26
|
+
|
|
27
|
+
- Every single commit detail (git history covers that)
|
|
28
|
+
- Temporary workarounds without context
|
|
29
|
+
- Opinion or speculation
|