@xgjktech/xg-openclaw-shared 0.2.1 → 0.2.3

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.
@@ -0,0 +1,37 @@
1
+ import type { ActionDispatchEvent } from "./action-types.js";
2
+ export interface ActionStore {
3
+ get(eventId: string): ActionDispatchEvent | undefined;
4
+ set(event: ActionDispatchEvent): void;
5
+ delete(eventId: string): void;
6
+ list(): ActionDispatchEvent[];
7
+ listActionsByOrigin(origin: {
8
+ channel: string;
9
+ to: string;
10
+ }): ActionDispatchEvent[];
11
+ }
12
+ export declare class ActionStoreError extends Error {
13
+ constructor(message: string, options?: {
14
+ cause?: unknown;
15
+ });
16
+ }
17
+ /**
18
+ * SqliteActionStore:动作指令的唯一写者。读写走事务,异常一律转 ActionStoreError。
19
+ */
20
+ export declare class SqliteActionStore implements ActionStore {
21
+ private readonly db;
22
+ constructor(dbPath: string);
23
+ close(): void;
24
+ get(eventId: string): ActionDispatchEvent | undefined;
25
+ set(event: ActionDispatchEvent): void;
26
+ delete(eventId: string): void;
27
+ list(): ActionDispatchEvent[];
28
+ listActionsByOrigin(origin: {
29
+ channel: string;
30
+ to: string;
31
+ }): ActionDispatchEvent[];
32
+ }
33
+ /**
34
+ * IM 侧只读入口:以 readOnly 模式打开同一 DB,杜绝误写。
35
+ */
36
+ export declare function openActionStoreReadonly(dbPath: string): Pick<ActionStore, "get" | "list" | "listActionsByOrigin">;
37
+ //# sourceMappingURL=action-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action-store.d.ts","sourceRoot":"","sources":["../src/action-store.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAE7D,MAAM,WAAW,WAAW;IAC1B,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS,CAAC;IACtD,GAAG,CAAC,KAAK,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACtC,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,IAAI,IAAI,mBAAmB,EAAE,CAAC;IAC9B,mBAAmB,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG,mBAAmB,EAAE,CAAC;CACrF;AAED,qBAAa,gBAAiB,SAAQ,KAAK;gBAC7B,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAI3D;AAsJD;;GAEG;AACH,qBAAa,iBAAkB,YAAW,WAAW;IACnD,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAe;gBAEtB,MAAM,EAAE,MAAM;IAI1B,KAAK,IAAI,IAAI;IAIb,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS;IAIrD,GAAG,CAAC,KAAK,EAAE,mBAAmB,GAAG,IAAI;IAyCrC,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAQ7B,IAAI,IAAI,mBAAmB,EAAE;IAI7B,mBAAmB,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG,mBAAmB,EAAE;CAGpF;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,MAAM,GACb,IAAI,CAAC,WAAW,EAAE,KAAK,GAAG,MAAM,GAAG,qBAAqB,CAAC,CAkB3D"}
@@ -0,0 +1,202 @@
1
+ import { DatabaseSync } from "node:sqlite";
2
+ export class ActionStoreError extends Error {
3
+ constructor(message, options) {
4
+ super(message, options);
5
+ this.name = "ActionStoreError";
6
+ }
7
+ }
8
+ const BUSY_TIMEOUT_MS = 5000;
9
+ const MAX_RETAIN_RECORDS = 1000;
10
+ const RETENTION_MS = 7 * 24 * 60 * 60 * 1000; // 7 天
11
+ const CREATE_TABLE_SQL = `
12
+ CREATE TABLE IF NOT EXISTS actions (
13
+ event_id TEXT PRIMARY KEY,
14
+ action TEXT NOT NULL,
15
+ target TEXT NOT NULL,
16
+ version INTEGER NOT NULL,
17
+ origin_channel TEXT,
18
+ origin_to TEXT,
19
+ created_at TEXT NOT NULL,
20
+ doc TEXT NOT NULL
21
+ ) STRICT;
22
+ `;
23
+ const CREATE_INDEX_CREATED_AT_SQL = `
24
+ CREATE INDEX IF NOT EXISTS idx_actions_created_at ON actions(created_at);
25
+ `;
26
+ const CREATE_INDEX_ORIGIN_SQL = `
27
+ CREATE INDEX IF NOT EXISTS idx_actions_origin ON actions(origin_channel, origin_to);
28
+ `;
29
+ function listColumnNames(db) {
30
+ const rows = db.prepare("PRAGMA table_info(actions);").all();
31
+ return new Set(rows.map((row) => row.name));
32
+ }
33
+ function parseActionDoc(row) {
34
+ try {
35
+ return JSON.parse(row.doc);
36
+ }
37
+ catch (cause) {
38
+ throw new ActionStoreError(`action ${row.event_id} 的 doc 列不是合法 JSON`, { cause });
39
+ }
40
+ }
41
+ function ensureSchema(db) {
42
+ db.exec(CREATE_TABLE_SQL);
43
+ db.exec(CREATE_INDEX_CREATED_AT_SQL);
44
+ db.exec(CREATE_INDEX_ORIGIN_SQL);
45
+ }
46
+ function openDatabase(dbPath, options) {
47
+ try {
48
+ const db = new DatabaseSync(dbPath, { readOnly: options.readOnly });
49
+ db.exec(`PRAGMA busy_timeout = ${BUSY_TIMEOUT_MS};`);
50
+ if (!options.readOnly) {
51
+ db.exec("PRAGMA journal_mode = WAL;");
52
+ ensureSchema(db);
53
+ }
54
+ return db;
55
+ }
56
+ catch (cause) {
57
+ throw new ActionStoreError(`打开 ActionStore 数据库失败: ${dbPath}`, { cause });
58
+ }
59
+ }
60
+ function readActionRow(db, eventId) {
61
+ return db
62
+ .prepare("SELECT event_id, action, target, version, origin_channel, origin_to, created_at, doc FROM actions WHERE event_id = ?")
63
+ .get(eventId);
64
+ }
65
+ function readAllActionRows(db) {
66
+ return db
67
+ .prepare("SELECT event_id, action, target, version, origin_channel, origin_to, created_at, doc FROM actions ORDER BY created_at DESC")
68
+ .all();
69
+ }
70
+ function readActionRowsByOrigin(db, origin) {
71
+ return db
72
+ .prepare(`SELECT event_id, action, target, version, origin_channel, origin_to, created_at, doc FROM actions
73
+ WHERE origin_channel = ? AND origin_to = ?
74
+ ORDER BY created_at DESC`)
75
+ .all(origin.channel, origin.to);
76
+ }
77
+ function getAction(db, eventId, errorPrefix) {
78
+ try {
79
+ const row = readActionRow(db, eventId);
80
+ return row === undefined ? undefined : parseActionDoc(row);
81
+ }
82
+ catch (cause) {
83
+ if (cause instanceof ActionStoreError)
84
+ throw cause;
85
+ throw new ActionStoreError(`${errorPrefix} action ${eventId} 失败`, { cause });
86
+ }
87
+ }
88
+ function listActions(db, errorPrefix) {
89
+ try {
90
+ return readAllActionRows(db).map((row) => parseActionDoc(row));
91
+ }
92
+ catch (cause) {
93
+ if (cause instanceof ActionStoreError)
94
+ throw cause;
95
+ throw new ActionStoreError(`${errorPrefix} action 列表失败`, { cause });
96
+ }
97
+ }
98
+ function listActionsByOrigin(db, origin, errorPrefix) {
99
+ try {
100
+ return readActionRowsByOrigin(db, origin).map((row) => parseActionDoc(row));
101
+ }
102
+ catch (cause) {
103
+ if (cause instanceof ActionStoreError)
104
+ throw cause;
105
+ throw new ActionStoreError(`${errorPrefix} action 按 origin 列表失败`, { cause });
106
+ }
107
+ }
108
+ function runTtlCleanup(db, nowMs) {
109
+ const cutoffIso = new Date(nowMs - RETENTION_MS).toISOString();
110
+ db.prepare("DELETE FROM actions WHERE created_at < ?").run(cutoffIso);
111
+ db.prepare(`DELETE FROM actions WHERE event_id NOT IN (
112
+ SELECT event_id FROM actions ORDER BY created_at DESC LIMIT ${MAX_RETAIN_RECORDS}
113
+ )`).run();
114
+ }
115
+ /**
116
+ * SqliteActionStore:动作指令的唯一写者。读写走事务,异常一律转 ActionStoreError。
117
+ */
118
+ export class SqliteActionStore {
119
+ db;
120
+ constructor(dbPath) {
121
+ this.db = openDatabase(dbPath, { readOnly: false });
122
+ }
123
+ close() {
124
+ this.db.close();
125
+ }
126
+ get(eventId) {
127
+ return getAction(this.db, eventId, "读取");
128
+ }
129
+ set(event) {
130
+ try {
131
+ this.db.exec("BEGIN IMMEDIATE");
132
+ try {
133
+ const createdAt = event.timestamp ?? new Date().toISOString();
134
+ this.db
135
+ .prepare(`INSERT INTO actions (event_id, action, target, version, origin_channel, origin_to, created_at, doc)
136
+ VALUES (@eventId, @action, @target, @version, @originChannel, @originTo, @createdAt, @doc)
137
+ ON CONFLICT(event_id) DO UPDATE SET
138
+ action = excluded.action,
139
+ target = excluded.target,
140
+ version = excluded.version,
141
+ origin_channel = excluded.origin_channel,
142
+ origin_to = excluded.origin_to,
143
+ created_at = excluded.created_at,
144
+ doc = excluded.doc`)
145
+ .run({
146
+ eventId: event.eventId,
147
+ action: event.action,
148
+ target: event.target,
149
+ version: event.version,
150
+ originChannel: event.origin?.channel ?? null,
151
+ originTo: event.origin?.to ?? null,
152
+ createdAt: createdAt,
153
+ doc: JSON.stringify(event),
154
+ });
155
+ runTtlCleanup(this.db, Date.now());
156
+ this.db.exec("COMMIT");
157
+ }
158
+ catch (cause) {
159
+ this.db.exec("ROLLBACK");
160
+ throw cause;
161
+ }
162
+ }
163
+ catch (cause) {
164
+ throw new ActionStoreError(`写入 action ${event.eventId} 失败`, { cause });
165
+ }
166
+ }
167
+ delete(eventId) {
168
+ try {
169
+ this.db.prepare("DELETE FROM actions WHERE event_id = ?").run(eventId);
170
+ }
171
+ catch (cause) {
172
+ throw new ActionStoreError(`删除 action ${eventId} 失败`, { cause });
173
+ }
174
+ }
175
+ list() {
176
+ return listActions(this.db, "列出");
177
+ }
178
+ listActionsByOrigin(origin) {
179
+ return listActionsByOrigin(this.db, origin, "列出");
180
+ }
181
+ }
182
+ /**
183
+ * IM 侧只读入口:以 readOnly 模式打开同一 DB,杜绝误写。
184
+ */
185
+ export function openActionStoreReadonly(dbPath) {
186
+ const db = openDatabase(dbPath, { readOnly: true });
187
+ return {
188
+ get(eventId) {
189
+ return getAction(db, eventId, "只读读取");
190
+ },
191
+ list() {
192
+ return listActions(db, "只读列出");
193
+ },
194
+ listActionsByOrigin(origin) {
195
+ const columns = listColumnNames(db);
196
+ if (!columns.has("origin_channel") || !columns.has("origin_to")) {
197
+ return [];
198
+ }
199
+ return listActionsByOrigin(db, origin, "只读列出");
200
+ },
201
+ };
202
+ }
@@ -0,0 +1,28 @@
1
+ import type { PlanOrigin } from "./plan-types.js";
2
+ export type ActionStatus = "completed" | "running" | "pending" | "failed";
3
+ export interface ActionScopeControl {
4
+ activeSessionOnly?: boolean;
5
+ }
6
+ export interface ActionDispatchPayload {
7
+ data: Record<string, unknown>;
8
+ }
9
+ export interface ActionDispatchEvent {
10
+ eventId: string;
11
+ event: "action_dispatch";
12
+ action: string;
13
+ topic?: string;
14
+ target: string;
15
+ version: number;
16
+ status: ActionStatus;
17
+ scopeControl?: ActionScopeControl;
18
+ payload: ActionDispatchPayload;
19
+ timestamp: string;
20
+ origin?: PlanOrigin;
21
+ }
22
+ export declare class ActionInvariantError extends Error {
23
+ constructor(message: string);
24
+ }
25
+ export declare function isActionStatus(value: unknown): value is ActionStatus;
26
+ export declare function isActionDispatchEvent(value: unknown): value is ActionDispatchEvent;
27
+ export declare function assertActionDispatchEventInvariants(event: ActionDispatchEvent): void;
28
+ //# sourceMappingURL=action-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action-types.d.ts","sourceRoot":"","sources":["../src/action-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;AAI1E,MAAM,WAAW,kBAAkB;IACjC,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,iBAAiB,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,YAAY,CAAC;IACrB,YAAY,CAAC,EAAE,kBAAkB,CAAC;IAClC,OAAO,EAAE,qBAAqB,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB;AAED,qBAAa,oBAAqB,SAAQ,KAAK;gBACjC,OAAO,EAAE,MAAM;CAI5B;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,YAAY,CAEpE;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,mBAAmB,CA+BlF;AAED,wBAAgB,mCAAmC,CAAC,KAAK,EAAE,mBAAmB,GAAG,IAAI,CAkCpF"}
@@ -0,0 +1,83 @@
1
+ const ACTION_STATUSES = ["completed", "running", "pending", "failed"];
2
+ export class ActionInvariantError extends Error {
3
+ constructor(message) {
4
+ super(message);
5
+ this.name = "ActionInvariantError";
6
+ }
7
+ }
8
+ export function isActionStatus(value) {
9
+ return typeof value === "string" && ACTION_STATUSES.includes(value);
10
+ }
11
+ export function isActionDispatchEvent(value) {
12
+ if (typeof value !== "object" || value === null) {
13
+ return false;
14
+ }
15
+ const evt = value;
16
+ if (typeof evt["eventId"] !== "string" || evt["eventId"].trim() === "")
17
+ return false;
18
+ if (evt["event"] !== "action_dispatch")
19
+ return false;
20
+ if (typeof evt["action"] !== "string" || evt["action"].trim() === "")
21
+ return false;
22
+ if (evt["topic"] !== undefined && typeof evt["topic"] !== "string")
23
+ return false;
24
+ if (typeof evt["target"] !== "string" || evt["target"].trim() === "")
25
+ return false;
26
+ if (typeof evt["version"] !== "number" || !Number.isFinite(evt["version"]) || evt["version"] <= 0) {
27
+ return false;
28
+ }
29
+ if (!isActionStatus(evt["status"]))
30
+ return false;
31
+ if (evt["scopeControl"] !== undefined) {
32
+ if (typeof evt["scopeControl"] !== "object" || evt["scopeControl"] === null)
33
+ return false;
34
+ const scopeCtrl = evt["scopeControl"];
35
+ if (scopeCtrl["activeSessionOnly"] !== undefined &&
36
+ typeof scopeCtrl["activeSessionOnly"] !== "boolean") {
37
+ return false;
38
+ }
39
+ }
40
+ if (typeof evt["payload"] !== "object" || evt["payload"] === null)
41
+ return false;
42
+ const payload = evt["payload"];
43
+ if (typeof payload["data"] !== "object" || payload["data"] === null)
44
+ return false;
45
+ if (typeof evt["timestamp"] !== "string" || evt["timestamp"].trim() === "")
46
+ return false;
47
+ return true;
48
+ }
49
+ export function assertActionDispatchEventInvariants(event) {
50
+ if (typeof event.eventId !== "string" || event.eventId.trim() === "") {
51
+ throw new ActionInvariantError("eventId 不能为空");
52
+ }
53
+ if (event.event !== "action_dispatch") {
54
+ throw new ActionInvariantError(`event 必须为 'action_dispatch',实际为: ${event.event}`);
55
+ }
56
+ if (typeof event.action !== "string" || event.action.trim() === "") {
57
+ throw new ActionInvariantError("action 不能为空");
58
+ }
59
+ if (event.topic !== undefined && typeof event.topic !== "string") {
60
+ throw new ActionInvariantError("topic 类型必须为 string");
61
+ }
62
+ if (typeof event.target !== "string" || event.target.trim() === "") {
63
+ throw new ActionInvariantError("target 不能为空");
64
+ }
65
+ if (event.target.length > 128) {
66
+ throw new ActionInvariantError("target 长度不能超过 128");
67
+ }
68
+ if (typeof event.version !== "number" || !Number.isFinite(event.version) || event.version <= 0) {
69
+ throw new ActionInvariantError(`version 取值非法: ${event.version}`);
70
+ }
71
+ if (!isActionStatus(event.status)) {
72
+ throw new ActionInvariantError(`status 取值非法: ${event.status}`);
73
+ }
74
+ if (typeof event.payload !== "object" || event.payload === null) {
75
+ throw new ActionInvariantError("payload 必须是非空对象");
76
+ }
77
+ if (typeof event.payload.data !== "object" || event.payload.data === null) {
78
+ throw new ActionInvariantError("payload.data 必须是非空对象");
79
+ }
80
+ if (typeof event.timestamp !== "string" || event.timestamp.trim() === "") {
81
+ throw new ActionInvariantError("timestamp 不能为空");
82
+ }
83
+ }
package/dist/index.d.ts CHANGED
@@ -1,7 +1,11 @@
1
1
  export type { Plan, Task, Step, TaskStatus, StepStatus, PlanOrigin } from "./plan-types.js";
2
- export { assertPlanInvariants, PlanInvariantError } from "./plan-types.js";
2
+ export { assertPlanInvariants, isPlanCancelled, 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";
5
5
  export type { PlanEventPayload, PlanEventSummary } from "./plan-events.js";
6
6
  export { planEventStream, isPlanEventPayload } from "./plan-events.js";
7
+ export type { ActionStatus, ActionScopeControl, ActionDispatchPayload, ActionDispatchEvent, } from "./action-types.js";
8
+ export { ActionInvariantError, isActionStatus, isActionDispatchEvent, assertActionDispatchEventInvariants, } from "./action-types.js";
9
+ export type { ActionStore } from "./action-store.js";
10
+ export { SqliteActionStore, openActionStoreReadonly, ActionStoreError } from "./action-store.js";
7
11
  //# sourceMappingURL=index.d.ts.map
@@ -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,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"}
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,eAAe,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAE5F,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;AAEvE,YAAY,EACV,YAAY,EACZ,kBAAkB,EAClB,qBAAqB,EACrB,mBAAmB,GACpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,oBAAoB,EACpB,cAAc,EACd,qBAAqB,EACrB,mCAAmC,GACpC,MAAM,mBAAmB,CAAC;AAE3B,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC"}
package/dist/index.js CHANGED
@@ -1,4 +1,6 @@
1
1
  // 桶文件:xg-shared 的唯一公开面。两插件(本插件 + IM 插件)均只从此入口 import。
2
- export { assertPlanInvariants, PlanInvariantError } from "./plan-types.js";
2
+ export { assertPlanInvariants, isPlanCancelled, PlanInvariantError } from "./plan-types.js";
3
3
  export { SqlitePlanStore, openPlanStoreReadonly, resolvePlanDbPath, PlanStoreError } from "./plan-store.js";
4
4
  export { planEventStream, isPlanEventPayload } from "./plan-events.js";
5
+ export { ActionInvariantError, isActionStatus, isActionDispatchEvent, assertActionDispatchEventInvariants, } from "./action-types.js";
6
+ export { SqliteActionStore, openActionStoreReadonly, ActionStoreError } from "./action-store.js";
@@ -34,17 +34,20 @@ export interface Plan {
34
34
  updatedAt: string;
35
35
  tasks: Task[];
36
36
  origin?: PlanOrigin;
37
+ status?: "active" | "cancelled";
38
+ cancelledAt?: string;
37
39
  }
38
40
  export declare class PlanInvariantError extends Error {
39
41
  constructor(message: string);
40
42
  }
43
+ export declare function isPlanCancelled(plan: Plan): boolean;
41
44
  /**
42
45
  * 校验 Plan 是否满足全部领域不变量,违反则抛出 PlanInvariantError。
43
46
  * 写入方落库前调用;共享函数签名保持稳定,供旧消费者继续复用。
44
47
  *
45
48
  * 仅校验持久化文档的基础形状:planId / goal 非空、tasks 至少一项,
46
- * task.status / step.status 取值在枚举内。执行纪律由工具 description 引导,
47
- * 不在共享层阻断草稿计划落库。
49
+ * plan.status / task.status / step.status 取值在枚举内。执行纪律由工具
50
+ * description 引导,不在共享层阻断草稿计划落库。
48
51
  */
49
52
  export declare function assertPlanInvariants(plan: Plan): void;
50
53
  //# 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;;;;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"}
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;IACpB,MAAM,CAAC,EAAE,QAAQ,GAAG,WAAW,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAUD,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAEnD;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAiCrD"}
@@ -18,13 +18,16 @@ function isTaskStatus(value) {
18
18
  function isStepStatus(value) {
19
19
  return STEP_STATUSES.includes(value);
20
20
  }
21
+ export function isPlanCancelled(plan) {
22
+ return plan.status === "cancelled";
23
+ }
21
24
  /**
22
25
  * 校验 Plan 是否满足全部领域不变量,违反则抛出 PlanInvariantError。
23
26
  * 写入方落库前调用;共享函数签名保持稳定,供旧消费者继续复用。
24
27
  *
25
28
  * 仅校验持久化文档的基础形状:planId / goal 非空、tasks 至少一项,
26
- * task.status / step.status 取值在枚举内。执行纪律由工具 description 引导,
27
- * 不在共享层阻断草稿计划落库。
29
+ * plan.status / task.status / step.status 取值在枚举内。执行纪律由工具
30
+ * description 引导,不在共享层阻断草稿计划落库。
28
31
  */
29
32
  export function assertPlanInvariants(plan) {
30
33
  if (plan.planId.trim() === "") {
@@ -36,6 +39,9 @@ export function assertPlanInvariants(plan) {
36
39
  if (plan.tasks.length === 0) {
37
40
  throw new PlanInvariantError("tasks 至少一项");
38
41
  }
42
+ if (plan.status !== undefined && plan.status !== "active" && plan.status !== "cancelled") {
43
+ throw new PlanInvariantError(`status 取值非法: ${plan.status}`);
44
+ }
39
45
  plan.tasks.forEach((task, taskIndex) => {
40
46
  if (!isTaskStatus(task.status)) {
41
47
  throw new PlanInvariantError(`tasks[${taskIndex}].status 取值非法: ${task.status}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xgjktech/xg-openclaw-shared",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "xgjktech OpenClaw 插件共享层:Plan 数据模型/不变量、SQLite PlanStore、计划事件契约",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -32,4 +32,4 @@
32
32
  "@types/node": "^22.19.0",
33
33
  "typescript": "^5.6.0"
34
34
  }
35
- }
35
+ }