@bolloon/bolloon-agent 0.6.1 → 0.6.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.
- package/README.md +128 -111
- package/dist/agents/gateway-group.js +231 -6
- package/dist/agents/group-autopilot.js +256 -0
- package/dist/agents/pi-sdk-tools.js +302 -0
- package/dist/agents/pi-sdk.js +89 -140
- package/dist/agents/task-group.js +21 -4
- package/dist/cli/log-gate.js +68 -4
- package/dist/cli/mention-data.js +1 -1
- package/dist/cli/model-selector.js +84 -104
- package/dist/cli/setup-wizard.js +2 -10
- package/dist/cli/tui-select.js +99 -10
- package/dist/cli-entry.js +1 -9
- package/dist/electron-build/agents/gateway-group.js +814 -0
- package/dist/electron-build/agents/gateway-group.js.map +1 -0
- package/dist/electron-build/agents/group-autopilot.js +302 -0
- package/dist/electron-build/agents/group-autopilot.js.map +1 -0
- package/dist/electron-build/agents/pi-sdk-tools.js +302 -0
- package/dist/electron-build/agents/pi-sdk-tools.js.map +1 -1
- package/dist/electron-build/agents/pi-sdk.js +88 -139
- package/dist/electron-build/agents/pi-sdk.js.map +1 -1
- package/dist/electron-build/agents/task-group.js +601 -0
- package/dist/electron-build/agents/task-group.js.map +1 -0
- package/dist/electron-build/agents/task-public-text.js +97 -0
- package/dist/electron-build/agents/task-public-text.js.map +1 -0
- package/dist/electron-build/efficode/compress.js +86 -0
- package/dist/electron-build/efficode/compress.js.map +1 -0
- package/dist/electron-build/efficode/crc32.js +44 -0
- package/dist/electron-build/efficode/crc32.js.map +1 -0
- package/dist/electron-build/efficode/did.js +74 -0
- package/dist/electron-build/efficode/did.js.map +1 -0
- package/dist/electron-build/efficode/lz77.js +175 -0
- package/dist/electron-build/efficode/lz77.js.map +1 -0
- package/dist/electron-build/efficode/negotiate.js +233 -0
- package/dist/electron-build/efficode/negotiate.js.map +1 -0
- package/dist/electron-build/efficode/ops.js +96 -0
- package/dist/electron-build/efficode/ops.js.map +1 -0
- package/dist/electron-build/efficode/packet.js +279 -0
- package/dist/electron-build/efficode/packet.js.map +1 -0
- package/dist/electron-build/efficode/types.js +50 -0
- package/dist/electron-build/efficode/types.js.map +1 -0
- package/dist/electron-build/kernel/code-write-gate.js +55 -0
- package/dist/electron-build/kernel/code-write-gate.js.map +1 -0
- package/dist/electron-build/kernel/run-lifecycle.js +23 -0
- package/dist/electron-build/kernel/run-lifecycle.js.map +1 -1
- package/dist/electron-build/kernel/run-resume.js +62 -0
- package/dist/electron-build/kernel/run-resume.js.map +1 -0
- package/dist/electron-build/kernel/session-lifecycle.js +223 -0
- package/dist/electron-build/kernel/session-lifecycle.js.map +1 -0
- package/dist/electron-build/kernel/turn-selfcheck.js +65 -0
- package/dist/electron-build/kernel/turn-selfcheck.js.map +1 -0
- package/dist/electron-build/llm/config-store.js +2 -2
- package/dist/electron-build/llm/config-store.js.map +1 -1
- package/dist/electron-build/llm/model-selection.js +16 -2
- package/dist/electron-build/llm/model-selection.js.map +1 -1
- package/dist/electron-build/llm/pi-ai.js +2 -2
- package/dist/electron-build/llm/pi-ai.js.map +1 -1
- package/dist/electron-build/orbitdb/cid-database.js +98 -19
- package/dist/electron-build/orbitdb/cid-database.js.map +1 -1
- package/dist/electron-build/orbitdb/group-access.js +399 -0
- package/dist/electron-build/orbitdb/group-access.js.map +1 -0
- package/dist/index.js +143 -10
- package/dist/ios/bolloon-web.json +3 -3
- package/dist/ios/sw.js +1 -1
- package/dist/kernel/code-write-gate.js +47 -0
- package/dist/kernel/gate-scan.js +5 -1
- package/dist/kernel/plan-harness.js +3 -2
- package/dist/kernel/roster.js +6 -6
- package/dist/kernel/run-lifecycle.js +21 -0
- package/dist/kernel/run-resume.js +58 -0
- package/dist/kernel/session-lifecycle.js +210 -0
- package/dist/kernel/turn-selfcheck.js +60 -0
- package/dist/llm/config-store.js +2 -2
- package/dist/llm/model-selection.js +16 -2
- package/dist/llm/pi-ai.js +2 -2
- package/dist/orbitdb/cid-database.js +95 -19
- package/dist/web/bolloon-web.json +3 -3
- package/dist/web/sw.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* task-public-text.ts — **公开文本的隐私红线 (单一来源)** (2026-09-25)
|
|
4
|
+
*
|
|
5
|
+
* 为什么要单独一个文件: 同一条红线要同时把住**两个入口** ——
|
|
6
|
+
* · 桌面/CLI: 群消息发送前 (`task-group.ts` → `requirePublicText`)
|
|
7
|
+
* · 手机 WebView: 手机侧发公告/接单/入群前 (mobile-tasks.ts → 同一函数)
|
|
8
|
+
* 两边各抄一份规则 = 迟早漂移 (一边加了规则另一边没加, 于是手机成了绕过红线的后门)。
|
|
9
|
+
* 所以规则表放在这里, 双方都 import 这一份; 本文件**不 import 任何 node: 模块**,
|
|
10
|
+
* 因此既能进 Node, 也能进浏览器 bundle。
|
|
11
|
+
*
|
|
12
|
+
* 群消息**禁止出现**的形态 (顺序即报告顺序)。宁可多拦 (拒发 + 说清哪条规则),
|
|
13
|
+
* 也不放行一条带标识符的消息。
|
|
14
|
+
*
|
|
15
|
+
* 实现纪律 (2026-09-23 踩过的坑, 不可回退):
|
|
16
|
+
* · **不要用 `\b` 当边界** —— 消息里的 token 会把空白折成 `_`, 而 `_` 也是 \w, 于是 `判据见_0x1111…`
|
|
17
|
+
* 这种"粘在 `_`/汉字后面"的标识符 `\b` 判不出来, 会直接漏进群。一律改成对标识符自身字母表的
|
|
18
|
+
* lookahead/lookbehind (`(?<![0-9a-fA-FxX])`, `(?![0-9a-fA-F])`), 粘在 `_`/中文/符号后面也照样命中。
|
|
19
|
+
* · ipv6 规则要**避开 ISO 时间戳** (`09:50:12.345Z` 只有两个冒号): 只认 3 组以上 `x:y:z:w` 或含 `::` 的压缩形态。
|
|
20
|
+
* · 内容哈希 (sha256 64 位裸 hex) **不在**禁列 —— 交付就是要贴哈希 (它不是密钥, 也没有 0x 前缀)。
|
|
21
|
+
*/
|
|
22
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
+
exports.NODE_IDENTITY_RULES = exports.PRIVACY_RULES = void 0;
|
|
24
|
+
exports.mask = mask;
|
|
25
|
+
exports.scanPublicText = scanPublicText;
|
|
26
|
+
exports.requirePublicText = requirePublicText;
|
|
27
|
+
exports.scanNodeIdentity = scanNodeIdentity;
|
|
28
|
+
exports.PRIVACY_RULES = [
|
|
29
|
+
{ rule: 'wallet-address', why: '以太坊/EVM 钱包地址', re: /(?<![0-9a-fA-FxX])0x[0-9a-fA-F]{40}(?![0-9a-fA-F])/ },
|
|
30
|
+
{ rule: 'private-key-hex', why: '32 字节私钥形态 (0x + 64 hex)', re: /(?<![0-9a-fA-FxX])0x[0-9a-fA-F]{64}(?![0-9a-fA-F])/ },
|
|
31
|
+
{ rule: 'hex-0x-blob', why: '0x 开头的十六进制块 (签名/哈希/密钥都长这样, 群里一律不放原文)', re: /(?<![0-9a-fA-FxX])0x[0-9a-fA-F]{8,}(?![0-9a-fA-F])/ },
|
|
32
|
+
{ rule: 'did', why: '去中心化身份 DID', re: /did:[a-z0-9]+:[A-Za-z0-9._:%-]{3,}/i },
|
|
33
|
+
{ rule: 'peer-id', why: 'libp2p peerId (12D3Koo…/Qm…)', re: /12D3Koo[A-Za-z0-9]{8,}|Qm[1-9A-HJ-NP-Za-km-z]{30,}/ },
|
|
34
|
+
{ rule: 'multiaddr', why: 'libp2p multiaddr 或 store 地址 (/ip4 /tcp /p2p /orbitdb …)', re: /\/(ip4|ip6|dns|dns4|dns6|tcp|udp|ws|wss|quic|p2p|p2p-circuit|ipfs|orbitdb)(?![A-Za-z0-9])/i },
|
|
35
|
+
{ rule: 'orbitdb-link', why: 'OrbitDB 链接 (群邀请链接本身也不进群消息)', re: /orbitdb:\/\//i },
|
|
36
|
+
{ rule: 'ipv4', why: 'IPv4 地址', re: /(?<!\d)\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(?!\d)/ },
|
|
37
|
+
{ rule: 'ipv6', why: 'IPv6 地址 (不含 ISO 时间戳)', re: /(?<![0-9A-Za-z])(?:[0-9a-fA-F]{1,4})?::(?:[0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4}(?![0-9A-Za-z])|(?<![0-9A-Za-z:])(?:[0-9a-fA-F]{1,4}:){3,}[0-9a-fA-F]{1,4}(?![0-9A-Za-z])/ },
|
|
38
|
+
{ rule: 'pem', why: 'PEM 材料 (私钥/证书)', re: /-----BEGIN [A-Z ]*(PRIVATE KEY|CERTIFICATE)/ },
|
|
39
|
+
{ rule: 'secret-words', why: '私钥/助记词/密钥字样 (正文里提到即停, 人不该把密钥写进群)', re: /(private\s*key|seed\s*phrase|mnemonic|私钥|助记词|密钥材料)/i },
|
|
40
|
+
{ rule: 'url', why: 'http(s) 端点 (端点会带 host/IP 与用途指纹)', re: /https?:\/\/\S+/i },
|
|
41
|
+
{ rule: 'email', why: '邮箱地址', re: /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/ },
|
|
42
|
+
];
|
|
43
|
+
/** 标识符打码 (只留前 4 字符) */
|
|
44
|
+
function mask(s) {
|
|
45
|
+
const t = String(s || '');
|
|
46
|
+
return t.length <= 4 ? '****' : `${t.slice(0, 4)}…`;
|
|
47
|
+
}
|
|
48
|
+
/** 扫一遍文本, 返回命中的隐私规则 (空数组 = 可以发) */
|
|
49
|
+
function scanPublicText(text) {
|
|
50
|
+
const t = String(text ?? '');
|
|
51
|
+
const out = [];
|
|
52
|
+
const seen = new Set();
|
|
53
|
+
for (const r of exports.PRIVACY_RULES) {
|
|
54
|
+
const m = r.re.exec(t);
|
|
55
|
+
if (!m)
|
|
56
|
+
continue;
|
|
57
|
+
if (seen.has(r.rule))
|
|
58
|
+
continue;
|
|
59
|
+
seen.add(r.rule);
|
|
60
|
+
out.push({ rule: r.rule, why: r.why, masked: mask(String(m[0])), chars: String(m[0]).length });
|
|
61
|
+
}
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
64
|
+
/** 发送前唯一闸门: 命中任何规则 → 拒绝发送 (返回 violations 供 CLI/手机如实报出) */
|
|
65
|
+
function requirePublicText(text) {
|
|
66
|
+
const v = scanPublicText(text);
|
|
67
|
+
return v.length === 0 ? { ok: true } : { ok: false, violations: v };
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* **节点身份**口径 (群管理命令 `task group create|join|list|link|leave` 的输出用, 2026-09-24):
|
|
71
|
+
* 与群消息同一张规则表, 但**去掉 orbitdb 那一档** ——
|
|
72
|
+
* · `orbitdb-link` 整条去掉: 邀请链接是群唯一能让别人入群的东西, 必须能打印出来;
|
|
73
|
+
* · `multiaddr` 规则里的 `orbitdb` 选项去掉 (只留节点地址 /ip4 /ip6 /dns /tcp /p2p …):
|
|
74
|
+
* 群 store 地址 (`/orbitdb/zdpu…`) 是**群自己的公开标识**, 不是节点地址。
|
|
75
|
+
* 仍然全拦: 原始 DID · 钱包地址 · peerId · 节点 multiaddr · IPv4/IPv6 · 私钥形态 · PEM · URL · 邮箱。
|
|
76
|
+
*
|
|
77
|
+
* 用法: 群管理**输出**再过一遍这道闸 (`scanNodeIdentity`)。命中 = 输出里真有节点身份/联系方式泄漏,
|
|
78
|
+
* 此时**拒输出并如实报**, 不静默脱敏掉再当成没事 (那样人看到的就不是事实了)。
|
|
79
|
+
*/
|
|
80
|
+
exports.NODE_IDENTITY_RULES = exports.PRIVACY_RULES
|
|
81
|
+
.filter((r) => r.rule !== 'orbitdb-link')
|
|
82
|
+
.map((r) => (r.rule === 'multiaddr'
|
|
83
|
+
? { ...r, re: /\/(ip4|ip6|dns|dns4|dns6|tcp|udp|ws|wss|quic|p2p|p2p-circuit|ipfs)(?![A-Za-z0-9])/i }
|
|
84
|
+
: r));
|
|
85
|
+
/** 扫节点身份泄漏 (空数组 = 输出干净) */
|
|
86
|
+
function scanNodeIdentity(text) {
|
|
87
|
+
const t = String(text ?? '');
|
|
88
|
+
const out = [];
|
|
89
|
+
for (const r of exports.NODE_IDENTITY_RULES) {
|
|
90
|
+
const m = r.re.exec(t);
|
|
91
|
+
if (!m)
|
|
92
|
+
continue;
|
|
93
|
+
out.push({ rule: r.rule, why: r.why, masked: mask(String(m[0])), chars: String(m[0]).length });
|
|
94
|
+
}
|
|
95
|
+
return out;
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=task-public-text.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-public-text.js","sourceRoot":"","sources":["../../../src/agents/task-public-text.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* efficode/compress - 可插拔压缩层
|
|
4
|
+
*
|
|
5
|
+
* 三个算法 (数据块第一字节 = 算法 id):
|
|
6
|
+
* 0 none 原样 (小消息用 —— 头上开箱代价为 0)
|
|
7
|
+
* 1 lz77 本仓实现, 纯 JS 可跑任何运行面 (含手机 WebView)
|
|
8
|
+
* 2 deflate node:zlib deflateRaw (真压缩, 真数字)
|
|
9
|
+
*
|
|
10
|
+
* 默认策略 `pickAlgo`: 先按内容选, 选完**真压一遍**, 压不小就退回 `none`
|
|
11
|
+
* (小消息硬压一定更贵 —— 这是实测结论, 见 wiki §"更高效吗")。
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.COMPRESSION_ALGOS = void 0;
|
|
15
|
+
exports.compressBlock = compressBlock;
|
|
16
|
+
exports.decompressBlock = decompressBlock;
|
|
17
|
+
exports.pickAlgo = pickAlgo;
|
|
18
|
+
exports.measureAll = measureAll;
|
|
19
|
+
const node_zlib_1 = require("node:zlib");
|
|
20
|
+
const lz77_js_1 = require("./lz77.js");
|
|
21
|
+
const types_js_1 = require("./types.js");
|
|
22
|
+
exports.COMPRESSION_ALGOS = ['none', 'lz77', 'deflate'];
|
|
23
|
+
/** 压缩。返回 [1B 算法 id][载荷] 的完整数据块体 */
|
|
24
|
+
function compressBlock(raw, algo) {
|
|
25
|
+
const body = compressPayload(raw, algo);
|
|
26
|
+
const out = new Uint8Array(body.length + 1);
|
|
27
|
+
out[0] = types_js_1.COMPRESSION_IDS[algo];
|
|
28
|
+
out.set(body, 1);
|
|
29
|
+
return out;
|
|
30
|
+
}
|
|
31
|
+
function compressPayload(raw, algo) {
|
|
32
|
+
switch (algo) {
|
|
33
|
+
case 'none':
|
|
34
|
+
return raw;
|
|
35
|
+
case 'lz77':
|
|
36
|
+
return (0, lz77_js_1.lz77Compress)(raw);
|
|
37
|
+
case 'deflate':
|
|
38
|
+
return new Uint8Array((0, node_zlib_1.deflateRawSync)(raw));
|
|
39
|
+
default:
|
|
40
|
+
throw new types_js_1.EfficodeError('EFFICODE_UNKNOWN_COMPRESSION', `未知压缩算法 ${String(algo)}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/** 解压 [1B 算法 id][载荷]。未知算法 id → 报错, 绝不"跳过解压直接给原文" */
|
|
44
|
+
function decompressBlock(block) {
|
|
45
|
+
if (block.length < 1)
|
|
46
|
+
throw new types_js_1.EfficodeError('EFFICODE_TRUNCATED', '数据块空 (缺算法 id)');
|
|
47
|
+
const algo = types_js_1.COMPRESSION_BY_ID[block[0]];
|
|
48
|
+
if (!algo) {
|
|
49
|
+
throw new types_js_1.EfficodeError('EFFICODE_UNKNOWN_COMPRESSION', `数据块声明未知压缩算法 id=${block[0]}`, 0);
|
|
50
|
+
}
|
|
51
|
+
const payload = block.subarray(1);
|
|
52
|
+
switch (algo) {
|
|
53
|
+
case 'none':
|
|
54
|
+
return { algo, data: payload.slice() };
|
|
55
|
+
case 'lz77':
|
|
56
|
+
return { algo, data: (0, lz77_js_1.lz77Decompress)(payload) };
|
|
57
|
+
case 'deflate': {
|
|
58
|
+
try {
|
|
59
|
+
return { algo, data: new Uint8Array((0, node_zlib_1.inflateRawSync)(payload)) };
|
|
60
|
+
}
|
|
61
|
+
catch (e) {
|
|
62
|
+
throw new types_js_1.EfficodeError('EFFICODE_TRUNCATED', `deflate 解压失败: ${String(e?.message ?? e).slice(0, 120)}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
default:
|
|
66
|
+
throw new types_js_1.EfficodeError('EFFICODE_UNKNOWN_COMPRESSION', `未知压缩算法 ${String(algo)}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* 默认算法选择: 结构化文本 (重复多) 用 deflate; 其余先用 lz77 试;
|
|
71
|
+
* 压缩后**不小于**原长就退回 none (省下的不该是负数)。
|
|
72
|
+
*/
|
|
73
|
+
function pickAlgo(raw, preferred = 'deflate') {
|
|
74
|
+
if (raw.length < 32)
|
|
75
|
+
return 'none'; // 小消息: 头上开箱代价 > 收益
|
|
76
|
+
const got = compressPayload(raw, preferred);
|
|
77
|
+
return got.length < raw.length ? preferred : 'none';
|
|
78
|
+
}
|
|
79
|
+
/** 真测: 三个算法分别压出来的字节数 (给门和 wiki 用真实数字, 不许估) */
|
|
80
|
+
function measureAll(raw) {
|
|
81
|
+
const out = {};
|
|
82
|
+
for (const a of exports.COMPRESSION_ALGOS)
|
|
83
|
+
out[a] = compressPayload(raw, a).length + 1; // +1 = 算法 id 字节
|
|
84
|
+
return out;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=compress.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compress.js","sourceRoot":"","sources":["../../../src/efficode/compress.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* efficode/crc32 - 包尾 CRC32 (IEEE 802.3, 多项式 0xEDB88320)
|
|
4
|
+
*
|
|
5
|
+
* 为什么自己写而不是用 zlib.crc32: 包层要能在**任何**运行面 (含手机 WebView 的
|
|
6
|
+
* 纯 JS 上下文) 真算同一个校验值, 所以这里是不依赖 node:zlib 的纯实现。
|
|
7
|
+
* 自证向量写在 src/test/efficode.test.ts: crc32("123456789") === 0xCBF43926 (规范值)。
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.crc32 = crc32;
|
|
11
|
+
exports.crc32Bytes = crc32Bytes;
|
|
12
|
+
exports.readCrc32Bytes = readCrc32Bytes;
|
|
13
|
+
const TABLE = (() => {
|
|
14
|
+
const t = new Uint32Array(256);
|
|
15
|
+
for (let n = 0; n < 256; n++) {
|
|
16
|
+
let c = n;
|
|
17
|
+
for (let k = 0; k < 8; k++)
|
|
18
|
+
c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
|
|
19
|
+
t[n] = c >>> 0;
|
|
20
|
+
}
|
|
21
|
+
return t;
|
|
22
|
+
})();
|
|
23
|
+
/** 算 CRC32, 返回无符号 32 位整数 */
|
|
24
|
+
function crc32(buf) {
|
|
25
|
+
let c = 0xffffffff;
|
|
26
|
+
for (let i = 0; i < buf.length; i++)
|
|
27
|
+
c = TABLE[(c ^ buf[i]) & 0xff] ^ (c >>> 8);
|
|
28
|
+
return (c ^ 0xffffffff) >>> 0;
|
|
29
|
+
}
|
|
30
|
+
/** 打包成 4 字节大端 */
|
|
31
|
+
function crc32Bytes(buf) {
|
|
32
|
+
const v = crc32(buf);
|
|
33
|
+
const out = new Uint8Array(4);
|
|
34
|
+
out[0] = (v >>> 24) & 0xff;
|
|
35
|
+
out[1] = (v >>> 16) & 0xff;
|
|
36
|
+
out[2] = (v >>> 8) & 0xff;
|
|
37
|
+
out[3] = v & 0xff;
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
/** 从 4 字节大端读回 */
|
|
41
|
+
function readCrc32Bytes(b, offset = 0) {
|
|
42
|
+
return (((b[offset] << 24) | (b[offset + 1] << 16) | (b[offset + 2] << 8) | b[offset + 3]) >>> 0);
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=crc32.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crc32.js","sourceRoot":"","sources":["../../../src/efficode/crc32.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;;;;AAEH,MAAM,KAAK,GAAgB,CAAC,GAAG,EAAE;IAC/B,MAAM,CAAC,GAAG,IAAI,WAAW,CAAC,GAAG,CAAC,CAAC;IAC/B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7B,IAAI,CAAC,GAAG,CAAC,CAAC;QACV,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE;YAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QACzE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACjB,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC,CAAC,EAAE,CAAC;AAEL,4BAA4B;AAC5B,eAAsB,GAAe;IACnC,IAAI,CAAC,GAAG,UAAU,CAAC;IACnB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE;QAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IAChF,OAAO,CAAC,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;AAChC,CAAC;AAED,iBAAiB;AACjB,oBAA2B,GAAe;IACxC,MAAM,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;IACrB,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;IAC9B,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;IAC3B,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;IAC3B,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC;IAC1B,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;IAClB,OAAO,GAAG,CAAC;AACb,CAAC;AAED,iBAAiB;AACjB,wBAA+B,CAAa,EAAE,MAAM,GAAG,CAAC;IACtD,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;AACpG,CAAC"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* efficode/did - DID 验证段 (固定 32B)
|
|
4
|
+
*
|
|
5
|
+
* 布局 (32 字节):
|
|
6
|
+
* [0] algo id (= 1, sha256-截断)
|
|
7
|
+
* [1..3] 原始身份串字节数 (24 位大端, 上限 16MB)
|
|
8
|
+
* [4..31] sha256(身份串) 的**前 28 字节**
|
|
9
|
+
*
|
|
10
|
+
* 诚实边界 (必须写进 wiki, 不许含糊):
|
|
11
|
+
* - 32B **装不下**一个 DID / 公钥, 所以本段是**摘要**, 只能做"是不是同一个身份"的比对,
|
|
12
|
+
* **不能**从段里还原身份, 也**不能**独立证明持有私钥;
|
|
13
|
+
* - 真正的"链上签名验证 + ECC 临时会话密钥"属**规范中/未实现** —— 本模块没有签名函数,
|
|
14
|
+
* 也没有密钥交换函数 (不写假实现)。签名校验的接线点在 packet 层的 `verifyDid` 回调,
|
|
15
|
+
* 现成可用的只有摘要比对这一条。
|
|
16
|
+
*/
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.DID_ALGO_SHA256_TRUNC28 = void 0;
|
|
19
|
+
exports.encodeDidSegment = encodeDidSegment;
|
|
20
|
+
exports.decodeDidSegment = decodeDidSegment;
|
|
21
|
+
exports.didDigestString = didDigestString;
|
|
22
|
+
exports.matchesDidSegment = matchesDidSegment;
|
|
23
|
+
const node_crypto_1 = require("node:crypto");
|
|
24
|
+
const types_js_1 = require("./types.js");
|
|
25
|
+
exports.DID_ALGO_SHA256_TRUNC28 = 1;
|
|
26
|
+
/** 编: 身份串 → 32B 段 */
|
|
27
|
+
function encodeDidSegment(did) {
|
|
28
|
+
const raw = Buffer.from(String(did), 'utf-8');
|
|
29
|
+
if (raw.length === 0)
|
|
30
|
+
throw new types_js_1.EfficodeError('EFFICODE_DID_SEGMENT_BAD', '身份串为空');
|
|
31
|
+
if (raw.length > 0xffffff)
|
|
32
|
+
throw new types_js_1.EfficodeError('EFFICODE_DID_SEGMENT_BAD', `身份串过长 (${raw.length}B > 16MB)`);
|
|
33
|
+
const full = (0, node_crypto_1.createHash)('sha256').update(raw).digest();
|
|
34
|
+
const seg = new Uint8Array(types_js_1.DID_SEGMENT_BYTES);
|
|
35
|
+
seg[0] = exports.DID_ALGO_SHA256_TRUNC28;
|
|
36
|
+
seg[1] = (raw.length >>> 16) & 0xff;
|
|
37
|
+
seg[2] = (raw.length >>> 8) & 0xff;
|
|
38
|
+
seg[3] = raw.length & 0xff;
|
|
39
|
+
seg.set(full.subarray(0, types_js_1.DID_SEGMENT_BYTES - 4), 4);
|
|
40
|
+
return seg;
|
|
41
|
+
}
|
|
42
|
+
/** 解: 32B 段 → 结构。长度不足 / algo 未知 → 抛 */
|
|
43
|
+
function decodeDidSegment(seg) {
|
|
44
|
+
if (seg.length !== types_js_1.DID_SEGMENT_BYTES) {
|
|
45
|
+
throw new types_js_1.EfficodeError('EFFICODE_DID_SEGMENT_BAD', `DID 段应为 ${types_js_1.DID_SEGMENT_BYTES}B, 实得 ${seg.length}B`);
|
|
46
|
+
}
|
|
47
|
+
const algo = seg[0];
|
|
48
|
+
if (algo !== exports.DID_ALGO_SHA256_TRUNC28) {
|
|
49
|
+
throw new types_js_1.EfficodeError('EFFICODE_DID_SEGMENT_BAD', `未知 DID 摘要算法 id=${algo}`, 0);
|
|
50
|
+
}
|
|
51
|
+
const origLen = (seg[1] << 16) | (seg[2] << 8) | seg[3];
|
|
52
|
+
return { algo, origLen, digest: seg.slice(4) };
|
|
53
|
+
}
|
|
54
|
+
/** 摘要段的稳定文本形式: `algo1:<56 hex>` —— 可安全落日志 (单向, 不可还原身份) */
|
|
55
|
+
function didDigestString(seg) {
|
|
56
|
+
return `algo${seg.algo}:${Buffer.from(seg.digest).toString('hex')}`;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* 校验一条身份声明是否与段匹配。
|
|
60
|
+
* 这是**明文比对** (拿原身份串再算一遍摘要), 不是签名验证 —— 返回值里如实标出方式。
|
|
61
|
+
*/
|
|
62
|
+
function matchesDidSegment(did, seg) {
|
|
63
|
+
const expected = encodeDidSegment(did);
|
|
64
|
+
let match = expected.length === seg.length;
|
|
65
|
+
if (match) {
|
|
66
|
+
for (let i = 0; i < seg.length; i++)
|
|
67
|
+
if (expected[i] !== seg[i]) {
|
|
68
|
+
match = false;
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return { match, method: 'digest', signed: false };
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=did.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"did.js","sourceRoot":"","sources":["../../../src/efficode/did.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* efficode/lz77 - 自解压数据块用的轻量 LZ77 (真实实现, 不是占位)
|
|
4
|
+
*
|
|
5
|
+
* 数据块布局 (自解压头 + 码流):
|
|
6
|
+
* [varint origLen][varint compLen][token stream]
|
|
7
|
+
*
|
|
8
|
+
* token stream = 控制字节 + 载荷, 每控制字节管 8 个 token, MSB 在前:
|
|
9
|
+
* 控制位 0 → 后接 1 字节字面量
|
|
10
|
+
* 控制位 1 → 后接 2 字节匹配 (12 位 offset, 4 位 length-3)
|
|
11
|
+
*
|
|
12
|
+
* origLen 在头里 ⇒ 解码器**不需要**猜结束位置 (靠输出长度收敛), 尾随多余码流一律报错。
|
|
13
|
+
*
|
|
14
|
+
* 匹配查找用 deflate 那套**哈希链**: 3 字节前缀哈希 → 链上回溯 (最多 MAX_CHAIN 个候选).
|
|
15
|
+
* 第一版是"从 pos-1 逐个往前比"的朴素写法, 在 `...\n` 这种周期 > 16 的重复文本上**找不到匹配**
|
|
16
|
+
* (300 行重复文本压完反而膨胀 1538B —— 实测出来的, 已换掉)。
|
|
17
|
+
*
|
|
18
|
+
* 诚实边界 (写进 wiki 时按实测数字):
|
|
19
|
+
* - 参考实现: 窗口 4095B / 最短匹配 3B / 最长匹配 18B, **没有**哈夫曼那一层;
|
|
20
|
+
* - 对重复度高的结构化文本有效, 对**随机字节**会膨胀 (控制位占 1/8);
|
|
21
|
+
* - 真实压缩率对比见 scripts/verify-efficode.ts 的真测输出, 不许在文档里编。
|
|
22
|
+
*/
|
|
23
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
+
exports.lz77Compress = lz77Compress;
|
|
25
|
+
exports.lz77Decompress = lz77Decompress;
|
|
26
|
+
const types_js_1 = require("./types.js");
|
|
27
|
+
const WINDOW = 4095; // 12 位 offset (0 保留为非法)
|
|
28
|
+
const MIN_MATCH = 3; // length-3 存 4 位 ⇒ 3..18
|
|
29
|
+
const MAX_MATCH = 18;
|
|
30
|
+
const MAX_CHAIN = 16; // 哈希链回溯上限 (控制最坏耗时)
|
|
31
|
+
const HASH_SIZE = 1 << 16; // 3 字节前缀哈希表大小
|
|
32
|
+
function writeVarint(out, n) {
|
|
33
|
+
let v = n >>> 0;
|
|
34
|
+
while (v >= 0x80) {
|
|
35
|
+
out.push((v & 0x7f) | 0x80);
|
|
36
|
+
v >>>= 7;
|
|
37
|
+
}
|
|
38
|
+
out.push(v & 0xff);
|
|
39
|
+
}
|
|
40
|
+
function readVarint(buf, pos) {
|
|
41
|
+
let value = 0;
|
|
42
|
+
let shift = 0;
|
|
43
|
+
let i = pos;
|
|
44
|
+
for (;;) {
|
|
45
|
+
if (i >= buf.length)
|
|
46
|
+
throw new types_js_1.EfficodeError('EFFICODE_TRUNCATED', 'varint 未读完就到底', i);
|
|
47
|
+
const b = buf[i++];
|
|
48
|
+
value |= (b & 0x7f) << shift;
|
|
49
|
+
if ((b & 0x80) === 0)
|
|
50
|
+
break;
|
|
51
|
+
shift += 7;
|
|
52
|
+
if (shift > 28)
|
|
53
|
+
throw new types_js_1.EfficodeError('EFFICODE_TRUNCATED', 'varint 超过 32 位上限', i);
|
|
54
|
+
}
|
|
55
|
+
return { value: value >>> 0, next: i };
|
|
56
|
+
}
|
|
57
|
+
/** 压缩。返回 [varint origLen][varint compLen][tokens] */
|
|
58
|
+
function lz77Compress(input) {
|
|
59
|
+
const out = [];
|
|
60
|
+
writeVarint(out, input.length);
|
|
61
|
+
const bodyStart = out.length;
|
|
62
|
+
out.push(0); // 占位 (1B, varint(0)); 最后整体重拼, 不依赖占位宽度
|
|
63
|
+
const head = new Int32Array(HASH_SIZE).fill(-1);
|
|
64
|
+
const prev = new Int32Array(input.length).fill(-1);
|
|
65
|
+
const insertAt = (pos) => {
|
|
66
|
+
if (pos + MIN_MATCH > input.length)
|
|
67
|
+
return;
|
|
68
|
+
const h = hash3(input, pos);
|
|
69
|
+
prev[pos] = head[h];
|
|
70
|
+
head[h] = pos;
|
|
71
|
+
};
|
|
72
|
+
const findMatch = (pos) => {
|
|
73
|
+
if (pos + MIN_MATCH > input.length)
|
|
74
|
+
return null;
|
|
75
|
+
const h = hash3(input, pos);
|
|
76
|
+
const maxLen = Math.min(MAX_MATCH, input.length - pos);
|
|
77
|
+
let cand = head[h];
|
|
78
|
+
let chain = 0;
|
|
79
|
+
let bestLen = 0;
|
|
80
|
+
let bestOff = 0;
|
|
81
|
+
while (cand >= 0 && chain < MAX_CHAIN) {
|
|
82
|
+
const distance = pos - cand;
|
|
83
|
+
if (distance > WINDOW)
|
|
84
|
+
break;
|
|
85
|
+
let len = 0;
|
|
86
|
+
while (len < maxLen && input[cand + len] === input[pos + len])
|
|
87
|
+
len++;
|
|
88
|
+
if (len > bestLen) {
|
|
89
|
+
bestLen = len;
|
|
90
|
+
bestOff = distance;
|
|
91
|
+
if (len === maxLen)
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
cand = prev[cand];
|
|
95
|
+
chain++;
|
|
96
|
+
}
|
|
97
|
+
return bestLen >= MIN_MATCH ? { offset: bestOff, length: bestLen } : null;
|
|
98
|
+
};
|
|
99
|
+
let i = 0;
|
|
100
|
+
while (i < input.length) {
|
|
101
|
+
const controlIdx = out.length;
|
|
102
|
+
let control = 0;
|
|
103
|
+
out.push(0);
|
|
104
|
+
for (let bit = 7; bit >= 0 && i < input.length; bit--) {
|
|
105
|
+
const match = findMatch(i);
|
|
106
|
+
if (match) {
|
|
107
|
+
control |= 1 << bit;
|
|
108
|
+
const v = ((match.offset & 0xfff) << 4) | ((match.length - MIN_MATCH) & 0xf);
|
|
109
|
+
out.push((v >>> 8) & 0xff, v & 0xff);
|
|
110
|
+
for (let k = 0; k < match.length; k++)
|
|
111
|
+
insertAt(i + k);
|
|
112
|
+
i += match.length;
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
out.push(input[i]);
|
|
116
|
+
insertAt(i);
|
|
117
|
+
i += 1;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
out[controlIdx] = control;
|
|
121
|
+
}
|
|
122
|
+
// compLen = 码流字节数 (= 占位字节之后的所有字节); 之前这里把占位字节也算进去了 ⇒ 声明比实际多 1 ⇒ 解码必报截断
|
|
123
|
+
const body = out.slice(bodyStart + 1);
|
|
124
|
+
const filled = [];
|
|
125
|
+
writeVarint(filled, input.length);
|
|
126
|
+
writeVarint(filled, body.length);
|
|
127
|
+
filled.push(...body);
|
|
128
|
+
return Uint8Array.from(filled);
|
|
129
|
+
}
|
|
130
|
+
function hash3(b, i) {
|
|
131
|
+
return (((b[i] << 10) ^ (b[i + 1] << 5) ^ b[i + 2]) & (HASH_SIZE - 1)) >>> 0;
|
|
132
|
+
}
|
|
133
|
+
/** 解压 (严格)。码流不足 / 长度对不上 / 超长一律抛, 不做任何"尽量解" */
|
|
134
|
+
function lz77Decompress(block) {
|
|
135
|
+
const head = readVarint(block, 0);
|
|
136
|
+
const origLen = head.value;
|
|
137
|
+
const head2 = readVarint(block, head.next);
|
|
138
|
+
const compLen = head2.value;
|
|
139
|
+
const bodyEnd = head2.next + compLen;
|
|
140
|
+
if (bodyEnd > block.length) {
|
|
141
|
+
throw new types_js_1.EfficodeError('EFFICODE_TRUNCATED', `LZ77 码流声明 ${compLen}B 但只剩 ${block.length - head2.next}B`, head2.next);
|
|
142
|
+
}
|
|
143
|
+
const out = [];
|
|
144
|
+
let pos = head2.next;
|
|
145
|
+
while (out.length < origLen) {
|
|
146
|
+
if (pos >= bodyEnd) {
|
|
147
|
+
throw new types_js_1.EfficodeError('EFFICODE_TRUNCATED', `LZ77 码流提前结束 (期望 ${origLen}B, 实得 ${out.length}B)`, pos);
|
|
148
|
+
}
|
|
149
|
+
const control = block[pos++];
|
|
150
|
+
for (let bit = 7; bit >= 0 && out.length < origLen; bit--) {
|
|
151
|
+
if (control & (1 << bit)) {
|
|
152
|
+
if (pos + 2 > bodyEnd) {
|
|
153
|
+
throw new types_js_1.EfficodeError('EFFICODE_TRUNCATED', 'LZ77 匹配 token 缺字节', pos);
|
|
154
|
+
}
|
|
155
|
+
const v = (block[pos] << 8) | block[pos + 1];
|
|
156
|
+
pos += 2;
|
|
157
|
+
const offset = (v >>> 4) & 0xfff;
|
|
158
|
+
const length = (v & 0xf) + MIN_MATCH;
|
|
159
|
+
if (offset === 0 || offset > out.length) {
|
|
160
|
+
throw new types_js_1.EfficodeError('EFFICODE_TRUNCATED', `LZ77 匹配 offset=${offset} 越界 (已解 ${out.length}B)`, pos);
|
|
161
|
+
}
|
|
162
|
+
for (let k = 0; k < length; k++)
|
|
163
|
+
out.push(out[out.length - offset]);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
if (pos >= bodyEnd) {
|
|
167
|
+
throw new types_js_1.EfficodeError('EFFICODE_TRUNCATED', 'LZ77 字面量 token 缺字节', pos);
|
|
168
|
+
}
|
|
169
|
+
out.push(block[pos++]);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return Uint8Array.from(out);
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=lz77.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lz77.js","sourceRoot":"","sources":["../../../src/efficode/lz77.ts"],"names":[],"mappings":"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"}
|