kodu 1.1.13 → 1.1.15
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/AGENTS.md +184 -199
- package/README.md +32 -3
- package/dist/src/app.module.js +2 -0
- package/dist/src/app.module.js.map +1 -1
- package/dist/src/commands/init/init.command.js +16 -0
- package/dist/src/commands/init/init.command.js.map +1 -1
- package/dist/src/commands/ops/ops.command.d.ts +4 -0
- package/dist/src/commands/ops/ops.command.js +39 -0
- package/dist/src/commands/ops/ops.command.js.map +1 -0
- package/dist/src/commands/ops/ops.module.d.ts +2 -0
- package/dist/src/commands/ops/ops.module.js +33 -0
- package/dist/src/commands/ops/ops.module.js.map +1 -0
- package/dist/src/commands/ops/ops.types.d.ts +13 -0
- package/dist/src/commands/ops/ops.types.js +12 -0
- package/dist/src/commands/ops/ops.types.js.map +1 -0
- package/dist/src/commands/ops/ops.utils.d.ts +13 -0
- package/dist/src/commands/ops/ops.utils.js +117 -0
- package/dist/src/commands/ops/ops.utils.js.map +1 -0
- package/dist/src/commands/ops/subcommands/ops-env.command.d.ts +17 -0
- package/dist/src/commands/ops/subcommands/ops-env.command.js +109 -0
- package/dist/src/commands/ops/subcommands/ops-env.command.js.map +1 -0
- package/dist/src/commands/ops/subcommands/ops-routes.command.d.ts +18 -0
- package/dist/src/commands/ops/subcommands/ops-routes.command.js +166 -0
- package/dist/src/commands/ops/subcommands/ops-routes.command.js.map +1 -0
- package/dist/src/commands/ops/subcommands/ops-service.command.d.ts +16 -0
- package/dist/src/commands/ops/subcommands/ops-service.command.js +128 -0
- package/dist/src/commands/ops/subcommands/ops-service.command.js.map +1 -0
- package/dist/src/commands/ops/subcommands/ops-sysinfo.command.d.ts +9 -0
- package/dist/src/commands/ops/subcommands/ops-sysinfo.command.js +60 -0
- package/dist/src/commands/ops/subcommands/ops-sysinfo.command.js.map +1 -0
- package/dist/src/commands/pack/pack.command.js +1 -2
- package/dist/src/commands/pack/pack.command.js.map +1 -1
- package/dist/src/core/config/config.schema.d.ts +28 -0
- package/dist/src/core/config/config.schema.js +19 -0
- package/dist/src/core/config/config.schema.js.map +1 -1
- package/dist/src/core/file-system/fs.service.d.ts +4 -1
- package/dist/src/core/file-system/fs.service.js +57 -21
- package/dist/src/core/file-system/fs.service.js.map +1 -1
- package/dist/src/shared/constants.d.ts +1 -0
- package/dist/src/shared/constants.js +2 -1
- package/dist/src/shared/constants.js.map +1 -1
- package/dist/src/shared/ssh/ssh.module.d.ts +2 -0
- package/dist/src/shared/ssh/ssh.module.js +21 -0
- package/dist/src/shared/ssh/ssh.module.js.map +1 -0
- package/dist/src/shared/ssh/ssh.service.d.ts +11 -0
- package/dist/src/shared/ssh/ssh.service.js +53 -0
- package/dist/src/shared/ssh/ssh.service.js.map +1 -0
- package/dist/src/shared/tokenizer/tokenizer.service.js +1 -1
- package/dist/src/shared/tokenizer/tokenizer.service.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/docs/plans/2026-03-01-agentops-design.md +194 -0
- package/docs/plans/2026-03-01-agentops-implementation.md +358 -0
- package/kodu.json +15 -0
- package/kodu.schema.json +59 -0
- package/package.json +1 -1
- package/src/app.module.ts +2 -0
- package/src/commands/init/init.command.ts +16 -0
- package/src/commands/ops/ops.command.ts +30 -0
- package/src/commands/ops/ops.module.ts +20 -0
- package/src/commands/ops/ops.types.ts +24 -0
- package/src/commands/ops/ops.utils.ts +156 -0
- package/src/commands/ops/subcommands/ops-env.command.ts +121 -0
- package/src/commands/ops/subcommands/ops-routes.command.ts +185 -0
- package/src/commands/ops/subcommands/ops-service.command.ts +154 -0
- package/src/commands/ops/subcommands/ops-sysinfo.command.ts +53 -0
- package/src/commands/pack/pack.command.ts +1 -2
- package/src/core/config/config.schema.ts +23 -0
- package/src/core/file-system/fs.service.ts +72 -23
- package/src/shared/constants.ts +1 -0
- package/src/shared/ssh/ssh.module.ts +8 -0
- package/src/shared/ssh/ssh.service.ts +61 -0
- package/src/shared/tokenizer/tokenizer.service.ts +2 -2
- package/.cursor/commands/openspec-apply.md +0 -23
- package/.cursor/commands/openspec-archive.md +0 -27
- package/.cursor/commands/openspec-proposal.md +0 -28
- package/.windsurf/workflows/openspec-apply.md +0 -21
- package/.windsurf/workflows/openspec-archive.md +0 -25
- package/.windsurf/workflows/openspec-proposal.md +0 -26
- package/openspec/AGENTS.md +0 -456
- package/openspec/changes/archive/2026-01-26-translate-project-to-english/design.md +0 -30
- package/openspec/changes/archive/2026-01-26-translate-project-to-english/proposal.md +0 -17
- package/openspec/changes/archive/2026-01-26-translate-project-to-english/specs/ai/spec.md +0 -26
- package/openspec/changes/archive/2026-01-26-translate-project-to-english/specs/cleaner/spec.md +0 -26
- package/openspec/changes/archive/2026-01-26-translate-project-to-english/specs/config/spec.md +0 -22
- package/openspec/changes/archive/2026-01-26-translate-project-to-english/specs/ui/spec.md +0 -33
- package/openspec/changes/archive/2026-01-26-translate-project-to-english/tasks.md +0 -33
- package/openspec/project.md +0 -72
- package/openspec/specs/cleaner/spec.md +0 -31
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# AgentOps Design
|
|
2
|
+
|
|
3
|
+
## Context
|
|
4
|
+
|
|
5
|
+
Add a new `kodu ops` namespace for AI agents to manage remote servers through SSH. The output contract is strict JSON in stdout/stderr with no spinner UI, no colors, and no interactive prompts.
|
|
6
|
+
|
|
7
|
+
## Goals
|
|
8
|
+
|
|
9
|
+
- Add project-local config for remote server aliases in `kodu.json`.
|
|
10
|
+
- Add a shared SSH abstraction over `execa` that does not throw on remote command failures.
|
|
11
|
+
- Add `kodu ops` subcommands for server diagnostics, env management, Caddy routes, and Docker Compose service lifecycle.
|
|
12
|
+
- Preserve machine-readable behavior for all success and error paths.
|
|
13
|
+
|
|
14
|
+
## Config Design
|
|
15
|
+
|
|
16
|
+
Extend `src/core/config/config.schema.ts` with optional `ops` section:
|
|
17
|
+
|
|
18
|
+
- `ops.servers` is a record keyed by alias (`prod`, `dev`, etc.).
|
|
19
|
+
- Each server includes:
|
|
20
|
+
- `host`, `user`, `sshKeyPath`
|
|
21
|
+
- `port` default `22`
|
|
22
|
+
- optional `description`
|
|
23
|
+
- optional `paths` with:
|
|
24
|
+
- `apps` default `/var/agent-apps`
|
|
25
|
+
- optional `caddy` for custom Caddy project path
|
|
26
|
+
- optional `env` map for SSH process environment variables
|
|
27
|
+
|
|
28
|
+
All commands validate:
|
|
29
|
+
|
|
30
|
+
1. alias exists in `ops.servers`
|
|
31
|
+
2. ssh key file exists (`fs.access`)
|
|
32
|
+
|
|
33
|
+
## SSH Shared Module Design
|
|
34
|
+
|
|
35
|
+
Create `src/shared/ssh/` with:
|
|
36
|
+
|
|
37
|
+
- `ssh.module.ts`
|
|
38
|
+
- `ssh.service.ts`
|
|
39
|
+
|
|
40
|
+
`SshService.execute(serverConfig, command)` behavior:
|
|
41
|
+
|
|
42
|
+
- Builds `ssh` args:
|
|
43
|
+
- `-i <keyPath>`
|
|
44
|
+
- `-p <port>`
|
|
45
|
+
- `-o StrictHostKeyChecking=no`
|
|
46
|
+
- `-o ConnectTimeout=10`
|
|
47
|
+
- `<user>@<host>`
|
|
48
|
+
- `<command>`
|
|
49
|
+
- Runs via `execa('ssh', args, { env })`
|
|
50
|
+
- Returns:
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
type SshResult = {
|
|
54
|
+
success: boolean;
|
|
55
|
+
stdout: string;
|
|
56
|
+
stderr: string;
|
|
57
|
+
exitCode: number;
|
|
58
|
+
error?: string;
|
|
59
|
+
};
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
No throw for SSH command failures. Local/system failures also map to `SshResult` with `success: false`.
|
|
63
|
+
|
|
64
|
+
## Commands Design
|
|
65
|
+
|
|
66
|
+
Create `src/commands/ops/`:
|
|
67
|
+
|
|
68
|
+
- `ops.module.ts`
|
|
69
|
+
- `ops.command.ts`
|
|
70
|
+
- `subcommands/ops-sysinfo.command.ts`
|
|
71
|
+
- `subcommands/ops-env.command.ts`
|
|
72
|
+
- `subcommands/ops-routes.command.ts`
|
|
73
|
+
- `subcommands/ops-service.command.ts`
|
|
74
|
+
|
|
75
|
+
Register `OpsModule` in `src/app.module.ts`.
|
|
76
|
+
|
|
77
|
+
All `ops` commands:
|
|
78
|
+
|
|
79
|
+
- no `UiService`
|
|
80
|
+
- no spinners
|
|
81
|
+
- no prompts
|
|
82
|
+
- print JSON only:
|
|
83
|
+
- success/data via `console.log(JSON.stringify(...))`
|
|
84
|
+
- fatal CLI errors via `console.error(JSON.stringify(...))` and `process.exitCode = 1`
|
|
85
|
+
|
|
86
|
+
## JSON Contracts
|
|
87
|
+
|
|
88
|
+
Success pattern:
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
{ "status": "ok", "data": {} }
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Remote command failure pattern:
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"status": "error",
|
|
99
|
+
"code": 255,
|
|
100
|
+
"stderr": "ssh: connect to host ... port 22: Connection refused",
|
|
101
|
+
"command": "uptime"
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Local validation/fatal pattern:
|
|
106
|
+
|
|
107
|
+
```json
|
|
108
|
+
{
|
|
109
|
+
"status": "error",
|
|
110
|
+
"code": "VALIDATION_ERROR",
|
|
111
|
+
"error": "Server alias 'prod' not found in kodu.json"
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Subcommand Behavior
|
|
116
|
+
|
|
117
|
+
### `ops sysinfo <alias>`
|
|
118
|
+
|
|
119
|
+
Runs the agreed payload and proxies JSON response:
|
|
120
|
+
|
|
121
|
+
- uptime
|
|
122
|
+
- root disk usage
|
|
123
|
+
- free memory (MB)
|
|
124
|
+
|
|
125
|
+
### `ops env <alias> <action> <project>`
|
|
126
|
+
|
|
127
|
+
Actions: `get`, `set`, `unset`.
|
|
128
|
+
|
|
129
|
+
- target file: `${appsPath}/${project}/.env`
|
|
130
|
+
- `get`: read file contents
|
|
131
|
+
- `set`: update or append key
|
|
132
|
+
- `unset`: remove key line
|
|
133
|
+
|
|
134
|
+
Required options:
|
|
135
|
+
|
|
136
|
+
- `set`: `--key`, `--val`
|
|
137
|
+
- `unset`: `--key`
|
|
138
|
+
|
|
139
|
+
### `ops routes <alias> <action>`
|
|
140
|
+
|
|
141
|
+
Use Caddy project workflow from `/home/dex/Рабочий стол/Work/caddy-caddy` model:
|
|
142
|
+
|
|
143
|
+
- Caddy runs via Docker Compose in separate project folder
|
|
144
|
+
- config source is `data/Caddyfile`
|
|
145
|
+
- apply command is `./caddy.sh` (default apply mode)
|
|
146
|
+
|
|
147
|
+
Actions:
|
|
148
|
+
|
|
149
|
+
- `list`: return raw Caddyfile
|
|
150
|
+
- `add`: add domain `reverse_proxy` block, then run `./caddy.sh`
|
|
151
|
+
- `remove`: remove domain block, then run `./caddy.sh`
|
|
152
|
+
- `update`: update domain upstream, then run `./caddy.sh`
|
|
153
|
+
|
|
154
|
+
Path resolution for Caddy project:
|
|
155
|
+
|
|
156
|
+
- `server.paths.caddy` if configured
|
|
157
|
+
- fallback `${appsPath}/caddy`
|
|
158
|
+
|
|
159
|
+
### `ops service <alias> <action> <project>`
|
|
160
|
+
|
|
161
|
+
Actions: `clone`, `pull`, `up`, `down`, `logs`, `status`.
|
|
162
|
+
|
|
163
|
+
- `clone`: clone repo to project path (skip/fail if exists)
|
|
164
|
+
- `pull`: git pull in project dir
|
|
165
|
+
- `up`: docker compose up -d
|
|
166
|
+
- `down`: docker compose down
|
|
167
|
+
- `logs`: docker compose logs
|
|
168
|
+
- `status`: docker compose ps (json format when available)
|
|
169
|
+
|
|
170
|
+
## Caddyfile Editing Strategy
|
|
171
|
+
|
|
172
|
+
For `routes add/remove/update`, perform robust remote text transforms with a small `node -e` script executed over SSH:
|
|
173
|
+
|
|
174
|
+
- read Caddyfile
|
|
175
|
+
- locate target domain block
|
|
176
|
+
- apply deterministic update
|
|
177
|
+
- write to temp file then rename
|
|
178
|
+
- return structured JSON from remote helper
|
|
179
|
+
|
|
180
|
+
Then run `cd <caddyPath> && ./caddy.sh`.
|
|
181
|
+
|
|
182
|
+
If domain not found for `remove`/`update`, return JSON error with code `NOT_FOUND`.
|
|
183
|
+
|
|
184
|
+
## Error Handling Rules
|
|
185
|
+
|
|
186
|
+
- SSH exit code non-zero must not crash command.
|
|
187
|
+
- Return JSON with `status:error`, numeric `code`, `stderr`, and original `command`.
|
|
188
|
+
- Keep stdout/stderr machine-friendly; no additional prose lines.
|
|
189
|
+
|
|
190
|
+
## Out of Scope
|
|
191
|
+
|
|
192
|
+
- Caddy Admin API integration.
|
|
193
|
+
- Interactive operator UX.
|
|
194
|
+
- Non-JSON presentation layer.
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
# AgentOps Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
|
4
|
+
|
|
5
|
+
**Goal:** Add a new JSON-only `kodu ops` command namespace for remote server diagnostics and operations over SSH.
|
|
6
|
+
|
|
7
|
+
**Architecture:** Extend config with an optional `ops.servers` map, add a shared `SshService` that wraps `execa('ssh')` and never throws for remote failures, and implement `ops` subcommands (`sysinfo`, `env`, `routes`, `service`) that validate inputs locally and return strict JSON responses.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** NestJS + nest-commander, zod, execa, node:fs/promises.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
### Task 1: Extend Config Schema For Ops
|
|
14
|
+
|
|
15
|
+
**Files:**
|
|
16
|
+
- Modify: `src/core/config/config.schema.ts`
|
|
17
|
+
|
|
18
|
+
**Step 1: Write the failing check (type-level usage target)**
|
|
19
|
+
|
|
20
|
+
Use this target snippet as acceptance criteria while editing:
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
const cfg = configService.getConfig();
|
|
24
|
+
const maybeServer = cfg.ops?.servers?.prod;
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**Step 2: Run type check to confirm current gap**
|
|
28
|
+
|
|
29
|
+
Run: `npm run ts:check`
|
|
30
|
+
Expected: type errors in future ops command code until `ops` is added.
|
|
31
|
+
|
|
32
|
+
**Step 3: Add minimal schema/types**
|
|
33
|
+
|
|
34
|
+
Add `serverConfigSchema`, `opsSchema`, and `ops: opsSchema.optional()` in root schema.
|
|
35
|
+
|
|
36
|
+
**Step 4: Run type check**
|
|
37
|
+
|
|
38
|
+
Run: `npm run ts:check`
|
|
39
|
+
Expected: PASS for config layer.
|
|
40
|
+
|
|
41
|
+
**Step 5: Commit**
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
git add src/core/config/config.schema.ts
|
|
45
|
+
git commit -m "feat(config): add ops servers schema"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Task 2: Add Shared SSH Module And Service
|
|
49
|
+
|
|
50
|
+
**Files:**
|
|
51
|
+
- Create: `src/shared/ssh/ssh.service.ts`
|
|
52
|
+
- Create: `src/shared/ssh/ssh.module.ts`
|
|
53
|
+
|
|
54
|
+
**Step 1: Define failing usage target**
|
|
55
|
+
|
|
56
|
+
Target call shape:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
const result = await sshService.execute(server, 'uptime');
|
|
60
|
+
if (!result.success) {
|
|
61
|
+
console.log(result.stderr);
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Step 2: Run type check to verify service does not exist yet**
|
|
66
|
+
|
|
67
|
+
Run: `npm run ts:check`
|
|
68
|
+
Expected: FAIL when referenced by future ops command wiring.
|
|
69
|
+
|
|
70
|
+
**Step 3: Implement minimal `SshService`**
|
|
71
|
+
|
|
72
|
+
Implement:
|
|
73
|
+
- `SshResult` type
|
|
74
|
+
- `execute(serverConfig, command)`
|
|
75
|
+
- `execa('ssh', args, { env: serverConfig.env })`
|
|
76
|
+
- capture non-zero exits and system errors without throw
|
|
77
|
+
|
|
78
|
+
**Step 4: Export via `SshModule`**
|
|
79
|
+
|
|
80
|
+
Use Nest module pattern consistent with shared modules.
|
|
81
|
+
|
|
82
|
+
**Step 5: Run type check**
|
|
83
|
+
|
|
84
|
+
Run: `npm run ts:check`
|
|
85
|
+
Expected: PASS for ssh module/service.
|
|
86
|
+
|
|
87
|
+
**Step 6: Commit**
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
git add src/shared/ssh/ssh.service.ts src/shared/ssh/ssh.module.ts
|
|
91
|
+
git commit -m "feat(ops): add shared ssh service"
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Task 3: Scaffold Ops Command Module
|
|
95
|
+
|
|
96
|
+
**Files:**
|
|
97
|
+
- Create: `src/commands/ops/ops.module.ts`
|
|
98
|
+
- Create: `src/commands/ops/ops.command.ts`
|
|
99
|
+
- Modify: `src/app.module.ts`
|
|
100
|
+
|
|
101
|
+
**Step 1: Create failing command target**
|
|
102
|
+
|
|
103
|
+
Expected CLI shape:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
kodu ops --help
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**Step 2: Run build to confirm command missing**
|
|
110
|
+
|
|
111
|
+
Run: `npm run build`
|
|
112
|
+
Expected: `ops` command not present before scaffolding.
|
|
113
|
+
|
|
114
|
+
**Step 3: Implement root ops module/command**
|
|
115
|
+
|
|
116
|
+
Add root namespace command and register providers/imports.
|
|
117
|
+
|
|
118
|
+
**Step 4: Register in AppModule**
|
|
119
|
+
|
|
120
|
+
Import `OpsModule` (and `SshModule` if needed at root level).
|
|
121
|
+
|
|
122
|
+
**Step 5: Run build**
|
|
123
|
+
|
|
124
|
+
Run: `npm run build`
|
|
125
|
+
Expected: PASS and `kodu ops --help` shows namespace.
|
|
126
|
+
|
|
127
|
+
**Step 6: Commit**
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
git add src/commands/ops/ops.module.ts src/commands/ops/ops.command.ts src/app.module.ts
|
|
131
|
+
git commit -m "feat(ops): register ops root namespace"
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Task 4: Implement Shared Ops Validation + JSON Helpers
|
|
135
|
+
|
|
136
|
+
**Files:**
|
|
137
|
+
- Create: `src/commands/ops/ops.types.ts`
|
|
138
|
+
- Create: `src/commands/ops/ops.utils.ts`
|
|
139
|
+
|
|
140
|
+
**Step 1: Define failing usage target**
|
|
141
|
+
|
|
142
|
+
Helpers should support:
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
const server = await resolveServerOrThrow(config, alias);
|
|
146
|
+
await assertSshKeyExists(server);
|
|
147
|
+
printOk({ ping: true });
|
|
148
|
+
printSshError(result, command);
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**Step 2: Implement helpers**
|
|
152
|
+
|
|
153
|
+
Include:
|
|
154
|
+
- server alias resolution
|
|
155
|
+
- ssh key path normalization (`absolute` or relative to `process.cwd()`)
|
|
156
|
+
- `fs.access` validation
|
|
157
|
+
- consistent JSON output builders
|
|
158
|
+
|
|
159
|
+
**Step 3: Run type check**
|
|
160
|
+
|
|
161
|
+
Run: `npm run ts:check`
|
|
162
|
+
Expected: PASS.
|
|
163
|
+
|
|
164
|
+
**Step 4: Commit**
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
git add src/commands/ops/ops.types.ts src/commands/ops/ops.utils.ts
|
|
168
|
+
git commit -m "feat(ops): add validation and json response helpers"
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Task 5: Implement `ops sysinfo`
|
|
172
|
+
|
|
173
|
+
**Files:**
|
|
174
|
+
- Create: `src/commands/ops/subcommands/ops-sysinfo.command.ts`
|
|
175
|
+
- Modify: `src/commands/ops/ops.module.ts`
|
|
176
|
+
|
|
177
|
+
**Step 1: Write failing command run target**
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
kodu ops sysinfo dev
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Expected shape:
|
|
184
|
+
|
|
185
|
+
```json
|
|
186
|
+
{"status":"ok","data":{"uptime":"...","disk_usage":"...","mem_free":"..."}}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
**Step 2: Run build or command to confirm missing subcommand**
|
|
190
|
+
|
|
191
|
+
Run: `npm run build`
|
|
192
|
+
Expected: missing subcommand before implementation.
|
|
193
|
+
|
|
194
|
+
**Step 3: Implement command**
|
|
195
|
+
|
|
196
|
+
Use agreed SSH payload and `SshService`. Map non-zero SSH result to JSON error without crash.
|
|
197
|
+
|
|
198
|
+
**Step 4: Verify**
|
|
199
|
+
|
|
200
|
+
Run: `npm run build && npm run ts:check`
|
|
201
|
+
Expected: PASS.
|
|
202
|
+
|
|
203
|
+
**Step 5: Commit**
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
git add src/commands/ops/subcommands/ops-sysinfo.command.ts src/commands/ops/ops.module.ts
|
|
207
|
+
git commit -m "feat(ops): add sysinfo subcommand"
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Task 6: Implement `ops env`
|
|
211
|
+
|
|
212
|
+
**Files:**
|
|
213
|
+
- Create: `src/commands/ops/subcommands/ops-env.command.ts`
|
|
214
|
+
- Modify: `src/commands/ops/ops.module.ts`
|
|
215
|
+
|
|
216
|
+
**Step 1: Write failing run targets**
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
kodu ops env dev get my-app
|
|
220
|
+
kodu ops env dev set my-app --key PORT --val 3001
|
|
221
|
+
kodu ops env dev unset my-app --key PORT
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
**Step 2: Implement action validation and command builders**
|
|
225
|
+
|
|
226
|
+
Implement `get|set|unset`, enforce required flags, derive `.env` path from `apps` root.
|
|
227
|
+
|
|
228
|
+
**Step 3: Implement JSON-only outputs**
|
|
229
|
+
|
|
230
|
+
- success: `status: ok`
|
|
231
|
+
- remote failure: `status: error` + code/stderr/command
|
|
232
|
+
- local validation failure: JSON error in stderr
|
|
233
|
+
|
|
234
|
+
**Step 4: Verify**
|
|
235
|
+
|
|
236
|
+
Run: `npm run build && npm run ts:check`
|
|
237
|
+
Expected: PASS.
|
|
238
|
+
|
|
239
|
+
**Step 5: Commit**
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
git add src/commands/ops/subcommands/ops-env.command.ts src/commands/ops/ops.module.ts
|
|
243
|
+
git commit -m "feat(ops): add env management subcommand"
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Task 7: Implement `ops routes` (list/add/remove/update)
|
|
247
|
+
|
|
248
|
+
**Files:**
|
|
249
|
+
- Create: `src/commands/ops/subcommands/ops-routes.command.ts`
|
|
250
|
+
- Modify: `src/commands/ops/ops.module.ts`
|
|
251
|
+
|
|
252
|
+
**Step 1: Write failing run targets**
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
kodu ops routes dev list
|
|
256
|
+
kodu ops routes dev add --domain api.example.com --upstream 127.0.0.1:3000
|
|
257
|
+
kodu ops routes dev update --domain api.example.com --upstream 127.0.0.1:4000
|
|
258
|
+
kodu ops routes dev remove --domain api.example.com
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
**Step 2: Implement path resolution and list**
|
|
262
|
+
|
|
263
|
+
- caddy root: `server.paths.caddy ?? path.posix.join(appsPath, 'caddy')`
|
|
264
|
+
- list: read `data/Caddyfile` and return raw text
|
|
265
|
+
|
|
266
|
+
**Step 3: Implement add/remove/update edits**
|
|
267
|
+
|
|
268
|
+
Execute remote `node -e` transformation scripts over SSH for deterministic block edits.
|
|
269
|
+
|
|
270
|
+
**Step 4: Apply config via caddy script**
|
|
271
|
+
|
|
272
|
+
After successful edit, run: `cd <caddyRoot> && ./caddy.sh`
|
|
273
|
+
|
|
274
|
+
**Step 5: Add NOT_FOUND behavior**
|
|
275
|
+
|
|
276
|
+
If domain absent for `remove`/`update`, return structured JSON error with code `NOT_FOUND`.
|
|
277
|
+
|
|
278
|
+
**Step 6: Verify**
|
|
279
|
+
|
|
280
|
+
Run: `npm run build && npm run ts:check`
|
|
281
|
+
Expected: PASS.
|
|
282
|
+
|
|
283
|
+
**Step 7: Commit**
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
git add src/commands/ops/subcommands/ops-routes.command.ts src/commands/ops/ops.module.ts
|
|
287
|
+
git commit -m "feat(ops): add caddy routes management subcommand"
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### Task 8: Implement `ops service`
|
|
291
|
+
|
|
292
|
+
**Files:**
|
|
293
|
+
- Create: `src/commands/ops/subcommands/ops-service.command.ts`
|
|
294
|
+
- Modify: `src/commands/ops/ops.module.ts`
|
|
295
|
+
|
|
296
|
+
**Step 1: Write failing run targets**
|
|
297
|
+
|
|
298
|
+
```bash
|
|
299
|
+
kodu ops service dev status my-app
|
|
300
|
+
kodu ops service dev up my-app
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
**Step 2: Implement actions**
|
|
304
|
+
|
|
305
|
+
Support `clone|pull|up|down|logs|status` with remote compose/git commands.
|
|
306
|
+
|
|
307
|
+
**Step 3: Add status JSON fallback**
|
|
308
|
+
|
|
309
|
+
Attempt `docker compose ps --format json`; fallback to raw `docker compose ps` output if unsupported.
|
|
310
|
+
|
|
311
|
+
**Step 4: Verify**
|
|
312
|
+
|
|
313
|
+
Run: `npm run build && npm run ts:check`
|
|
314
|
+
Expected: PASS.
|
|
315
|
+
|
|
316
|
+
**Step 5: Commit**
|
|
317
|
+
|
|
318
|
+
```bash
|
|
319
|
+
git add src/commands/ops/subcommands/ops-service.command.ts src/commands/ops/ops.module.ts
|
|
320
|
+
git commit -m "feat(ops): add service lifecycle subcommand"
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### Task 9: Full Validation And Manual Smoke Checks
|
|
324
|
+
|
|
325
|
+
**Files:**
|
|
326
|
+
- Modify: `kodu.json` (local test aliases only if needed)
|
|
327
|
+
|
|
328
|
+
**Step 1: Run static checks**
|
|
329
|
+
|
|
330
|
+
Run: `npm run check`
|
|
331
|
+
Expected: PASS.
|
|
332
|
+
|
|
333
|
+
**Step 2: Build final artifact**
|
|
334
|
+
|
|
335
|
+
Run: `npm run build`
|
|
336
|
+
Expected: PASS.
|
|
337
|
+
|
|
338
|
+
**Step 3: Manual command checks against test server**
|
|
339
|
+
|
|
340
|
+
Run and confirm valid JSON output each time:
|
|
341
|
+
|
|
342
|
+
```bash
|
|
343
|
+
kodu ops sysinfo <alias>
|
|
344
|
+
kodu ops env <alias> get <project>
|
|
345
|
+
kodu ops routes <alias> list
|
|
346
|
+
kodu ops service <alias> status <project>
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
**Step 4: Record results in PR/body notes**
|
|
350
|
+
|
|
351
|
+
Capture one success sample and one remote error sample to prove agent-readable behavior.
|
|
352
|
+
|
|
353
|
+
**Step 5: Commit (if test fixture/config changed)**
|
|
354
|
+
|
|
355
|
+
```bash
|
|
356
|
+
git add <changed-files>
|
|
357
|
+
git commit -m "test(ops): validate json outputs and command flows"
|
|
358
|
+
```
|
package/kodu.json
CHANGED
|
@@ -36,6 +36,21 @@
|
|
|
36
36
|
"useGitignore": true,
|
|
37
37
|
"contentBasedBinaryDetection": false
|
|
38
38
|
},
|
|
39
|
+
"ops": {
|
|
40
|
+
"servers": {
|
|
41
|
+
"dev": {
|
|
42
|
+
"host": "example.com",
|
|
43
|
+
"port": 22,
|
|
44
|
+
"user": "ubuntu",
|
|
45
|
+
"sshKeyPath": "~/.ssh/id_rsa",
|
|
46
|
+
"description": "Example AgentOps server",
|
|
47
|
+
"paths": {
|
|
48
|
+
"apps": "/var/agent-apps",
|
|
49
|
+
"caddy": "/var/agent-apps/caddy"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
},
|
|
39
54
|
"prompts": {
|
|
40
55
|
"review": {
|
|
41
56
|
"bug": ".kodu/prompts/review-bug.md",
|
package/kodu.schema.json
CHANGED
|
@@ -173,6 +173,65 @@
|
|
|
173
173
|
}
|
|
174
174
|
},
|
|
175
175
|
"additionalProperties": false
|
|
176
|
+
},
|
|
177
|
+
"ops": {
|
|
178
|
+
"type": "object",
|
|
179
|
+
"properties": {
|
|
180
|
+
"servers": {
|
|
181
|
+
"type": "object",
|
|
182
|
+
"propertyNames": {
|
|
183
|
+
"type": "string"
|
|
184
|
+
},
|
|
185
|
+
"additionalProperties": {
|
|
186
|
+
"type": "object",
|
|
187
|
+
"properties": {
|
|
188
|
+
"host": {
|
|
189
|
+
"type": "string"
|
|
190
|
+
},
|
|
191
|
+
"port": {
|
|
192
|
+
"default": 22,
|
|
193
|
+
"type": "number"
|
|
194
|
+
},
|
|
195
|
+
"user": {
|
|
196
|
+
"type": "string"
|
|
197
|
+
},
|
|
198
|
+
"sshKeyPath": {
|
|
199
|
+
"type": "string"
|
|
200
|
+
},
|
|
201
|
+
"description": {
|
|
202
|
+
"type": "string"
|
|
203
|
+
},
|
|
204
|
+
"paths": {
|
|
205
|
+
"type": "object",
|
|
206
|
+
"properties": {
|
|
207
|
+
"apps": {
|
|
208
|
+
"default": "/var/agent-apps",
|
|
209
|
+
"type": "string"
|
|
210
|
+
},
|
|
211
|
+
"caddy": {
|
|
212
|
+
"type": "string"
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
"required": ["apps"],
|
|
216
|
+
"additionalProperties": false
|
|
217
|
+
},
|
|
218
|
+
"env": {
|
|
219
|
+
"type": "object",
|
|
220
|
+
"propertyNames": {
|
|
221
|
+
"type": "string"
|
|
222
|
+
},
|
|
223
|
+
"additionalProperties": {
|
|
224
|
+
"type": "string"
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
"required": ["host", "port", "user", "sshKeyPath"],
|
|
229
|
+
"additionalProperties": false
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
"required": ["servers"],
|
|
234
|
+
"additionalProperties": false
|
|
176
235
|
}
|
|
177
236
|
},
|
|
178
237
|
"required": ["cleaner", "packer"],
|
package/package.json
CHANGED
package/src/app.module.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { Module } from '@nestjs/common';
|
|
|
2
2
|
import { CleanModule } from './commands/clean/clean.module';
|
|
3
3
|
import { CommitModule } from './commands/commit/commit.module';
|
|
4
4
|
import { InitModule } from './commands/init/init.module';
|
|
5
|
+
import { OpsModule } from './commands/ops/ops.module';
|
|
5
6
|
import { PackModule } from './commands/pack/pack.module';
|
|
6
7
|
import { ReviewModule } from './commands/review/review.module';
|
|
7
8
|
import { ConfigModule } from './core/config/config.module';
|
|
@@ -24,6 +25,7 @@ import { TokenizerModule } from './shared/tokenizer/tokenizer.module';
|
|
|
24
25
|
CleanModule,
|
|
25
26
|
ReviewModule,
|
|
26
27
|
CommitModule,
|
|
28
|
+
OpsModule,
|
|
27
29
|
],
|
|
28
30
|
})
|
|
29
31
|
export class AppModule {}
|
|
@@ -37,6 +37,21 @@ export class InitCommand extends CommandRunner {
|
|
|
37
37
|
$schema:
|
|
38
38
|
'https://raw.githubusercontent.com/uxname/kodu/refs/heads/master/kodu.schema.json',
|
|
39
39
|
llm: defaultLlmConfig,
|
|
40
|
+
ops: {
|
|
41
|
+
servers: {
|
|
42
|
+
dev: {
|
|
43
|
+
host: 'example.com',
|
|
44
|
+
port: 22,
|
|
45
|
+
user: 'ubuntu',
|
|
46
|
+
sshKeyPath: '~/.ssh/id_rsa',
|
|
47
|
+
description: 'Example AgentOps server',
|
|
48
|
+
paths: {
|
|
49
|
+
apps: '/var/agent-apps',
|
|
50
|
+
caddy: '/var/agent-apps/caddy',
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
},
|
|
40
55
|
cleaner: {
|
|
41
56
|
whitelist: ['//!'],
|
|
42
57
|
keepJSDoc: true,
|
|
@@ -134,6 +149,7 @@ export class InitCommand extends CommandRunner {
|
|
|
134
149
|
contentBasedBinaryDetection:
|
|
135
150
|
defaultConfig.packer.contentBasedBinaryDetection,
|
|
136
151
|
},
|
|
152
|
+
ops: defaultConfig.ops,
|
|
137
153
|
prompts: {
|
|
138
154
|
review: {
|
|
139
155
|
bug: promptPaths.review.bug,
|