ai-helper-agent 2.1.0 → 2.3.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 +114 -2
- package/README.zh-CN.md +114 -2
- package/bin/agent.js +150 -0
- package/dist/assets/{agent-store-C4hZkQT5.js → agent-store-qLZ_Nmmz.js} +2 -2
- package/dist/assets/{agent-store-C4hZkQT5.js.map → agent-store-qLZ_Nmmz.js.map} +1 -1
- package/dist/assets/background.js +106 -113
- package/dist/assets/background.js.map +1 -1
- package/dist/assets/content.js +82 -80
- package/dist/assets/{i18n-CAh-vB_T.js → i18n-sOTxZemV.js} +7 -7
- package/dist/assets/i18n-sOTxZemV.js.map +1 -0
- package/dist/assets/model-profiles-DcTdflVP.js +25 -0
- package/dist/assets/model-profiles-DcTdflVP.js.map +1 -0
- package/dist/assets/options-B6ca-cwx.css +1 -0
- package/dist/assets/options.js +218 -197
- package/dist/assets/options.js.map +1 -1
- package/dist/assets/page-snapshot-renderer-C-4bo-cW.js +4 -0
- package/dist/assets/page-snapshot-renderer-C-4bo-cW.js.map +1 -0
- package/dist/assets/{session-store-uZ_LNasH.js → session-store-U8WKrdGX.js} +4 -4
- package/dist/assets/session-store-U8WKrdGX.js.map +1 -0
- package/dist/assets/side_panel.css +1 -1
- package/dist/assets/side_panel.js +344 -304
- package/dist/assets/side_panel.js.map +1 -1
- package/dist/assets/version-DzUoXKcK.js +2 -0
- package/dist/assets/version-DzUoXKcK.js.map +1 -0
- package/dist/manifest.json +7 -4
- package/dist/offscreen/offscreen.js +7 -7
- package/dist/options.html +111 -12
- package/dist/side_panel.html +29 -6
- package/package.json +1 -1
- package/src/config.js +72 -24
- package/src/crypto-lite.js +31 -0
- package/src/install-mode.js +128 -0
- package/src/locales/cli.json +24 -0
- package/src/locales/en.json +55 -0
- package/src/locales/zh.json +55 -0
- package/src/memory/embedding-config.js +95 -0
- package/src/memory/fusion.js +60 -0
- package/src/memory/injection.js +118 -0
- package/src/memory/routes.js +193 -0
- package/src/memory/service.js +957 -0
- package/src/memory/tags.js +62 -0
- package/src/memory/tier.js +37 -0
- package/src/memory/tokens.js +158 -0
- package/src/memory/vectors.js +128 -0
- package/src/rag/embedding/index.js +7 -0
- package/src/rag/embedding/local.js +25 -0
- package/src/rag/install.js +4 -0
- package/src/rag/keyword-match.js +112 -0
- package/src/rag/manager.js +15 -0
- package/src/rag/searcher.js +15 -87
- package/src/server.js +9 -1
- package/dist/assets/i18n-CAh-vB_T.js.map +0 -1
- package/dist/assets/model-profiles-CGxULM3O.js +0 -25
- package/dist/assets/model-profiles-CGxULM3O.js.map +0 -1
- package/dist/assets/options-Df_1Dtn8.css +0 -1
- package/dist/assets/session-store-uZ_LNasH.js.map +0 -1
- /package/dist/_locales/{zh → zh_CN}/messages.json +0 -0
package/dist/options.html
CHANGED
|
@@ -4,11 +4,12 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title data-i18n="settings.docTitle">AI Helper - 配置</title>
|
|
7
|
-
<script type="module" crossorigin src="/assets/i18n-
|
|
8
|
-
<script type="module" crossorigin src="/assets/model-profiles-
|
|
7
|
+
<script type="module" crossorigin src="/assets/i18n-sOTxZemV.js"></script>
|
|
8
|
+
<script type="module" crossorigin src="/assets/model-profiles-DcTdflVP.js"></script>
|
|
9
|
+
<script type="module" crossorigin src="/assets/version-DzUoXKcK.js"></script>
|
|
9
10
|
<script type="module" crossorigin src="/assets/options.js"></script>
|
|
10
11
|
<script type="module" crossorigin src="/assets/modulepreload-polyfill-P2Xu9kJm.js"></script>
|
|
11
|
-
<link rel="stylesheet" crossorigin href="/assets/options-
|
|
12
|
+
<link rel="stylesheet" crossorigin href="/assets/options-B6ca-cwx.css">
|
|
12
13
|
</head>
|
|
13
14
|
<body>
|
|
14
15
|
<div class="container">
|
|
@@ -43,6 +44,7 @@
|
|
|
43
44
|
<button class="tab-nav-btn" data-tab="agent" data-i18n="settings.tabAgent">代理</button>
|
|
44
45
|
<button class="tab-nav-btn" data-tab="toolbox" data-i18n="settings.tabToolbox">扩展</button>
|
|
45
46
|
<button class="tab-nav-btn" data-tab="knowledge" data-i18n="knowledge.tabTitle">知识库</button>
|
|
47
|
+
<button class="tab-nav-btn" data-tab="memory" data-i18n="memoryPanel.tabTitle">记忆</button>
|
|
46
48
|
</div>
|
|
47
49
|
|
|
48
50
|
<div class="tab-panel active" data-tab="basic">
|
|
@@ -440,14 +442,6 @@
|
|
|
440
442
|
<span class="reflection-toggle-slider"></span>
|
|
441
443
|
</label>
|
|
442
444
|
</div>
|
|
443
|
-
<div class="reflection-toggle-row" id="toolReflectionSection">
|
|
444
|
-
<span class="reflection-row-icon">🔧</span>
|
|
445
|
-
<span class="reflection-row-text"><span data-i18n="reflection.toolReflection">工具级反思</span><span class="reflection-row-hint" data-i18n="reflection.toolReflectionHint">(工具执行后评估结果)</span></span>
|
|
446
|
-
<label class="reflection-toggle-switch">
|
|
447
|
-
<input type="checkbox" id="toolReflectionEnabled" checked>
|
|
448
|
-
<span class="reflection-toggle-slider"></span>
|
|
449
|
-
</label>
|
|
450
|
-
</div>
|
|
451
445
|
<div class="reflection-toggle-row" id="subtaskReflectionSection">
|
|
452
446
|
<span class="reflection-row-icon">📋</span>
|
|
453
447
|
<span class="reflection-row-text"><span data-i18n="reflection.subtaskReflection">子任务反思</span><span class="reflection-row-hint" data-i18n="reflection.subtaskReflectionHint">(高级功能,默认关闭)</span></span>
|
|
@@ -462,7 +456,6 @@
|
|
|
462
456
|
<h3 data-i18n="reflection.configGuide">💡 反思配置说明</h3>
|
|
463
457
|
<p>
|
|
464
458
|
<strong data-i18n="reflection.guidePostReflection">后置反思</strong><span data-i18n="reflection.guidePostReflectionDesc">:在推理循环结束后,AI 会对最终结果进行自我评估。如果评分低于阈值,会自动修订或重新执行。</span><br>
|
|
465
|
-
<strong data-i18n="reflection.guideToolReflection">工具级反思</strong><span data-i18n="reflection.guideToolReflectionDesc">:在每次工具调用后,AI 会自动检查执行结果是否符合预期,遇到错误或异常时调整策略。</span><br>
|
|
466
459
|
<strong data-i18n="reflection.guideSubtaskReflection">子任务反思</strong><span data-i18n="reflection.guideSubtaskReflectionDesc">:对于复杂任务拆解后的子任务进行单独评估,默认关闭(高级功能)。</span><br>
|
|
467
460
|
<span data-i18n="reflection.guideTip">• 启用反思会增加 API 调用次数,建议根据需求权衡</span>
|
|
468
461
|
</p>
|
|
@@ -589,6 +582,29 @@
|
|
|
589
582
|
</div>
|
|
590
583
|
</div>
|
|
591
584
|
|
|
585
|
+
<!-- 代理端安装指引(与当前插件版本匹配,一键复制) -->
|
|
586
|
+
<div class="agent-install-guide" id="agentInstallGuide">
|
|
587
|
+
<div class="agent-install-guide-header">
|
|
588
|
+
<h3 id="agentInstallTitle" data-i18n="agentConfig.installGuideTitle">安装代理端</h3>
|
|
589
|
+
<button class="btn btn-primary" id="copyInstallAuto" data-i18n="agentConfig.copyCmdAuto">一键复制(按当前系统)</button>
|
|
590
|
+
</div>
|
|
591
|
+
<p class="agent-install-guide-hint" data-i18n="agentConfig.installGuideHint">复制下方命令到终端执行,将安装与当前插件版本一致的代理端,避免版本不匹配导致无法连接。</p>
|
|
592
|
+
<div class="agent-install-cmd-row">
|
|
593
|
+
<div class="agent-install-cmd-main">
|
|
594
|
+
<span class="agent-install-os-label" data-i18n="agentConfig.installGuideMacLinux">macOS / Linux</span>
|
|
595
|
+
<code class="agent-install-cmd" id="installCmdUnix"></code>
|
|
596
|
+
</div>
|
|
597
|
+
<button class="btn btn-secondary agent-install-copy-btn" id="copyInstallUnix" data-i18n="agentConfig.copyCmd">复制</button>
|
|
598
|
+
</div>
|
|
599
|
+
<div class="agent-install-cmd-row">
|
|
600
|
+
<div class="agent-install-cmd-main">
|
|
601
|
+
<span class="agent-install-os-label">Windows (PowerShell)</span>
|
|
602
|
+
<code class="agent-install-cmd" id="installCmdWin"></code>
|
|
603
|
+
</div>
|
|
604
|
+
<button class="btn btn-secondary agent-install-copy-btn" id="copyInstallWin" data-i18n="agentConfig.copyCmd">复制</button>
|
|
605
|
+
</div>
|
|
606
|
+
</div>
|
|
607
|
+
|
|
592
608
|
<div class="info-box collapsible collapsed" style="margin-top: 15px;">
|
|
593
609
|
<h3 class="collapsible-header"><span data-i18n="agentConfig.useInstructionTitle">💡 使用说明及安全提示</span> <span class="collapsible-arrow">▶</span></h3>
|
|
594
610
|
<div class="collapsible-body" style="display:none;">
|
|
@@ -746,6 +762,89 @@
|
|
|
746
762
|
</div>
|
|
747
763
|
</div><!-- /tab-knowledge -->
|
|
748
764
|
|
|
765
|
+
<!-- 记忆:语义召回向量配置(数据在 agent 端 config.json 的 memoryEmbedding) -->
|
|
766
|
+
<div class="tab-panel" data-tab="memory">
|
|
767
|
+
<div class="knowledge-header">
|
|
768
|
+
<div class="knowledge-title-row">
|
|
769
|
+
<h2 class="knowledge-title" data-i18n="memoryPanel.title">记忆向量</h2>
|
|
770
|
+
</div>
|
|
771
|
+
<div style="display:flex; align-items:center; gap:16px; flex-shrink:0;">
|
|
772
|
+
<label class="global-toggle">
|
|
773
|
+
<input type="checkbox" id="memoryEnabled">
|
|
774
|
+
<span class="global-toggle-slider"></span>
|
|
775
|
+
<span class="global-toggle-label" id="memoryEnabledLabel"></span>
|
|
776
|
+
</label>
|
|
777
|
+
</div>
|
|
778
|
+
</div>
|
|
779
|
+
|
|
780
|
+
<div id="memoryGate" style="display:none;"></div>
|
|
781
|
+
|
|
782
|
+
<!-- 本 Tab 的配置在点底部「保存」时写入代理 config.json(代理未连接则跳过);底部保存同时存扩展自身设置 -->
|
|
783
|
+
<div class="memory-scope-hint" data-i18n="memoryPanel.scopeHint"></div>
|
|
784
|
+
|
|
785
|
+
<div id="memoryMain">
|
|
786
|
+
<!-- 状态卡:条数 + 指纹陈旧(stale 是本次设计的核心收益) -->
|
|
787
|
+
<div class="form-group">
|
|
788
|
+
<label data-i18n="memoryPanel.statusTitle">向量索引状态</label>
|
|
789
|
+
<div id="memoryStatus" class="memory-status"></div>
|
|
790
|
+
</div>
|
|
791
|
+
|
|
792
|
+
<div class="form-group">
|
|
793
|
+
<label data-i18n="memoryPanel.modeLabel">嵌入来源</label>
|
|
794
|
+
<label style="display:inline-flex; align-items:center; gap:6px; margin-right:16px;">
|
|
795
|
+
<input type="radio" name="memoryEmbeddingMode" value="openai-compat" style="width:auto;">
|
|
796
|
+
<span data-i18n="memoryPanel.modeRemote">远端服务(OpenAI 兼容接口)</span>
|
|
797
|
+
</label>
|
|
798
|
+
<label style="display:inline-flex; align-items:center; gap:6px;">
|
|
799
|
+
<input type="radio" name="memoryEmbeddingMode" value="local" style="width:auto;">
|
|
800
|
+
<span data-i18n="memoryPanel.modeLocal">本地模型</span>
|
|
801
|
+
</label>
|
|
802
|
+
</div>
|
|
803
|
+
|
|
804
|
+
<div class="form-group" id="memoryRemoteGroup">
|
|
805
|
+
<label data-i18n="memoryPanel.baseUrlLabel">服务地址</label>
|
|
806
|
+
<input type="text" id="memoryBaseUrlInput" data-i18n-placeholder="memoryPanel.baseUrlPlaceholder" placeholder="https://api.openai.com/v1">
|
|
807
|
+
</div>
|
|
808
|
+
|
|
809
|
+
<div class="form-group" id="memoryModelGroup">
|
|
810
|
+
<label data-i18n="memoryPanel.modelLabel">模型名称</label>
|
|
811
|
+
<input type="text" id="memoryModelInput" data-i18n-placeholder="memoryPanel.modelPlaceholder" placeholder="BAAI/bge-m3">
|
|
812
|
+
</div>
|
|
813
|
+
|
|
814
|
+
<div class="form-group" id="memoryApiKeyGroup">
|
|
815
|
+
<label data-i18n="memoryPanel.apiKeyLabel">API Key</label>
|
|
816
|
+
<input type="password" id="memoryApiKeyInput" data-i18n-placeholder="memoryPanel.apiKeyPlaceholder" placeholder="">
|
|
817
|
+
<!-- L1:明文 key 永不回填 + 留空=不修改 ⇒ 用户没有清除路径,这是唯一显式清除入口(hasApiKey 时显示) -->
|
|
818
|
+
<button class="kb-action-btn" type="button" id="memoryApiKeyClearBtn" data-i18n="memoryPanel.apiKeyClearBtn" style="display:none; margin-top:8px;">清除已存 Key</button>
|
|
819
|
+
</div>
|
|
820
|
+
|
|
821
|
+
<div class="form-group" id="memoryDimsGroup">
|
|
822
|
+
<label data-i18n="memoryPanel.dimsLabel">向量维度(可留空,测试时自动探测)</label>
|
|
823
|
+
<div class="kb-vec-test-row" id="memoryTestRow">
|
|
824
|
+
<input type="number" id="memoryDimsInput" min="1" max="8192" placeholder="">
|
|
825
|
+
<button class="kb-action-btn" type="button" id="memoryTestBtn" data-i18n="memoryPanel.testBtn">测试连通性</button>
|
|
826
|
+
<span class="kb-vec-test-result" id="memoryTestResult"></span>
|
|
827
|
+
</div>
|
|
828
|
+
</div>
|
|
829
|
+
|
|
830
|
+
<div class="form-group" id="memoryPrefixGroup">
|
|
831
|
+
<label data-i18n="memoryPanel.prefixLabel">查询前缀(queryPrefix,可留空)</label>
|
|
832
|
+
<input type="text" id="memoryPrefixInput" placeholder="">
|
|
833
|
+
</div>
|
|
834
|
+
|
|
835
|
+
<div class="form-group" id="memoryLocalNotice" style="display:none;">
|
|
836
|
+
<span data-i18n="memoryPanel.localNotice">本地档无需测试连通性:保存后直接重建向量索引(首次使用需下载模型,可能数十分钟)。嵌入在 512 token 处静默截断,超长条目只有前部进入向量。</span>
|
|
837
|
+
</div>
|
|
838
|
+
|
|
839
|
+
<!-- 「保存到代理」按钮已并入底部全局「保存配置」:点保存时若代理已连接会自动推到这里 -->
|
|
840
|
+
<div class="form-group" style="display:flex; gap:12px; align-items:center;">
|
|
841
|
+
<button class="toolbox-add-btn" type="button" id="memoryReindexBtn" style="width:auto; margin:0; padding:8px 18px; border-style:solid;" data-i18n="memoryPanel.reindexBtn">重建向量索引</button>
|
|
842
|
+
</div>
|
|
843
|
+
|
|
844
|
+
<div id="memoryResult" class="memory-status" style="display:none;"></div>
|
|
845
|
+
</div>
|
|
846
|
+
</div><!-- /tab-memory -->
|
|
847
|
+
|
|
749
848
|
<!-- 全局保存按钮(所有 tab 可见;内容超出一屏时吸附在视口底部,保证按钮始终可见) -->
|
|
750
849
|
<div class="save-bar" id="saveBar">
|
|
751
850
|
<button class="btn btn-save" id="saveBtn">
|
package/dist/side_panel.html
CHANGED
|
@@ -5,10 +5,11 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title data-i18n="header.docTitle">AI Helper - 模型问答</title>
|
|
7
7
|
<script type="module" crossorigin src="/assets/rolldown-runtime-C0FnF6B9.js"></script>
|
|
8
|
-
<script type="module" crossorigin src="/assets/i18n-
|
|
9
|
-
<script type="module" crossorigin src="/assets/model-profiles-
|
|
10
|
-
<script type="module" crossorigin src="/assets/session-store-
|
|
11
|
-
<script type="module" crossorigin src="/assets/agent-store-
|
|
8
|
+
<script type="module" crossorigin src="/assets/i18n-sOTxZemV.js"></script>
|
|
9
|
+
<script type="module" crossorigin src="/assets/model-profiles-DcTdflVP.js"></script>
|
|
10
|
+
<script type="module" crossorigin src="/assets/session-store-U8WKrdGX.js"></script>
|
|
11
|
+
<script type="module" crossorigin src="/assets/agent-store-qLZ_Nmmz.js"></script>
|
|
12
|
+
<script type="module" crossorigin src="/assets/version-DzUoXKcK.js"></script>
|
|
12
13
|
<script type="module" crossorigin src="/assets/side_panel.js"></script>
|
|
13
14
|
<script type="module" crossorigin src="/assets/modulepreload-polyfill-P2Xu9kJm.js"></script>
|
|
14
15
|
<link rel="stylesheet" crossorigin href="/assets/side_panel.css">
|
|
@@ -547,6 +548,10 @@
|
|
|
547
548
|
<div class="memory-limit-dropdown-title">
|
|
548
549
|
<span data-i18n="memory.historyLimit">记忆历史限制</span>
|
|
549
550
|
<span class="memory-limit-info-icon" data-i18n-title="memory.hint" title="记忆越多,上下文越连贯,但 token 消耗也越大;记忆太少,则可能无法结合前文准确回答。">ⓘ</span>
|
|
551
|
+
<!-- 跳配置页记忆 Tab 的快捷入口:与输入框 ✚ 面板的 MCP/Skill 管理入口同一形状同一通道 -->
|
|
552
|
+
<button class="memory-limit-config-entry" id="memoryLimitConfigEntry" type="button" data-i18n-title="memory.configEntry" title="打开记忆配置">
|
|
553
|
+
<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M12 5v14"/><path d="M5 12h14"/></svg>
|
|
554
|
+
</button>
|
|
550
555
|
</div>
|
|
551
556
|
<div class="memory-limit-options" id="memoryLimitOptions">
|
|
552
557
|
<div class="memory-limit-option" data-value="0"><span class="memory-limit-check"></span><span data-i18n="memory.unlimited">不限制</span></div>
|
|
@@ -563,6 +568,24 @@
|
|
|
563
568
|
<input type="number" id="memoryLimitInput" min="1" max="400" placeholder="1-400" class="memory-limit-input">
|
|
564
569
|
<span class="memory-limit-custom-unit" data-i18n="memory.unit">条</span>
|
|
565
570
|
</div>
|
|
571
|
+
<div class="memory-extra-rows">
|
|
572
|
+
<div class="memory-switch-row">
|
|
573
|
+
<label class="memory-switch" for="autoCompactBtn">
|
|
574
|
+
<input type="checkbox" id="autoCompactBtn" style="display: none;">
|
|
575
|
+
<span class="toggle-switch"></span>
|
|
576
|
+
<span class="memory-switch-label" data-i18n="memory.autoCompactLabel">自动压缩</span>
|
|
577
|
+
</label>
|
|
578
|
+
<span class="memory-limit-info-icon" data-i18n-title="memory.autoCompactHint" title="上下文占用达到 85% 时,发送前自动压缩一次为摘要(会额外产生一次摘要调用)。">ⓘ</span>
|
|
579
|
+
</div>
|
|
580
|
+
<div class="memory-switch-row">
|
|
581
|
+
<label class="memory-switch" for="persistSummaryBtn">
|
|
582
|
+
<input type="checkbox" id="persistSummaryBtn" style="display: none;">
|
|
583
|
+
<span class="toggle-switch"></span>
|
|
584
|
+
<span class="memory-switch-label" data-i18n="memory.persistSummaryLabel">摘要存入记忆</span>
|
|
585
|
+
</label>
|
|
586
|
+
<span class="memory-limit-info-icon" data-i18n-title="memory.persistSummaryHint" title="压缩时把摘要沉淀为跨会话长期记忆;同一会话重复压缩只更新同一条。">ⓘ</span>
|
|
587
|
+
</div>
|
|
588
|
+
</div>
|
|
566
589
|
</div>
|
|
567
590
|
</div>
|
|
568
591
|
<div class="tool-toggle-wrapper">
|
|
@@ -695,7 +718,7 @@
|
|
|
695
718
|
<button class="prompt-tab" data-tab="proxies" data-i18n="promptSelector.proxies">代理</button>
|
|
696
719
|
<div class="prompt-merged-title" id="agentAtMergedTitle" data-i18n="promptSelector.mergedTitle">聚合搜索</div>
|
|
697
720
|
</div>
|
|
698
|
-
<div class="prompt-dropdown-header"><span data-i18n="promptSelector.switchHintSelect">方向键切换 · Enter选择 · Ctrl+Enter选中并关闭 · Esc取消</span><span class="agent-add-btn" id="agentAddBtn" data-i18n-title="header.addEditAssistant" title="新增/编辑助手">✚</span><span class="agent-add-btn" id="proxyAddBtn" data-i18n-title="header.addEditAgent" title="新增/编辑代理" style="display:none;">✚</span><span class="agent-add-btn" id="knowledgeAddBtn" data-i18n-title="header.manageKnowledge" title="管理知识库" style="display:none;">✚</span></div>
|
|
721
|
+
<div class="prompt-dropdown-header"><span data-i18n="promptSelector.switchHintSelect">方向键切换 · Enter选择 · Ctrl+Enter选中并关闭 · Esc取消</span><span class="agent-add-btn" id="agentAddBtn" data-i18n-title="header.addEditAssistant" title="新增/编辑助手">✚</span><span class="agent-add-btn" id="proxyAddBtn" data-i18n-title="header.addEditAgent" title="新增/编辑代理" style="display:none;">✚</span><span class="agent-add-btn" id="knowledgeAddBtn" data-i18n-title="header.manageKnowledge" title="管理知识库" style="display:none;">✚</span><span class="agent-add-btn" id="pageAddBtn" data-i18n-title="pageSelector.openNewTab" title="新建标签页" style="display:none;">✚</span></div>
|
|
699
722
|
<div id="agentAtList"></div>
|
|
700
723
|
<div id="agentPageList" style="display: none;"></div>
|
|
701
724
|
<div id="agentProxyList" style="display: none;"></div>
|
|
@@ -709,7 +732,7 @@
|
|
|
709
732
|
<span class="file-at-title-label" data-i18n="fileSelector.workspaceTitle">工作目录</span>
|
|
710
733
|
<span class="file-at-title-path" id="fileAtTitlePath"></span>
|
|
711
734
|
</div>
|
|
712
|
-
<div class="prompt-dropdown-header"><span class="prompt-dropdown-header-text" data-i18n="fileSelector.switchHint">方向键切换 · Enter添加到文件问答 · Esc取消</span></div>
|
|
735
|
+
<div class="prompt-dropdown-header"><span class="prompt-dropdown-header-text" data-i18n="fileSelector.switchHint">方向键切换 · Enter添加到文件问答 · Esc取消</span><span class="agent-add-btn" id="fileAtWorkspaceOpenBtn" data-i18n-title="workspace.openPanel" title="打开工作目录面板">✚</span></div>
|
|
713
736
|
<div id="fileAtList"></div>
|
|
714
737
|
</div>
|
|
715
738
|
</div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-helper-agent",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "AI Helper Agent - Local file access and system command execution proxy for Chrome extension / AI Helper 代理服务,为 Chrome 扩展提供本地文件读写和系统命令执行",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/server.js",
|
package/src/config.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
// agent/src/config.js - 配置管理(带内存缓存 + 写入互斥锁)
|
|
2
2
|
import { homedir } from 'os';
|
|
3
3
|
import { join } from 'path';
|
|
4
|
-
import { readFileSync, writeFileSync, existsSync, mkdirSync, statSync } from 'fs';
|
|
4
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, statSync, chmodSync } from 'fs';
|
|
5
5
|
import { t as translate } from './i18n.js';
|
|
6
|
+
import { encodeSecret, decodeSecret } from './crypto-lite.js';
|
|
6
7
|
|
|
7
8
|
const AGENT_DIR = join(homedir(), '.ai-helper-agent');
|
|
8
9
|
const CONFIG_FILE = join(AGENT_DIR, 'config.json');
|
|
@@ -21,7 +22,19 @@ const DEFAULTS = {
|
|
|
21
22
|
fileMaxSize: 50 * 1024 * 1024,
|
|
22
23
|
// RAG URL 导入内网访问策略:allowedPrivateHosts 非空时仅放行白名单命中(总开关不生效);
|
|
23
24
|
// 为空时由 allowPrivateNetwork 决定;缺省均关闭(默认拒绝内网),公网访问不受影响
|
|
24
|
-
ragUrlIngest: { allowPrivateNetwork: false, allowedPrivateHosts: [] }
|
|
25
|
+
ragUrlIngest: { allowPrivateNetwork: false, allowedPrivateHosts: [] },
|
|
26
|
+
// 长期记忆语义召回(hybrid recall)的 embedding 配置:缺省关闭 → recall 只走关键词通道(Tier A),
|
|
27
|
+
// 不引入任何新的默认网络调用。mode: 'openai-compat'(远端,零依赖)| 'local'(本地模型,需 RAG 依赖)。
|
|
28
|
+
// apiKey 只落本地 config.json,任何对外响应均经 mask 脱敏(hasApiKey 供前端提示“已配置”)。
|
|
29
|
+
memoryEmbedding: {
|
|
30
|
+
enabled: false,
|
|
31
|
+
mode: 'openai-compat',
|
|
32
|
+
baseUrl: '',
|
|
33
|
+
modelName: '',
|
|
34
|
+
apiKey: '',
|
|
35
|
+
dimensions: 0,
|
|
36
|
+
queryPrefix: ''
|
|
37
|
+
}
|
|
25
38
|
};
|
|
26
39
|
|
|
27
40
|
const VALID_CONFIG_KEYS = Object.keys(DEFAULTS);
|
|
@@ -51,14 +64,18 @@ function withWriteLock(fn) {
|
|
|
51
64
|
const prev = writeLock;
|
|
52
65
|
let release;
|
|
53
66
|
writeLock = new Promise(resolve => { release = resolve; });
|
|
54
|
-
|
|
67
|
+
// fn 允许返回非 Promise(同步写入体):统一用 Promise.resolve 兜住,
|
|
68
|
+
// 否则 fn().finally 直接 TypeError,且锁永远不会释放。
|
|
69
|
+
return prev
|
|
70
|
+
.then(() => Promise.resolve(fn()))
|
|
71
|
+
.finally(release);
|
|
55
72
|
}
|
|
56
73
|
|
|
57
74
|
function ensureAgentDir() {
|
|
58
75
|
try {
|
|
59
|
-
// 确保 Agent
|
|
76
|
+
// 确保 Agent 根目录存在(0700:仅当前用户可进入)
|
|
60
77
|
if (!existsSync(AGENT_DIR)) {
|
|
61
|
-
mkdirSync(AGENT_DIR, { recursive: true });
|
|
78
|
+
mkdirSync(AGENT_DIR, { recursive: true, mode: 0o700 });
|
|
62
79
|
}
|
|
63
80
|
// 确保 skills、workspace 和 memory 子目录存在
|
|
64
81
|
for (const dir of [SKILLS_DIR, WORKSPACE_DIR, MEMORY_DIR]) {
|
|
@@ -105,6 +122,10 @@ export function loadConfig() {
|
|
|
105
122
|
try {
|
|
106
123
|
const raw = readFileSync(CONFIG_FILE, 'utf-8');
|
|
107
124
|
const userConfig = JSON.parse(raw);
|
|
125
|
+
// 磁盘密文 → 内存明文(兼容旧版无 enc: 前缀的明文)
|
|
126
|
+
if (userConfig.memoryEmbedding && userConfig.memoryEmbedding.apiKey) {
|
|
127
|
+
userConfig.memoryEmbedding = { ...userConfig.memoryEmbedding, apiKey: decodeSecret(userConfig.memoryEmbedding.apiKey) };
|
|
128
|
+
}
|
|
108
129
|
configCache = { ...DEFAULTS, ...userConfig };
|
|
109
130
|
configCacheMtime = mtime;
|
|
110
131
|
} catch (err) {
|
|
@@ -135,26 +156,41 @@ function saveConfigSync(config) {
|
|
|
135
156
|
for (const key of VALID_CONFIG_KEYS) {
|
|
136
157
|
if (config[key] !== undefined) safe[key] = config[key];
|
|
137
158
|
}
|
|
138
|
-
|
|
159
|
+
// 落盘前混淆 apiKey(内存缓存保持明文)
|
|
160
|
+
if (safe.memoryEmbedding && safe.memoryEmbedding.apiKey) {
|
|
161
|
+
safe.memoryEmbedding = { ...safe.memoryEmbedding, apiKey: encodeSecret(safe.memoryEmbedding.apiKey) };
|
|
162
|
+
}
|
|
163
|
+
writeFileSync(CONFIG_FILE, JSON.stringify(safe, null, 2), { encoding: 'utf-8', mode: 0o600 });
|
|
164
|
+
try { chmodSync(CONFIG_FILE, 0o600); } catch {} // 历史宽松文件收紧
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* 取锁后的实际写入体:落盘 + 刷新缓存(saveConfig 与 updateConfigField 共用)。
|
|
169
|
+
* 必须不加锁:两者都已在自己的 withWriteLock 内,再嵌一次会 自我等自我 而永久挂起。
|
|
170
|
+
*/
|
|
171
|
+
function writeConfigBody(config) {
|
|
172
|
+
try {
|
|
173
|
+
saveConfigSync(config);
|
|
174
|
+
// 从磁盘重新加载确保缓存与磁盘一致(解码后缓存保持明文)
|
|
175
|
+
const mtime = getMtime(CONFIG_FILE);
|
|
176
|
+
const raw = readFileSync(CONFIG_FILE, 'utf-8');
|
|
177
|
+
const parsed = JSON.parse(raw);
|
|
178
|
+
if (parsed.memoryEmbedding && parsed.memoryEmbedding.apiKey) {
|
|
179
|
+
parsed.memoryEmbedding = { ...parsed.memoryEmbedding, apiKey: decodeSecret(parsed.memoryEmbedding.apiKey) };
|
|
180
|
+
}
|
|
181
|
+
configCache = { ...DEFAULTS, ...parsed };
|
|
182
|
+
configCacheMtime = mtime;
|
|
183
|
+
} catch (err) {
|
|
184
|
+
console.error('[Config] Save config failed:', err.message);
|
|
185
|
+
throw err;
|
|
186
|
+
}
|
|
139
187
|
}
|
|
140
188
|
|
|
141
189
|
/**
|
|
142
190
|
* 保存配置(写入磁盘并更新缓存,加写锁防并发覆盖)
|
|
143
191
|
*/
|
|
144
192
|
export async function saveConfig(config) {
|
|
145
|
-
return withWriteLock(
|
|
146
|
-
try {
|
|
147
|
-
saveConfigSync(config);
|
|
148
|
-
// 从磁盘重新加载确保缓存与磁盘一致
|
|
149
|
-
const mtime = getMtime(CONFIG_FILE);
|
|
150
|
-
const raw = readFileSync(CONFIG_FILE, 'utf-8');
|
|
151
|
-
configCache = { ...DEFAULTS, ...JSON.parse(raw) };
|
|
152
|
-
configCacheMtime = mtime;
|
|
153
|
-
} catch (err) {
|
|
154
|
-
console.error('[Config] Save config failed:', err.message);
|
|
155
|
-
throw err;
|
|
156
|
-
}
|
|
157
|
-
});
|
|
193
|
+
return withWriteLock(() => writeConfigBody(config));
|
|
158
194
|
}
|
|
159
195
|
|
|
160
196
|
/**
|
|
@@ -165,9 +201,10 @@ export async function updateConfigField(key, value) {
|
|
|
165
201
|
throw new Error(translate(currentLang, 'error.invalidConfigKey', { key }));
|
|
166
202
|
}
|
|
167
203
|
return withWriteLock(async () => {
|
|
168
|
-
|
|
169
|
-
config[key]
|
|
170
|
-
|
|
204
|
+
// 浅拷贝后再改:loadConfig() 返回的是缓存本体,直接赋值会就地修改调用方已持有的对象
|
|
205
|
+
const config = { ...loadConfig(), [key]: value };
|
|
206
|
+
writeConfigBody(config);
|
|
207
|
+
return config;
|
|
171
208
|
});
|
|
172
209
|
}
|
|
173
210
|
|
|
@@ -189,7 +226,12 @@ export function loadPairings() {
|
|
|
189
226
|
}
|
|
190
227
|
|
|
191
228
|
try {
|
|
192
|
-
|
|
229
|
+
const parsed = JSON.parse(readFileSync(PAIRINGS_FILE, 'utf-8'));
|
|
230
|
+
// 磁盘密文 → 内存明文(兼容旧版)
|
|
231
|
+
for (const entry of Object.values(parsed)) {
|
|
232
|
+
if (entry.token) entry.token = decodeSecret(entry.token);
|
|
233
|
+
}
|
|
234
|
+
pairingsCache = parsed;
|
|
193
235
|
pairingsCacheMtime = mtime;
|
|
194
236
|
return pairingsCache;
|
|
195
237
|
} catch (err) {
|
|
@@ -209,7 +251,13 @@ export async function savePairing(extensionId, token) {
|
|
|
209
251
|
pairings[extensionId] = { token, pairedAt: Date.now() };
|
|
210
252
|
ensureAgentDir();
|
|
211
253
|
try {
|
|
212
|
-
|
|
254
|
+
// 落盘前混淆 token(内存缓存保持明文)
|
|
255
|
+
const diskPairings = {};
|
|
256
|
+
for (const [id, entry] of Object.entries(pairings)) {
|
|
257
|
+
diskPairings[id] = { ...entry, token: encodeSecret(entry.token) };
|
|
258
|
+
}
|
|
259
|
+
writeFileSync(PAIRINGS_FILE, JSON.stringify(diskPairings, null, 2), { encoding: 'utf-8', mode: 0o600 });
|
|
260
|
+
try { chmodSync(PAIRINGS_FILE, 0o600); } catch {} // 历史宽松文件收紧
|
|
213
261
|
pairingsCache = pairings;
|
|
214
262
|
pairingsCacheMtime = getMtime(PAIRINGS_FILE);
|
|
215
263
|
} catch (err) {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// agent/src/crypto-lite.js - 密钥/Token 落盘混淆(Base64 + enc: 前缀标记)
|
|
2
|
+
// 非密码学安全,目的:避免配置文件被 cat/截图时一眼识别出密钥明文。
|
|
3
|
+
// 兼容规则:读取时若无 enc: 前缀视为旧明文原样返回;下次写入自动升级。
|
|
4
|
+
|
|
5
|
+
const PREFIX = 'enc:';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 编码:明文 → "enc:" + base64(明文)
|
|
9
|
+
* 空值原样返回(空字符串不编码)
|
|
10
|
+
* @param {string} plain
|
|
11
|
+
* @returns {string}
|
|
12
|
+
*/
|
|
13
|
+
export function encodeSecret(plain) {
|
|
14
|
+
if (!plain) return plain;
|
|
15
|
+
return PREFIX + Buffer.from(plain, 'utf-8').toString('base64');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 解码:"enc:..." → 明文;无 enc: 前缀 → 原样返回(兼容旧明文)
|
|
20
|
+
* @param {string} stored
|
|
21
|
+
* @returns {string}
|
|
22
|
+
*/
|
|
23
|
+
export function decodeSecret(stored) {
|
|
24
|
+
if (!stored) return stored;
|
|
25
|
+
if (!stored.startsWith(PREFIX)) return stored;
|
|
26
|
+
try {
|
|
27
|
+
return Buffer.from(stored.slice(PREFIX.length), 'base64').toString('utf-8');
|
|
28
|
+
} catch {
|
|
29
|
+
return stored; // 解码失败(损坏数据)→ 原样返回,不崩溃
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
// agent/src/install-mode.js - 安装模式检测与内嵌运行时工具函数
|
|
2
|
+
//
|
|
3
|
+
// 支持两种运行模式,所有功能模块通过本模块获取路径/配置,无需感知具体模式:
|
|
4
|
+
// 1. 内嵌模式:由 install.sh / install.ps1 安装,runtime + app 均在 ~/.ai-helper-agent/ 下
|
|
5
|
+
// 2. npm 全局模式:由 npm install -g ai-helper-agent 安装(传统路径)
|
|
6
|
+
//
|
|
7
|
+
// 判断依据:agent 包入口 (bin/agent.js) 所在路径是否在 ~/.ai-helper-agent/app/ 下。
|
|
8
|
+
// 设计原则:所有改动向后兼容——npm -g 用户 isEmbedded()=false,走原逻辑。
|
|
9
|
+
|
|
10
|
+
import { homedir } from 'os';
|
|
11
|
+
import { join, dirname, resolve } from 'path';
|
|
12
|
+
import { readFileSync, existsSync } from 'fs';
|
|
13
|
+
import { fileURLToPath } from 'url';
|
|
14
|
+
|
|
15
|
+
const EMBEDDED_ROOT = join(homedir(), '.ai-helper-agent');
|
|
16
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
// 本模块位于 <agent-package>/src/,向上两级到包根
|
|
18
|
+
const PACKAGE_ROOT = resolve(__dirname, '..');
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 是否运行在内嵌模式(由 install.sh 安装的自管理环境)
|
|
22
|
+
*/
|
|
23
|
+
export function isEmbedded() {
|
|
24
|
+
return PACKAGE_ROOT.startsWith(EMBEDDED_ROOT);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 内嵌模式的根目录(~/.ai-helper-agent/),非内嵌模式返回 null
|
|
29
|
+
*/
|
|
30
|
+
export function getEmbeddedRoot() {
|
|
31
|
+
return isEmbedded() ? EMBEDDED_ROOT : null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 获取 Node 可执行文件路径
|
|
36
|
+
* - 内嵌模式:~/.ai-helper-agent/runtime/bin/node
|
|
37
|
+
* - 全局模式:process.execPath(当前正在运行的 node)
|
|
38
|
+
*/
|
|
39
|
+
export function getNodePath() {
|
|
40
|
+
if (!isEmbedded()) return process.execPath;
|
|
41
|
+
const candidate = join(EMBEDDED_ROOT, 'runtime', 'bin', 'node');
|
|
42
|
+
return existsSync(candidate) ? candidate : process.execPath;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 获取 npm 可执行文件路径
|
|
47
|
+
* - 内嵌模式:~/.ai-helper-agent/runtime/bin/npm
|
|
48
|
+
* - 全局模式:'npm'(系统 PATH 中的)
|
|
49
|
+
*/
|
|
50
|
+
export function getNpmPath() {
|
|
51
|
+
if (!isEmbedded()) return 'npm';
|
|
52
|
+
const isWin = process.platform === 'win32';
|
|
53
|
+
const candidate = isWin
|
|
54
|
+
? join(EMBEDDED_ROOT, 'runtime', 'npm.cmd')
|
|
55
|
+
: join(EMBEDDED_ROOT, 'runtime', 'bin', 'npm');
|
|
56
|
+
return existsSync(candidate) ? candidate : 'npm';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* 读取镜像探测结果(~/.ai-helper-agent/mirrors.json)
|
|
61
|
+
* 文件不存在时返回 null(npm -g 用户通常没有此文件)
|
|
62
|
+
* @returns {{ registry?: string, node?: string, huggingface?: string, detectedAt?: string } | null}
|
|
63
|
+
*/
|
|
64
|
+
export function readMirrors() {
|
|
65
|
+
try {
|
|
66
|
+
const mirrorsFile = join(EMBEDDED_ROOT, 'mirrors.json');
|
|
67
|
+
if (!existsSync(mirrorsFile)) return null;
|
|
68
|
+
// 剥 BOM:Windows PowerShell 5.1 的 Set-Content -Encoding UTF8 会写入 BOM,导致 JSON.parse 抛错
|
|
69
|
+
const text = readFileSync(mirrorsFile, 'utf-8').replace(/^\uFEFF/, '');
|
|
70
|
+
return JSON.parse(text);
|
|
71
|
+
} catch {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* 获取当前应使用的 npm registry URL
|
|
78
|
+
* - 有 mirrors.json 且 registry 字段为 mirror → https://registry.npmmirror.com
|
|
79
|
+
* - 否则 → undefined(使用 npm 默认)
|
|
80
|
+
* @returns {string|undefined}
|
|
81
|
+
*/
|
|
82
|
+
export function getRegistryUrl() {
|
|
83
|
+
const mirrors = readMirrors();
|
|
84
|
+
if (!mirrors) return undefined;
|
|
85
|
+
if (mirrors.registry === 'mirror') return 'https://registry.npmmirror.com';
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* 获取 HuggingFace 模型下载端点
|
|
91
|
+
* - 有 mirrors.json 且 huggingface 为 mirror → https://hf-mirror.com/
|
|
92
|
+
* - 否则 → undefined(使用 transformers 默认)
|
|
93
|
+
* @returns {string|undefined}
|
|
94
|
+
*/
|
|
95
|
+
export function getHfEndpoint() {
|
|
96
|
+
const mirrors = readMirrors();
|
|
97
|
+
if (!mirrors) return undefined;
|
|
98
|
+
if (mirrors.huggingface === 'mirror') return 'https://hf-mirror.com/';
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* 获取主动停止标记文件路径(用于 autostart 检查)
|
|
104
|
+
*/
|
|
105
|
+
export function getStoppedFlagPath() {
|
|
106
|
+
return join(EMBEDDED_ROOT, '.stopped');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* 获取 INSTALL_MANIFEST.json 路径与内容
|
|
111
|
+
*/
|
|
112
|
+
export function readInstallManifest() {
|
|
113
|
+
try {
|
|
114
|
+
const file = join(EMBEDDED_ROOT, 'INSTALL_MANIFEST.json');
|
|
115
|
+
if (!existsSync(file)) return null;
|
|
116
|
+
const text = readFileSync(file, 'utf-8').replace(/^\uFEFF/, '');
|
|
117
|
+
return JSON.parse(text);
|
|
118
|
+
} catch {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* agent 包根目录(无论哪种模式,都是本文件所在包的根)
|
|
125
|
+
*/
|
|
126
|
+
export function getAgentPackageRoot() {
|
|
127
|
+
return PACKAGE_ROOT;
|
|
128
|
+
}
|
package/src/locales/cli.json
CHANGED
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
"cmdConfig": " config 查看当前配置",
|
|
12
12
|
"cmdHelp": " help 显示此帮助信息",
|
|
13
13
|
"cmdRag": " rag 管理 RAG 可选依赖 (status | install)",
|
|
14
|
+
"cmdUpdate": " update 检查并更新到最新版本 (--check 仅检查)",
|
|
15
|
+
"cmdUninstall": " uninstall 一键卸载代理端",
|
|
16
|
+
"cmdAutostart": " autostart 开机自启管理 (on | off)",
|
|
14
17
|
"startupOptions": "启动选项:",
|
|
15
18
|
"optBackground": " --background, -b 后台运行(守护进程模式)",
|
|
16
19
|
"optPort": " --port <端口> 设置监听端口 (默认: 18910)",
|
|
@@ -69,6 +72,15 @@
|
|
|
69
72
|
"ragRestartAdvice": "提示:若多次安装失败,可尝试重启代理服务(aha restart)后再重试。",
|
|
70
73
|
"ragUnknownSub": "未知的 rag 子命令: {sub}",
|
|
71
74
|
"ragHelpUsage": "用法: aha rag <status|install>",
|
|
75
|
+
"updateLatest": "已是最新版本 v{version}",
|
|
76
|
+
"updateAvailable": "发现新版本: v{current} → v{latest}(运行 aha update 更新)",
|
|
77
|
+
"updateInstalling": "正在更新 v{current} → v{latest}...",
|
|
78
|
+
"updateFailed": "更新失败,请稍后重试",
|
|
79
|
+
"updateSuccess": "✅ 已更新至 v{version},服务已重启",
|
|
80
|
+
"uninstallAutostartRemoved": "已移除开机自启",
|
|
81
|
+
"uninstallComplete": "✅ 卸载完成。用户数据已保留在 {dataDir}",
|
|
82
|
+
"autostartDisabled": "开机自启已关闭",
|
|
83
|
+
"autostartUseScript": "开启自启请重新运行安装脚本,或手动配置系统服务",
|
|
72
84
|
"restartHelper": {
|
|
73
85
|
"waitingOldExit": "等待老进程 {pid} 退出...",
|
|
74
86
|
"waitOldTimeout": "等待老进程退出超时,尝试强制终止...",
|
|
@@ -91,6 +103,9 @@
|
|
|
91
103
|
"cmdConfig": " config View current configuration",
|
|
92
104
|
"cmdHelp": " help Show this help message",
|
|
93
105
|
"cmdRag": " rag Manage RAG optional dependencies (status | install)",
|
|
106
|
+
"cmdUpdate": " update Check and update to latest (--check for info only)",
|
|
107
|
+
"cmdUninstall": " uninstall One-click uninstall agent",
|
|
108
|
+
"cmdAutostart": " autostart Manage auto-start (on | off)",
|
|
94
109
|
"startupOptions": "Startup Options:",
|
|
95
110
|
"optBackground": " --background, -b Run in background (daemon mode)",
|
|
96
111
|
"optPort": " --port <port> Set listening port (default: 18910)",
|
|
@@ -149,6 +164,15 @@
|
|
|
149
164
|
"ragRestartAdvice": "Tip: if installation keeps failing, try restarting the agent (aha restart) and retry.",
|
|
150
165
|
"ragUnknownSub": "Unknown rag subcommand: {sub}",
|
|
151
166
|
"ragHelpUsage": "Usage: aha rag <status|install>",
|
|
167
|
+
"updateLatest": "Already at latest version v{version}",
|
|
168
|
+
"updateAvailable": "New version available: v{current} → v{latest} (run aha update)",
|
|
169
|
+
"updateInstalling": "Updating v{current} → v{latest}...",
|
|
170
|
+
"updateFailed": "Update failed, please retry",
|
|
171
|
+
"updateSuccess": "✅ Updated to v{version}, service restarted",
|
|
172
|
+
"uninstallAutostartRemoved": "Auto-start removed",
|
|
173
|
+
"uninstallComplete": "✅ Uninstall complete. User data kept at {dataDir}",
|
|
174
|
+
"autostartDisabled": "Auto-start disabled",
|
|
175
|
+
"autostartUseScript": "To enable auto-start, re-run the install script or configure system service manually",
|
|
152
176
|
"restartHelper": {
|
|
153
177
|
"waitingOldExit": "Waiting for old process {pid} to exit...",
|
|
154
178
|
"waitOldTimeout": "Wait for old process exit timeout, trying force kill...",
|