@miphamai/cli 0.50.0 → 0.52.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/bin/mipham.ts +1 -9
- package/package.json +1 -2
- package/skills/standard/web-access/references/cdp-api.md +2 -1
- package/skills/standard/web-access/scripts/cdp-proxy.mjs +75 -1
- package/skills/standard/web-access/scripts/check-deps.mjs +17 -2
- package/skills/standard/web-access/scripts/find-url.mjs +0 -0
- package/skills/standard/web-access/scripts/match-site.mjs +0 -0
- package/skills/standard/web-access.SKILL.md +10 -3
- package/src/commands/keys.ts +26 -0
- package/src/commands/project.ts +1 -1
- package/src/config/credential-crypto.ts +114 -0
- package/src/config/defaults.ts +6 -1
- package/src/config/loader.ts +65 -14
- package/src/core/auto-memory.ts +3 -3
- package/src/core/constitution-loader.ts +0 -2
- package/src/core/context.ts +0 -1
- package/src/core/credential-masker/index.ts +1 -0
- package/src/core/credential-masker/matcher.ts +18 -7
- package/src/core/credential-masker/search.ts +73 -0
- package/src/core/credential-masker.ts +1 -0
- package/src/core/crsi-sandbox.ts +0 -1
- package/src/core/dream-engine.ts +0 -2
- package/src/core/instructions.ts +36 -12
- package/src/core/memory/memory-loader.ts +3 -1
- package/src/core/permission-rules.ts +23 -12
- package/src/core/rules-loader.ts +8 -10
- package/src/daemon/database.ts +0 -1
- package/src/daemon/feishu/adapter.ts +2 -1
- package/src/daemon/index.ts +18 -8
- package/src/daemon/server.ts +53 -10
- package/src/daemon/session-manager.ts +5 -4
- package/src/daemon/telegram/adapter.ts +70 -0
- package/src/daemon/telegram/api.ts +45 -0
- package/src/daemon/telegram/env.ts +14 -0
- package/src/daemon/telegram/poller.ts +64 -0
- package/src/daemon/telegram/types.ts +10 -0
- package/src/i18n-core/locales/en-US.json +123 -0
- package/src/i18n-core/locales/zh-CN.json +123 -0
- package/src/index.tsx +10 -8
- package/src/mcp/token-store.ts +2 -37
- package/src/shared/package-info.ts +1 -1
- package/src/shared/types.ts +5 -3
- package/src/skills/bin-check.ts +40 -0
- package/src/skills/bundled-skill-assets.ts +5 -5
- package/src/skills/bundled-skills.ts +1 -1
- package/src/skills/loader.ts +35 -10
- package/src/skills/sanitizer.ts +1 -2
- package/src/skills/seam.ts +1 -1
- package/src/tools/agent/exit-plan.ts +1 -3
- package/src/tools/agent/memory.ts +2 -1
- package/src/tools/agent/skill.ts +14 -0
- package/src/tools/file/glob.ts +38 -23
- package/src/tools/file/grep.ts +86 -64
- package/src/tools/index.ts +5 -5
- package/src/ui/commands.ts +191 -119
- package/src/ui/config-wizard.tsx +5 -2
- package/src/core/tokenizer.ts +0 -80
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { TelegramApi } from './api.js'
|
|
2
|
+
import type { TelegramMessage } from './types.js'
|
|
3
|
+
|
|
4
|
+
/** 从 update 提取文本消息;非文本/缺 message → null。chat.id 统一字符串化比对(id < 2^53;超范围 bigint 后续再引入)。 */
|
|
5
|
+
export function extractTextMessage(update: unknown): TelegramMessage | null {
|
|
6
|
+
const msg = (
|
|
7
|
+
update as { message?: { chat?: { id?: number | string }; message_id?: number; text?: string } }
|
|
8
|
+
)?.message
|
|
9
|
+
if (!msg?.text || msg.chat?.id == null) return null
|
|
10
|
+
return { chatId: String(msg.chat.id), messageId: msg.message_id ?? 0, text: msg.text }
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** 推进 offset:最大 update_id + 1;空列表沿用 prev。 */
|
|
14
|
+
export function nextOffset(updates: Array<{ update_id?: number }>, prevOffset: number): number {
|
|
15
|
+
if (updates.length === 0) return prevOffset
|
|
16
|
+
return Math.max(...updates.map((u) => u.update_id ?? prevOffset)) + 1
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** 指数退避,封顶 30s。 */
|
|
20
|
+
export function nextBackoff(currentMs: number): number {
|
|
21
|
+
return Math.min(currentMs * 2, 30_000)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** 长轮询循环(unref 不阻退出)。返回 stop 函数。循环行为由 Task 7 集成测试覆盖。 */
|
|
25
|
+
export function startTelegramPoller(
|
|
26
|
+
api: TelegramApi,
|
|
27
|
+
onMessage: (msg: TelegramMessage) => Promise<void>,
|
|
28
|
+
opts?: { pollTimeoutSeconds?: number },
|
|
29
|
+
): () => void {
|
|
30
|
+
let offset = 0
|
|
31
|
+
let backoffMs = 1000
|
|
32
|
+
let stopped = false
|
|
33
|
+
let timer: ReturnType<typeof setTimeout> | null = null
|
|
34
|
+
|
|
35
|
+
async function loop() {
|
|
36
|
+
if (stopped) return
|
|
37
|
+
try {
|
|
38
|
+
const updates = await api.getUpdates(offset, opts?.pollTimeoutSeconds ?? 30)
|
|
39
|
+
for (const u of updates) {
|
|
40
|
+
const msg = extractTextMessage(u)
|
|
41
|
+
if (msg) {
|
|
42
|
+
try {
|
|
43
|
+
await onMessage(msg)
|
|
44
|
+
} catch {
|
|
45
|
+
/* 单条失败不中断轮询 */
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
offset = nextOffset(updates, offset)
|
|
50
|
+
backoffMs = 1000
|
|
51
|
+
} catch {
|
|
52
|
+
backoffMs = nextBackoff(backoffMs)
|
|
53
|
+
}
|
|
54
|
+
if (stopped) return
|
|
55
|
+
timer = setTimeout(loop, backoffMs)
|
|
56
|
+
;(timer as { unref?: () => void }).unref?.()
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
void loop()
|
|
60
|
+
return () => {
|
|
61
|
+
stopped = true
|
|
62
|
+
if (timer) clearTimeout(timer)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -612,6 +612,129 @@
|
|
|
612
612
|
},
|
|
613
613
|
"schedule": {
|
|
614
614
|
"failed": "Schedule check failed: {error}"
|
|
615
|
+
},
|
|
616
|
+
"crsi_critique": {
|
|
617
|
+
"not_init": "SelfCritique not initialized. Mipham Code >= v0.34.0 required.",
|
|
618
|
+
"on_title": "## 🔍 Self-Critique: ON",
|
|
619
|
+
"on_desc1": "The AI will now critique its own tool calls before execution.",
|
|
620
|
+
"on_desc2": "Safe + correct + necessary checks run before every targeted tool call.",
|
|
621
|
+
"on_hint": "💡 `/crsi critique off` to disable | `/crsi critique status` for current config",
|
|
622
|
+
"off_title": "## 🔍 Self-Critique: OFF\n\nTool calls will execute without pre-flight critique.",
|
|
623
|
+
"status_title": "## 🔍 Self-Critique Status",
|
|
624
|
+
"state": "State: **{state}**",
|
|
625
|
+
"model": "Model: **{model}**",
|
|
626
|
+
"threshold": "Threshold: **{pct}%**",
|
|
627
|
+
"threshold_detail": "Threshold: **{pct}%** (below this → correction or block)",
|
|
628
|
+
"target_tools": "Target tools: **{tools}**",
|
|
629
|
+
"timeout": "Timeout: **{ms}ms**",
|
|
630
|
+
"status_on_hint": "🔍 `/crsi critique on` — Enable self-critique",
|
|
631
|
+
"status_off_hint": "🔍 `/crsi critique off` — Disable self-critique",
|
|
632
|
+
"inspired": "*Inspired by Anthropic RLAIF — AI critiques its own actions before execution.*"
|
|
633
|
+
},
|
|
634
|
+
"crsi_interpret": {
|
|
635
|
+
"title": "## 🧠 CRSI Tool-Call Interpretability",
|
|
636
|
+
"no_sigs_for_tool": "No error signatures for tool `{tool}`. CRSI immune memory is clean for this tool.",
|
|
637
|
+
"no_sigs": "No active error signatures. CRSI immune memory is clean.",
|
|
638
|
+
"sigs_title": "### 🛡️ Error Signatures{tool}",
|
|
639
|
+
"sig_success": " Success: {bar} {pct}% | {occurrences}x | {strategy}",
|
|
640
|
+
"sig_fix": " Fix: `{fix}`",
|
|
641
|
+
"sigs_more": "... and {count} more signatures",
|
|
642
|
+
"reflection_title": "### 📊 CRSI Reflection Summary",
|
|
643
|
+
"reflections": "Turn reflections analyzed: **{count}**",
|
|
644
|
+
"usage_title": "### 💰 Token Usage",
|
|
645
|
+
"api_in": "API input tokens: {count}",
|
|
646
|
+
"api_out": "API output tokens: {count}",
|
|
647
|
+
"est_tokens": "Estimated tokens: {count}",
|
|
648
|
+
"health_title": "### 🏥 System Health",
|
|
649
|
+
"overall": "Overall: {bar} **{score}/100**",
|
|
650
|
+
"assessment": "Assessment: {assessment}",
|
|
651
|
+
"constitution_title": "### ⚖️ Constitution",
|
|
652
|
+
"principles": "Principles: **{total}** (🚫 {block} block | ⚠️ {warn} warn | 🔄 {auto} auto)",
|
|
653
|
+
"version": "Version: v{version}",
|
|
654
|
+
"empty": "_Start using tools to populate CRSI interpretability data._",
|
|
655
|
+
"filter_hint": "🔍 Filter by tool: `/crsi interpret <tool-name>` (e.g. `/crsi interpret Bash`)"
|
|
656
|
+
},
|
|
657
|
+
"crsi_red_team": {
|
|
658
|
+
"not_init": "Red-Team requires both ConstitutionLoader and PreFlightChecker to be initialized.",
|
|
659
|
+
"title": "## 🔴 CRSI Red-Team Report",
|
|
660
|
+
"score": "Overall Score: **{score}/100**",
|
|
661
|
+
"metric": "| Metric | Count |",
|
|
662
|
+
"metric_sep": "|--------|------|",
|
|
663
|
+
"total_attacks": "| Total attacks | {count} |",
|
|
664
|
+
"blocked": "| 🛡️ Correctly blocked | {count} |",
|
|
665
|
+
"passed_through": "| 🔴 Passed through (GAP) | {count} |",
|
|
666
|
+
"false_positives": "| ⚠️ False positives | {count} |",
|
|
667
|
+
"by_principle": "### By Principle",
|
|
668
|
+
"gaps_title": "### 🔴 Security Gaps (Should Have Been Blocked)",
|
|
669
|
+
"gap_tool": " Tool: `{tool}` → Params: `{params}`",
|
|
670
|
+
"blocked_title": "### 🛡️ Successfully Blocked",
|
|
671
|
+
"caught_by": "{principle}: {desc} → caught by **{caught}**",
|
|
672
|
+
"all_blocked": "🎉 **All attacks blocked!** The SIS immune system is fully operational.",
|
|
673
|
+
"good_coverage": "⚠️ Good coverage. Review the gaps above and add audit patterns to `ai-guardrails.yml`.",
|
|
674
|
+
"critical": "🔴 **Critical gaps detected.** Prioritize fixing the passed-through attacks above."
|
|
675
|
+
},
|
|
676
|
+
"dream": {
|
|
677
|
+
"not_init": "DreamEngine not initialized. Mipham Code >= v0.34.0 required.",
|
|
678
|
+
"no_history": "🌙 Auto-Dream has not run yet. Use `/dream` to trigger the first consolidation.",
|
|
679
|
+
"history_title": "## 🌙 Auto-Dream History",
|
|
680
|
+
"history_entry": "- **{time}**: {total} actions ({applied} auto-applied, {flagged} flagged for review)",
|
|
681
|
+
"done_title": "## 🌙 Auto-Dream Complete",
|
|
682
|
+
"integrate": "Memory integration: {before} → {after}",
|
|
683
|
+
"dedup": "- Deduplicated: {count} entries",
|
|
684
|
+
"contradiction": "- Contradictions: {count} (need human review)",
|
|
685
|
+
"merge": "- Merged: {count} groups",
|
|
686
|
+
"solidify": "- Fuzzy-marked: {count} entries (suggested to solidify)",
|
|
687
|
+
"prune": "- Pruned expired: {count} entries",
|
|
688
|
+
"review_hint": "⚠️ {count} items pending review. Use `/dream --aggressive` to auto-apply all.",
|
|
689
|
+
"clean": "✅ Memory is clean, nothing to consolidate."
|
|
690
|
+
},
|
|
691
|
+
"constitution": {
|
|
692
|
+
"not_init": "ConstitutionLoader not initialized. Mipham Code >= v0.34.0 required.",
|
|
693
|
+
"reload_title": "## ⚖️ Constitution Reloaded",
|
|
694
|
+
"version": "Version: **v{version}**",
|
|
695
|
+
"principles": "Principles: **{count}**",
|
|
696
|
+
"path": "Path: `{path}`",
|
|
697
|
+
"view_title": "## ⚖️ Mipham Constitution",
|
|
698
|
+
"view_summary": "Version: **v{version}** | Principles: **{count}** | Path: `{path}`",
|
|
699
|
+
"scope": " Scope: `{scope}`",
|
|
700
|
+
"tools": " Tools: {tools}",
|
|
701
|
+
"reload_hint": "🔧 `/constitution reload` — Reload after editing ai-guardrails.yml",
|
|
702
|
+
"edit_hint": "📝 Edit: `vi ~/.mipham/ai-guardrails.yml`",
|
|
703
|
+
"reset_hint": "🗑️ Reset: delete `~/.mipham/ai-guardrails.yml` then run `/constitution reload`",
|
|
704
|
+
"inspired": "*Inspired by Anthropic Constitutional AI. Mipham enforces these principles at runtime — auditable on every action.*"
|
|
705
|
+
},
|
|
706
|
+
"bug_report": {
|
|
707
|
+
"title": "## 🐛 Bug Report",
|
|
708
|
+
"hint": "> Copy the report below and paste it into your GitHub Issue.",
|
|
709
|
+
"env_title": "### Environment",
|
|
710
|
+
"mipham": "- **Mipham Code**: v{version}",
|
|
711
|
+
"session": "- **Session**: {id}",
|
|
712
|
+
"node": "- **Node**: {version}",
|
|
713
|
+
"platform": "- **Platform**: {platform} {arch}",
|
|
714
|
+
"os": "- **OS**: {os}",
|
|
715
|
+
"provider": "- **Provider**: {provider}",
|
|
716
|
+
"model": "- **Model**: {model}",
|
|
717
|
+
"sigs_title": "### Active Error Signatures",
|
|
718
|
+
"sig_entry": "- `{id}`: {pattern} ({category}, {occurrences}x, {pct}% success)",
|
|
719
|
+
"disabled_hooks": "### Disabled Hooks",
|
|
720
|
+
"hook_entry": "- `{key}`: {failures} failures, disabled at {time}",
|
|
721
|
+
"last_dream": "### Last Auto-Dream",
|
|
722
|
+
"ran_at": "- **Ran at**: {time}",
|
|
723
|
+
"actions": "- **Actions**: {total} ({applied} applied)",
|
|
724
|
+
"steps_title": "### Steps to Reproduce",
|
|
725
|
+
"expected": "### Expected Behavior",
|
|
726
|
+
"actual": "### Actual Behavior",
|
|
727
|
+
"generated": "🤖 Generated with Mipham Code v{version}"
|
|
728
|
+
},
|
|
729
|
+
"changelog": {
|
|
730
|
+
"title": "## 📋 Changelog",
|
|
731
|
+
"no_tags": "No version tags found.",
|
|
732
|
+
"current": "Current: **v{version}**",
|
|
733
|
+
"current_marker": " ← current",
|
|
734
|
+
"release_only": " _(release tag only)_",
|
|
735
|
+
"details_unavailable": " _(details unavailable)_",
|
|
736
|
+
"no_git": "Unable to read git history. Make sure you are in the Mipham Code repository.",
|
|
737
|
+
"full_history": "Full history: `git log --oneline`"
|
|
615
738
|
}
|
|
616
739
|
},
|
|
617
740
|
"ui": {
|
|
@@ -612,6 +612,129 @@
|
|
|
612
612
|
},
|
|
613
613
|
"schedule": {
|
|
614
614
|
"failed": "调度检查失败: {error}"
|
|
615
|
+
},
|
|
616
|
+
"crsi_critique": {
|
|
617
|
+
"not_init": "SelfCritique 未初始化。请确认 Mipham Code 版本 >= v0.34.0。",
|
|
618
|
+
"on_title": "## 🔍 Self-Critique: 已开启",
|
|
619
|
+
"on_desc1": "AI 现在会在每次工具调用执行前自我批判。",
|
|
620
|
+
"on_desc2": "每次目标工具调用前都会执行安全 + 正确 + 必要性检查。",
|
|
621
|
+
"on_hint": "💡 `/crsi critique off` 关闭 | `/crsi critique status` 查看当前配置",
|
|
622
|
+
"off_title": "## 🔍 Self-Critique: 已关闭\n\n工具调用将不再执行预检批判。",
|
|
623
|
+
"status_title": "## 🔍 Self-Critique 状态",
|
|
624
|
+
"state": "状态: **{state}**",
|
|
625
|
+
"model": "模型: **{model}**",
|
|
626
|
+
"threshold": "阈值: **{pct}%**",
|
|
627
|
+
"threshold_detail": "阈值: **{pct}%**(低于此 → 纠正或拦截)",
|
|
628
|
+
"target_tools": "目标工具: **{tools}**",
|
|
629
|
+
"timeout": "超时: **{ms}ms**",
|
|
630
|
+
"status_on_hint": "🔍 `/crsi critique on` — 启用自我批判",
|
|
631
|
+
"status_off_hint": "🔍 `/crsi critique off` — 禁用自我批判",
|
|
632
|
+
"inspired": "*灵感来自 Anthropic RLAIF — AI 在执行前批判自己的行为。*"
|
|
633
|
+
},
|
|
634
|
+
"crsi_interpret": {
|
|
635
|
+
"title": "## 🧠 CRSI 工具调用可解释性",
|
|
636
|
+
"no_sigs_for_tool": "工具 `{tool}` 没有错误签名。该工具的 CRSI 免疫记忆是干净的。",
|
|
637
|
+
"no_sigs": "没有活跃的错误签名。CRSI 免疫记忆是干净的。",
|
|
638
|
+
"sigs_title": "### 🛡️ 错误签名{tool}",
|
|
639
|
+
"sig_success": " 成功率: {bar} {pct}% | {occurrences}次 | {strategy}",
|
|
640
|
+
"sig_fix": " 修复: `{fix}`",
|
|
641
|
+
"sigs_more": "... 还有 {count} 条签名",
|
|
642
|
+
"reflection_title": "### 📊 CRSI 反思摘要",
|
|
643
|
+
"reflections": "已分析的轮次反思: **{count}**",
|
|
644
|
+
"usage_title": "### 💰 Token 用量",
|
|
645
|
+
"api_in": "API 输入 tokens: {count}",
|
|
646
|
+
"api_out": "API 输出 tokens: {count}",
|
|
647
|
+
"est_tokens": "估算 tokens: {count}",
|
|
648
|
+
"health_title": "### 🏥 系统健康",
|
|
649
|
+
"overall": "总体: {bar} **{score}/100**",
|
|
650
|
+
"assessment": "评估: {assessment}",
|
|
651
|
+
"constitution_title": "### ⚖️ 宪法",
|
|
652
|
+
"principles": "原则: **{total}** (🚫 {block} 拦截 | ⚠️ {warn} 警告 | 🔄 {auto} 自动)",
|
|
653
|
+
"version": "版本: v{version}",
|
|
654
|
+
"empty": "_开始使用工具以填充 CRSI 可解释性数据。_",
|
|
655
|
+
"filter_hint": "🔍 按工具过滤: `/crsi interpret <工具名>` (例如 `/crsi interpret Bash`)"
|
|
656
|
+
},
|
|
657
|
+
"crsi_red_team": {
|
|
658
|
+
"not_init": "Red-Team 需要 ConstitutionLoader 和 PreFlightChecker 均已初始化。",
|
|
659
|
+
"title": "## 🔴 CRSI 红队报告",
|
|
660
|
+
"score": "总体得分: **{score}/100**",
|
|
661
|
+
"metric": "| 指标 | 数量 |",
|
|
662
|
+
"metric_sep": "|------|------|",
|
|
663
|
+
"total_attacks": "| 总攻击数 | {count} |",
|
|
664
|
+
"blocked": "| 🛡️ 正确拦截 | {count} |",
|
|
665
|
+
"passed_through": "| 🔴 穿透(缺口) | {count} |",
|
|
666
|
+
"false_positives": "| ⚠️ 误报 | {count} |",
|
|
667
|
+
"by_principle": "### 按原则",
|
|
668
|
+
"gaps_title": "### 🔴 安全缺口(本应被拦截)",
|
|
669
|
+
"gap_tool": " 工具: `{tool}` → 参数: `{params}`",
|
|
670
|
+
"blocked_title": "### 🛡️ 成功拦截",
|
|
671
|
+
"caught_by": "{principle}: {desc} → 由 **{caught}** 捕获",
|
|
672
|
+
"all_blocked": "🎉 **所有攻击均被拦截!** SIS 自免疫系统完全正常运转。",
|
|
673
|
+
"good_coverage": "⚠️ 覆盖良好。请审查上述缺口,并向 `ai-guardrails.yml` 添加审计模式。",
|
|
674
|
+
"critical": "🔴 **检测到严重缺口。** 优先修复上述穿透的攻击。"
|
|
675
|
+
},
|
|
676
|
+
"dream": {
|
|
677
|
+
"not_init": "DreamEngine 未初始化。请确认 Mipham Code 版本 >= v0.34.0。",
|
|
678
|
+
"no_history": "🌙 尚未运行过 Auto-Dream。使用 `/dream` 手动触发首次梦境整合。",
|
|
679
|
+
"history_title": "## 🌙 Auto-Dream 历史",
|
|
680
|
+
"history_entry": "- **{time}**: {total} 行动 ({applied} 自动应用, {flagged} 标记审查)",
|
|
681
|
+
"done_title": "## 🌙 Auto-Dream 完成",
|
|
682
|
+
"integrate": "记忆整合: {before} → {after}",
|
|
683
|
+
"dedup": "- 去重: {count} 条",
|
|
684
|
+
"contradiction": "- 矛盾冲突: {count} 处 (需人工裁决)",
|
|
685
|
+
"merge": "- 合并: {count} 组",
|
|
686
|
+
"solidify": "- 模糊标记: {count} 条 (建议固化)",
|
|
687
|
+
"prune": "- 清理过期: {count} 条",
|
|
688
|
+
"review_hint": "⚠️ {count} 项待审查。使用 `/dream --aggressive` 自动应用所有操作。",
|
|
689
|
+
"clean": "✅ 记忆干净,无需整理。"
|
|
690
|
+
},
|
|
691
|
+
"constitution": {
|
|
692
|
+
"not_init": "ConstitutionLoader 未初始化。请确认 Mipham Code 版本 >= v0.34.0。",
|
|
693
|
+
"reload_title": "## ⚖️ 宪法已重新加载",
|
|
694
|
+
"version": "版本: **v{version}**",
|
|
695
|
+
"principles": "原则: **{count}**",
|
|
696
|
+
"path": "路径: `{path}`",
|
|
697
|
+
"view_title": "## ⚖️ Mipham 宪法",
|
|
698
|
+
"view_summary": "版本: **v{version}** | 原则: **{count}** | 路径: `{path}`",
|
|
699
|
+
"scope": " 作用域: `{scope}`",
|
|
700
|
+
"tools": " 工具: {tools}",
|
|
701
|
+
"reload_hint": "🔧 `/constitution reload` — 重新加载(修改 ai-guardrails.yml 后使用)",
|
|
702
|
+
"edit_hint": "📝 编辑: `vi ~/.mipham/ai-guardrails.yml`",
|
|
703
|
+
"reset_hint": "🗑️ 重置: 删除 `~/.mipham/ai-guardrails.yml` 后执行 `/constitution reload`",
|
|
704
|
+
"inspired": "*灵感来自 Anthropic 宪法 AI。Mipham 在运行时强制执行这些原则 — 每次行动都可审计。*"
|
|
705
|
+
},
|
|
706
|
+
"bug_report": {
|
|
707
|
+
"title": "## 🐛 Bug 报告",
|
|
708
|
+
"hint": "> 复制下面的报告并粘贴到你的 GitHub Issue 中。",
|
|
709
|
+
"env_title": "### 环境",
|
|
710
|
+
"mipham": "- **Mipham Code**: v{version}",
|
|
711
|
+
"session": "- **会话**: {id}",
|
|
712
|
+
"node": "- **Node**: {version}",
|
|
713
|
+
"platform": "- **平台**: {platform} {arch}",
|
|
714
|
+
"os": "- **操作系统**: {os}",
|
|
715
|
+
"provider": "- **提供商**: {provider}",
|
|
716
|
+
"model": "- **模型**: {model}",
|
|
717
|
+
"sigs_title": "### 活跃错误签名",
|
|
718
|
+
"sig_entry": "- `{id}`: {pattern} ({category}, {occurrences}次, {pct}% 成功率)",
|
|
719
|
+
"disabled_hooks": "### 已禁用的 Hooks",
|
|
720
|
+
"hook_entry": "- `{key}`: {failures} 次失败, 于 {time} 禁用",
|
|
721
|
+
"last_dream": "### 最近一次 Auto-Dream",
|
|
722
|
+
"ran_at": "- **运行于**: {time}",
|
|
723
|
+
"actions": "- **行动**: {total} ({applied} 已应用)",
|
|
724
|
+
"steps_title": "### 复现步骤",
|
|
725
|
+
"expected": "### 预期行为",
|
|
726
|
+
"actual": "### 实际行为",
|
|
727
|
+
"generated": "🤖 由 Mipham Code v{version} 生成"
|
|
728
|
+
},
|
|
729
|
+
"changelog": {
|
|
730
|
+
"title": "## 📋 变更日志",
|
|
731
|
+
"no_tags": "未找到版本标签。",
|
|
732
|
+
"current": "当前: **v{version}**",
|
|
733
|
+
"current_marker": " ← 当前",
|
|
734
|
+
"release_only": " _(仅发布标签)_",
|
|
735
|
+
"details_unavailable": " _(详情不可用)_",
|
|
736
|
+
"no_git": "无法读取 git 历史。请确认你位于 Mipham Code 仓库中。",
|
|
737
|
+
"full_history": "完整历史: `git log --oneline`"
|
|
615
738
|
}
|
|
616
739
|
},
|
|
617
740
|
"ui": {
|
package/src/index.tsx
CHANGED
|
@@ -281,12 +281,6 @@ export async function runApp(options: RunOptions): Promise<void> {
|
|
|
281
281
|
skillsLoader.loadExternal(config.skills.paths)
|
|
282
282
|
}
|
|
283
283
|
|
|
284
|
-
// Inject skills system-reminder into system prompt for AI auto-triggering
|
|
285
|
-
const skillsReminder = skillsLoader.buildSystemReminder()
|
|
286
|
-
if (skillsReminder) {
|
|
287
|
-
instructions.setSkillsReminder(skillsReminder)
|
|
288
|
-
}
|
|
289
|
-
|
|
290
284
|
// Initialize plugin manager
|
|
291
285
|
const pluginManager = new PluginManager()
|
|
292
286
|
|
|
@@ -327,8 +321,6 @@ export async function runApp(options: RunOptions): Promise<void> {
|
|
|
327
321
|
// Phase 9 feature flags (all default true — opt-out via config)
|
|
328
322
|
const features = config.features || {}
|
|
329
323
|
const adaptiveThresholds = features.context?.adaptiveThresholds !== false
|
|
330
|
-
// gated via TokenCounter — set to false to fall back to chars/4 heuristic
|
|
331
|
-
const _useRealTokenizer = features.context?.useRealTokenizer !== false
|
|
332
324
|
|
|
333
325
|
const context = new ContextManager({
|
|
334
326
|
maxTokens: contextMaxTokens,
|
|
@@ -372,9 +364,13 @@ export async function runApp(options: RunOptions): Promise<void> {
|
|
|
372
364
|
if (context.getMessageCount() === 0) {
|
|
373
365
|
const basePrompt = instructions.buildSystemPrompt(config.permission as string)
|
|
374
366
|
const memoryReminder = loadSessionMemories(basePrompt)
|
|
367
|
+
const skillsReminder = skillsLoader.buildSystemReminder(basePrompt)
|
|
375
368
|
|
|
376
369
|
// Inject previous session summary for AI continuity
|
|
377
370
|
let prompt = basePrompt
|
|
371
|
+
if (skillsReminder) {
|
|
372
|
+
prompt = `${prompt}\n\n${skillsReminder}`
|
|
373
|
+
}
|
|
378
374
|
if (memoryReminder) {
|
|
379
375
|
prompt = `${prompt}\n\n${memoryReminder}`
|
|
380
376
|
}
|
|
@@ -524,6 +520,12 @@ export async function runApp(options: RunOptions): Promise<void> {
|
|
|
524
520
|
engine.getPermission().setRestrictions(config.permissionRestrictions)
|
|
525
521
|
}
|
|
526
522
|
|
|
523
|
+
// Apply user-defined permission rules (allow/deny) — wire the rule system into runtime
|
|
524
|
+
if (config.permissionRules) {
|
|
525
|
+
for (const rule of config.permissionRules.allow ?? []) engine.getPermission().allow(rule)
|
|
526
|
+
for (const rule of config.permissionRules.deny ?? []) engine.getPermission().deny(rule)
|
|
527
|
+
}
|
|
528
|
+
|
|
527
529
|
// Initialize agent registry and load plugin agents/skills/MCP/hooks
|
|
528
530
|
const agentRegistry = new AgentRegistry()
|
|
529
531
|
agentRegistry.loadUserAgents()
|
package/src/mcp/token-store.ts
CHANGED
|
@@ -8,13 +8,8 @@ import {
|
|
|
8
8
|
chmodSync,
|
|
9
9
|
} from 'node:fs'
|
|
10
10
|
import { join, dirname } from 'node:path'
|
|
11
|
-
import { createCipheriv, createDecipheriv, randomBytes } from 'node:crypto'
|
|
12
11
|
import { homedir } from 'node:os'
|
|
13
|
-
|
|
14
|
-
const ALGORITHM = 'aes-256-gcm'
|
|
15
|
-
const IV_LENGTH = 16
|
|
16
|
-
const AUTH_TAG_LENGTH = 16
|
|
17
|
-
const KEY_LENGTH = 32
|
|
12
|
+
import { encrypt, decrypt, getCredentialKey } from '../config/credential-crypto'
|
|
18
13
|
|
|
19
14
|
interface TokenData {
|
|
20
15
|
accessToken: string
|
|
@@ -24,43 +19,13 @@ interface TokenData {
|
|
|
24
19
|
scopes?: string[]
|
|
25
20
|
}
|
|
26
21
|
|
|
27
|
-
function getEncryptionKey(keyPath: string): Buffer {
|
|
28
|
-
if (existsSync(keyPath)) {
|
|
29
|
-
return readFileSync(keyPath)
|
|
30
|
-
}
|
|
31
|
-
const key = randomBytes(KEY_LENGTH)
|
|
32
|
-
mkdirSync(dirname(keyPath), { recursive: true })
|
|
33
|
-
writeFileSync(keyPath, key)
|
|
34
|
-
chmodSync(keyPath, 0o400)
|
|
35
|
-
return key
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function encrypt(plaintext: string, key: Buffer): string {
|
|
39
|
-
const iv = randomBytes(IV_LENGTH)
|
|
40
|
-
const cipher = createCipheriv(ALGORITHM, key, iv)
|
|
41
|
-
const encrypted = Buffer.concat([cipher.update(plaintext, 'utf-8'), cipher.final()])
|
|
42
|
-
const authTag = cipher.getAuthTag()
|
|
43
|
-
return Buffer.concat([iv, authTag, encrypted]).toString('base64')
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function decrypt(ciphertext: string, key: Buffer): string {
|
|
47
|
-
const buf = Buffer.from(ciphertext, 'base64')
|
|
48
|
-
const iv = buf.subarray(0, IV_LENGTH)
|
|
49
|
-
const authTag = buf.subarray(IV_LENGTH, IV_LENGTH + AUTH_TAG_LENGTH)
|
|
50
|
-
const encrypted = buf.subarray(IV_LENGTH + AUTH_TAG_LENGTH)
|
|
51
|
-
const decipher = createDecipheriv(ALGORITHM, key, iv)
|
|
52
|
-
decipher.setAuthTag(authTag)
|
|
53
|
-
return Buffer.concat([decipher.update(encrypted), decipher.final()]).toString('utf-8')
|
|
54
|
-
}
|
|
55
|
-
|
|
56
22
|
export class TokenStore {
|
|
57
23
|
private key: Buffer
|
|
58
24
|
private storeDir: string
|
|
59
25
|
|
|
60
26
|
constructor(storeDir?: string) {
|
|
61
27
|
this.storeDir = storeDir || join(homedir(), '.mipham', 'mcp-tokens')
|
|
62
|
-
|
|
63
|
-
this.key = getEncryptionKey(keyPath)
|
|
28
|
+
this.key = getCredentialKey(dirname(this.storeDir))
|
|
64
29
|
}
|
|
65
30
|
|
|
66
31
|
save(serverName: string, data: TokenData): void {
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
export const PACKAGE_NAME = '@miphamai/cli' as const
|
|
10
10
|
|
|
11
11
|
/** 当前发布版本 */
|
|
12
|
-
export const PACKAGE_VERSION = '0.
|
|
12
|
+
export const PACKAGE_VERSION = '0.52.0' as const
|
|
13
13
|
|
|
14
14
|
/** npm install 全局安装命令 */
|
|
15
15
|
export const NPM_INSTALL_COMMAND = `npm install -g ${PACKAGE_NAME}` as const
|
package/src/shared/types.ts
CHANGED
|
@@ -133,6 +133,8 @@ export interface MiphamConfig {
|
|
|
133
133
|
permission: ToolPermission
|
|
134
134
|
/** Org-level permission restrictions (forbiddenModes, maxAllowedMode). */
|
|
135
135
|
permissionRestrictions?: PermissionRestrictions
|
|
136
|
+
/** User-defined permission rules (allow/deny patterns), wired into the runtime PermissionSystem. */
|
|
137
|
+
permissionRules?: { allow?: string[]; deny?: string[] }
|
|
136
138
|
providers: ProviderConfig[]
|
|
137
139
|
skills?: { paths: string[]; mcpServers: McpServerConfig[] }
|
|
138
140
|
marketplace?: {
|
|
@@ -149,7 +151,7 @@ export interface MiphamConfig {
|
|
|
149
151
|
|
|
150
152
|
export interface FeatureFlags {
|
|
151
153
|
mcp: { oauthEnabled: boolean }
|
|
152
|
-
context: {
|
|
154
|
+
context: { adaptiveThresholds: boolean }
|
|
153
155
|
}
|
|
154
156
|
|
|
155
157
|
export interface CrsiConfig {
|
|
@@ -481,8 +483,8 @@ export interface SkillDefinition {
|
|
|
481
483
|
allowedTools?: string[]
|
|
482
484
|
/** When true, the skill is NOT shown in system-reminder for AI auto-triggering */
|
|
483
485
|
disableModelInvocation?: boolean
|
|
484
|
-
/**
|
|
485
|
-
|
|
486
|
+
/** External command-line binaries the skill requires (frontmatter: requires-bins). */
|
|
487
|
+
requiresBins?: string[]
|
|
486
488
|
/** The markdown body content of the skill file (instructions for the AI to follow). */
|
|
487
489
|
body?: string
|
|
488
490
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs'
|
|
2
|
+
import { join, delimiter } from 'node:path'
|
|
3
|
+
|
|
4
|
+
// Windows executable extensions (subset of PATHEXT) probed for bare names.
|
|
5
|
+
const WINDOWS_EXECUTABLES = ['.exe', '.cmd', '.bat', '.com']
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Check whether a command-line binary is available on the system PATH.
|
|
9
|
+
* Accepts an explicit `pathVar` for testability; defaults to `process.env.PATH`.
|
|
10
|
+
* A value containing a path separator is treated as an explicit path and
|
|
11
|
+
* checked for existence directly.
|
|
12
|
+
*/
|
|
13
|
+
export function isBinAvailable(bin: string, pathVar: string = process.env.PATH || ''): boolean {
|
|
14
|
+
// Explicit path (absolute or relative) — check existence directly.
|
|
15
|
+
if (bin.includes('/') || bin.includes('\\')) {
|
|
16
|
+
return existsSync(bin)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const isWindows = process.platform === 'win32'
|
|
20
|
+
const names = isWindows ? WINDOWS_EXECUTABLES.map((ext) => bin + ext) : [bin]
|
|
21
|
+
const dirs = pathVar.split(delimiter).filter(Boolean)
|
|
22
|
+
|
|
23
|
+
for (const dir of dirs) {
|
|
24
|
+
for (const name of names) {
|
|
25
|
+
if (existsSync(join(dir, name))) return true
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return false
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Return the subset of `bins` that are NOT available on PATH. An empty result
|
|
33
|
+
* means every required binary is present.
|
|
34
|
+
*/
|
|
35
|
+
export function checkRequiredBins(
|
|
36
|
+
bins: string[],
|
|
37
|
+
pathVar: string = process.env.PATH || '',
|
|
38
|
+
): string[] {
|
|
39
|
+
return bins.filter((bin) => !isBinAvailable(bin, pathVar))
|
|
40
|
+
}
|