@lmzhen/dsh-evolution-approval 0.26.26 → 0.27.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/lib/index.js CHANGED
@@ -1,93 +1,82 @@
1
1
  import { Service } from "@deepseek-ai/cordis";
2
2
  import { randomUUID } from "node:crypto";
3
- import { clampOnce, elapsedSince, isLoopbackRequest, readJsonObject, textDiffFacts, writeJson } from "@lmzhen/dsh-evolution-core";
3
+ import { clampOnce, elapsedSince, textDiffFacts } from "@lmzhen/dsh-evolution-core";
4
4
  import z from "@deepseek-ai/schemastery";
5
5
  import { PENDING_STATUSES } from "@lmzhen/dsh-evolution-state-storage";
6
+ import { HOST_ROUTES, json, methodNotAllowed, readJsonObject, registerHostRoutes } from "@lmzhen/dsh-evolution-core/http-routes";
6
7
  //#region lib/types/routes.js
7
8
  /**
8
- * The approval HTTP surface: four loopback routes over the write gate's pending window.
9
+ * The approval HTTP surface: four exact Fetch routes over the write gate's pending window.
10
+ *
11
+ * W6/G4: the carrier is the platform's Host connection data plane (`connection.fetch`) — the trust
12
+ * fence, browser auth and owner-only admission are applied by the platform BEFORE `fetch(request)`
13
+ * runs, so this module has no fence and no 403/401 branch. It still answers the same JSON envelope
14
+ * the browser half reads; the loopback f-string that used to live here was a SECOND admission path
15
+ * (the S2 spike showed it could diverge from the platform's).
9
16
  *
10
17
  * Layering: this module maps a route to a USE CASE and does nothing else. The pending window itself
11
18
  * (ids, statuses, claim/resolve rules, the replay runners) lives in this package's service and in
12
19
  * `evolution-state-storage`; the routes only read the window and forward the two decisions, so the
13
20
  * console command face (`/evolution pending`, `approve <id>`) and this one cannot drift — both call
14
21
  * the same methods.
15
- *
16
- * The browser half never touches the state medium: it lists, it approves, it rejects.
17
22
  * @module @lmzhen/dsh-evolution-approval/routes
18
23
  */
19
- /** Route paths. The browser half mirrors these literals; a spec asserts the two sides agree. */
20
- const APPROVAL_ROUTES = {
21
- pending: "/api/dsh-evolution/approval/pending",
22
- approve: "/api/dsh-evolution/approval/approve",
23
- reject: "/api/dsh-evolution/approval/reject",
24
- preview: "/api/dsh-evolution/approval/preview"
25
- };
24
+ /** Route paths: the ONE declaration lives in core (W1) and the browser half is generated from it. */
25
+ const APPROVAL_ROUTES = HOST_ROUTES.approval;
26
+ /**
27
+ * Run one decision against the service.
28
+ *
29
+ * A refusal is a BUSINESS answer, not a transport failure: the body carries the service's own
30
+ * sentence — the same one the console command prints — so the two faces cannot drift. A throw is
31
+ * reported the same way rather than escaping as an empty 500, because the operator's next step
32
+ * ("reject it", "retry") is only actionable if the reason is visible.
33
+ */
34
+ async function decide(run, id) {
35
+ try {
36
+ const result = await run(id);
37
+ return json(200, result.ok ? {
38
+ ok: true,
39
+ data: result
40
+ } : {
41
+ ok: false,
42
+ code: "approval-refused",
43
+ message: result.message
44
+ });
45
+ } catch (error) {
46
+ return json(200, {
47
+ ok: false,
48
+ code: "approval-failed",
49
+ message: error instanceof Error ? error.message : String(error)
50
+ });
51
+ }
52
+ }
53
+ /** The one shape a decision route accepts: a non-empty string id. */
54
+ function idOf(payload) {
55
+ return typeof payload?.id === "string" ? payload.id.trim() : "";
56
+ }
26
57
  /**
27
- * Build the four routes over the approval service.
58
+ * Build the four exact Fetch routes over the approval service.
28
59
  * @param approval - the host's approval face (the service instance, or a spec fixture).
29
- * @returns the route list for `ctx.webServer.register`.
60
+ * @param audit - the DecisionAudit captured where the connection service is known (registration).
61
+ * @returns the route list for `registerHostRoutes`.
30
62
  */
31
- function makeApprovalRoutes(approval) {
32
- /** Guard: trust fence, then method. Every refusal is a JSON body, never an empty response. */
33
- const guard = (req, res, method) => {
34
- if (!isLoopbackRequest(req)) {
35
- writeJson(res, 403, { error: "禁止访问:仅限回环地址" });
36
- return false;
37
- }
38
- if (req.method !== method) {
39
- writeJson(res, 405, { error: "方法不允许:" + method });
40
- return false;
41
- }
42
- return true;
43
- };
44
- /**
45
- * Run one decision against the service.
46
- *
47
- * A refusal is a BUSINESS answer, not a transport failure: the body carries the service's own
48
- * sentence — the same one the console command prints — so the two faces cannot drift. A throw is
49
- * reported the same way rather than escaping as an empty 500, because the operator's next step
50
- * ("reject it", "retry") is only actionable if the reason is visible.
51
- */
52
- const decide = async (res, run, id) => {
53
- try {
54
- const result = await run(id);
55
- writeJson(res, 200, result.ok ? {
56
- ok: true,
57
- data: result
58
- } : {
59
- ok: false,
60
- code: "approval-refused",
61
- message: result.message
62
- });
63
- } catch (error) {
64
- writeJson(res, 200, {
65
- ok: false,
66
- code: "approval-failed",
67
- message: error instanceof Error ? error.message : String(error)
68
- });
69
- }
70
- };
71
- /** The one shape a decision route accepts: a non-empty string id. */
72
- const idOf = (payload) => typeof payload?.id === "string" ? payload.id.trim() : "";
63
+ function makeApprovalRoutes(approval, audit) {
73
64
  return [
74
65
  {
75
- kind: "exact",
76
66
  path: APPROVAL_ROUTES.pending,
77
- handler: async (req, res) => {
78
- if (!guard(req, res, "GET")) return;
79
- const raw = new URL(req.url ?? "/", "http://loopback").searchParams.get("status")?.trim() ?? "";
67
+ methods: ["GET"],
68
+ requestBody: "buffered",
69
+ fetch: async (request) => {
70
+ if (request.method !== "GET") return methodNotAllowed(["GET"]);
71
+ const raw = new URL(request.url).searchParams.get("status")?.trim() ?? "";
80
72
  const status = raw === "" ? "pending" : raw;
81
- if (!PENDING_STATUSES.includes(status)) {
82
- writeJson(res, 400, {
83
- ok: false,
84
- code: "bad-request",
85
- message: "status 必须是以下之一:" + PENDING_STATUSES.join(", ")
86
- });
87
- return;
88
- }
73
+ if (!PENDING_STATUSES.includes(status)) return json(400, {
74
+ ok: false,
75
+ code: "bad-request",
76
+ message: "status 必须是以下之一:" + PENDING_STATUSES.join(", ")
77
+ });
89
78
  try {
90
- writeJson(res, 200, {
79
+ return json(200, {
91
80
  ok: true,
92
81
  data: (await approval.list(status)).map((record) => ({
93
82
  id: record.id,
@@ -100,7 +89,7 @@ function makeApprovalRoutes(approval) {
100
89
  }))
101
90
  });
102
91
  } catch (error) {
103
- writeJson(res, 200, {
92
+ return json(200, {
104
93
  ok: false,
105
94
  code: "approval-failed",
106
95
  message: error instanceof Error ? error.message : String(error)
@@ -109,53 +98,42 @@ function makeApprovalRoutes(approval) {
109
98
  }
110
99
  },
111
100
  {
112
- kind: "exact",
113
101
  path: APPROVAL_ROUTES.preview,
114
- handler: async (req, res) => {
115
- if (!guard(req, res, "GET")) return;
116
- const id = new URL(req.url ?? "/", "http://loopback").searchParams.get("id")?.trim() ?? "";
117
- if (id === "") {
118
- writeJson(res, 400, {
119
- ok: false,
120
- code: "bad-request",
121
- message: "缺少 id"
122
- });
123
- return;
124
- }
102
+ methods: ["GET"],
103
+ requestBody: "buffered",
104
+ fetch: async (request) => {
105
+ if (request.method !== "GET") return methodNotAllowed(["GET"]);
106
+ const id = new URL(request.url).searchParams.get("id")?.trim() ?? "";
107
+ if (id === "") return json(400, {
108
+ ok: false,
109
+ code: "bad-request",
110
+ message: "缺少 id"
111
+ });
125
112
  try {
126
113
  const record = [...await approval.list("pending"), ...await approval.list("executing")].find((candidate) => candidate.id === id);
127
- if (record === void 0) {
128
- writeJson(res, 200, {
129
- ok: false,
130
- code: "not-found",
131
- message: "没有 id 为「" + id + "」的待批准写入"
132
- });
133
- return;
134
- }
114
+ if (record === void 0) return json(200, {
115
+ ok: false,
116
+ code: "not-found",
117
+ message: "没有 id 为「" + id + "」的待批准写入"
118
+ });
135
119
  const preview = approval.previewOf(record.kind);
136
- if (preview === void 0) {
137
- writeJson(res, 200, {
138
- ok: true,
139
- data: {
140
- available: false,
141
- reason: "类型 \"" + record.kind + "\" 的写入没有可预览内容"
142
- }
143
- });
144
- return;
145
- }
120
+ if (preview === void 0) return json(200, {
121
+ ok: true,
122
+ data: {
123
+ available: false,
124
+ reason: "类型 \"" + record.kind + "\" 的写入没有可预览内容"
125
+ }
126
+ });
146
127
  const answer = await preview(record.args);
147
- if (!answer.available) {
148
- writeJson(res, 200, {
149
- ok: true,
150
- data: {
151
- available: false,
152
- reason: answer.reason
153
- }
154
- });
155
- return;
156
- }
128
+ if (!answer.available) return json(200, {
129
+ ok: true,
130
+ data: {
131
+ available: false,
132
+ reason: answer.reason
133
+ }
134
+ });
157
135
  const facts = textDiffFacts(answer.before ?? "", answer.after ?? "", answer.path);
158
- writeJson(res, 200, {
136
+ return json(200, {
159
137
  ok: true,
160
138
  data: {
161
139
  available: true,
@@ -164,7 +142,7 @@ function makeApprovalRoutes(approval) {
164
142
  }
165
143
  });
166
144
  } catch (error) {
167
- writeJson(res, 200, {
145
+ return json(200, {
168
146
  ok: false,
169
147
  code: "approval-failed",
170
148
  message: error instanceof Error ? error.message : String(error)
@@ -173,37 +151,33 @@ function makeApprovalRoutes(approval) {
173
151
  }
174
152
  },
175
153
  {
176
- kind: "exact",
177
154
  path: APPROVAL_ROUTES.approve,
178
- handler: async (req, res) => {
179
- if (!guard(req, res, "POST")) return;
180
- const id = idOf(await readJsonObject(req));
181
- if (id === "") {
182
- writeJson(res, 400, {
183
- ok: false,
184
- code: "bad-request",
185
- message: "缺少 id"
186
- });
187
- return;
188
- }
189
- await decide(res, (current) => approval.approve(current), id);
155
+ methods: ["POST"],
156
+ requestBody: "buffered",
157
+ fetch: async (request) => {
158
+ if (request.method !== "POST") return methodNotAllowed(["POST"]);
159
+ const id = idOf(await readJsonObject(request));
160
+ if (id === "") return json(400, {
161
+ ok: false,
162
+ code: "bad-request",
163
+ message: "缺少 id"
164
+ });
165
+ return await decide((current) => approval.approve(current, audit), id);
190
166
  }
191
167
  },
192
168
  {
193
- kind: "exact",
194
169
  path: APPROVAL_ROUTES.reject,
195
- handler: async (req, res) => {
196
- if (!guard(req, res, "POST")) return;
197
- const id = idOf(await readJsonObject(req));
198
- if (id === "") {
199
- writeJson(res, 400, {
200
- ok: false,
201
- code: "bad-request",
202
- message: "缺少 id"
203
- });
204
- return;
205
- }
206
- await decide(res, (current) => approval.reject(current), id);
170
+ methods: ["POST"],
171
+ requestBody: "buffered",
172
+ fetch: async (request) => {
173
+ if (request.method !== "POST") return methodNotAllowed(["POST"]);
174
+ const id = idOf(await readJsonObject(request));
175
+ if (id === "") return json(400, {
176
+ ok: false,
177
+ code: "bad-request",
178
+ message: "缺少 id"
179
+ });
180
+ return await decide((current) => approval.reject(current, audit), id);
207
181
  }
208
182
  }
209
183
  ];
@@ -300,14 +274,7 @@ var EvolutionApproval = class extends Service {
300
274
  const disposer = settings.configure({ auto: false }, this.ctx.fiber);
301
275
  if (typeof disposer === "function") this.ctx.effect(() => disposer, "evolution-approval: settings presentation");
302
276
  });
303
- this.ctx.inject(["webServer"], (scope) => {
304
- scope.effect(() => {
305
- const disposers = makeApprovalRoutes(this).map((route) => scope.webServer.register(route));
306
- return () => {
307
- for (const dispose of disposers) dispose();
308
- };
309
- }, "evolution-approval: routes");
310
- });
277
+ registerHostRoutes(this.ctx, (audit) => makeApprovalRoutes(this, audit));
311
278
  }
312
279
  state() {
313
280
  return this.ctx.evolutionState;
@@ -446,8 +413,8 @@ var EvolutionApproval = class extends Service {
446
413
  await this.expireStale();
447
414
  return await this.state().listPending(status);
448
415
  }
449
- async approve(id) {
450
- return await this.dedupe(`approve:${id}`, () => this.doApprove(id));
416
+ async approve(id, audit) {
417
+ return await this.dedupe(`approve:${id}`, () => this.doApprove(id, audit));
451
418
  }
452
419
  /** S2-P2-22 (0.3.80): return an ORPHANED executing record to the pending
453
420
  * window. "Orphaned" is DECIDED here, never assumed: the older rationale
@@ -513,7 +480,7 @@ var EvolutionApproval = class extends Service {
513
480
  message: `已把「${id}」放回待批准窗口。${holderPid === null ? " Its claim carries no pid, so liveness could NOT be verified: another process may still be running this approve." : ""} 批准前请先核验效果:批准会重跑该写入,非幂等的重放会造成效果重复;改为拒绝则可关闭该记录。`
514
481
  };
515
482
  }
516
- async reject(id) {
483
+ async reject(id, audit) {
517
484
  return await this.dedupe(`reject:${id}`, async () => {
518
485
  const claimId = this.newClaimId();
519
486
  if (!await this.state().claimPending(id, claimId)) {
@@ -537,6 +504,7 @@ var EvolutionApproval = class extends Service {
537
504
  message: `待批准写入「${id}」已不在待批准状态(已了结或不存在)。`
538
505
  };
539
506
  }
507
+ await this.recordDecision(resolution.record, audit);
540
508
  return {
541
509
  ok: true,
542
510
  message: `已拒绝 ${resolution.record.kind} 写入「${id}」。`
@@ -577,7 +545,20 @@ var EvolutionApproval = class extends Service {
577
545
  this.inFlight.set(id, run);
578
546
  return run;
579
547
  }
580
- async doApprove(id) {
548
+ /** W6: attach the platform's admission decision to a resolved record (read-tolerant; absent on
549
+ * records resolved by expiry or before 0.2.1). */
550
+ async recordDecision(record, audit) {
551
+ if (record === null) return;
552
+ try {
553
+ await this.state().savePending({
554
+ ...record,
555
+ decidedBy: audit
556
+ });
557
+ } catch (error) {
558
+ this.ctx.logger.warn("evolution-approval: could not attach decidedBy audit: " + (error instanceof Error ? error.message : String(error)));
559
+ }
560
+ }
561
+ async doApprove(id, audit) {
581
562
  const claimId = this.newClaimId();
582
563
  const record = await this.state().claimPending(id, claimId);
583
564
  if (!record) {
@@ -593,7 +574,8 @@ var EvolutionApproval = class extends Service {
593
574
  const runner = this.runners.get(record.kind);
594
575
  if (!runner) {
595
576
  if (record.kind === "capability") {
596
- if (!(await this.state().tryResolvePending(id, "approved", claimId)).applied) {
577
+ const resolution = await this.state().tryResolvePending(id, "approved", claimId);
578
+ if (!resolution.applied) {
597
579
  if ((await this.state().listPending("rejected")).find((item) => item.id === id)) return {
598
580
  ok: false,
599
581
  message: `能力「${id}」已获批准,但记录被并发地了结为「rejected」:没有代码运行,也无需重放;重新提交前请先核验。`
@@ -603,6 +585,7 @@ var EvolutionApproval = class extends Service {
603
585
  message: `待批准写入「${id}」已被另一个写入方了结。`
604
586
  };
605
587
  }
588
+ await this.recordDecision(resolution.record, audit);
606
589
  return {
607
590
  ok: true,
608
591
  message: "能力已批准,可在 Creator 模式下手动激活(未执行任何代码)。"
@@ -649,6 +632,7 @@ var EvolutionApproval = class extends Service {
649
632
  message: `待批准写入「${id}」已被另一个写入方了结。`
650
633
  };
651
634
  }
635
+ await this.recordDecision(resolution.record, audit);
652
636
  return {
653
637
  ok: true,
654
638
  message: `已批准 ${record.kind} 写入「${id}」(${record.summary})。`
@@ -21,6 +21,7 @@ import type { Volatile } from '@deepseek-ai/cordis';
21
21
  import { type WriteConfirmMode } from '@lmzhen/dsh-evolution-core';
22
22
  import z from '@deepseek-ai/schemastery';
23
23
  import type { PendingKind, PendingRecord, PendingStatus } from '@lmzhen/dsh-evolution-state-storage';
24
+ import type { DecisionAudit } from '@lmzhen/dsh-evolution-core/http-routes';
24
25
  export type { PendingKind, PendingRecord, PendingStatus };
25
26
  export type WriteRunner = (args: unknown) => Promise<{
26
27
  ok: boolean;
@@ -262,7 +263,7 @@ export declare class EvolutionApproval extends Service {
262
263
  */
263
264
  expireStale(): Promise<string[]>;
264
265
  list(status?: PendingStatus): Promise<PendingRecord[]>;
265
- approve(id: string): Promise<{
266
+ approve(id: string, audit: DecisionAudit): Promise<{
266
267
  ok: boolean;
267
268
  message: string;
268
269
  }>;
@@ -290,7 +291,7 @@ export declare class EvolutionApproval extends Service {
290
291
  ok: boolean;
291
292
  message: string;
292
293
  }>;
293
- reject(id: string): Promise<{
294
+ reject(id: string, audit: DecisionAudit): Promise<{
294
295
  ok: boolean;
295
296
  message: string;
296
297
  }>;
@@ -311,6 +312,9 @@ export declare class EvolutionApproval extends Service {
311
312
  * claim-id grammar accepts). */
312
313
  private newClaimId;
313
314
  private dedupe;
315
+ /** W6: attach the platform's admission decision to a resolved record (read-tolerant; absent on
316
+ * records resolved by expiry or before 0.2.1). */
317
+ private recordDecision;
314
318
  private doApprove;
315
319
  }
316
320
  export default EvolutionApproval;
@@ -1,25 +1,29 @@
1
1
  /**
2
- * The approval HTTP surface: four loopback routes over the write gate's pending window.
2
+ * The approval HTTP surface: four exact Fetch routes over the write gate's pending window.
3
+ *
4
+ * W6/G4: the carrier is the platform's Host connection data plane (`connection.fetch`) — the trust
5
+ * fence, browser auth and owner-only admission are applied by the platform BEFORE `fetch(request)`
6
+ * runs, so this module has no fence and no 403/401 branch. It still answers the same JSON envelope
7
+ * the browser half reads; the loopback f-string that used to live here was a SECOND admission path
8
+ * (the S2 spike showed it could diverge from the platform's).
3
9
  *
4
10
  * Layering: this module maps a route to a USE CASE and does nothing else. The pending window itself
5
11
  * (ids, statuses, claim/resolve rules, the replay runners) lives in this package's service and in
6
12
  * `evolution-state-storage`; the routes only read the window and forward the two decisions, so the
7
13
  * console command face (`/evolution pending`, `approve <id>`) and this one cannot drift — both call
8
14
  * the same methods.
9
- *
10
- * The browser half never touches the state medium: it lists, it approves, it rejects.
11
15
  * @module @lmzhen/dsh-evolution-approval/routes
12
16
  */
13
- import type { WebRoute } from '@deepseek-ai/dsh-host-webserver';
14
17
  import type { PendingKind, PendingRecord, PendingStatus } from '@lmzhen/dsh-evolution-state-storage';
15
18
  import { elapsedSince } from '@lmzhen/dsh-evolution-core';
19
+ import type { ConnectionFetchRouteLike, DecisionAudit } from '@lmzhen/dsh-evolution-core/http-routes';
16
20
  import type { WritePreview } from './index.ts';
17
- /** Route paths. The browser half mirrors these literals; a spec asserts the two sides agree. */
21
+ /** Route paths: the ONE declaration lives in core (W1) and the browser half is generated from it. */
18
22
  export declare const APPROVAL_ROUTES: {
19
23
  readonly pending: "/api/dsh-evolution/approval/pending";
24
+ readonly preview: "/api/dsh-evolution/approval/preview";
20
25
  readonly approve: "/api/dsh-evolution/approval/approve";
21
26
  readonly reject: "/api/dsh-evolution/approval/reject";
22
- readonly preview: "/api/dsh-evolution/approval/preview";
23
27
  };
24
28
  /** What one row of the pending list carries to the browser half. */
25
29
  export interface PendingRow {
@@ -36,26 +40,30 @@ export interface PendingRow {
36
40
  /**
37
41
  * The slice of the approval service these routes read.
38
42
  *
39
- * Structural on purpose: a spec fixture implements it without constructing the service, and the
40
- * routes stay usable from any host that exposes the same three methods.
43
+ * W6: approve/reject take the DecisionAudit captured at registration (channel/principal/
44
+ * deploymentAllowsRemote). The audit rides the service call so the ledger records the ONE admission
45
+ * decision the platform already made — the route never re-derives it.
41
46
  */
42
47
  export interface ApprovalFace {
43
48
  list(status?: PendingStatus): Promise<PendingRecord[]>;
44
- approve(id: string): Promise<{
49
+ approve(id: string, audit: DecisionAudit): Promise<{
45
50
  ok: boolean;
46
51
  message: string;
47
52
  }>;
48
- reject(id: string): Promise<{
53
+ reject(id: string, audit: DecisionAudit): Promise<{
49
54
  ok: boolean;
50
55
  message: string;
51
56
  }>;
52
57
  /** The preview the replayer of that kind registered, when it registered one. */
53
58
  previewOf(kind: PendingKind): WritePreview | undefined;
54
59
  }
60
+ /** The one shape a decision route accepts: a non-empty string id. */
61
+ export declare function idOf(payload: Record<string, unknown> | null): string;
55
62
  /**
56
- * Build the four routes over the approval service.
63
+ * Build the four exact Fetch routes over the approval service.
57
64
  * @param approval - the host's approval face (the service instance, or a spec fixture).
58
- * @returns the route list for `ctx.webServer.register`.
65
+ * @param audit - the DecisionAudit captured where the connection service is known (registration).
66
+ * @returns the route list for `registerHostRoutes`.
59
67
  */
60
- export declare function makeApprovalRoutes(approval: ApprovalFace): WebRoute[];
68
+ export declare function makeApprovalRoutes(approval: ApprovalFace, audit: DecisionAudit): ConnectionFetchRouteLike[];
61
69
  //# sourceMappingURL=routes.d.ts.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lmzhen/dsh-evolution-approval",
3
3
  "description": "Stage/pending approval service for Hermes-style self-evolution writes (community build)",
4
- "version": "0.26.26",
4
+ "version": "0.27.1",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -26,19 +26,21 @@
26
26
  ],
27
27
  "license": "MIT",
28
28
  "dependencies": {
29
- "@deepseek-ai/schemastery": "^3.18.4",
30
- "@lmzhen/dsh-evolution-core": "^0.26.26"
29
+ "@deepseek-ai/schemastery": "^3.18.5",
30
+ "@lmzhen/dsh-evolution-core": "^0.27.1"
31
31
  },
32
32
  "peerDependencies": {
33
- "@deepseek-ai/cordis": "^4.0.1",
34
- "@deepseek-ai/dsh-host-webserver": "^0.2.0-rc.1",
35
- "@lmzhen/dsh-evolution-state-storage": "^0.26.26",
36
- "@lmzhen/dsh-evolution-state": "^0.26.26"
33
+ "@deepseek-ai/cordis": "^4.0.5",
34
+ "@lmzhen/dsh-evolution-core": "^0.27.1",
35
+ "@lmzhen/dsh-evolution-state": "^0.27.1",
36
+ "@lmzhen/dsh-evolution-state-storage": "^0.27.1"
37
37
  },
38
38
  "devDependencies": {
39
- "@deepseek-ai/dsh-host-webserver": "^0.2.0-rc.1",
40
- "@lmzhen/dsh-evolution-core": "^0.26.26",
41
- "@lmzhen/dsh-evolution-state-storage": "^0.26.26",
42
- "@lmzhen/dsh-evolution-state": "^0.26.26"
39
+ "@lmzhen/dsh-evolution-core": "^0.27.1",
40
+ "@lmzhen/dsh-evolution-state": "^0.27.1",
41
+ "@lmzhen/dsh-evolution-state-storage": "^0.27.1"
42
+ },
43
+ "engines": {
44
+ "dsh": ">=0.2.1-rc.1 <0.2.2-0"
43
45
  }
44
46
  }