dodo-mcp 1.2.0 → 1.3.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/README.md +45 -6
- package/dist/cli/dataRecovery.js +38 -0
- package/dist/cli/dataRecovery.js.map +1 -0
- package/dist/cli/deployment.js +61 -0
- package/dist/cli/deployment.js.map +1 -0
- package/dist/cli/main.js +35 -1
- package/dist/cli/main.js.map +1 -1
- package/dist/config/globalConfig.js +8 -0
- package/dist/config/globalConfig.js.map +1 -1
- package/dist/errors.js +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/ipc/server.js +2 -0
- package/dist/ipc/server.js.map +1 -1
- package/dist/ipc/timeouts.js +5 -0
- package/dist/ipc/timeouts.js.map +1 -0
- package/dist/platform/privateFs.js +32 -3
- package/dist/platform/privateFs.js.map +1 -1
- package/dist/platform/windowsPrivateAcl.js +90 -0
- package/dist/platform/windowsPrivateAcl.js.map +1 -0
- package/dist/security/outboundAddress.js +20 -0
- package/dist/security/outboundAddress.js.map +1 -0
- package/dist/server/aiAdmin.js +35 -10
- package/dist/server/aiAdmin.js.map +1 -1
- package/dist/server/appServer.js +11 -3
- package/dist/server/appServer.js.map +1 -1
- package/dist/server/bootstrap.js +14 -0
- package/dist/server/bootstrap.js.map +1 -1
- package/dist/server/configUi/index.html +3 -0
- package/dist/server/configUi/ui/dataRecovery.js +90 -0
- package/dist/server/configUi/ui/deployment.js +111 -0
- package/dist/server/configUi/ui/recovery.js +167 -0
- package/dist/server/configUi/workbench.css +28 -1
- package/dist/server/configUi/workbench.js +136 -25
- package/dist/server/installationRuntime.js +3 -1
- package/dist/server/installationRuntime.js.map +1 -1
- package/dist/server/instructions.js +6 -2
- package/dist/server/instructions.js.map +1 -1
- package/dist/server/ipcDispatch.js +219 -0
- package/dist/server/ipcDispatch.js.map +1 -1
- package/dist/server/localConfig.js +1 -1
- package/dist/server/localConfig.js.map +1 -1
- package/dist/server/stdioServer.js +11 -3
- package/dist/server/stdioServer.js.map +1 -1
- package/dist/services/ai/network.js +1 -18
- package/dist/services/ai/network.js.map +1 -1
- package/dist/services/ai/subagents.js +5 -2
- package/dist/services/ai/subagents.js.map +1 -1
- package/dist/services/assistance/verification.js +43 -10
- package/dist/services/assistance/verification.js.map +1 -1
- package/dist/services/changes/applier.js +472 -297
- package/dist/services/changes/applier.js.map +1 -1
- package/dist/services/git/gitService.js +150 -34
- package/dist/services/git/gitService.js.map +1 -1
- package/dist/services/jobs/jobManager.js +59 -2
- package/dist/services/jobs/jobManager.js.map +1 -1
- package/dist/services/jobs/spool.js +9 -3
- package/dist/services/jobs/spool.js.map +1 -1
- package/dist/services/multimodal/mediaService.js +6 -6
- package/dist/services/multimodal/mediaService.js.map +1 -1
- package/dist/services/recovery/configKeys.js +115 -0
- package/dist/services/recovery/configKeys.js.map +1 -0
- package/dist/services/recovery/configVault.js +361 -0
- package/dist/services/recovery/configVault.js.map +1 -0
- package/dist/services/recovery/contracts.js +24 -0
- package/dist/services/recovery/contracts.js.map +1 -0
- package/dist/services/recovery/databaseAwareness.js +179 -0
- package/dist/services/recovery/databaseAwareness.js.map +1 -0
- package/dist/services/recovery/deploymentArchive.js +129 -0
- package/dist/services/recovery/deploymentArchive.js.map +1 -0
- package/dist/services/recovery/deploymentContracts.js +70 -0
- package/dist/services/recovery/deploymentContracts.js.map +1 -0
- package/dist/services/recovery/deploymentHealth.js +143 -0
- package/dist/services/recovery/deploymentHealth.js.map +1 -0
- package/dist/services/recovery/deploymentMaintenance.js +197 -0
- package/dist/services/recovery/deploymentMaintenance.js.map +1 -0
- package/dist/services/recovery/deploymentService.js +429 -0
- package/dist/services/recovery/deploymentService.js.map +1 -0
- package/dist/services/recovery/deploymentSource.js +36 -0
- package/dist/services/recovery/deploymentSource.js.map +1 -0
- package/dist/services/recovery/dockerDeployment.js +172 -0
- package/dist/services/recovery/dockerDeployment.js.map +1 -0
- package/dist/services/recovery/drift.js +178 -0
- package/dist/services/recovery/drift.js.map +1 -0
- package/dist/services/recovery/evidence.js +127 -0
- package/dist/services/recovery/evidence.js.map +1 -0
- package/dist/services/recovery/gitCopies.js +219 -0
- package/dist/services/recovery/gitCopies.js.map +1 -0
- package/dist/services/recovery/history.js +423 -0
- package/dist/services/recovery/history.js.map +1 -0
- package/dist/services/recovery/recoveryService.js +521 -0
- package/dist/services/recovery/recoveryService.js.map +1 -0
- package/dist/services/recovery/storage.js +344 -0
- package/dist/services/recovery/storage.js.map +1 -0
- package/dist/services/schedules/scheduleService.js +9 -10
- package/dist/services/schedules/scheduleService.js.map +1 -1
- package/dist/store/db.js +71 -0
- package/dist/store/db.js.map +1 -1
- package/dist/tools/catalog.js +4 -0
- package/dist/tools/catalog.js.map +1 -1
- package/dist/tools/changeTools.js +1 -1
- package/dist/tools/changeTools.js.map +1 -1
- package/dist/tools/context.js +23 -3
- package/dist/tools/context.js.map +1 -1
- package/dist/tools/deploymentTools.js +58 -0
- package/dist/tools/deploymentTools.js.map +1 -0
- package/dist/tools/directTools.js +9 -5
- package/dist/tools/directTools.js.map +1 -1
- package/dist/tools/gitTools.js +7 -2
- package/dist/tools/gitTools.js.map +1 -1
- package/dist/tools/jobTools.js +4 -4
- package/dist/tools/jobTools.js.map +1 -1
- package/dist/tools/projectTools.js +1 -0
- package/dist/tools/projectTools.js.map +1 -1
- package/dist/tools/recoveryTools.js +79 -0
- package/dist/tools/recoveryTools.js.map +1 -0
- package/dist/tools/runtimeTools.js +1 -1
- package/dist/tools/runtimeTools.js.map +1 -1
- package/dist/tools/surface.js +101 -41
- package/dist/tools/surface.js.map +1 -1
- package/dist/tunnel/credentials.js +1 -1
- package/dist/tunnel/credentials.js.map +1 -1
- package/docs/AI_PROVIDERS.md +6 -3
- package/docs/ARCHITECTURE.md +88 -4
- package/docs/CI.md +118 -0
- package/docs/COMPATIBILITY.md +7 -6
- package/docs/EVALUATION.md +17 -12
- package/docs/MANUAL_ACCEPTANCE.md +59 -2
- package/docs/RECOVERY.md +448 -0
- package/docs/RELEASE_1.2.1.md +47 -0
- package/docs/RELEASE_1.3.0.md +64 -0
- package/docs/RELEASE_NOTES.md +41 -0
- package/docs/SECURITY.md +122 -0
- package/docs/TEST_REPORT.md +327 -5
- package/docs/WEB_CLIENTS.md +6 -1
- package/docs/adr/051-source-recovery-foundation.md +45 -0
- package/docs/adr/052-reviewed-source-restore.md +50 -0
- package/docs/adr/053-content-drift-and-private-git-recovery.md +29 -0
- package/docs/adr/054-verified-source-checkpoints.md +58 -0
- package/docs/adr/055-reviewed-docker-deployments.md +42 -0
- package/docs/adr/056-owner-data-recovery.md +47 -0
- package/docs/adr/README.md +7 -0
- package/native/private-state-windows.cs +105 -0
- package/package.json +7 -1
- package/schemas/global-config.schema.json +44 -0
- package/schemas/tools.compact.json +175 -15
- package/schemas/tools.hybrid.json +350 -16
- package/schemas/tools.json +4883 -816
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
- MCP ผ่าน HTTP ที่ `127.0.0.1:21730/mcp` พร้อม OAuth และ PKCE
|
|
8
8
|
- Local Config แบบ loopback ที่ `127.0.0.1:21731` และ Remote Config ชั่วคราวผ่าน tunnel เมื่อเจ้าของสั่ง `dodo --web`
|
|
9
9
|
- HTTP ใช้ Compact Tool Surface 20 tools เพื่อลดภาระการโหลด schema
|
|
10
|
-
- STDIO ใช้ Full Tool Surface; มี capability definitions ทั้งหมด
|
|
10
|
+
- STDIO ใช้ Full Tool Surface; มี capability definitions ทั้งหมด 158 รายการ แต่ค่าเริ่มต้นซ่อน Sub-agent 4 operations จาก MCP จึงเห็น 154 tools
|
|
11
11
|
- Hybrid Surface 49 tools สำหรับ client ที่รับ catalog ขนาดกลาง
|
|
12
12
|
- อ่าน ค้นหา สร้าง แก้ ย้าย ลบไฟล์ พร้อม expected hash, journal และ rollback
|
|
13
13
|
- รันคำสั่ง งานแบบขนาน jobs, Git, TypeScript/JavaScript intelligence, LSP และ task assistance
|
|
@@ -162,6 +162,11 @@ Chat & Tasks · Runs & Jobs · Approvals · Knowledge · Settings) หน้า
|
|
|
162
162
|
ไม่มี CDN, CSP `'self'` เท่าเดิม) การลบ/ยกเลิก/เปลี่ยนโหมดต้องยืนยันก่อนเสมอ
|
|
163
163
|
และงานที่มีค่าใช้จ่ายหรือผลไม่แน่นอนจะบอกชัดว่า "ไม่ retry อัตโนมัติ"
|
|
164
164
|
|
|
165
|
+
การสร้าง Agent ใช้หน้า **Providers & Profiles**: เลือกประเภท Coding/Review/Research,
|
|
166
|
+
Provider connection และ Model ID แล้วกด **สร้าง Agent** รายชื่อโมเดลที่โหลดจาก
|
|
167
|
+
Provider จะเข้าแบบฟอร์มให้อัตโนมัติ ส่วน permission, tool calling, token limits และราคา
|
|
168
|
+
อยู่ในหัวข้อ **ตั้งค่าขั้นสูง** หาก endpoint ไม่รองรับการลิสต์โมเดลยังกรอก Model ID เองได้
|
|
169
|
+
|
|
165
170
|
หน้า **Settings → Sub-agent tools ใน MCP** มีสวิตช์เปิด/ปิดการ expose
|
|
166
171
|
`subagent_spawn`, `subagent_status`, `subagent_result` และ `subagent_control` ให้ AI
|
|
167
172
|
ภายนอก ค่าเริ่มต้นปิดเพื่อให้ catalog สำหรับงานทั่วไปกระชับขึ้น แต่หน้า Chat & Tasks
|
|
@@ -169,6 +174,13 @@ Chat & Tasks · Runs & Jobs · Approvals · Knowledge · Settings) หน้า
|
|
|
169
174
|
สร้าง MCP app ใหม่ตามพฤติกรรมของ client การตั้งค่านี้เปลี่ยนเฉพาะการมองเห็น tools
|
|
170
175
|
และไม่เพิ่ม OAuth scope, trust, approval หรือสิทธิ์ของ profile
|
|
171
176
|
|
|
177
|
+
หน้า **Settings → Tools ที่ AI มองเห็น** เลือกเปิด/ปิดแต่ละ operation ได้ พร้อมค้นหา
|
|
178
|
+
และเปิด/ปิดทั้งหมวด ค่าใช้กับ Compact/Hybrid: operation ที่ปิดจะหายจาก
|
|
179
|
+
`dodo_discover`, gateway enum และ direct duplicate ใน Hybrid หากปิดครบทั้งหมวด gateway
|
|
180
|
+
นั้นจะไม่ถูกส่งให้ client จึงลดทั้งจำนวน schema และ context ที่ AI ต้องอ่าน Full/STDIO
|
|
181
|
+
ยังคง definitions รายตัวครบตามเดิม เมื่อบันทึกต้อง restart DODO แล้ว rescan หรือสร้าง
|
|
182
|
+
MCP app ใหม่ การตั้งค่านี้เป็น visibility เท่านั้นและไม่ grant สิทธิ์ใด ๆ
|
|
183
|
+
|
|
172
184
|
### เพิ่มโปรเจกต์ครั้งเดียว แล้วสั่งงานด้วยชื่อ
|
|
173
185
|
|
|
174
186
|
เพิ่มโปรเจกต์จากหน้า Projects (หรือ CLI) โดยกรอกแค่ **ชื่อ**, **absolute path** และ
|
|
@@ -470,6 +482,10 @@ dodo_read(operation="read_files", args={...})
|
|
|
470
482
|
|
|
471
483
|
`dodo_discover` คืน schema ของ operation ที่เลือกและ hash ที่ deterministic ส่วน gateway จะส่ง request ผ่าน policy pipeline เดียวกับ tool รายตัว จึงยังตรวจ scope, ACL, workspace ID/epoch, trust, approval, path guard, secret guard, hash และ audit ครบทุกชั้น
|
|
472
484
|
|
|
485
|
+
เจ้าของลดรายการที่ AI เห็นได้จาก **Local Config → Settings → Tools ที่ AI มองเห็น**
|
|
486
|
+
โดยไม่แตะ permission เมื่อปิด operation แล้ว schema ของ Compact/Hybrid จะตัดรายการนั้น
|
|
487
|
+
ออกจริงหลัง restart/rescan ส่วน Full surface ไม่เปลี่ยน
|
|
488
|
+
|
|
473
489
|
ชื่อ gateway `dodo_*` เป็นชื่อ MCP protocol ที่คงไว้เพื่อ compatibility กับ client ที่เชื่อมต่ออยู่ การเปลี่ยนชื่อ gateway ต้องทำเป็น protocol migration แยกต่างหาก
|
|
474
490
|
|
|
475
491
|
## การเปลี่ยน workspace
|
|
@@ -551,19 +567,23 @@ dodo desktop allow --app com.google.Chrome --mode control --persist --yes
|
|
|
551
567
|
|
|
552
568
|
## Development
|
|
553
569
|
|
|
554
|
-
Platform CI ใช้ dedicated self-hosted runners ที่เจ้าของควบคุม: Linux X64
|
|
555
|
-
|
|
570
|
+
Platform CI ใช้ dedicated self-hosted runners ที่เจ้าของควบคุม: Linux X64 และ
|
|
571
|
+
Windows X64 รัน native candidate gate บนเครื่อง runner โดยตรง ไม่ใช้ Docker ส่วน
|
|
556
572
|
macOS ยังรัน release gate บนเครื่องพัฒนา Workflow รับเฉพาะ push ที่ `main` และ manual
|
|
557
573
|
dispatch ไม่รัน pull request จากภายนอกบน self-hosted runner
|
|
558
574
|
|
|
559
|
-
รัน regression benchmark
|
|
575
|
+
รัน regression benchmark และ macOS candidate gate ได้ด้วย:
|
|
560
576
|
|
|
561
577
|
```bash
|
|
562
578
|
npm run bench
|
|
563
579
|
npm run release:gate
|
|
564
|
-
npm run test:linux:docker
|
|
565
580
|
```
|
|
566
581
|
|
|
582
|
+
สั่ง Linux/Windows CI ผ่าน GitHub Actions → `platform-gates` → Run workflow →
|
|
583
|
+
เลือก `all` หรือใช้ `gh workflow run platform-gates.yml --ref main -f platform=all`
|
|
584
|
+
Linux รันเมื่อสั่ง workflow; Windows รันทั้งเมื่อ push ไป main และเมื่อสั่ง workflow
|
|
585
|
+
อ่าน [การเตรียม runner และวิธีแก้ CI](docs/CI.md) ก่อนรันครั้งแรก
|
|
586
|
+
|
|
567
587
|
GitHub workflow รัน Node 22 และ 24 ทั้ง Linux/Windows โดยไม่ publish npm Windows จะ
|
|
568
588
|
ยังไม่ถูกประกาศเป็น supported platform จนกว่า automated native gate และ manual
|
|
569
589
|
Windows 11 acceptance จะผ่านตามเอกสาร
|
|
@@ -572,8 +592,9 @@ Release gate สร้างหลักฐาน non-secret ใน ignored `rel
|
|
|
572
592
|
exact-tarball ผ่าน STDIO Full กับ HTTP/OAuth Compact โดยไม่ publish npm ดูรายละเอียดที่
|
|
573
593
|
[DodoBench และ Release Gate](docs/EVALUATION.md)
|
|
574
594
|
|
|
575
|
-
เมื่อทั้งสอง report มาจาก Git revision และ `package-lock.json` เดียวกัน ให้รวมหลักฐาน
|
|
595
|
+
เมื่อทั้งสอง report มาจาก clean Git revision, source fingerprint และ `package-lock.json` เดียวกัน ให้รวมหลักฐาน
|
|
576
596
|
บน macOS ด้วย `node scripts/release-gate.mjs --release --platform-evidence /absolute/path/to/linux/gate-report.json`
|
|
597
|
+
Linux report ต้องมาจาก native GitHub Actions; หลักฐาน Docker เก่าไม่แทน CI รอบใหม่
|
|
577
598
|
คำสั่งนี้ไม่ publish package
|
|
578
599
|
|
|
579
600
|
```bash
|
|
@@ -586,3 +607,21 @@ npm pack
|
|
|
586
607
|
```
|
|
587
608
|
|
|
588
609
|
โปรเจกต์นี้ใช้ GitHub repository [arthittakun/dodo-mcp](https://github.com/arthittakun/dodo-mcp) และ package `dodo-mcp`
|
|
610
|
+
|
|
611
|
+
### Recovery — สำรองและกู้คืนงาน
|
|
612
|
+
|
|
613
|
+
โปรเจกต์ที่ลงทะเบียนเปิด source backup เป็นค่าเริ่มต้น ก่อนแก้ไฟล์หรือรันคำสั่ง
|
|
614
|
+
หน้า **Projects → Recovery** ใช้ตรวจประวัติ เปรียบเทียบ และยืนยันแผนกู้คืนได้จริง
|
|
615
|
+
หรือใช้ `dodo recovery --help` ผ่าน terminal สำเนาอยู่ใน private state แยกจาก Git
|
|
616
|
+
ของโปรเจกต์ และไม่ push ขึ้น GitHub อัตโนมัติ
|
|
617
|
+
|
|
618
|
+
- กู้คืน checkpoint หรือ session ที่เลือก พร้อม expected hashes, สำเนาก่อนกู้ และ receipt หลัง reconnect
|
|
619
|
+
- ตรวจไฟล์ที่ถูกแก้จากภายนอก และแยก `SAVED`, `VERIFIED`, `FAILED`, `INCONCLUSIVE`, `STALE` ให้เห็นตามหลักฐาน
|
|
620
|
+
- ตั้งชื่อ/pin จุดกู้คืน และตรวจ retention ก่อนล้าง โดยรักษางานที่ไม่เกี่ยวข้อง
|
|
621
|
+
- เพิ่ม Docker deployment target ได้โดยเจ้าของ: source ที่ตรวจแล้ว → image → health/stabilization → known-good พร้อม reviewed rollback
|
|
622
|
+
- ตรวจ SQLite migration compatibility แบบอ่านอย่างเดียว และสำรอง private config แบบเข้ารหัสเมื่อเจ้าของเปิดแยก
|
|
623
|
+
|
|
624
|
+
**Source restore ไม่ย้อนฐานข้อมูล, secrets, volumes หรือระบบภายนอก** การสำรอง config
|
|
625
|
+
ใช้กุญแจจาก OS credential store และไม่ส่งเนื้อหาให้ AI; ไม่มี generic database rollback
|
|
626
|
+
หรือการ restart service อัตโนมัติ ดู [คู่มือ Recovery](docs/RECOVERY.md),
|
|
627
|
+
[การเปลี่ยนแปลง 1.3.0](docs/RELEASE_1.3.0.md) และ [ผลตรวจรับจริง](docs/TEST_REPORT.md)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { DodoError } from '../errors.js';
|
|
3
|
+
export function registerDataRecoveryCommands(recovery, ipc) {
|
|
4
|
+
const database = recovery.command('database').description('owner-only read-only migration compatibility; never database rollback');
|
|
5
|
+
const print = (value) => console.log(JSON.stringify(value, null, 2));
|
|
6
|
+
database.command('targets').action(async () => print(await ipc('recovery.database.list')));
|
|
7
|
+
const review = (cmd) => cmd.requiredOption('--workspace <id>', 'current workspace ID').requiredOption('--epoch <epoch>', 'current epoch').option('--yes', 'confirm the exact owner operation', false);
|
|
8
|
+
const envelope = (opts, input) => {
|
|
9
|
+
if (!opts.yes)
|
|
10
|
+
throw new DodoError('INVALID_INPUT', 'review the owner operation and add --yes');
|
|
11
|
+
return { workspaceId: opts.workspace, workspaceEpoch: opts.epoch, confirm: true, input };
|
|
12
|
+
};
|
|
13
|
+
review(database.command('inspect <targetId>')).action(async (targetId, opts) => print(await ipc('recovery.database.inspect', envelope(opts, { targetId }))));
|
|
14
|
+
const jsonOperation = (group, operation, method) => review(group.command(operation).requiredOption('--file <path>', 'owner-reviewed bounded JSON; never secret values'))
|
|
15
|
+
.action(async (opts) => {
|
|
16
|
+
const st = fs.lstatSync(opts.file);
|
|
17
|
+
if (!st.isFile() || st.isSymbolicLink() || st.nlink !== 1 || st.size > 128 * 1024)
|
|
18
|
+
throw new DodoError('INVALID_INPUT', 'expected a bounded regular JSON file');
|
|
19
|
+
let input;
|
|
20
|
+
try {
|
|
21
|
+
input = JSON.parse(fs.readFileSync(opts.file, 'utf8'));
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
throw new DodoError('INVALID_INPUT', 'invalid owner JSON');
|
|
25
|
+
}
|
|
26
|
+
print(await ipc(method, envelope(opts, input)));
|
|
27
|
+
});
|
|
28
|
+
for (const op of ['configure', 'bind', 'unbind'])
|
|
29
|
+
jsonOperation(database, op, 'recovery.database.' + op);
|
|
30
|
+
const vault = recovery.command('private-config').description('owner-only encrypted secret-file recovery; explicit opt-in and OS key store required');
|
|
31
|
+
vault.command('list').action(async () => print(await ipc('recovery.config.list')));
|
|
32
|
+
jsonOperation(vault, 'configure', 'recovery.config.configure');
|
|
33
|
+
review(vault.command('backup <targetId>')).action(async (targetId, opts) => print(await ipc('recovery.config.backup', envelope(opts, { targetId }))));
|
|
34
|
+
review(vault.command('preview <backupId>')).action(async (backupId, opts) => print(await ipc('recovery.config.preview', envelope(opts, { backupId }))));
|
|
35
|
+
review(vault.command('apply <planId>').requiredOption('--hash <hash>', 'exact reviewed plan hash')).action(async (planId, opts) => print(await ipc('recovery.config.apply', envelope(opts, { planId, planHash: opts.hash }))));
|
|
36
|
+
review(vault.command('rotate <targetId>').requiredOption('--revision <n>', 'current target revision', Number)).action(async (targetId, opts) => print(await ipc('recovery.config.rotate', envelope(opts, { targetId, expectedRevision: opts.revision }))));
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=dataRecovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dataRecovery.js","sourceRoot":"","sources":["../../src/cli/dataRecovery.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { DodoError } from '../errors.js';
|
|
3
|
+
/** Private owner IPC only; reviewed files are bounded JSON, never executable repository configuration. */
|
|
4
|
+
export function registerDeploymentCommands(program, ipc) {
|
|
5
|
+
const group = program.command('deployment').description('review registered Docker deployment targets and durable build/deploy outcomes');
|
|
6
|
+
const print = (value) => console.log(JSON.stringify(value, null, 2));
|
|
7
|
+
for (const op of ['targets', 'list'])
|
|
8
|
+
group.command(op).action(async () => print(await ipc(`deployment.${op}`)));
|
|
9
|
+
for (const op of ['inspect', 'compare'])
|
|
10
|
+
group.command(`${op} <deploymentId>`).action(async (deploymentId) => print(await ipc(`deployment.${op}`, { deploymentId })));
|
|
11
|
+
const reviewed = (cmd) => cmd.requiredOption('--workspace <id>', 'reviewed workspace ID')
|
|
12
|
+
.requiredOption('--epoch <epoch>', 'reviewed workspace epoch').option('--yes', 'confirm the exact reviewed operation', false);
|
|
13
|
+
const envelope = (opts, input) => {
|
|
14
|
+
if (!opts.yes)
|
|
15
|
+
throw new DodoError('INVALID_INPUT', 'review target/source and add --yes; Docker commands may affect resources outside the workspace');
|
|
16
|
+
return { workspaceId: opts.workspace, workspaceEpoch: opts.epoch, confirm: true, input };
|
|
17
|
+
};
|
|
18
|
+
reviewed(group.command('configure').requiredOption('--file <path>', 'owner-reviewed target JSON: expectedRevision, enabled, definition, confirmDaemonAccess'))
|
|
19
|
+
.action(async (opts) => {
|
|
20
|
+
const st = fs.lstatSync(opts.file);
|
|
21
|
+
if (!st.isFile() || st.isSymbolicLink() || st.size > 128 * 1024)
|
|
22
|
+
throw new DodoError('INVALID_INPUT', 'target configuration must be a regular JSON file at most 128 KiB');
|
|
23
|
+
let input;
|
|
24
|
+
try {
|
|
25
|
+
input = JSON.parse(fs.readFileSync(opts.file, 'utf8'));
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
throw new DodoError('INVALID_INPUT', 'invalid target JSON');
|
|
29
|
+
}
|
|
30
|
+
print(await ipc('deployment.configure', envelope(opts, input)));
|
|
31
|
+
});
|
|
32
|
+
reviewed(group.command('prepare').requiredOption('--target <id>', 'owner-registered target ID').requiredOption('--revision <n>', 'target revision')
|
|
33
|
+
.requiredOption('--verification <id>', 'current VERIFIED verification ID').requiredOption('--key <key>', 'idempotency key; reuse for retries'))
|
|
34
|
+
.action(async (opts) => print(await ipc('deployment.prepare', envelope(opts, {
|
|
35
|
+
targetId: opts.target, expectedTargetRevision: Number(opts.revision), verificationId: opts.verification, idempotencyKey: opts.key,
|
|
36
|
+
}))));
|
|
37
|
+
reviewed(group.command('build <deploymentId>').requiredOption('--hash <hash>', 'reviewed plan hash'))
|
|
38
|
+
.action(async (deploymentId, opts) => print(await ipc('deployment.build', envelope(opts, { deploymentId, planHash: opts.hash }))));
|
|
39
|
+
reviewed(group.command('apply <deploymentId>').requiredOption('--hash <hash>', 'reviewed plan hash').requiredOption('--image <digest>', 'reviewed immutable image digest'))
|
|
40
|
+
.action(async (deploymentId, opts) => print(await ipc('deployment.apply', envelope(opts, { deploymentId, planHash: opts.hash, imageDigest: opts.image }))));
|
|
41
|
+
for (const op of ['observe', 'source-preview'])
|
|
42
|
+
reviewed(group.command(`${op} <deploymentId>`))
|
|
43
|
+
.action(async (deploymentId, opts) => print(await ipc('deployment.' + op.replaceAll('-', '_'), envelope(opts, { deploymentId }))));
|
|
44
|
+
reviewed(group.command('maintenance').requiredOption('--file <path>', 'owner-reviewed maintenance JSON; preview returns a review ID/hash for apply'))
|
|
45
|
+
.action(async (opts) => {
|
|
46
|
+
const st = fs.lstatSync(opts.file);
|
|
47
|
+
if (!st.isFile() || st.isSymbolicLink() || st.nlink !== 1 || st.size > 65536)
|
|
48
|
+
throw new DodoError('INVALID_INPUT', 'maintenance JSON must be a bounded regular file');
|
|
49
|
+
let input;
|
|
50
|
+
try {
|
|
51
|
+
input = JSON.parse(fs.readFileSync(opts.file, 'utf8'));
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
throw new DodoError('INVALID_INPUT', 'invalid maintenance JSON');
|
|
55
|
+
}
|
|
56
|
+
print(await ipc('deployment.maintenance', envelope(opts, input)));
|
|
57
|
+
});
|
|
58
|
+
reviewed(group.command('rollback-prepare <deploymentId>').requiredOption('--key <key>', 'idempotency key for this rollback review'))
|
|
59
|
+
.action(async (deploymentId, opts) => print(await ipc('deployment.rollback_prepare', envelope(opts, { deploymentId, idempotencyKey: opts.key }))));
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=deployment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deployment.js","sourceRoot":"","sources":["../../src/cli/deployment.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/main.js
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
const invokedCwd = process.cwd();
|
|
8
8
|
import fs from 'node:fs';
|
|
9
9
|
import { Command } from 'commander';
|
|
10
|
+
import { registerDataRecoveryCommands } from './dataRecovery.js';
|
|
11
|
+
import { registerDeploymentCommands } from './deployment.js';
|
|
10
12
|
import { startServer, DODO_VERSION } from '../server/appServer.js';
|
|
11
13
|
import { startStdioServer } from '../server/stdioServer.js';
|
|
12
14
|
import { resolveConfigDir, ensureConfigDir, statePaths, ipcSocketPath } from '../config/paths.js';
|
|
@@ -16,6 +18,7 @@ import { mintWorkspaceId } from '../workspace/identity.js';
|
|
|
16
18
|
import { openDatabase, openDatabaseReadonly } from '../store/db.js';
|
|
17
19
|
import { Store } from '../store/store.js';
|
|
18
20
|
import { ipcCall, IpcError } from '../ipc/client.js';
|
|
21
|
+
import { ownerCommandTimeout } from '../ipc/timeouts.js';
|
|
19
22
|
import { installationIpcCall } from '../ipc/installationClient.js';
|
|
20
23
|
import { formatTerminalLine } from './terminal.js';
|
|
21
24
|
import { TRUST_MODE_DESCRIPTIONS } from '../security/policy.js';
|
|
@@ -31,6 +34,7 @@ import { runCliMenu } from './menu.js';
|
|
|
31
34
|
import { TunnelRuntime } from '../tunnel/runtime.js';
|
|
32
35
|
import { INSTALLATION_AUTHORITY_ID } from '../auth/constants.js';
|
|
33
36
|
const program = new Command();
|
|
37
|
+
registerDeploymentCommands(program, ipcForCwd);
|
|
34
38
|
program.name('dodo').description('DODO — local-first, single-owner, project-scoped coding MCP server').version(DODO_VERSION);
|
|
35
39
|
function fail(message, code = 1) {
|
|
36
40
|
console.error(`dodo: ${message}`);
|
|
@@ -61,7 +65,7 @@ function workspaceIpcPath(root) {
|
|
|
61
65
|
}
|
|
62
66
|
async function ipcForCwd(cmd, args = {}) {
|
|
63
67
|
const { ipcPath } = workspaceIpcPath();
|
|
64
|
-
return ipcCall(ipcPath, cmd, args);
|
|
68
|
+
return ipcCall(ipcPath, cmd, args, ownerCommandTimeout(cmd));
|
|
65
69
|
}
|
|
66
70
|
/** OAuth identity belongs to the installation, not the shell's CWD. */
|
|
67
71
|
async function ipcForInstallation(cmd, args = {}) {
|
|
@@ -1268,6 +1272,36 @@ auth
|
|
|
1268
1272
|
throw err;
|
|
1269
1273
|
}
|
|
1270
1274
|
});
|
|
1275
|
+
// -------------------------------------------------------------- recovery ---
|
|
1276
|
+
const recovery = program.command('recovery').description('inspect or configure automatic source backups for the current registered project');
|
|
1277
|
+
registerDataRecoveryCommands(recovery, ipcForCwd);
|
|
1278
|
+
recovery.command('status').action(async () => console.log(JSON.stringify(await ipcForCwd('recovery.status'), null, 2)));
|
|
1279
|
+
recovery.command('scan').option('--cursor <n>', 'page offset', '0').option('--limit <n>', 'page size (1–100)', '50').action(async (opts) => console.log(JSON.stringify(await ipcForCwd('recovery.drift.scan', { cursor: Number(opts.cursor), limit: Number(opts.limit) }), null, 2)));
|
|
1280
|
+
recovery.command('acknowledge <digest>').requiredOption('--workspace <id>', 'reviewed workspace ID').requiredOption('--epoch <epoch>', 'reviewed workspace epoch').option('--yes', 'accept exactly the reviewed observed state', false)
|
|
1281
|
+
.action(async (digest, opts) => { if (!opts.yes)
|
|
1282
|
+
throw new DodoError('INVALID_INPUT', 'review drift then add --yes'); console.log(JSON.stringify(await ipcForCwd('recovery.drift.acknowledge', { digest, workspaceId: opts.workspace, workspaceEpoch: opts.epoch, confirm: true }), null, 2)); });
|
|
1283
|
+
recovery.command('evidence').option('--id <verificationId>', 'refresh evidence for a specific verification').action(async (opts) => console.log(JSON.stringify(await ipcForCwd(opts.id ? 'recovery.evidence.inspect' : 'recovery.evidence.list', opts.id ? { verificationId: opts.id } : {}), null, 2)));
|
|
1284
|
+
recovery.command('cleanup-preview').action(async () => console.log(JSON.stringify(await ipcForCwd('recovery.cleanup.preview'), null, 2)));
|
|
1285
|
+
recovery.command('mark <name> <checkpointId>').requiredOption('--revision <n>', 'reviewed pointer revision; 0 for a new name').requiredOption('--workspace <id>', 'reviewed workspace ID').requiredOption('--epoch <epoch>', 'reviewed epoch').option('--yes', 'confirm owner label (does not certify tests)', false)
|
|
1286
|
+
.action(async (name, checkpointId, opts) => { if (!opts.yes)
|
|
1287
|
+
throw new DodoError('INVALID_INPUT', 'review checkpoint and add --yes'); console.log(JSON.stringify(await ipcForCwd('recovery.mark', { name, snapshotId: checkpointId, expectedRevision: Number(opts.revision), workspaceId: opts.workspace, workspaceEpoch: opts.epoch, confirm: true }), null, 2)); });
|
|
1288
|
+
recovery.command('checkpoint').action(async () => console.log(JSON.stringify(await ipcForCwd('recovery.checkpoint'), null, 2)));
|
|
1289
|
+
recovery.command('list').option('--sessions', 'list sessions instead of checkpoints', false).action(async (opts) => console.log(JSON.stringify(await ipcForCwd(opts.sessions ? 'recovery.recovery_session_list' : 'recovery.checkpoint_list'), null, 2)));
|
|
1290
|
+
recovery.command('preview <id>').option('--session', 'undo caller session receipts', false).option('--path <relative>', 'select a file or directory').option('--exact-mirror', 'explicitly include extra source-file deletions', false)
|
|
1291
|
+
.action(async (id, opts) => console.log(JSON.stringify(await ipcForCwd('recovery.restore_preview', { ...(opts.session ? { sessionId: id } : { checkpointId: id }), ...(opts.path ? { paths: [opts.path] } : {}), exactMirror: opts.exactMirror }), null, 2)));
|
|
1292
|
+
recovery.command('apply <planId>').requiredOption('--hash <hash>', 'exact reviewed planHash').requiredOption('--key <key>', 'durable idempotency key; reuse for retries').requiredOption('--workspace <id>', 'reviewed workspace ID').requiredOption('--epoch <epoch>', 'reviewed workspace epoch').option('--yes', 'confirm exactly this reviewed plan', false)
|
|
1293
|
+
.action(async (planId, opts) => {
|
|
1294
|
+
if (!opts.yes)
|
|
1295
|
+
fail('review the preview, then pass --yes with the same plan/hash and workspace context');
|
|
1296
|
+
console.log(JSON.stringify(await ipcForCwd('recovery.restore_apply', { planId, planHash: opts.hash, idempotencyKey: opts.key, workspaceId: opts.workspace, workspaceEpoch: opts.epoch, confirm: true }), null, 2));
|
|
1297
|
+
});
|
|
1298
|
+
recovery.command('configure').option('--enabled <value>', 'true or false').option('--yes', 'confirm the owner policy change', false)
|
|
1299
|
+
.action(async (opts) => {
|
|
1300
|
+
if (!opts.yes || !['true', 'false'].includes(opts.enabled ?? ''))
|
|
1301
|
+
fail('use --enabled true|false --yes to confirm');
|
|
1302
|
+
const current = await ipcForCwd('recovery.status');
|
|
1303
|
+
console.log(JSON.stringify(await ipcForCwd('recovery.configure', { policy: { ...current.policy, enabled: opts.enabled === 'true' }, confirm: true }), null, 2));
|
|
1304
|
+
});
|
|
1271
1305
|
// --------------------------------------------------------------- recover ---
|
|
1272
1306
|
program
|
|
1273
1307
|
.command('recover')
|