@sema-agent/server 7.49.0 → 7.51.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/USAGE.md +39 -0
- package/dist/boot/device-lane.d.ts +79 -0
- package/dist/boot/device-lane.js +63 -0
- package/dist/boot/engine-lease.d.ts +95 -0
- package/dist/boot/engine-lease.js +56 -0
- package/dist/boot/execution-env.d.ts +48 -2
- package/dist/boot/execution-env.js +57 -5
- package/dist/boot/resolve-spec.d.ts +3 -0
- package/dist/boot/resolve-spec.js +12 -9
- package/dist/boot/session-faces.d.ts +5 -0
- package/dist/boot/session-faces.js +4 -1
- package/dist/boot/shutdown.d.ts +10 -0
- package/dist/boot/shutdown.js +19 -1
- package/dist/boot/stores.js +9 -0
- package/dist/config-center/types.d.ts +10 -3
- package/dist/config-invariants.d.ts +2 -2
- package/dist/config-invariants.js +18 -0
- package/dist/config-types.d.ts +32 -1
- package/dist/config.js +69 -10
- package/dist/device-enrollment.d.ts +125 -0
- package/dist/device-enrollment.js +156 -0
- package/dist/device-store.d.ts +385 -0
- package/dist/device-store.js +407 -0
- package/dist/device-ws-hub.d.ts +182 -0
- package/dist/device-ws-hub.js +1012 -0
- package/dist/device-ws-protocol.d.ts +429 -0
- package/dist/device-ws-protocol.js +464 -0
- package/dist/env-facts.d.ts +4 -1
- package/dist/env-facts.js +1 -0
- package/dist/execution-lane-caps.d.ts +152 -0
- package/dist/execution-lane-caps.js +166 -0
- package/dist/fleet/subagent-tail-bus.d.ts +1 -1
- package/dist/fleet/subagent-tail-bus.js +10 -0
- package/dist/http/routes/approvals-assistant.js +1 -1
- package/dist/http/routes/capabilities.js +7 -2
- package/dist/http/routes/sessions.js +1 -1
- package/dist/http/routes/tasks.js +12 -6
- package/dist/http/send.d.ts +14 -0
- package/dist/http/send.js +5 -0
- package/dist/http/server.d.ts +19 -0
- package/dist/http/server.js +21 -2
- package/dist/http/wire-types.d.ts +1 -1
- package/dist/leader/wire.js +4 -2
- package/dist/main.js +10 -3
- package/dist/orchestration/hardened-vm-runner.d.ts +7 -0
- package/dist/orchestration/hardened-vm-runner.js +11 -1
- package/dist/orchestration/hardened-vm-worker-runner.js +2 -2
- package/dist/plugins/device-store-sql.d.ts +130 -0
- package/dist/plugins/device-store-sql.js +574 -0
- package/dist/plugins/remote-env-device.d.ts +271 -0
- package/dist/plugins/remote-env-device.js +727 -0
- package/dist/plugins/remote-scratchpad.js +1 -1
- package/dist/plugins/store-backend.d.ts +9 -0
- package/dist/plugins/store-backend.js +3 -0
- package/dist/plugins/usage-window-store-sql.d.ts +2 -2
- package/dist/plugins/usage-window-store-sql.js +17 -6
- package/dist/task-cwd.d.ts +25 -0
- package/dist/task-cwd.js +3 -0
- package/dist/task-settings.js +3 -1
- package/dist/trace/core-keyset-guard.d.ts +3 -3
- package/dist/trace/engine-notice-wire.d.ts +1 -1
- package/dist/trace/engine-notice-wire.js +1 -0
- package/package.json +3 -3
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* device lane 四表持久层(design/device-executor-lane-v2 §6)—— `DeviceStore` 双方言实现
|
|
3
|
+
* (`TiDBDeviceStore` / `PgDeviceStore`,SINGLE-FILE DUAL-DIALECT,design/158 A12 形:一个
|
|
4
|
+
* `SqlDeviceStore` 接 `SqlDriver`,两个薄 ctor 子类落方言绑定)。语义真源 = `../device-store.js`
|
|
5
|
+
* (类型 + 闭集 + InMemory 参照实现),准入链 = `../device-enrollment.js`。
|
|
6
|
+
*
|
|
7
|
+
* 四张表:
|
|
8
|
+
* · `devices` —— 设备注册簿 + presence 投影(单设备单活连 ⇒ 1:1,省一次 join,§6 注①)
|
|
9
|
+
* · `device_enroll_tokens` —— 一次性 enrollment 凭据(明文一现,落库只有 sha256)
|
|
10
|
+
* · `device_sessions` —— **根会话**↔设备绑定(resume 路由真源,§4.5)
|
|
11
|
+
* · `device_audit` —— 安全轴事件审计(§4.8 标「审计行」的 8 个 event)
|
|
12
|
+
* 刻意**没有** `device_instructions` 表:指令 pending 表是内存态(§6 注③——指令比 run 短命,落库只会
|
|
13
|
+
* 造「表里 pending、run 已死」的僵尸行;at-most-once 靠 §5.3 投递边界,不靠指令落库)。
|
|
14
|
+
*
|
|
15
|
+
* ── CAS 铁则(与 approval-ask-store-sql.ts 头注同源,写在这里防遗忘)─────────────────────────────
|
|
16
|
+
* 1. 一切转移带 `WHERE <前态>`,赢 = affected 恰为 1。禁读-改-写两步(TOCTOU 窗口)。
|
|
17
|
+
* 2. 🔴 MySQL/TiDB `affectedRows` 坑:同值 UPDATE(SET 的新值与当前列逐字节相同)affectedRows=0
|
|
18
|
+
* ——**所有** CAS UPDATE 因此必须带一个恒变列(`rev = rev + 1`)。本文件每一条 CAS UPDATE 都带。
|
|
19
|
+
* 在本店这条尤其致命:心跳续租在「同一毫秒内重复心跳」时 SET 的值可能与当前值相同,没有 rev
|
|
20
|
+
* 就会被判成「本连接已失权」⇒ 设备被自己的心跳踢下线。
|
|
21
|
+
* 3. 事务:`SqlDriver.connect()` → `begin()`/`beginPessimistic()` → `query()` → `commit()`,失败
|
|
22
|
+
* `rollback()`。**失败臂的回读必须走当前这条 `conn`**(approval 店复审 F1 的真缺陷:走池级 query
|
|
23
|
+
* 会与自己持有的连接死锁),且**先 rollback 再读**(读到最新已提交视图)。
|
|
24
|
+
*
|
|
25
|
+
* ── 方言差异(A12 doctrine:每处显式写在调用点)────────────────────────────────────────────────────
|
|
26
|
+
* - `?`(位置序) vs `$n`(显式编号);动态 WHERE(`listAudit`)靠 `ph(dialect, n)` 生成。
|
|
27
|
+
* - `INSERT IGNORE` vs `ON CONFLICT (pk) DO NOTHING`(`bindSession` 的首绑 CAS)。
|
|
28
|
+
* - `AUTO_INCREMENT` vs `BIGSERIAL`;`JSON` vs `JSONB`(+ `$n::jsonb` 显式 cast)。
|
|
29
|
+
* - `KEY …` 内联 vs 独立 `CREATE INDEX IF NOT EXISTS`。
|
|
30
|
+
* - 回读名单:PG 用 `UPDATE … RETURNING`,TiDB 无 RETURNING ⇒ 同事务 `UPDATE` 后 `SELECT`。
|
|
31
|
+
* - 时长运算:`NOW(3) + INTERVAL ? SECOND` vs `now() + ($n::double precision * INTERVAL '1 second')`。
|
|
32
|
+
* - 键列字节等价:MySQL `VARBINARY` vs PG `VARCHAR … COLLATE "C"`(理由见 ../device-store.ts 头注;
|
|
33
|
+
* `utf8mb4_bin` **不够**,它仍是 PAD SPACE collation)。
|
|
34
|
+
* - VARBINARY 的读回:mysql2 给 **Buffer**,PG 给 string ⇒ 一律过 `bufToStr`(mailbox 真库坑同解)。
|
|
35
|
+
*
|
|
36
|
+
* ── 时间列的存取形(§4.6 task-R3-F18「时间权威 = DB」的落地)──────────────────────────────────────
|
|
37
|
+
* 列是原生 `DATETIME(3)` / `TIMESTAMPTZ(3)`,但**跨边界一律是 epoch 毫秒**:写侧送**时长秒**由 SQL 从
|
|
38
|
+
* DB 的 now 起算,读侧在 SELECT 里就转成 `*_ms`。
|
|
39
|
+
* 🔴 为什么不让驱动把时间戳解成 JS `Date` 再比:①mysql2 按**进程**时区解析 DATETIME,DB 会话时区与
|
|
40
|
+
* Node 时区不同即整体偏移几小时(而这条偏移落在「租约还有效吗」上就是双主);②`Date.now()` 一旦进入
|
|
41
|
+
* 判据,副本时钟落后即越租。两条都在 SELECT 表达式里就地关掉。
|
|
42
|
+
*/
|
|
43
|
+
import type { Pool as MySqlPool } from "mysql2/promise";
|
|
44
|
+
import type { Pool as PgPool } from "pg";
|
|
45
|
+
import { type SqlDriver } from "./sql-driver.js";
|
|
46
|
+
import { type AcquireConnectionInput, type AcquireConnectionResult, type AppendAuditInput, type BindSessionInput, type BindSessionResult, type ConsumeEnrollTokenInput, type ConsumeEnrollTokenResult, type DeviceAdmissionView, type DeviceAuditRow, type DeviceEnrollTokenRow, type DeviceOwner, type DeviceRow, type DeviceSessionRow, type DeviceStore, type HeartbeatConnectionInput, type IssueEnrollTokenInput, type ListAuditFilter, type RevokeDeviceInput, type RevokeDeviceResult } from "../device-store.js";
|
|
47
|
+
/**
|
|
48
|
+
* ── 命名(**落库一律单数**,与设计稿的复数形不同)──────────────────────────────────────────────────
|
|
49
|
+
* design v2 §6 的 DDL 写的是 `devices` / `device_sessions` / `device_enrollment_tokens`(复数),但本仓有一条
|
|
50
|
+
* 常驻机器门:`test/schema-naming-invariants.test.ts` 判据① —— 建表的表名必须在 `TABLE_VOCABULARY` 闭集里
|
|
51
|
+
* **且词表全是单数形**。#270 车1 的 `retention_hold` / `retention_tombstone` 是同一处先例(那份设计稿同样
|
|
52
|
+
* 写的复数,落库单数)。所以四张表落库名为 `device` / `device_session` / `device_enroll_token` /
|
|
53
|
+
* `device_audit`,**表结构与设计稿逐列一致**,只有名字随仓规。新表 = 先进词表再建。
|
|
54
|
+
*/
|
|
55
|
+
export declare const DEVICES_TABLE = "device";
|
|
56
|
+
export declare const DEVICE_ENROLL_TOKENS_TABLE = "device_enroll_token";
|
|
57
|
+
export declare const DEVICE_SESSIONS_TABLE = "device_session";
|
|
58
|
+
export declare const DEVICE_AUDIT_TABLE = "device_audit";
|
|
59
|
+
export declare const TIDB_DEVICE_STATEMENTS: readonly string[];
|
|
60
|
+
/** {@link TIDB_DEVICE_STATEMENTS} 的遍历壳(集成测试用;生产装配由中央 ensureSchema 展开同一份数组)。 */
|
|
61
|
+
export declare function ensureTiDBDeviceSchema(pool: MySqlPool): Promise<void>;
|
|
62
|
+
type PgQuery = (sql: string, params?: unknown[]) => Promise<unknown>;
|
|
63
|
+
export declare function ensurePgDeviceSchema(q: PgQuery): Promise<void>;
|
|
64
|
+
export declare class SqlDeviceStore implements DeviceStore {
|
|
65
|
+
private readonly db;
|
|
66
|
+
constructor(db: SqlDriver);
|
|
67
|
+
private q;
|
|
68
|
+
private json;
|
|
69
|
+
issueEnrollToken(input: IssueEnrollTokenInput): Promise<DeviceEnrollTokenRow>;
|
|
70
|
+
getEnrollToken(tokenHash: string): Promise<DeviceEnrollTokenRow | null>;
|
|
71
|
+
/**
|
|
72
|
+
* §6 注册协议 —— **单事务**:消费 CAS + INSERT devices + 审计行一起提交。
|
|
73
|
+
*
|
|
74
|
+
* 谓词四件(缺一即 affected=0):`consumed_at IS NULL`(一次性)、`expires_at > NOW(3)`(TTL,DB 钟)、
|
|
75
|
+
* `target_tenant/target_subject` 等值(绑定 principal)。affected=0 的**理由**靠回滚后的回读判别 ——
|
|
76
|
+
* 顺序刻意是「先 principal 后消费态」:token 不是你的就连「它被没被用过」都不告诉你。
|
|
77
|
+
*/
|
|
78
|
+
consumeEnrollToken(input: ConsumeEnrollTokenInput): Promise<ConsumeEnrollTokenResult>;
|
|
79
|
+
/** 事务内/持连接期间的设备回读(见 consumeEnrollToken 的 F2 注:此期间**禁**走池级读)。 */
|
|
80
|
+
private readDeviceOn;
|
|
81
|
+
readDeviceForAdmission(deviceId: string, owner: DeviceOwner): Promise<DeviceAdmissionView | null>;
|
|
82
|
+
getDeviceUnscoped(deviceId: string): Promise<DeviceRow | null>;
|
|
83
|
+
listDevicesByOwner(owner: DeviceOwner): Promise<DeviceRow[]>;
|
|
84
|
+
/**
|
|
85
|
+
* 吊销 = 终态 CAS(`status='active'` 前态)+ **租约作废 + 世代推进**(§8-R4 在途语义):
|
|
86
|
+
* 世代一推,在途连接的每一次带世代谓词的写(心跳/结果帧入账)当场影响 0 行 ⇒ 设备侧立即失权。
|
|
87
|
+
*/
|
|
88
|
+
revokeDevice(input: RevokeDeviceInput): Promise<RevokeDeviceResult>;
|
|
89
|
+
/**
|
|
90
|
+
* 连接建立 = 带 expiry 的租约 CAS。谓词 `lease_expires_at IS NULL OR <= NOW(3)`:**活租约恒不被撕**
|
|
91
|
+
* (§5.2 单活连不变量;`DEVICE_SUPERSEDE=stale_only` 的语义就在这一句),过期租约才可被新连夺取。
|
|
92
|
+
* 赢家 `conn_generation` 单调 +1,后续所有写都带这个世代谓词。
|
|
93
|
+
*/
|
|
94
|
+
acquireConnection(input: AcquireConnectionInput): Promise<AcquireConnectionResult>;
|
|
95
|
+
/**
|
|
96
|
+
* 心跳续租 —— 谓词 = **世代等值 + active**。影响 0 行 = 本连接已失权(被顶替 / 被吊销)⇒ 调用方
|
|
97
|
+
* 主动断开(§4.6 步 2)。
|
|
98
|
+
*
|
|
99
|
+
* 🔴 允许续一条**已过期但没被别人夺走**的租约(generation 未变 ⇒ 排他性仍在本连接手上);夺租者
|
|
100
|
+
* 一旦成功,generation 就变了,这条心跳当场 0 行。`rev = rev + 1` 是 CAS 铁则②:同毫秒重复心跳的
|
|
101
|
+
* SET 值可能与当前逐字节相同,没有它 MySQL 会把「成功」报成 affectedRows=0。
|
|
102
|
+
*/
|
|
103
|
+
heartbeatConnection(input: HeartbeatConnectionInput): Promise<boolean>;
|
|
104
|
+
releaseConnection(deviceId: string, generation: number): Promise<boolean>;
|
|
105
|
+
/**
|
|
106
|
+
* §4.3.2 首绑写协议 —— **单事务**:设备校验(active ∧ owner)与 `INSERT IGNORE` 在一个事务里。
|
|
107
|
+
*
|
|
108
|
+
* 🔴 `beginPessimistic` + `FOR UPDATE`:设备行必须是**当前读**。快照读下,一次与吊销并发的绑定会
|
|
109
|
+
* 读到「还 active」的旧版本,于是把 session 绑到一台**刚被吊销**的设备上(sql-driver.ts 头注点名的
|
|
110
|
+
* double-admit 形)。
|
|
111
|
+
* 并发首绑:`INSERT IGNORE` / `ON CONFLICT DO NOTHING` 的 affected 是引擎对「谁赢」的权威回答;
|
|
112
|
+
* 输家回读既有行 —— 同 deviceId = 幂等成功,异 deviceId = `conflict`(无未定义态)。
|
|
113
|
+
*/
|
|
114
|
+
bindSession(input: BindSessionInput): Promise<BindSessionResult>;
|
|
115
|
+
getSessionBinding(rootSessionId: string): Promise<DeviceSessionRow | null>;
|
|
116
|
+
deleteByRootSession(rootSessionId: string): Promise<number>;
|
|
117
|
+
appendAudit(input: AppendAuditInput): Promise<void>;
|
|
118
|
+
/** 审计写的**唯一**语句(池级与事务内共用;`SqlExec` 是两者的公共面)。 */
|
|
119
|
+
private insertAuditOn;
|
|
120
|
+
listAudit(filter?: ListAuditFilter): Promise<DeviceAuditRow[]>;
|
|
121
|
+
}
|
|
122
|
+
/** 方言绑定薄壳(A12 形:ctor 收池,行为全在基类)。 */
|
|
123
|
+
export declare class TiDBDeviceStore extends SqlDeviceStore {
|
|
124
|
+
constructor(pool: MySqlPool);
|
|
125
|
+
}
|
|
126
|
+
export declare class PgDeviceStore extends SqlDeviceStore {
|
|
127
|
+
constructor(pool: PgPool);
|
|
128
|
+
}
|
|
129
|
+
export {};
|
|
130
|
+
//# sourceMappingURL=device-store-sql.d.ts.map
|