@master0071/dsh-systemone 0.7.1 → 0.8.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/README.md CHANGED
@@ -365,7 +365,8 @@ const decision = await waterfall('agent/pre-step', { // ③ 再跑瀑布
365
365
  | `model` | `u2-decision` | 热更新 | 决策模型名 |
366
366
  | `timeoutMs` | `30000` | 热更新 | 请求超时(毫秒) |
367
367
  | `minConfidence` | `0.6` | 热更新 | **次要**复核信号:置信度低于该值时也标记需复核。主要判据是分布平坦度,见「复核判定」 |
368
- | `redact` | `false` | 热更新 | 发送前对 `state` 脱敏:手机号/身份证/邮箱/银行卡号替换为 `[手机号]` 等类型标签(判断语义保留;处理含隐私数据的内容时建议开启) |
368
+ | `redact` | `false` | 热更新 | 发送前对 `state` **与 `questions`** 脱敏:手机号/身份证/邮箱/银行卡号替换为 `[手机号]` 等类型标签(判断语义保留;`questions` 只脱敏 `instructions`/`label`,不动 qid 与 criteria 的键。处理含隐私数据的内容时建议开启) |
369
+ | `allowInsecureHttp` | `false` | 热更新 | 是否允许向 **非 https** 端点发请求。带 `Bearer` 凭据的请求走明文等于把密钥发到网络上,所以默认只放行 `https` 与本机回环(`127.0.0.1` / `localhost` / `[::1]`);连内网明文网关时才显式打开 |
369
370
  | `customScenarios` | 空 | 热更新 | 自定义场景 JSON 数组(见下)。保存后**场景库立即重建**,无需重启 |
370
371
  | `autoDecide` | `false` | 需重启 | 是否开启自动决策(挂载 `agent/pre-step`) |
371
372
  | `auto*` | 见「自动决策」 | 热更新 | 自动决策的运行参数(钩子挂载后实时生效) |
@@ -432,7 +433,7 @@ refs:
432
433
 
433
434
  ## 添加自定义场景
434
435
 
435
- **推荐用图形配置页**:插件 → @master0071/dsh-systemone → systemone → 「自定义场景」卡片,粘贴 JSON 即可。编辑框会实时校验(JSON 语法、`id`/`title`/`questions`、问题类型与选项数量),不通过时保存按钮禁用并给出具体原因;保存后场景库立即重建,新场景的 id 也会自动出现在「固定场景」的候选列表里。
436
+ **推荐用图形配置页**:插件 → @master0071/dsh-systemone → systemone → 「自定义场景」卡片,粘贴 JSON 即可。编辑框会实时校验(JSON 语法、`id`/`title`/`questions`、问题类型与选项数量、`patch` 的目标是否存在),不通过时保存按钮禁用并给出具体原因;保存后场景库立即重建,新场景的 id 也会自动出现在「固定场景」的候选列表里。
436
437
 
437
438
  卡片右上角的**「插入意图识别模板」**会填入下面这个通用意图识别场景,改 `criteria` 即可用。
438
439
 
@@ -440,6 +441,7 @@ refs:
440
441
 
441
442
  覆盖与别名的解析规则:
442
443
 
444
+ - **patch 的目标必须已存在**:内置 11 个场景,或**本数组中更早出现**的自定义场景。宿主按数组顺序登记(与 `indexById` 语义一致),指向不存在或更晚出现的 id 会被**整条跳过**——配置页会提前报“patch 目标不存在”,不必等日志。
443
445
  - **patch 式覆盖**:默认同 `id` 是**整体替换**。只想改一两个字段时,给条目加 `"patch": true` 即可做**字段级合并**——`title`/`description`/`recommendation` 给了才覆盖;`questions` 按 qid 合并(只换给出的字段,如只覆盖某个问题的 `criteria`,其余问题原样保留,新 qid 追加);`derive` 同名覆盖;`aliases` 求并集。例如给 `customer_service` 的部门问题加一个 VIP 通道:
444
446
 
445
447
  ```json
@@ -579,6 +581,7 @@ SystemOne 是「结构化问题 → 概率分布」的决策模型,**不是开
579
581
  插件与厂商解耦:只要目标服务实现相同协议(`POST {endpoint}`,请求体 `{ model, state, questions }`,响应 `{ answers: { [id]: { type, ... } } }`)即可切换。
580
582
 
581
583
  - **换一家云厂商**:配置 `provider: "http"` + `endpoint` 指向该厂商或自建网关,`apiKey` 填对应凭证。
584
+ **端点必须是 `https`**(或本机回环):请求带 `Authorization: Bearer <key>`,明文 `http` 会把密钥发到网络上,插件会直接拒绝;连内网明文网关需显式设置 `allowInsecureHttp: true`。
582
585
  - **本地联调**:`provider: "mock"`,无需 Key,确定性输出。
583
586
  - **编程扩展**:参考 `lib/provider.js`,实现 `{ name, decide() }` 后注册到 `createProvider()`。
584
587
 
@@ -586,16 +589,19 @@ SystemOne 是「结构化问题 → 概率分布」的决策模型,**不是开
586
589
 
587
590
  ```powershell
588
591
  npm run check # 9 个 lib/*.js 语法检查通过
589
- npm run test # 198 项测试:
592
+ npm run test # 206 项测试:
590
593
  # - 场景库完整性、11 大场景端到端(有断言强制 sample 覆盖全部内置场景)、
591
594
  # 自定义场景合并/覆盖/patch/非法跳过
592
595
  # - params 覆盖语义、确定性、失败路径(无网络、无 Key)
596
+ # - provider 端点安全(非 https 拒绝 / 回环放行 / allowInsecureHttp 逃生口)
597
+ # 与 state+questions 脱敏
593
598
  # - 插件入口装配(临时桩实例化:工具注册、降级、卸载)
594
599
  # - 自动决策(开关、注入形状与自有 source.kind、fail-open、硬超时、
595
600
  # 缓存与"改配置即失效"、去重、跳过规则、历史捕获、自动路由、
596
601
  # 路由复用与热失效、失败熔断、卸载)
597
602
  # - 用量台账(累计、跨日、重启后从文件恢复、坏行容错、日限判定)
598
- # - 配置页(场景 JSON 校验、数字范围、保存被拒的冲突/校验区分、
603
+ # - 配置页(场景 JSON 校验、patch 目标存在性与顺序语义、数字范围、
604
+ # 保存被拒的冲突/校验区分、
599
605
  # 三张卡各自保存(提交的 ops 只含本卡字段)、清除 Key 的 unset 语义、
600
606
  # 加载态与"为什么不能保存"的渲染冒烟)
601
607
  ```
package/lib/client.js CHANGED
@@ -42,7 +42,7 @@ window.__ModuleLoader__.load({
42
42
  tabCustom: '自定义场景',
43
43
  tabCustomHint: '用 JSON 定义自己的场景(含「通用意图识别」)。保存后场景库立即重建,无需重启。',
44
44
  customScenarios: '场景定义(JSON 数组)',
45
- customScenariosHint: '同 id 默认整体覆盖内置场景;加 "patch": true 则只改给出的字段。留空 [] 表示只用内置 11 个。每个场景最多 16 个问题,choice 选项 2~26 个。',
45
+ customScenariosHint: '同 id 默认整体覆盖内置场景;加 "patch": true 则只改给出的字段。patch 的目标必须是内置场景,或本数组中更早出现的自定义场景。留空 [] 表示只用内置 11 个。每个场景最多 16 个问题,choice 选项 2~26 个。',
46
46
  customScenariosPlaceholder: '[\n {\n "id": "intent",\n "title": "通用意图识别",\n "questions": {\n "intent": {\n "type": "choice",\n "instructions": "用户最想做什么?",\n "criteria": { "query": "查询", "action": "执行操作", "chat": "闲聊" }\n }\n }\n }\n]',
47
47
  insertTemplate: '插入意图识别模板',
48
48
  jsonEmpty: '(空 = 只用内置场景)',
@@ -78,9 +78,13 @@ window.__ModuleLoader__.load({
78
78
  minConfidence: '置信度阈值',
79
79
  minConfidenceHint: '任一答案低于该值时,结果会标记为建议人工复核(0~1)',
80
80
  redact: '发送前脱敏',
81
- redactHint: '开启后把 state 里的手机号/身份证/邮箱/银行卡号替换为 [类型] 标签再发给决策服务,判断语义保留',
81
+ redactHint: '开启后把 state 与 questions 里的手机号/身份证/邮箱/银行卡号替换为 [类型] 标签再发给决策服务,判断语义保留',
82
82
  redactOn: '开启(含隐私数据时建议)',
83
83
  redactOff: '关闭',
84
+ allowInsecureHttp: '允许明文 HTTP 端点',
85
+ allowInsecureHttpHint: '默认拒绝向非 https、非本机的端点发请求(Authorization 里的 API Key 会明文过网)。仅在本机/内网联调且明确知险时开启',
86
+ allowInsecureOn: '允许(不推荐)',
87
+ allowInsecureOff: '拒绝(默认)',
84
88
  autoScenario: '固定场景(可选)',
85
89
  autoScenarioHint: '点右侧箭头可从 11 个内置场景里选,也可手填场景 id 或别名(如 software_dev / 开发);留空则每次自动选场景(多一次路由请求)',
86
90
  autoInject: '注入通道',
@@ -137,7 +141,7 @@ window.__ModuleLoader__.load({
137
141
  tabCustom: 'Custom scenarios',
138
142
  tabCustomHint: 'Define your own scenarios in JSON (including general intent recognition). Saving rebuilds the library immediately.',
139
143
  customScenarios: 'Scenario definitions (JSON array)',
140
- customScenariosHint: 'An id matching a built-in overrides it; add "patch": true to merge only the given fields. Empty [] keeps only the 11 built-ins. Max 16 questions per scenario, 2~26 choice options.',
144
+ customScenariosHint: 'An id matching a built-in overrides it; add "patch": true to merge only the given fields. A patch target must be a built-in, or a custom scenario declared earlier in this array. Empty [] keeps only the 11 built-ins. Max 16 questions per scenario, 2~26 choice options.',
141
145
  customScenariosPlaceholder: '[\n {\n "id": "intent",\n "title": "General intent",\n "questions": {\n "intent": {\n "type": "choice",\n "instructions": "What does the user want?",\n "criteria": { "query": "Look up", "action": "Do something", "chat": "Small talk" }\n }\n }\n }\n]',
142
146
  insertTemplate: 'Insert intent template',
143
147
  jsonEmpty: '(empty = built-in scenarios only)',
@@ -173,9 +177,13 @@ window.__ModuleLoader__.load({
173
177
  minConfidence: 'Confidence threshold',
174
178
  minConfidenceHint: 'Below this value a result is flagged for human review (0~1)',
175
179
  redact: 'Redact before send',
176
- redactHint: 'Replace phone/ID-card/email/bank-card numbers in state with [type] tags before sending to the decision service; semantics preserved',
180
+ redactHint: 'Replace phone/ID-card/email/bank-card numbers in state and questions with [type] tags before sending to the decision service; semantics preserved',
177
181
  redactOn: 'On (recommended with PII)',
178
182
  redactOff: 'Off',
183
+ allowInsecureHttp: 'Allow plain HTTP endpoints',
184
+ allowInsecureHttpHint: 'By default requests to non-https, non-loopback endpoints are refused (the API key in Authorization would travel in cleartext). Enable only for trusted local/LAN testing',
185
+ allowInsecureOn: 'Allow (not recommended)',
186
+ allowInsecureOff: 'Refuse (default)',
179
187
  autoScenario: 'Fixed scenario (optional)',
180
188
  autoScenarioHint: 'Pick one of the 11 built-in scenarios from the dropdown, or type a scenario id/alias (e.g. software_dev); empty routes automatically on every step (one extra request)',
181
189
  autoInject: 'Injection channel',
@@ -348,6 +356,8 @@ window.__ModuleLoader__.load({
348
356
  { key: 'timeoutMs', kind: 'number', min: 1000 },
349
357
  { key: 'minConfidence', kind: 'number', min: 0, max: 1 },
350
358
  { key: 'redact', kind: 'select', options: ['false', 'true'] },
359
+ // 明文端点默认拒绝(API Key 会明文过网);只有内网联调才该开
360
+ { key: 'allowInsecureHttp', kind: 'select', options: ['false', 'true'] },
351
361
  // 台账路径也是 volatile:保存后宿主重建台账(新文件从当天记录恢复累计)
352
362
  { key: 'usageLogPath', kind: 'text' },
353
363
  ]
@@ -359,8 +369,9 @@ window.__ModuleLoader__.load({
359
369
  * 客户端侧校验自定义场景 JSON。
360
370
  *
361
371
  * 只做「能立刻发现」的检查:JSON 语法 + 每条的 id/title/questions 与
362
- * 问题类型/选项数量。宿主侧 `resolveScenarios` 还会做一遍同样的校验,
363
- * 语义问题(非法条目)宿主会跳过并写入日志;这里提前拦住,用户不必去翻日志。
372
+ * 问题类型/选项数量,以及 `patch` 的目标是否存在。宿主侧 `resolveScenarios`
373
+ * 还会做一遍同样的校验,语义问题(非法条目)宿主会跳过并写入日志;
374
+ * 这里提前拦住,用户不必去翻日志。
364
375
  *
365
376
  * @param {string} text - 编辑框里的原始文本
366
377
  * @returns {{ ok: boolean, error?: string, ids: string[], count: number }}
@@ -378,6 +389,9 @@ window.__ModuleLoader__.load({
378
389
  if (!Array.isArray(parsed)) return { ok: false, error: 'notArray', ids: [], count: 0 }
379
390
 
380
391
  const ids = []
392
+ // patch 只能改「已有」场景:内置 11 个,或本数组里**更早**出现的自定义条目。
393
+ // 与宿主 `resolveScenarios` 的 indexById 语义一致(内置先行、按数组顺序登记)。
394
+ const known = new Set(SCENARIO_OPTIONS.map((option) => option.id.toLowerCase()))
381
395
  for (let i = 0; i < parsed.length; i += 1) {
382
396
  const spec = parsed[i]
383
397
  const at = i + 1
@@ -387,6 +401,11 @@ window.__ModuleLoader__.load({
387
401
  const issues = []
388
402
  const isPatch = spec.patch === true
389
403
  if (typeof spec.id !== 'string' || spec.id.trim() === '') issues.push('缺少 id')
404
+ // 目标不存在时宿主会整条跳过(只写日志),界面若仍报「JSON 有效」,
405
+ // 用户会以为改好了——所以这里必须提前拦住。
406
+ else if (isPatch && !known.has(spec.id.trim().toLowerCase())) {
407
+ issues.push('patch 目标不存在(只能是内置场景,或本数组中更早出现的自定义场景)')
408
+ }
390
409
  if (!isPatch && (typeof spec.title !== 'string' || spec.title.trim() === '')) issues.push('缺少 title')
391
410
  const questions = spec.questions
392
411
  if (!isPatch || questions !== undefined) {
@@ -442,7 +461,11 @@ window.__ModuleLoader__.load({
442
461
  if (issues.length > 0) {
443
462
  return { ok: false, error: `issues:${at}:${spec.id ?? '?'}:${issues.join(';')}`, ids, count: 0 }
444
463
  }
445
- if (typeof spec.id === 'string') ids.push(spec.id.trim())
464
+ if (typeof spec.id === 'string') {
465
+ ids.push(spec.id.trim())
466
+ // 后续条目的 patch 可以指向它(与宿主按顺序登记一致)
467
+ known.add(spec.id.trim().toLowerCase())
468
+ }
446
469
  }
447
470
  return { ok: true, ids, count: ids.length }
448
471
  }
@@ -479,6 +502,9 @@ window.__ModuleLoader__.load({
479
502
  if (key === 'redact') {
480
503
  return value === 'true' ? t('redactOn') : t('redactOff')
481
504
  }
505
+ if (key === 'allowInsecureHttp') {
506
+ return value === 'true' ? t('allowInsecureOn') : t('allowInsecureOff')
507
+ }
482
508
  return value
483
509
  }
484
510
 
@@ -564,7 +590,8 @@ window.__ModuleLoader__.load({
564
590
  if (!Number.isFinite(parsed)) continue
565
591
  next = parsed
566
592
  }
567
- if (key === 'redact') next = value === 'true'
593
+ // 布尔字段:下拉在草稿里是字符串,schema 要的是布尔
594
+ if (key === 'redact' || key === 'allowInsecureHttp') next = value === 'true'
568
595
  ops.push({ op: 'set', path: [key], value: next })
569
596
  }
570
597
  if (cardKey === 'service' && clearApiKey) ops.push({ op: 'unset', path: ['apiKey'] })
@@ -1112,6 +1139,7 @@ window.__ModuleLoader__.load({
1112
1139
  numberErrorFor,
1113
1140
  saveResultText,
1114
1141
  cardOps,
1142
+ SCENARIO_OPTIONS,
1115
1143
  SERVICE_FIELDS,
1116
1144
  CUSTOM_FIELDS,
1117
1145
  AUTO_FIELDS,
package/lib/index.d.ts CHANGED
@@ -27,8 +27,13 @@ export interface Config {
27
27
  * 主要判据是概率分布平坦度(见 `reviewAssessment` 语义),因为这类概率未标定、跨后端不可比。
28
28
  */
29
29
  minConfidence?: number
30
- /** 发送前对 state 做内置脱敏(手机号/身份证/邮箱/银行卡号替换为类型标签) */
30
+ /** 发送前对 state 与 questions 做内置脱敏(手机号/身份证/邮箱/银行卡号替换为类型标签) */
31
31
  redact?: boolean
32
+ /**
33
+ * 允许向非 https 端点发送请求(默认 false)。请求带 Bearer 凭据,明文 http
34
+ * 会让密钥过网;默认只放行 https 与本机回环地址。
35
+ */
36
+ allowInsecureHttp?: boolean
32
37
  /** 自定义场景:JSON 数组字符串 */
33
38
  customScenarios?: string
34
39
  /** 用量台账文件路径;留空则用 `$DSH_HOME/dsh-systemone/usage.jsonl` */
package/lib/index.js CHANGED
@@ -101,11 +101,19 @@ const Config = z.object({
101
101
  /** 置信度阈值:任一答案低于该值则建议人工复核 */
102
102
  minConfidence: z.number().min(0).max(1).default(0.6).volatile(),
103
103
  /**
104
- * 发送前对 state 做内置脱敏:手机号/身份证/邮箱/银行卡号替换为
105
- * `[手机号]` 等类型标签(判断语义保留)。state 会发给上游决策服务,
104
+ * 发送前对 state 与 questions 做内置脱敏:手机号/身份证/邮箱/银行卡号替换为
105
+ * `[手机号]` 等类型标签(判断语义保留)。这两部分都会发给上游决策服务,
106
106
  * 处理含隐私数据的场景(尤其 data_governance)时建议开启。
107
107
  */
108
108
  redact: z.boolean().default(false).volatile(),
109
+ /**
110
+ * 允许向非 https 端点发送请求(默认 false)。
111
+ *
112
+ * 所有请求都带 `Authorization: Bearer <key>`,走明文 http 等于把密钥发到网络上,
113
+ * 所以默认只允许 https(以及本机回环 127.0.0.1/localhost)。只有明确知道自己在
114
+ * 连内网明文网关时才应打开。
115
+ */
116
+ allowInsecureHttp: z.boolean().default(false).volatile(),
109
117
 
110
118
  /* ── 结构性字段:不在配置页里,改动需要重启 ───────────────────────────────── */
111
119
 
package/lib/provider.js CHANGED
@@ -54,10 +54,10 @@ export class HttpProvider {
54
54
  if (!ep) {
55
55
  throw new Error('SystemOne: provider=http 需要配置 endpoint 或 baseUrl')
56
56
  }
57
- return assertValidUrl(ep)
57
+ return assertValidUrl(ep, this.config)
58
58
  }
59
59
  const base = String(baseUrl || DEFAULT_BASE_URL).trim().replace(/\/+$/, '')
60
- return assertValidUrl(`${base}/systemone`)
60
+ return assertValidUrl(`${base}/systemone`, this.config)
61
61
  }
62
62
 
63
63
  /**
@@ -93,10 +93,13 @@ export class HttpProvider {
93
93
  const endpoint = this.resolveEndpoint()
94
94
  const apiKey = this.resolveApiKey()
95
95
  const timeoutMs = readNumber(this.config, 'timeoutMs', 30000) || 30000
96
+ const redact = readBoolean(this.config, 'redact', false)
96
97
  const body = JSON.stringify({
97
98
  model: model || readString(this.config, 'model', DEFAULT_MODEL) || DEFAULT_MODEL,
98
- state: redactValue(state, readBoolean(this.config, 'redact', false)),
99
- questions,
99
+ state: redactValue(state, redact),
100
+ // questions 里的 instructions 同样是自由文本(systemone_decide 的 params 也并进来),
101
+ // 只脱敏 state 会让这条路径漏掉 PII。
102
+ questions: redactQuestions(questions, redact),
100
103
  })
101
104
 
102
105
  const controller = new AbortController()
@@ -322,6 +325,35 @@ function redactValue(value, enabled) {
322
325
  return value
323
326
  }
324
327
 
328
+ /**
329
+ * 递归脱敏 questions。
330
+ *
331
+ * 只处理**自由文本**(`instructions` / `label`),**不动 qid 与 criteria 的键**——
332
+ * 键是决策结果回填到场景的索引,改了就映射不回去了。`systemone_decide` 的
333
+ * `params.instructions` 覆盖会在发请求前合并进 questions,因此也一并覆盖。
334
+ *
335
+ * @param {object} questions - 问题定义(`{ [qid]: { type, instructions, ... } }`)
336
+ * @param {boolean} enabled - 是否开启脱敏
337
+ * @returns {object} 脱敏后的副本(未开启时原样返回)
338
+ */
339
+ function redactQuestions(questions, enabled) {
340
+ if (!enabled || !questions || typeof questions !== 'object' || Array.isArray(questions)) {
341
+ return questions
342
+ }
343
+ const out = {}
344
+ for (const [qid, question] of Object.entries(questions)) {
345
+ if (!question || typeof question !== 'object' || Array.isArray(question)) {
346
+ out[qid] = question
347
+ continue
348
+ }
349
+ const next = { ...question }
350
+ if (typeof next.instructions === 'string') next.instructions = redactString(next.instructions)
351
+ if (typeof next.label === 'string') next.label = redactString(next.label)
352
+ out[qid] = next
353
+ }
354
+ return out
355
+ }
356
+
325
357
  /* ─── DSH 凭证缝读取 ───────────────────────────────────────────────────────── */
326
358
 
327
359
  /** DSH 主目录:与用量台账同源(usage.js 导出)。 */
@@ -411,14 +443,42 @@ export function redactRuleSummary() {
411
443
 
412
444
  /* ─── 通用内部工具 ─────────────────────────────────────────────────────────── */
413
445
 
414
- /** URL 合法性在发请求前拦下:漏写协议时 fetch 的报错非常隐晦,不如直接说清楚。 */
415
- function assertValidUrl(url) {
446
+ /** 本机回环地址:http 只在这里被放行(凭据不出网卡)。 */
447
+ function isLoopbackHost(hostname) {
448
+ const host = String(hostname || '').toLowerCase()
449
+ return host === 'localhost' || host === '::1' || host === '[::1]' || /^127\./.test(host)
450
+ }
451
+
452
+ /**
453
+ * 发请求前的端点体检。
454
+ *
455
+ * 除了"是不是合法 URL",还要挡住**明文传输凭据**:本插件所有请求都带
456
+ * `Authorization: Bearer <key>`,走 http 等于把密钥明文发到网络上。因此默认
457
+ * 只允许 https;http 仅对本机回环放行(本地联调 / mock 网关),其他明文端点
458
+ * 必须显式配置 `allowInsecureHttp: true`(给内网自建网关留的逃生口)。
459
+ *
460
+ * @param {string} url - 已解析出的完整端点
461
+ * @param {object} [config] - 插件配置(读取 allowInsecureHttp)
462
+ */
463
+ function assertValidUrl(url, config) {
464
+ let parsed
416
465
  try {
417
- new URL(url)
466
+ parsed = new URL(url)
418
467
  } catch {
419
468
  throw new Error(`SystemOne: 端点不是合法 URL:${url}(需以 http:// 或 https:// 开头)`)
420
469
  }
421
- return url
470
+ if (parsed.protocol === 'https:') return url
471
+ if (parsed.protocol !== 'http:') {
472
+ throw new Error(`SystemOne: 不支持的端点协议 ${parsed.protocol}:${url}(只支持 http/https)`)
473
+ }
474
+ if (isLoopbackHost(parsed.hostname)) return url
475
+ if (readBoolean(config, 'allowInsecureHttp', false)) return url
476
+ throw new Error(
477
+ `SystemOne: 拒绝向非 https 端点发送请求:${url}`
478
+ + '(Authorization 里的 API Key 会明文过网)。'
479
+ + '本机联调请用 http://127.0.0.1 或 http://localhost;'
480
+ + '确需连接明文内网网关时,请在配置里显式设置 allowInsecureHttp: true。',
481
+ )
422
482
  }
423
483
 
424
484
  /** 仅临时故障值得重试:5xx、429 限流、网络层错误(fetch 失败抛 TypeError)。 */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@master0071/dsh-systemone",
3
- "version": "0.7.1",
3
+ "version": "0.8.0",
4
4
  "description": "DeepSeek Harness 插件:将 SystemOne 决策模型(默认云知声 u2-decision,可切换提供商)接入软件开发、需求预检、客服、内容审核、路由、销售线索、风控、招聘、数据治理、教育、需求变更等 11 大业务场景,为 Agent 返回带概率分布的决策与处置建议。",
5
5
  "license": "MIT",
6
6
  "type": "module",