@xgjktech/xg-openclaw-shared 0.1.0 → 0.2.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/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type { Plan, Task, Step, TaskStatus, StepStatus } from "./plan-types.js";
1
+ export type { Plan, Task, Step, TaskStatus, StepStatus, PlanOrigin } from "./plan-types.js";
2
2
  export { assertPlanInvariants, PlanInvariantError } from "./plan-types.js";
3
3
  export type { PlanStore, PlanSummary } from "./plan-store.js";
4
4
  export { SqlitePlanStore, openPlanStoreReadonly, resolvePlanDbPath, PlanStoreError } from "./plan-store.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,YAAY,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAChF,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAE3E,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAE5G,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAC3E,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,YAAY,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC5F,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAE3E,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAE5G,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAC3E,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC"}
@@ -15,6 +15,10 @@ export interface PlanStore {
15
15
  set(plan: Plan): void;
16
16
  delete(planId: string): void;
17
17
  list(): PlanSummary[];
18
+ listPlansByOrigin(origin: {
19
+ channel: string;
20
+ to: string;
21
+ }): Plan[];
18
22
  }
19
23
  export declare class PlanStoreError extends Error {
20
24
  constructor(message: string, options?: {
@@ -34,12 +38,16 @@ export declare class SqlitePlanStore implements PlanStore {
34
38
  set(plan: Plan): void;
35
39
  delete(planId: string): void;
36
40
  list(): PlanSummary[];
41
+ listPlansByOrigin(origin: {
42
+ channel: string;
43
+ to: string;
44
+ }): Plan[];
37
45
  }
38
46
  /**
39
47
  * IM 侧只读入口:以 readOnly 模式打开同一 DB,杜绝误写。
40
- * 只暴露 get/list(缝隙 C 读路径),不暴露 set/delete。
48
+ * 只暴露 get/list/listPlansByOrigin(缝隙 C 读路径),不暴露 set/delete。
41
49
  */
42
- export declare function openPlanStoreReadonly(dbPath: string): Pick<PlanStore, "get" | "list">;
50
+ export declare function openPlanStoreReadonly(dbPath: string): Pick<PlanStore, "get" | "list" | "listPlansByOrigin">;
43
51
  /**
44
52
  * 路径约定:<stateDir>/xgjktech/plans.sqlite(两插件共享 xgjktech 命名空间)。
45
53
  * 负责 mkdir -p 该子目录,调用方无需自行建目录。
@@ -1 +1 @@
1
- {"version":3,"file":"plan-store.d.ts","sourceRoot":"","sources":["../src/plan-store.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAE5C,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;CACvF;AAED,MAAM,WAAW,SAAS;IACxB,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACtC,GAAG,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IACtB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,IAAI,IAAI,WAAW,EAAE,CAAC;CACvB;AAED,qBAAa,cAAe,SAAQ,KAAK;gBAC3B,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAI3D;AAyGD;;;GAGG;AACH,qBAAa,eAAgB,YAAW,SAAS;IAC/C,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAe;gBAEtB,MAAM,EAAE,MAAM;IAI1B,kDAAkD;IAClD,KAAK,IAAI,IAAI;IAIb,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS;IAIrC,GAAG,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IA8BrB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAQ5B,IAAI,IAAI,WAAW,EAAE;CAGtB;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,KAAK,GAAG,MAAM,CAAC,CAWrF;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAI1D"}
1
+ {"version":3,"file":"plan-store.d.ts","sourceRoot":"","sources":["../src/plan-store.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAE5C,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;CACvF;AAED,MAAM,WAAW,SAAS;IACxB,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACtC,GAAG,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IACtB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,IAAI,IAAI,WAAW,EAAE,CAAC;IACtB,iBAAiB,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,EAAE,CAAC;CACpE;AAED,qBAAa,cAAe,SAAQ,KAAK;gBAC3B,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAI3D;AA0JD;;;GAGG;AACH,qBAAa,eAAgB,YAAW,SAAS;IAC/C,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAe;gBAEtB,MAAM,EAAE,MAAM;IAI1B,kDAAkD;IAClD,KAAK,IAAI,IAAI;IAIb,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS;IAIrC,GAAG,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAkCrB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAQ5B,IAAI,IAAI,WAAW,EAAE;IAIrB,iBAAiB,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,EAAE;CAGnE;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,GACb,IAAI,CAAC,SAAS,EAAE,KAAK,GAAG,MAAM,GAAG,mBAAmB,CAAC,CAoBvD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAI1D"}
@@ -9,7 +9,7 @@ export class PlanStoreError extends Error {
9
9
  this.name = "PlanStoreError";
10
10
  }
11
11
  }
12
- const SCHEMA_VERSION = 1;
12
+ const SCHEMA_VERSION = 2;
13
13
  const BUSY_TIMEOUT_MS = 5000;
14
14
  const CREATE_TABLE_SQL = `
15
15
  CREATE TABLE IF NOT EXISTS plans (
@@ -20,9 +20,16 @@ CREATE TABLE IF NOT EXISTS plans (
20
20
  doc TEXT NOT NULL
21
21
  ) STRICT;
22
22
  `;
23
- const CREATE_INDEX_SQL = `
23
+ const CREATE_INDEX_UPDATED_AT_SQL = `
24
24
  CREATE INDEX IF NOT EXISTS idx_plans_updated_at ON plans(updated_at);
25
25
  `;
26
+ const CREATE_INDEX_ORIGIN_SQL = `
27
+ CREATE INDEX IF NOT EXISTS idx_plans_origin ON plans(origin_channel, origin_to);
28
+ `;
29
+ function listColumnNames(db) {
30
+ const rows = db.prepare("PRAGMA table_info(plans);").all();
31
+ return new Set(rows.map((row) => row.name));
32
+ }
26
33
  function parsePlanDoc(row) {
27
34
  try {
28
35
  return JSON.parse(row.doc);
@@ -46,7 +53,15 @@ function summarizeTaskCounts(plan) {
46
53
  }
47
54
  function ensureSchema(db) {
48
55
  db.exec(CREATE_TABLE_SQL);
49
- db.exec(CREATE_INDEX_SQL);
56
+ const columns = listColumnNames(db);
57
+ if (!columns.has("origin_channel")) {
58
+ db.exec("ALTER TABLE plans ADD COLUMN origin_channel TEXT;");
59
+ }
60
+ if (!columns.has("origin_to")) {
61
+ db.exec("ALTER TABLE plans ADD COLUMN origin_to TEXT;");
62
+ }
63
+ db.exec(CREATE_INDEX_UPDATED_AT_SQL);
64
+ db.exec(CREATE_INDEX_ORIGIN_SQL);
50
65
  db.exec(`PRAGMA user_version = ${SCHEMA_VERSION};`);
51
66
  }
52
67
  function openDatabase(dbPath, options) {
@@ -73,6 +88,13 @@ function readAllPlanRows(db) {
73
88
  .prepare("SELECT plan_id, goal, created_at, updated_at, doc FROM plans ORDER BY updated_at DESC")
74
89
  .all();
75
90
  }
91
+ function readPlanRowsByOrigin(db, origin) {
92
+ return db
93
+ .prepare(`SELECT plan_id, goal, created_at, updated_at, doc FROM plans
94
+ WHERE origin_channel = ? AND origin_to = ?
95
+ ORDER BY updated_at DESC`)
96
+ .all(origin.channel, origin.to);
97
+ }
76
98
  function getPlan(db, planId, errorPrefix) {
77
99
  try {
78
100
  const row = readPlanRow(db, planId);
@@ -102,6 +124,16 @@ function listPlans(db, errorPrefix) {
102
124
  throw new PlanStoreError(`${errorPrefix} plan 列表失败`, { cause });
103
125
  }
104
126
  }
127
+ function listPlansByOrigin(db, origin, errorPrefix) {
128
+ try {
129
+ return readPlanRowsByOrigin(db, origin).map((row) => parsePlanDoc(row));
130
+ }
131
+ catch (cause) {
132
+ if (cause instanceof PlanStoreError)
133
+ throw cause;
134
+ throw new PlanStoreError(`${errorPrefix} plan 按 origin 列表失败`, { cause });
135
+ }
136
+ }
105
137
  /**
106
138
  * SqlitePlanStore:本插件的唯一写者。读写走事务,异常一律转 PlanStoreError,
107
139
  * 绝不让底层 SQLite 异常冒泡崩 gateway(企业工具与 gateway 同进程约束)。
@@ -123,18 +155,22 @@ export class SqlitePlanStore {
123
155
  this.db.exec("BEGIN IMMEDIATE");
124
156
  try {
125
157
  this.db
126
- .prepare(`INSERT INTO plans (plan_id, goal, created_at, updated_at, doc)
127
- VALUES (@planId, @goal, @createdAt, @updatedAt, @doc)
158
+ .prepare(`INSERT INTO plans (plan_id, goal, created_at, updated_at, doc, origin_channel, origin_to)
159
+ VALUES (@planId, @goal, @createdAt, @updatedAt, @doc, @originChannel, @originTo)
128
160
  ON CONFLICT(plan_id) DO UPDATE SET
129
161
  goal = excluded.goal,
130
162
  updated_at = excluded.updated_at,
131
- doc = excluded.doc`)
163
+ doc = excluded.doc,
164
+ origin_channel = excluded.origin_channel,
165
+ origin_to = excluded.origin_to`)
132
166
  .run({
133
167
  planId: plan.planId,
134
168
  goal: plan.goal,
135
169
  createdAt: plan.createdAt,
136
170
  updatedAt: plan.updatedAt,
137
171
  doc: JSON.stringify(plan),
172
+ originChannel: plan.origin?.channel ?? null,
173
+ originTo: plan.origin?.to ?? null,
138
174
  });
139
175
  this.db.exec("COMMIT");
140
176
  }
@@ -158,10 +194,13 @@ export class SqlitePlanStore {
158
194
  list() {
159
195
  return listPlans(this.db, "列出");
160
196
  }
197
+ listPlansByOrigin(origin) {
198
+ return listPlansByOrigin(this.db, origin, "列出");
199
+ }
161
200
  }
162
201
  /**
163
202
  * IM 侧只读入口:以 readOnly 模式打开同一 DB,杜绝误写。
164
- * 只暴露 get/list(缝隙 C 读路径),不暴露 set/delete。
203
+ * 只暴露 get/list/listPlansByOrigin(缝隙 C 读路径),不暴露 set/delete。
165
204
  */
166
205
  export function openPlanStoreReadonly(dbPath) {
167
206
  const db = openDatabase(dbPath, { readOnly: true });
@@ -172,6 +211,15 @@ export function openPlanStoreReadonly(dbPath) {
172
211
  list() {
173
212
  return listPlans(db, "只读列出");
174
213
  },
214
+ listPlansByOrigin(origin) {
215
+ // 只读连接无写权限、不能 ALTER 补齐旧库缺失的 origin 列;每次调用现探测,
216
+ // 缺列时静默降级为 [],绝不缓存该结论——writer 后续迁移后需能立即恢复可见。
217
+ const columns = listColumnNames(db);
218
+ if (!columns.has("origin_channel") || !columns.has("origin_to")) {
219
+ return [];
220
+ }
221
+ return listPlansByOrigin(db, origin, "只读列出");
222
+ },
175
223
  };
176
224
  }
177
225
  /**
@@ -10,26 +10,41 @@ export interface Task {
10
10
  note?: string;
11
11
  steps?: Step[];
12
12
  }
13
+ /**
14
+ * 计划的来源会话上下文。由调用方(工具插件)从工具工厂 ctx 采集后随 plan 落库,
15
+ * 供跨进程/跨重启把计划路由回原会话(IM 侧据此推送计划状态事件)。
16
+ * 全字段可选:采集不到就省略,绝不因缺字段阻断计划本身的落盘。
17
+ */
18
+ export interface PlanOrigin {
19
+ /** 通道 id,IM 插件为 "xg_cwork_im" */
20
+ channel?: string;
21
+ /** 通道内目标 id,IM 插件为 groupId */
22
+ to?: string;
23
+ /** 通道账户 id(多账户部署下区分机器人身份) */
24
+ accountId?: string;
25
+ /** thread/topic id;上游可能是 number,此处统一归一为 string */
26
+ threadId?: string;
27
+ /** 会话 key。诊断与未来关联用,不参与路由 */
28
+ sessionKey?: string;
29
+ }
13
30
  export interface Plan {
14
31
  planId: string;
15
32
  goal: string;
16
33
  createdAt: string;
17
34
  updatedAt: string;
18
35
  tasks: Task[];
36
+ origin?: PlanOrigin;
19
37
  }
20
38
  export declare class PlanInvariantError extends Error {
21
39
  constructor(message: string);
22
40
  }
23
41
  /**
24
42
  * 校验 Plan 是否满足全部领域不变量,违反则抛出 PlanInvariantError。
25
- * plan_write / plan_execute 写库前必须调用;供两插件共享同一份判定,避免理解漂移。
43
+ * 写入方落库前调用;共享函数签名保持稳定,供旧消费者继续复用。
26
44
  *
27
- * 覆盖的不变量(见 02-PlanStore设计 §2):
28
- * 1. 全计划最多一个 in_progress task。
29
- * 2. 每个 task 内最多一个 in_progress step。
30
- * 3. status==="blocked" 的 task 必须有非空 note。
31
- * 4. planId / goal 非空;tasks 至少一项。
32
- * 5. task.status / step.status 取值必须在枚举内。
45
+ * 仅校验持久化文档的基础形状:planId / goal 非空、tasks 至少一项,
46
+ * task.status / step.status 取值在枚举内。执行纪律由工具 description 引导,
47
+ * 不在共享层阻断草稿计划落库。
33
48
  */
34
49
  export declare function assertPlanInvariants(plan: Plan): void;
35
50
  //# sourceMappingURL=plan-types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"plan-types.d.ts","sourceRoot":"","sources":["../src/plan-types.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,aAAa,GAAG,WAAW,GAAG,SAAS,CAAC;AAC7E,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,aAAa,GAAG,WAAW,CAAC;AAUjE,MAAM,WAAW,IAAI;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,UAAU,CAAC;CACpB;AAED,MAAM,WAAW,IAAI;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,UAAU,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;CAChB;AAED,MAAM,WAAW,IAAI;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,IAAI,EAAE,CAAC;CACf;AAED,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAUD;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAwDrD"}
1
+ {"version":3,"file":"plan-types.d.ts","sourceRoot":"","sources":["../src/plan-types.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,aAAa,GAAG,WAAW,GAAG,SAAS,CAAC;AAC7E,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,aAAa,GAAG,WAAW,CAAC;AAUjE,MAAM,WAAW,IAAI;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,UAAU,CAAC;CACpB;AAED,MAAM,WAAW,IAAI;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,UAAU,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;CAChB;AAED;;;;GAIG;AACH,MAAM,WAAW,UAAU;IACzB,iCAAiC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8BAA8B;IAC9B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,6BAA6B;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4BAA4B;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,IAAI;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB;AAED,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAUD;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CA8BrD"}
@@ -20,14 +20,11 @@ function isStepStatus(value) {
20
20
  }
21
21
  /**
22
22
  * 校验 Plan 是否满足全部领域不变量,违反则抛出 PlanInvariantError。
23
- * plan_write / plan_execute 写库前必须调用;供两插件共享同一份判定,避免理解漂移。
23
+ * 写入方落库前调用;共享函数签名保持稳定,供旧消费者继续复用。
24
24
  *
25
- * 覆盖的不变量(见 02-PlanStore设计 §2):
26
- * 1. 全计划最多一个 in_progress task。
27
- * 2. 每个 task 内最多一个 in_progress step。
28
- * 3. status==="blocked" 的 task 必须有非空 note。
29
- * 4. planId / goal 非空;tasks 至少一项。
30
- * 5. task.status / step.status 取值必须在枚举内。
25
+ * 仅校验持久化文档的基础形状:planId / goal 非空、tasks 至少一项,
26
+ * task.status / step.status 取值在枚举内。执行纪律由工具 description 引导,
27
+ * 不在共享层阻断草稿计划落库。
31
28
  */
32
29
  export function assertPlanInvariants(plan) {
33
30
  if (plan.planId.trim() === "") {
@@ -39,34 +36,17 @@ export function assertPlanInvariants(plan) {
39
36
  if (plan.tasks.length === 0) {
40
37
  throw new PlanInvariantError("tasks 至少一项");
41
38
  }
42
- let inProgressTaskCount = 0;
43
39
  plan.tasks.forEach((task, taskIndex) => {
44
40
  if (!isTaskStatus(task.status)) {
45
41
  throw new PlanInvariantError(`tasks[${taskIndex}].status 取值非法: ${task.status}`);
46
42
  }
47
- if (task.status === "in_progress") {
48
- inProgressTaskCount += 1;
49
- }
50
- if (task.status === "blocked" && (task.note === undefined || task.note.trim() === "")) {
51
- throw new PlanInvariantError(`tasks[${taskIndex}] 状态为 blocked 时必须带非空 note`);
52
- }
53
43
  if (task.steps === undefined) {
54
44
  return;
55
45
  }
56
- let inProgressStepCount = 0;
57
46
  task.steps.forEach((step, stepIndex) => {
58
47
  if (!isStepStatus(step.status)) {
59
48
  throw new PlanInvariantError(`tasks[${taskIndex}].steps[${stepIndex}].status 取值非法: ${step.status}`);
60
49
  }
61
- if (step.status === "in_progress") {
62
- inProgressStepCount += 1;
63
- }
64
50
  });
65
- if (inProgressStepCount > 1) {
66
- throw new PlanInvariantError(`tasks[${taskIndex}] 内 in_progress 的 step 超过一个`);
67
- }
68
51
  });
69
- if (inProgressTaskCount > 1) {
70
- throw new PlanInvariantError("全计划 in_progress 的 task 超过一个");
71
- }
72
52
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xgjktech/xg-openclaw-shared",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "xgjktech OpenClaw 插件共享层:Plan 数据模型/不变量、SQLite PlanStore、计划事件契约",
5
5
  "license": "MIT",
6
6
  "type": "module",