@whyshu/argent-erp 0.0.0-stage → 0.2.2
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 +179 -2
- package/cordis.patch.yml +6 -0
- package/dist/client.js +614 -0
- package/package.json +55 -4
- package/presets/argent-office.patch.yml +78 -0
- package/src/data.mjs +297 -0
- package/src/index.js +123 -0
- package/src/office-guard.js +76 -0
- package/src/vendor/account-token.mjs +41 -0
- package/src/vendor/redact.mjs +37 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// 由 core/lib/account-token.mjs 复制而来(v0.9.0)。
|
|
2
|
+
// ⚠️ 插件是**可发布产物**:客户机/复制安装时仓库路径不存在,⛔ 不能相对 import core/lib。
|
|
3
|
+
// 漂移由 scripts/tests/argent-erp-ui-vendor-sync.test.mjs 拦截(逐字节比对)。
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 账号 token 的唯一解析点(2026-11-03)
|
|
7
|
+
*
|
|
8
|
+
* 为什么要单独成文件:ERP MCP 适配层(core/mcp/erp-server.mjs)与 dsh 宿主插件
|
|
9
|
+
* (plugins/argent-erp-ui)都要用**同一把**问述账号 JWT,⛔ 不许各写一套"先读哪个文件"。
|
|
10
|
+
*
|
|
11
|
+
* 顺序(实测依据见 docs/Argent-0.9-ERP入口与MCP网关-规划.md):
|
|
12
|
+
* ① `ARGENT_ACCOUNT_TOKEN`(显式注入,测试/排障)
|
|
13
|
+
* ② `WHYSHU_API_KEY` 环境变量(宿主 profile 注入)
|
|
14
|
+
* ③ `~/.dsh/.credentials.yaml` 的 `refs.WHYSHU_API_KEY`(现役;只读,⛔ 绝不写)
|
|
15
|
+
*
|
|
16
|
+
* ⚠️ `~/.dsh/argent/whyshu.env` 里的 JWT **已过期**(2026-11-03 实测 401),故意不读它。
|
|
17
|
+
*/
|
|
18
|
+
import { readFileSync } from 'node:fs'
|
|
19
|
+
import { homedir } from 'node:os'
|
|
20
|
+
import { join } from 'node:path'
|
|
21
|
+
|
|
22
|
+
/** 只读:从 credentials 文件里取 `WHYSHU_API_KEY: <token>`(窄正则,避免解析整份 YAML) */
|
|
23
|
+
export function readTokenFromCredentials(dshHome = process.env.DSH_HOME || join(homedir(), '.dsh')) {
|
|
24
|
+
try {
|
|
25
|
+
const raw = readFileSync(join(dshHome, '.credentials.yaml'), 'utf8')
|
|
26
|
+
const m = raw.match(/WHYSHU_API_KEY\s*:\s*"?([A-Za-z0-9._-]{20,})/)
|
|
27
|
+
return m ? m[1] : ''
|
|
28
|
+
} catch {
|
|
29
|
+
return ''
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** @returns {{token:string, source:'env-explicit'|'env-whyshu'|'credentials'|'none'}} */
|
|
34
|
+
export function resolveAccountToken({ env = process.env, dshHome } = {}) {
|
|
35
|
+
const explicit = String(env.ARGENT_ACCOUNT_TOKEN || '').trim()
|
|
36
|
+
if (explicit) return { token: explicit, source: 'env-explicit' }
|
|
37
|
+
const whyshu = String(env.WHYSHU_API_KEY || '').trim()
|
|
38
|
+
if (whyshu) return { token: whyshu, source: 'env-whyshu' }
|
|
39
|
+
const fromFile = readTokenFromCredentials(dshHome)
|
|
40
|
+
return fromFile ? { token: fromFile, source: 'credentials' } : { token: '', source: 'none' }
|
|
41
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// 由 core/lib/redact.mjs 复制而来(v0.9.0)。同理:插件不得依赖仓库相对路径。
|
|
2
|
+
// 漂移由 scripts/tests/argent-erp-ui-vendor-sync.test.mjs 拦截(逐字节比对)。
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 脱敏原语(2026-11-03,v0.9.0)
|
|
6
|
+
*
|
|
7
|
+
* 唯一实现点:ERP MCP 适配层(core/mcp/erp-server.mjs)与 dsh 宿主插件(plugins/argent-erp-ui)
|
|
8
|
+
* 都从这里取,⛔ 不许各写一套正则(两份必然漂移)。
|
|
9
|
+
*
|
|
10
|
+
* 当前规则(最小集,可扩):
|
|
11
|
+
* · 手机号 → 保留前3后4,中间 ****
|
|
12
|
+
* · 键名命中 token / downloadUrl / signedUrl ⇒ **丢弃**(只留文件名类字段)
|
|
13
|
+
* · 超长字符串(>400)截断,避免把整页 HTML 塞进模型上下文
|
|
14
|
+
* · `tenantId`(内部 Mongo id)不返回:对用户无意义,且能关联到别处
|
|
15
|
+
*/
|
|
16
|
+
export function maskPhone(value) {
|
|
17
|
+
return String(value || '').replace(/(\d{3})\d{4}(\d{4})/, '$1****$2')
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const DROP_KEYS = /(token|downloadUrl|download_url|signedUrl|signed_url|tenantId)/i
|
|
21
|
+
const PHONE_KEYS = /(phone|mobile)/i
|
|
22
|
+
|
|
23
|
+
export function redactDeep(value, depth = 0) {
|
|
24
|
+
if (depth > 6) return value
|
|
25
|
+
if (Array.isArray(value)) return value.map((v) => redactDeep(v, depth + 1))
|
|
26
|
+
if (value && typeof value === 'object') {
|
|
27
|
+
const out = {}
|
|
28
|
+
for (const [k, v] of Object.entries(value)) {
|
|
29
|
+
if (DROP_KEYS.test(k)) continue
|
|
30
|
+
if (PHONE_KEYS.test(k) && typeof v === 'string') { out[k] = maskPhone(v); continue }
|
|
31
|
+
out[k] = redactDeep(v, depth + 1)
|
|
32
|
+
}
|
|
33
|
+
return out
|
|
34
|
+
}
|
|
35
|
+
if (typeof value === 'string' && value.length > 400) return `${value.slice(0, 400)}…`
|
|
36
|
+
return value
|
|
37
|
+
}
|