dsh-hams 0.1.3 → 0.1.4
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/CHANGELOG.md +3 -3
- package/README.md +4 -0
- package/dist/index.js +11335 -0
- package/package.json +9 -5
- package/src/domain/check.ts +0 -193
- package/src/domain/errors.ts +0 -59
- package/src/domain/evidence.ts +0 -134
- package/src/domain/ids.ts +0 -31
- package/src/host/chat-runtime.ts +0 -489
- package/src/host/dsh-adapter.ts +0 -867
- package/src/host/session-adapter.ts +0 -276
- package/src/http/handler.ts +0 -1132
- package/src/index.js +0 -53
- package/src/service/audit.ts +0 -92
- package/src/service/chat.ts +0 -1100
- package/src/service/contracts.ts +0 -193
- package/src/service/defaults.ts +0 -182
- package/src/service/deliverables.ts +0 -240
- package/src/service/hams.ts +0 -3103
- package/src/service/knowledge.ts +0 -940
- package/src/service/member-config.ts +0 -39
- package/src/service/members.ts +0 -506
- package/src/service/memory.ts +0 -375
- package/src/service/permissions.ts +0 -118
- package/src/service/project-lifecycle.ts +0 -233
- package/src/service/projects.ts +0 -840
- package/src/service/receipts.ts +0 -63
- package/src/service/templates.ts +0 -140
- package/src/service/tick.ts +0 -751
- package/src/service/tools.ts +0 -711
- package/src/service/types.ts +0 -219
- package/src/service/work-item-create.ts +0 -159
- package/src/service/workspace-profile.ts +0 -443
- package/src/store/backup.ts +0 -239
- package/src/store/bootstrap.ts +0 -225
- package/src/store/db.ts +0 -179
- package/src/store/lock.ts +0 -194
- package/src/store/migrations.ts +0 -211
- package/src/store/schema/001_p1.sql +0 -846
- package/src/store/schema/001_p1.sql.ts +0 -865
- package/src/store/schema/002_knowledge.sql +0 -54
- package/src/store/schema/002_knowledge.sql.ts +0 -68
- package/src/store/schema/003_projects.sql.ts +0 -3
- package/src/store/schema/004_team.sql.ts +0 -50
- package/src/store/schema/005_target_check.sql.ts +0 -22
- package/src/store/schema/006_target_flow.sql.ts +0 -19
- package/src/store/schema/007_project_delete.sql.ts +0 -17
- package/src/store/schema/008_memory.sql.ts +0 -47
- package/src/store/schema/generate-projects.mjs +0 -16
- package/src/store/seed.ts +0 -114
package/src/service/receipts.ts
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 写命令幂等:`command_receipt`。
|
|
3
|
-
*
|
|
4
|
-
* 与 `outbox` 分开(backend-design §4.9):这里回答「这个请求做过没有」,
|
|
5
|
-
* outbox 回答「这个外部动作发出去了没有」。同键同载荷返回首次回执;同键不同载荷
|
|
6
|
-
* 直接拒绝(IDEMPOTENCY_MISMATCH),绝不「按新载荷再跑一次」。
|
|
7
|
-
*/
|
|
8
|
-
import type { DatabaseSync } from 'node:sqlite'
|
|
9
|
-
import { createHash } from 'node:crypto'
|
|
10
|
-
|
|
11
|
-
export function stableStringify(value: unknown): string {
|
|
12
|
-
if (value === null || typeof value !== 'object') return JSON.stringify(value) ?? 'null'
|
|
13
|
-
if (Array.isArray(value)) return `[${value.map((item) => stableStringify(item)).join(',')}]`
|
|
14
|
-
const record = value as Record<string, unknown>
|
|
15
|
-
const keys = Object.keys(record).sort()
|
|
16
|
-
return `{${keys.map((key) => `${JSON.stringify(key)}:${stableStringify(record[key])}`).join(',')}}`
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function hashPayload(value: unknown): string {
|
|
20
|
-
return createHash('sha256').update(stableStringify(value)).digest('hex')
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function receiptScopeKey(principalId: string, action: string, idempotencyKey: string): string {
|
|
24
|
-
return createHash('sha256').update(`${principalId}\u0000${action}\u0000${idempotencyKey}`).digest('hex')
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export interface StoredReceipt {
|
|
28
|
-
payloadHash: string
|
|
29
|
-
receiptJson: string
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function loadReceipt(db: DatabaseSync, scopeKey: string): StoredReceipt | undefined {
|
|
33
|
-
const row = db
|
|
34
|
-
.prepare('SELECT payload_hash, receipt_json FROM command_receipt WHERE scope_key = ?')
|
|
35
|
-
.get(scopeKey) as { payload_hash: string; receipt_json: string } | undefined
|
|
36
|
-
return row ? { payloadHash: row.payload_hash, receiptJson: row.receipt_json } : undefined
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface SaveReceiptInput {
|
|
40
|
-
scopeKey: string
|
|
41
|
-
commandId: string
|
|
42
|
-
principalId: string
|
|
43
|
-
action: string
|
|
44
|
-
payloadHash: string
|
|
45
|
-
receiptJson: string
|
|
46
|
-
at: number
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export function saveReceipt(db: DatabaseSync, input: SaveReceiptInput): void {
|
|
50
|
-
db.prepare(
|
|
51
|
-
`INSERT INTO command_receipt
|
|
52
|
-
(scope_key, command_id, principal_id, action, payload_hash, receipt_json, created_at)
|
|
53
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
54
|
-
).run(
|
|
55
|
-
input.scopeKey,
|
|
56
|
-
input.commandId,
|
|
57
|
-
input.principalId,
|
|
58
|
-
input.action,
|
|
59
|
-
input.payloadHash,
|
|
60
|
-
input.receiptJson,
|
|
61
|
-
input.at,
|
|
62
|
-
)
|
|
63
|
-
}
|
package/src/service/templates.ts
DELETED
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 起步模板与旧样例清理。
|
|
3
|
-
*
|
|
4
|
-
* 2026-11-15 起,空库不再造「电池告警追踪」这个示例模板,改为一条与真实使用
|
|
5
|
-
* 方式一致的通用模板:**研究与交付推进**——交付物一份(doc)+ 过程记录一份(log),
|
|
6
|
-
* 三天后复查,owner 审批 72 小时、不自动关单。理由有两条:
|
|
7
|
-
*
|
|
8
|
-
* 1. 电池资产运营是设计期的首个场景,不是当前工作;把示例写进用户库会让人以为
|
|
9
|
-
* 系统里已经有业务数据。
|
|
10
|
-
* 2. 通用模板能覆盖研究、开发、数据运维三类工作项,建单后都能走到「申请关单」,
|
|
11
|
-
* 不会出现「模板是别人业务的、自己的单关不掉」。
|
|
12
|
-
*
|
|
13
|
-
* 旧库升级时由工作区画像的 `cleanupTemplates` 调用 `removeTemplateIfUnused`:
|
|
14
|
-
* 只有模板确实存在、且没有任何工作项引用它的版本时才删除,历史单据不受影响。
|
|
15
|
-
*/
|
|
16
|
-
import type { DatabaseSync } from 'node:sqlite'
|
|
17
|
-
|
|
18
|
-
export const STARTER_TEMPLATE = {
|
|
19
|
-
id: 'tpl-research-delivery',
|
|
20
|
-
versionId: 'tpl-research-delivery-v1',
|
|
21
|
-
key: 'research_delivery',
|
|
22
|
-
name: '研究与交付推进',
|
|
23
|
-
domain: '研究',
|
|
24
|
-
category: '研究推进',
|
|
25
|
-
/** 交付物一份 + 过程记录一份;720 小时是上限,不是要求每天都补。 */
|
|
26
|
-
requiredEvidence: [
|
|
27
|
-
{ kind: 'doc', min_count: 1, max_age_hours: 720 },
|
|
28
|
-
{ kind: 'log', min_count: 1, max_age_hours: 720 },
|
|
29
|
-
],
|
|
30
|
-
checkPolicy: { mode: 'after_hours', hours: 72 },
|
|
31
|
-
reviewPolicy: { approver: 'owner', expires_hours: 72, auto_close: false },
|
|
32
|
-
} as const
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* 空库首次启动建一次起步模板,并记在 hams_meta。只补默认、不覆盖:
|
|
36
|
-
* 只要库里已有模板(或元数据已记),就不再造。
|
|
37
|
-
*/
|
|
38
|
-
export function ensureStarterTemplate(db: DatabaseSync, publishedBy: string, now = Date.now()): string | null {
|
|
39
|
-
const seeded = db.prepare("SELECT value FROM hams_meta WHERE key = 'starter_template_at'").get() as
|
|
40
|
-
| { value: string }
|
|
41
|
-
| undefined
|
|
42
|
-
if (seeded) return null
|
|
43
|
-
const existing = db.prepare('SELECT id FROM work_template LIMIT 1').get() as { id: string } | undefined
|
|
44
|
-
|
|
45
|
-
db.exec('BEGIN IMMEDIATE')
|
|
46
|
-
try {
|
|
47
|
-
if (!existing) {
|
|
48
|
-
upsertTemplate(db, publishedBy, now, STARTER_TEMPLATE)
|
|
49
|
-
}
|
|
50
|
-
db.prepare("INSERT OR REPLACE INTO hams_meta (key, value) VALUES ('starter_template_at', ?)").run(String(now))
|
|
51
|
-
db.exec('COMMIT')
|
|
52
|
-
return existing ? null : STARTER_TEMPLATE.versionId
|
|
53
|
-
} catch (error) {
|
|
54
|
-
if (db.isTransaction) db.exec('ROLLBACK')
|
|
55
|
-
throw error
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export interface TemplateDefinition {
|
|
60
|
-
id: string
|
|
61
|
-
versionId: string
|
|
62
|
-
key: string
|
|
63
|
-
name: string
|
|
64
|
-
domain: string
|
|
65
|
-
category: string
|
|
66
|
-
requiredEvidence: readonly { kind: string; min_count: number; max_age_hours?: number }[]
|
|
67
|
-
checkPolicy: Record<string, unknown>
|
|
68
|
-
reviewPolicy: Record<string, unknown>
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/** 幂等:按 key 找模板与版本;已存在就直接用已发布版本。 */
|
|
72
|
-
export function upsertTemplate(
|
|
73
|
-
db: DatabaseSync,
|
|
74
|
-
publishedBy: string,
|
|
75
|
-
now: number,
|
|
76
|
-
definition: TemplateDefinition,
|
|
77
|
-
): string {
|
|
78
|
-
const existing = db.prepare('SELECT * FROM work_template WHERE key = ?').get(definition.key) as
|
|
79
|
-
| { id: string; published_version_id: string | null }
|
|
80
|
-
| undefined
|
|
81
|
-
if (existing?.published_version_id) return existing.published_version_id
|
|
82
|
-
|
|
83
|
-
const templateId = existing?.id ?? definition.id
|
|
84
|
-
if (!existing) {
|
|
85
|
-
db.prepare('INSERT INTO work_template (id, key, name, domain, category) VALUES (?, ?, ?, ?, ?)').run(
|
|
86
|
-
templateId,
|
|
87
|
-
definition.key,
|
|
88
|
-
definition.name,
|
|
89
|
-
definition.domain,
|
|
90
|
-
definition.category,
|
|
91
|
-
)
|
|
92
|
-
}
|
|
93
|
-
const versionId = definition.versionId
|
|
94
|
-
db.prepare(
|
|
95
|
-
`INSERT INTO work_template_version
|
|
96
|
-
(id, template_id, version, required_evidence_json, validator_key, validator_params_json,
|
|
97
|
-
check_policy_json, review_policy_json, misfire_policy, status, published_by, published_at)
|
|
98
|
-
VALUES (?, ?, 1, ?, NULL, '{}', ?, ?, 'each', 'published', ?, ?)`,
|
|
99
|
-
).run(
|
|
100
|
-
versionId,
|
|
101
|
-
templateId,
|
|
102
|
-
JSON.stringify(definition.requiredEvidence),
|
|
103
|
-
JSON.stringify(definition.checkPolicy),
|
|
104
|
-
JSON.stringify(definition.reviewPolicy),
|
|
105
|
-
publishedBy,
|
|
106
|
-
now,
|
|
107
|
-
)
|
|
108
|
-
db.prepare('UPDATE work_template SET published_version_id = ? WHERE id = ?').run(versionId, templateId)
|
|
109
|
-
return versionId
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* 删除一个旧示例模板:只在**没有任何工作项引用它的版本**时执行。
|
|
114
|
-
* 返回 true 表示确实删了。
|
|
115
|
-
*/
|
|
116
|
-
export function removeTemplateIfUnused(db: DatabaseSync, templateIdOrKey: string): boolean {
|
|
117
|
-
const template = db
|
|
118
|
-
.prepare('SELECT id, published_version_id FROM work_template WHERE id = ? OR key = ?')
|
|
119
|
-
.get(templateIdOrKey, templateIdOrKey) as { id: string; published_version_id: string | null } | undefined
|
|
120
|
-
if (!template) return false
|
|
121
|
-
const versions = db.prepare('SELECT id FROM work_template_version WHERE template_id = ?').all(template.id) as {
|
|
122
|
-
id: string
|
|
123
|
-
}[]
|
|
124
|
-
if (!versions.length) {
|
|
125
|
-
db.prepare('DELETE FROM work_template WHERE id = ?').run(template.id)
|
|
126
|
-
return true
|
|
127
|
-
}
|
|
128
|
-
const placeholders = versions.map(() => '?').join(',')
|
|
129
|
-
const versionIds = versions.map((version) => version.id) as never[]
|
|
130
|
-
for (const table of ['work_item', 'work_item_check', 'schedule']) {
|
|
131
|
-
const referenced = db
|
|
132
|
-
.prepare(`SELECT 1 AS ok FROM ${table} WHERE template_version_id IN (${placeholders}) LIMIT 1`)
|
|
133
|
-
.get(...versionIds) as { ok: number } | undefined
|
|
134
|
-
if (referenced) return false
|
|
135
|
-
}
|
|
136
|
-
db.prepare('UPDATE work_template SET published_version_id = NULL WHERE id = ?').run(template.id)
|
|
137
|
-
db.prepare('DELETE FROM work_template_version WHERE template_id = ?').run(template.id)
|
|
138
|
-
db.prepare('DELETE FROM work_template WHERE id = ?').run(template.id)
|
|
139
|
-
return true
|
|
140
|
-
}
|