yuangs 2.26.0 → 2.28.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yuangs",
3
- "version": "2.26.0",
3
+ "version": "2.28.0",
4
4
  "description": "苑广山的个人应用集合 CLI(彩色版)",
5
5
  "author": "苑广山",
6
6
  "license": "ISC",
@@ -1,33 +0,0 @@
1
- import { AgentInput, AgentMode } from './types';
2
- import { ModelRegistry } from '../policy/model/ModelRegistry';
3
- export declare class AgentPipelineEnhanced {
4
- private contextBuffer;
5
- private modelRegistry;
6
- private policy;
7
- constructor(modelRegistry?: ModelRegistry);
8
- run(input: AgentInput, mode: AgentMode): Promise<void>;
9
- /**
10
- * 执行带 TokenPolicy 的 pipeline
11
- */
12
- private runWithTokenPolicy;
13
- /**
14
- * 处理 Policy 结果
15
- */
16
- private handlePolicyResult;
17
- /**
18
- * 应用用户决策
19
- */
20
- private applyDecision;
21
- /**
22
- * 执行 LLM Pipeline(原有的流程)
23
- */
24
- private executeLLMPipeline;
25
- /**
26
- * 从输入中提取上下文 tokens (@file, #dir)
27
- */
28
- private extractContextTokens;
29
- /**
30
- * 确定 mode
31
- */
32
- private determineMode;
33
- }
@@ -1,233 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.AgentPipelineEnhanced = void 0;
7
- const contextBuffer_1 = require("../commands/contextBuffer");
8
- const intent_1 = require("./intent");
9
- const context_1 = require("./context");
10
- const prompt_1 = require("./prompt");
11
- const llm_1 = require("./llm");
12
- const interpret_1 = require("./interpret");
13
- const planExecutor_1 = require("./planExecutor");
14
- const record_1 = require("./record");
15
- const skills_1 = require("./skills");
16
- const crypto_1 = require("crypto");
17
- const renderer_1 = require("../utils/renderer");
18
- const ora_1 = __importDefault(require("ora"));
19
- const chalk_1 = __importDefault(require("chalk"));
20
- const DefaultTokenPolicy_1 = require("../policy/token/DefaultTokenPolicy");
21
- const ModelRegistry_1 = require("../policy/model/ModelRegistry");
22
- const syntaxHandler_1 = require("../policy/syntaxHandler");
23
- const PolicyPresenter_1 = require("../ui/PolicyPresenter");
24
- const sampler_1 = require("../policy/sampler");
25
- const MAX_PIPELINE_ITERATIONS = 3;
26
- class AgentPipelineEnhanced {
27
- contextBuffer = new contextBuffer_1.ContextBuffer();
28
- modelRegistry;
29
- policy;
30
- constructor(modelRegistry) {
31
- this.modelRegistry = modelRegistry || new ModelRegistry_1.ModelRegistry([]);
32
- this.policy = new DefaultTokenPolicy_1.DefaultTokenPolicy();
33
- }
34
- async run(input, mode) {
35
- const id = (0, crypto_1.randomUUID)();
36
- try {
37
- await this.runWithTokenPolicy(input, mode, id);
38
- }
39
- catch (error) {
40
- if (error.message === 'MaxIterationsExceeded') {
41
- console.log(chalk_1.default.yellow('\n⚠️ 已达到最大迭代次数,操作终止'));
42
- }
43
- else if (error.message === 'UserAborted') {
44
- console.log(chalk_1.default.yellow('\n⚠️ 用户已取消操作'));
45
- }
46
- else {
47
- console.log(chalk_1.default.red(`\n❌ Pipeline 错误: ${error.message}`));
48
- }
49
- throw error;
50
- }
51
- PolicyPresenter_1.PolicyPresenter.clearSuppressCache();
52
- }
53
- /**
54
- * 执行带 TokenPolicy 的 pipeline
55
- */
56
- async runWithTokenPolicy(input, mode, executionId) {
57
- // 1. 意图解析 (Syntax Phase)
58
- const tokens = this.extractContextTokens(input.rawInput);
59
- let pendingItems = syntaxHandler_1.SyntaxHandler.parse(tokens);
60
- // 2. 治理审计循环 (Governance Loop)
61
- // 最多重试 3 次(包括初始评估)以防止无限循环
62
- const MAX_ITERATIONS = 3;
63
- let passed = false;
64
- let iterations = 0;
65
- let currentModel = this.modelRegistry.get(input.options?.model || 'gemini-2.5-flash-lite') || this.modelRegistry.getDefault();
66
- while (!passed && iterations < MAX_ITERATIONS) {
67
- iterations++;
68
- const result = await this.policy.evaluate({
69
- model: currentModel,
70
- contextItems: pendingItems,
71
- mode: this.determineMode(mode),
72
- userIntent: input.rawInput
73
- });
74
- passed = await this.handlePolicyResult(result, pendingItems, currentModel, iterations);
75
- if (passed)
76
- break;
77
- }
78
- if (!passed) {
79
- throw new Error('MaxIterationsExceeded');
80
- }
81
- // 3. 授权执行 (Execution Phase)
82
- const resolved = await Promise.all(pendingItems.map(item => item.resolve()));
83
- resolved.forEach(r => {
84
- this.contextBuffer.add({
85
- type: 'file',
86
- path: pendingItems.find((p) => p.id.includes(r.content.substring(0, 20)))?.id || 'unknown',
87
- content: r.content
88
- }, true // bypassTokenLimit = true (已通过 policy 审计)
89
- );
90
- });
91
- // 4. 正常 LLM Pipeline
92
- await this.executeLLMPipeline(input, mode, currentModel, executionId);
93
- }
94
- /**
95
- * 处理 Policy 结果
96
- */
97
- async handlePolicyResult(result, pendingItems, currentModel, iteration) {
98
- if (result.status === 'ok') {
99
- return true; // passed
100
- }
101
- if (result.status === 'block') {
102
- await PolicyPresenter_1.PolicyPresenter.presentBlock(result);
103
- return false;
104
- }
105
- if (result.status === 'warn') {
106
- const decision = await PolicyPresenter_1.PolicyPresenter.presentWarning(result, `${currentModel.name}:${pendingItems.map(p => p.id).join(',')}`);
107
- return this.applyDecision(decision, pendingItems, currentModel);
108
- }
109
- return false;
110
- }
111
- /**
112
- * 应用用户决策
113
- */
114
- async applyDecision(decision, pendingItems, currentModel) {
115
- switch (decision.type) {
116
- case 'continue':
117
- return true;
118
- case 'abort':
119
- throw new Error('UserAborted');
120
- case 'switch_model':
121
- if (decision.targetModel) {
122
- const newModel = this.modelRegistry.get(decision.targetModel);
123
- if (newModel) {
124
- console.log(chalk_1.default.green(`\n🔄 切换至模型: ${decision.targetModel}`));
125
- currentModel = newModel;
126
- return false; // 需要重新评估
127
- }
128
- }
129
- console.log(chalk_1.default.yellow(`⚠️ 模型 ${decision.targetModel} 未找到`));
130
- return false;
131
- case 'sample':
132
- if (decision.strategy === 'head_tail') {
133
- console.log(chalk_1.default.cyan('\n✂ 应用 head_tail 采样...'));
134
- pendingItems = await Promise.all(pendingItems.map(item => sampler_1.ContextSampler.applySampling(item, 'head_tail')));
135
- return false; // 需要重新评估
136
- }
137
- return true;
138
- default:
139
- return true;
140
- }
141
- }
142
- /**
143
- * 执行 LLM Pipeline(原有的流程)
144
- */
145
- async executeLLMPipeline(input, mode, model, executionId) {
146
- const intent = (0, intent_1.inferIntent)(input, mode);
147
- const context = (0, context_1.buildContext)(input, this.contextBuffer);
148
- const prompt = (0, prompt_1.buildPrompt)(intent, context, mode, input.rawInput);
149
- let renderer;
150
- let spinner;
151
- if (mode === 'chat') {
152
- spinner = (0, ora_1.default)(chalk_1.default.cyan('Thinking...')).start();
153
- renderer = new renderer_1.StreamMarkdownRenderer(chalk_1.default.bold.blue('🤖 AI: '), spinner);
154
- }
155
- const result = await (0, llm_1.runLLM)({
156
- prompt,
157
- model: model.name,
158
- stream: mode === 'chat',
159
- onChunk: mode === 'chat' && renderer
160
- ? (s) => renderer.onChunk(s)
161
- : undefined,
162
- });
163
- if (mode === 'chat' && renderer) {
164
- renderer.finish();
165
- }
166
- const isStreaming = mode === 'chat';
167
- const plan = (0, interpret_1.interpretResultToPlan)(result, intent, mode, isStreaming);
168
- result.plan = plan;
169
- (0, record_1.saveRecord)({
170
- id: executionId,
171
- timestamp: Date.now(),
172
- mode,
173
- input,
174
- prompt,
175
- model: model.name,
176
- llmResult: result,
177
- action: plan.tasks[0]?.type === 'shell' ? {
178
- type: 'execute',
179
- command: plan.tasks[0].payload.command,
180
- risk: plan.tasks[0].payload.risk
181
- } : { type: 'print', content: result.rawText },
182
- });
183
- const summary = await (0, planExecutor_1.executePlan)(plan, input.options);
184
- (0, skills_1.learnSkillFromRecord)({
185
- id: executionId,
186
- timestamp: Date.now(),
187
- mode,
188
- input,
189
- prompt,
190
- model: model.name,
191
- llmResult: result,
192
- action: plan.tasks[0]?.type === 'shell' ? {
193
- type: 'execute',
194
- command: plan.tasks[0].payload.command,
195
- risk: plan.tasks[0].payload.risk
196
- } : { type: 'print', content: result.rawText },
197
- }, summary.success);
198
- if (input.options?.verbose) {
199
- console.log(`\n${'-'.repeat(50)}`);
200
- console.log(`Execution ID: ${executionId}`);
201
- console.log(`Model: ${model.name}`);
202
- console.log(`Latency: ${result.latencyMs}ms`);
203
- if (result.tokens) {
204
- console.log(`Tokens: ${result.tokens.total}`);
205
- }
206
- console.log(`${'-'.repeat(50)}\n`);
207
- }
208
- }
209
- /**
210
- * 从输入中提取上下文 tokens (@file, #dir)
211
- */
212
- extractContextTokens(rawInput) {
213
- return rawInput
214
- .split(' ')
215
- .filter(token => token.startsWith('@') || token.startsWith('#'));
216
- }
217
- /**
218
- * 确定 mode
219
- */
220
- determineMode(mode) {
221
- // 将 Agent mode 映射到 Policy mode
222
- // chat → agent, command → command, command+exec → command
223
- if (mode === 'chat') {
224
- return 'agent';
225
- }
226
- if (mode === 'command+exec') {
227
- return 'command';
228
- }
229
- return mode; // command 默认为 command
230
- }
231
- }
232
- exports.AgentPipelineEnhanced = AgentPipelineEnhanced;
233
- //# sourceMappingURL=AgentPipelineEnhanced.js.map
@@ -1,3 +0,0 @@
1
- import type { CompletionRequest } from './types';
2
- export declare function complete(req: CompletionRequest): Promise<CompletionResponse>;
3
- export declare function setProgramInstance(program: any): void;
@@ -1,117 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.complete = complete;
4
- exports.setProgramInstance = setProgramInstance;
5
- const cache_1 = require("./cache");
6
- const apps_1 = require("../apps");
7
- const macros_1 = require("../macros");
8
- async function complete(req) {
9
- const { words, currentIndex, currentWord, previousWord, command } = req;
10
- const cache = cache_1.CompletionCache.getInstance();
11
- const cacheKey = command || 'root';
12
- const cached = cache.get(cacheKey);
13
- if (cached) {
14
- return {
15
- items: cached.filter(item => item.label.startsWith(currentWord)),
16
- isPartial: true
17
- };
18
- }
19
- let items = [];
20
- if (currentIndex === 1) {
21
- items = await getAllCommandItems();
22
- }
23
- else if (command && currentIndex > 1) {
24
- items = await getCompletionItemsForCommand(command, previousWord, currentWord);
25
- }
26
- cache.set(cacheKey, items);
27
- return {
28
- items: items.filter(item => item.label.startsWith(currentWord)),
29
- isPartial: false
30
- };
31
- }
32
- async function getAllCommandItems() {
33
- const items = [];
34
- const commands = getBuiltinCommands();
35
- commands.forEach((cmd) => {
36
- items.push({
37
- type: { type: 'command', name: cmd.name, description: cmd.description },
38
- label: cmd.name,
39
- description: cmd.description
40
- });
41
- });
42
- try {
43
- const apps = (0, apps_1.loadAppsConfig)();
44
- Object.entries(apps).forEach(([name, url]) => {
45
- items.push({
46
- type: { type: 'app', name, description: `打开 ${url}` },
47
- label: name,
48
- description: `打开 ${url}`
49
- });
50
- });
51
- }
52
- catch { }
53
- try {
54
- const macros = (0, macros_1.getMacros)();
55
- Object.entries(macros).forEach(([name, macro]) => {
56
- items.push({
57
- type: { type: 'macro', name, description: macro.description || '' },
58
- label: name,
59
- description: macro.description
60
- });
61
- });
62
- }
63
- catch { }
64
- return items;
65
- }
66
- async function getCompletionItemsForCommand(command, previousWord, currentWord) {
67
- const items = [];
68
- const program = getProgramInstance();
69
- const cmd = program.commands.find((c) => c.name() === command);
70
- if (!cmd)
71
- return items;
72
- cmd.options.forEach((opt) => {
73
- opt.flags.split(/[, ]+/).forEach((flag) => {
74
- if (flag.startsWith('-') && !flag.startsWith('--')) {
75
- items.push({
76
- type: { type: 'flag', parent: command, flag, description: opt.description || '' },
77
- label: flag,
78
- description: opt.description
79
- });
80
- }
81
- });
82
- });
83
- if (previousWord === '--model' || previousWord === '-m') {
84
- const models = getModelValues();
85
- models.forEach((model) => {
86
- items.push({
87
- type: { type: 'flag-value', flag: '--model', value: model, description: '' },
88
- label: model,
89
- description: ''
90
- });
91
- });
92
- }
93
- cmd.commands.forEach((subcmd) => {
94
- items.push({
95
- type: { type: 'subcommand', parent: command, name: subcmd.name(), description: subcmd.description() || '' },
96
- label: subcmd.name(),
97
- description: subcmd.description()
98
- });
99
- });
100
- return items;
101
- }
102
- function getModelValues() {
103
- return [
104
- 'gemini-2.5-flash-lite',
105
- 'gemini-2.5-pro',
106
- 'Assistant',
107
- 'GPT-4o-mini'
108
- ];
109
- }
110
- let programInstance = null;
111
- function setProgramInstance(program) {
112
- programInstance = program;
113
- }
114
- function getProgramInstance() {
115
- return programInstance;
116
- }
117
- //# sourceMappingURL=runtime.js.map
@@ -1,4 +0,0 @@
1
- import { Command } from 'commander';
2
- export declare function complete(req: any): Promise<any>;
3
- export declare function getCommandSubcommands(program: Command, commandName: string): any[];
4
- export declare function setProgramInstance(program: any): void;
@@ -1,113 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.complete = complete;
4
- exports.getCommandSubcommands = getCommandSubcommands;
5
- exports.setProgramInstance = setProgramInstance;
6
- const path_1 = require("./path");
7
- const macros_1 = require("../macros");
8
- const apps_1 = require("../apps");
9
- async function complete(req) {
10
- const { words, currentIndex, currentWord, previousWord } = req;
11
- const items = [];
12
- if (previousWord === '--model' || previousWord === '-m') {
13
- return [
14
- 'gemini-2.5-flash-lite',
15
- 'gemini-2.5-pro',
16
- 'Assistant',
17
- 'GPT-4o-mini'
18
- ];
19
- }
20
- if (currentWord.startsWith('@')) {
21
- return (0, path_1.resolvePathSuggestions)(currentWord, 'file');
22
- }
23
- if (currentWord.startsWith('#')) {
24
- return (0, path_1.resolvePathSuggestions)(currentWord, 'dir');
25
- }
26
- const commands = getBuiltinCommands();
27
- commands.forEach((cmd) => {
28
- items.push({
29
- type: 'command',
30
- name: cmd.name,
31
- label: cmd.name,
32
- description: cmd.description
33
- });
34
- });
35
- try {
36
- const apps = (0, apps_1.loadAppsConfig)();
37
- Object.entries(apps).forEach(([name, url]) => {
38
- items.push({
39
- type: 'app',
40
- name: name,
41
- label: name,
42
- description: `打开 ${url}`
43
- });
44
- });
45
- }
46
- catch { }
47
- try {
48
- const macros = (0, macros_1.getMacros)();
49
- Object.entries(macros).forEach(([name, macro]) => {
50
- items.push({
51
- type: 'macro',
52
- name: name,
53
- label: name,
54
- description: macro.description || ''
55
- });
56
- });
57
- }
58
- catch { }
59
- return items;
60
- }
61
- function getCommandSubcommands(program, commandName) {
62
- const items = [];
63
- const cmd = program.commands.find((c) => c.name() === commandName);
64
- if (!cmd)
65
- return items;
66
- cmd.options.forEach((opt) => {
67
- opt.flags.split(/[, ]+/).forEach((flag) => {
68
- if (flag.startsWith('-') && !flag.startsWith('--')) {
69
- items.push({
70
- type: 'flag',
71
- parent: commandName,
72
- flag: flag,
73
- label: flag,
74
- description: opt.description || ''
75
- });
76
- }
77
- });
78
- });
79
- cmd.commands.forEach((subcmd) => {
80
- items.push({
81
- type: 'subcommand',
82
- parent: commandName,
83
- name: subcmd.name(),
84
- label: subcmd.name(),
85
- description: subcmd.description() || ''
86
- });
87
- });
88
- return items;
89
- }
90
- function getBuiltinCommands() {
91
- return [
92
- { name: 'ai', description: '向 AI 提问' },
93
- { name: 'list', description: '列出所有应用' },
94
- { name: 'history', description: '查看及执行命令历史' },
95
- { name: 'config', description: '管理本地配置' },
96
- { name: 'macros', description: '查看所有快捷指令' },
97
- { name: 'save', description: '保存快捷指令' },
98
- { name: 'run', description: '执行快捷指令' },
99
- { name: 'help', description: '显示帮助信息' },
100
- { name: 'completion', description: '安装 Shell 补全' },
101
- { name: 'shici', description: '打开古诗词 PWA' },
102
- { name: 'dict', description: '打开英语词典' },
103
- { name: 'pong', description: '打开 Pong 游戏' }
104
- ];
105
- }
106
- let programInstance = null;
107
- function setProgramInstance(program) {
108
- programInstance = program;
109
- }
110
- function getProgramInstance() {
111
- return programInstance || {};
112
- }
113
- //# sourceMappingURL=unified.js.map
@@ -1,20 +0,0 @@
1
- import { Command } from 'commander';
2
- export declare function getAllCommands(program: Command): string[];
3
- /**
4
- * 获取命令的子命令或参数
5
- */
6
- export declare function getCommandSubcommands(program: Command, commandName: string): string[];
7
- /**
8
- * 生成 Bash 补全脚本
9
- */
10
- export declare function generateBashCompletion(program: Command): string;
11
- /**
12
- * 生成 Zsh 补全脚本
13
- */
14
- export declare function generateZshCompletion(program: Command): string;
15
- export declare function installBashCompletion(program: Command): Promise<boolean>;
16
- export declare function installZshCompletion(program: Command): Promise<boolean>;
17
- /**
18
- * 获取命令描述(用于补全提示)
19
- */
20
- export declare function getCommandDescription(program: Command, commandName: string): string;
@@ -1,214 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.getAllCommands = getAllCommands;
7
- exports.getCommandSubcommands = getCommandSubcommands;
8
- exports.generateBashCompletion = generateBashCompletion;
9
- exports.generateZshCompletion = generateZshCompletion;
10
- exports.installBashCompletion = installBashCompletion;
11
- exports.installZshCompletion = installZshCompletion;
12
- exports.getCommandDescription = getCommandDescription;
13
- const fs_1 = __importDefault(require("fs"));
14
- const path_1 = __importDefault(require("path"));
15
- const apps_1 = require("./apps");
16
- const macros_1 = require("./macros");
17
- function getAllCommands(program) {
18
- const commands = [];
19
- program.commands.forEach(cmd => {
20
- if (cmd.name()) {
21
- commands.push(cmd.name());
22
- }
23
- if (cmd.aliases()) {
24
- commands.push(...cmd.aliases());
25
- }
26
- });
27
- try {
28
- const apps = (0, apps_1.loadAppsConfig)();
29
- Object.keys(apps).forEach(app => {
30
- if (!commands.includes(app)) {
31
- commands.push(app);
32
- }
33
- });
34
- }
35
- catch {
36
- }
37
- try {
38
- const macros = (0, macros_1.getMacros)();
39
- Object.keys(macros).forEach(macro => {
40
- if (!commands.includes(macro)) {
41
- commands.push(macro);
42
- }
43
- });
44
- }
45
- catch {
46
- }
47
- return [...new Set(commands)].sort();
48
- }
49
- /**
50
- * 获取命令的子命令或参数
51
- */
52
- function getCommandSubcommands(program, commandName) {
53
- const command = program.commands.find(cmd => cmd.name() === commandName);
54
- if (!command)
55
- return [];
56
- const subcommands = [];
57
- command.commands.forEach(cmd => {
58
- if (cmd.name()) {
59
- subcommands.push(cmd.name());
60
- }
61
- });
62
- command.options.forEach(opt => {
63
- opt.flags.split(/[, ]+/).forEach(flag => {
64
- if (flag.startsWith('--')) {
65
- subcommands.push(flag);
66
- }
67
- else if (flag.startsWith('-')) {
68
- subcommands.push(flag);
69
- }
70
- });
71
- });
72
- return [...new Set(subcommands)].sort();
73
- }
74
- /**
75
- * 生成 Bash 补全脚本
76
- */
77
- function generateBashCompletion(program) {
78
- const commands = getAllCommands(program);
79
- return `#!/bin/bash
80
- # yuangs bash completion
81
-
82
- _yuangs_completion() {
83
- local cur prev words cword
84
- _init_completion || return
85
-
86
- # 补全命令名
87
- if [[ \${COMP_CWORD} -eq 1 ]]; then
88
- COMPREPLY=($(compgen -W '${commands.join(' ')}' -- "\${cur}"))
89
- return
90
- fi
91
-
92
- # 补全子命令和参数
93
- local cmd="\${words[1]}"
94
- case "\${cmd}" in
95
- ${commands.map(cmd => `
96
- ${cmd})
97
- case "\${prev}" in
98
- -m|--model)
99
- COMPREPLY=($(compgen -W "gemini-2.5-flash-lite gemini-2.5-pro" -- "\${cur}"))
100
- ;;
101
- *)
102
- COMPREPLY=($(compgen -W "$(yuangs _complete_subcommand ${cmd})" -- "\${cur}"))
103
- ;;
104
- esac
105
- ;;
106
- `).join('\n')}
107
-
108
- *)
109
- ;;
110
- esac
111
- }
112
-
113
- complete -F _yuangs_completion yuangs
114
- `;
115
- }
116
- /**
117
- * 生成 Zsh 补全脚本
118
- */
119
- function generateZshCompletion(program) {
120
- const commands = getAllCommands(program);
121
- return `#compdef yuangs
122
- # yuangs zsh completion
123
-
124
- _yuangs() {
125
- local -a commands
126
- commands=(
127
- ${commands.map(cmd => ` '${cmd}:$(yuangs _describe ${cmd})'`).join('\n')}
128
- )
129
-
130
- if (( CURRENT == 2 )); then
131
- _describe 'command' commands
132
- else
133
- local cmd="\${words[2]}"
134
- case "\${cmd}" in
135
- ${commands.map(cmd => `
136
- ${cmd})
137
- _values 'options' $(yuangs _complete_subcommand ${cmd})
138
- ;;
139
- `).join('\n')}
140
- *)
141
- ;;
142
- esac
143
- fi
144
- }
145
-
146
- _yuangs
147
- `;
148
- }
149
- async function installBashCompletion(program) {
150
- const bashrcPath = path_1.default.join(process.env.HOME || '', '.bashrc');
151
- const bashCompletionDir = path_1.default.join(process.env.HOME || '', '.bash_completion.d');
152
- try {
153
- if (!fs_1.default.existsSync(bashCompletionDir)) {
154
- fs_1.default.mkdirSync(bashCompletionDir, { recursive: true });
155
- }
156
- const completionPath = path_1.default.join(bashCompletionDir, 'yuangs-completion.bash');
157
- const completionScript = generateBashCompletion(program);
158
- fs_1.default.writeFileSync(completionPath, completionScript, { mode: 0o644 });
159
- const sourceLine = `# yuangs completion
160
- if [ -f ~/.bash_completion.d/yuangs-completion.bash ]; then
161
- source ~/.bash_completion.d/yuangs-completion.bash
162
- fi
163
- `;
164
- let bashrc = '';
165
- if (fs_1.default.existsSync(bashrcPath)) {
166
- bashrc = fs_1.default.readFileSync(bashrcPath, 'utf-8');
167
- }
168
- if (!bashrc.includes('yuangs-completion.bash')) {
169
- fs_1.default.appendFileSync(bashrcPath, `\n${sourceLine}`);
170
- }
171
- return true;
172
- }
173
- catch (error) {
174
- console.error('安装 Bash 补全失败:', error);
175
- return false;
176
- }
177
- }
178
- async function installZshCompletion(program) {
179
- const zshrcPath = path_1.default.join(process.env.HOME || '', '.zshrc');
180
- const zfuncDir = path_1.default.join(process.env.HOME || '', '.zfunctions');
181
- try {
182
- if (!fs_1.default.existsSync(zfuncDir)) {
183
- fs_1.default.mkdirSync(zfuncDir, { recursive: true });
184
- }
185
- const completionPath = path_1.default.join(zfuncDir, '_yuangs');
186
- const completionScript = generateZshCompletion(program);
187
- fs_1.default.writeFileSync(completionPath, completionScript, { mode: 0o644 });
188
- let zshrc = '';
189
- if (fs_1.default.existsSync(zshrcPath)) {
190
- zshrc = fs_1.default.readFileSync(zshrcPath, 'utf-8');
191
- }
192
- const fpathLine = 'fpath=(~/.zfunctions $fpath)';
193
- const autoloadLine = 'autoload -U compinit && compinit';
194
- if (!zshrc.includes('fpath=')) {
195
- fs_1.default.appendFileSync(zshrcPath, `\n${fpathLine}`);
196
- }
197
- if (!zshrc.includes('autoload -U compinit')) {
198
- fs_1.default.appendFileSync(zshrcPath, `\n${autoloadLine}`);
199
- }
200
- return true;
201
- }
202
- catch (error) {
203
- console.error('安装 Zsh 补全失败:', error);
204
- return false;
205
- }
206
- }
207
- /**
208
- * 获取命令描述(用于补全提示)
209
- */
210
- function getCommandDescription(program, commandName) {
211
- const command = program.commands.find(cmd => cmd.name() === commandName);
212
- return command?.description() || '';
213
- }
214
- //# sourceMappingURL=completion.js.map
@@ -1,52 +0,0 @@
1
- import { TokenPolicyResult, UserDecision } from '../policy/token/types';
2
- /**
3
- * PolicyPresenter - CLI 交互层
4
- *
5
- * 负责:
6
- * - 呈现 warn/block 状态
7
- * - 获取用户决策
8
- * - 防止重复警告(suppressKey)
9
- */
10
- export declare class PolicyPresenter {
11
- private static suppressCache;
12
- /**
13
- * 展现 Token 警告并获取用户决策
14
- */
15
- static presentWarning(result: TokenPolicyResult, suppressKey?: string): Promise<UserDecision>;
16
- /**
17
- * 展现 Token 阻断错误
18
- */
19
- static presentBlock(result: TokenPolicyResult): Promise<void>;
20
- /**
21
- * 渲染警告界面
22
- */
23
- private static renderWarning;
24
- /**
25
- * 渲染阻断界面
26
- */
27
- private static renderBlock;
28
- /**
29
- * 提示用户选择操作
30
- */
31
- private static promptForAction;
32
- /**
33
- * 解析用户选择
34
- */
35
- private static parseChoice;
36
- /**
37
- * 格式化占比
38
- */
39
- private static formatRatio;
40
- /**
41
- * 获取操作图标
42
- */
43
- private static getActionIcon;
44
- /**
45
- * 计算 suppress key
46
- */
47
- private static computeSuppressKey;
48
- /**
49
- * 清除抑制缓存(用于测试或会话重启)
50
- */
51
- static clearSuppressCache(): void;
52
- }
@@ -1,193 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.PolicyPresenter = void 0;
7
- const chalk_1 = __importDefault(require("chalk"));
8
- const readline_1 = require("readline");
9
- /**
10
- * PolicyPresenter - CLI 交互层
11
- *
12
- * 负责:
13
- * - 呈现 warn/block 状态
14
- * - 获取用户决策
15
- * - 防止重复警告(suppressKey)
16
- */
17
- // @ts-ignore - Node.js readline type compatibility
18
- class PolicyPresenter {
19
- static suppressCache = new Map();
20
- /**
21
- * 展现 Token 警告并获取用户决策
22
- */
23
- static async presentWarning(result, suppressKey) {
24
- const key = suppressKey || this.computeSuppressKey(result);
25
- if (suppressKey && this.suppressCache.get(key)) {
26
- return { type: 'continue' };
27
- }
28
- this.renderWarning(result);
29
- const choice = await this.promptForAction(result);
30
- if (choice.type === 'continue' && suppressKey) {
31
- this.suppressCache.set(key, true);
32
- }
33
- return choice;
34
- }
35
- /**
36
- * 展现 Token 阻断错误
37
- */
38
- static async presentBlock(result) {
39
- this.renderBlock(result);
40
- const rl = (0, readline_1.createInterface)({
41
- input: process.stdin,
42
- output: process.stdout
43
- });
44
- await new Promise(resolve => {
45
- rl.question(chalk_1.default.gray('\n按 Enter 退出...'), () => resolve());
46
- });
47
- rl.close();
48
- }
49
- /**
50
- * 渲染警告界面
51
- */
52
- static renderWarning(result) {
53
- console.log('\n');
54
- console.log(chalk_1.default.bold.yellow('⚠️ Token 预算预警'));
55
- console.log(chalk_1.default.gray('─'.repeat(50)));
56
- console.log(`预估 Token: ${chalk_1.default.bold(result.estimatedTokens.toLocaleString())}`);
57
- console.log(`模型上限: ${chalk_1.default.bold(result.limit.toLocaleString())}`);
58
- console.log(`占用率: ${this.formatRatio(result.ratio)}`);
59
- if (result.warnings && result.warnings.length > 0) {
60
- console.log(chalk_1.default.yellow('\n⚠️ 警告:'));
61
- result.warnings.forEach(w => {
62
- console.log(chalk_1.default.gray(` • ${w}`));
63
- });
64
- }
65
- console.log(chalk_1.default.gray('─'.repeat(50)));
66
- console.log(chalk_1.default.cyan('\n💡 建议操作:'));
67
- result.actions?.forEach((action, i) => {
68
- const icon = this.getActionIcon(action);
69
- const label = chalk_1.default.bold(action.label);
70
- const desc = chalk_1.default.gray(action.desc);
71
- if (action.type === 'auto_sample_pipe' && result.estimatedTokens > 0) {
72
- const savedTokens = Math.round(result.estimatedTokens * 0.4);
73
- console.log(` ${icon} ${i + 1}. ${label} ${desc} ${chalk_1.default.green(`(预估节省 ~${savedTokens} tokens)`)}`);
74
- }
75
- else {
76
- console.log(` ${icon} ${i + 1}. ${label} ${desc}`);
77
- }
78
- });
79
- console.log();
80
- }
81
- /**
82
- * 渲染阻断界面
83
- */
84
- static renderBlock(result) {
85
- console.log('\n');
86
- console.log(chalk_1.default.bold.red('⛔ Token 超限阻断'));
87
- console.log(chalk_1.default.gray('─'.repeat(50)));
88
- console.log(`预估 Token: ${chalk_1.default.bold.red(result.estimatedTokens.toLocaleString())}`);
89
- console.log(`模型上限: ${chalk_1.default.bold(result.limit.toLocaleString())}`);
90
- console.log(`占用率: ${this.formatRatio(result.ratio)}`);
91
- if (result.warnings && result.warnings.length > 0) {
92
- console.log(chalk_1.default.red('\n❌ 阻断原因:'));
93
- result.warnings.forEach(w => {
94
- console.log(chalk_1.default.gray(` • ${w}`));
95
- });
96
- }
97
- console.log(chalk_1.default.gray('─'.repeat(50)));
98
- console.log(chalk_1.default.yellow('\n💡 可选操作:'));
99
- result.actions?.forEach((action, i) => {
100
- const icon = this.getActionIcon(action);
101
- const label = chalk_1.default.bold(action.label);
102
- const desc = chalk_1.default.gray(action.desc);
103
- console.log(` ${icon} ${i + 1}. ${label} ${desc}`);
104
- });
105
- console.log();
106
- }
107
- /**
108
- * 提示用户选择操作
109
- */
110
- static async promptForAction(result) {
111
- const rl = (0, readline_1.createInterface)({
112
- input: process.stdin,
113
- output: process.stdout
114
- });
115
- const choices = result.actions || [];
116
- const answer = await new Promise(resolve => {
117
- rl.question(chalk_1.default.cyan('请选择操作序号 (默认 1): '), (input) => resolve((input || '1').trim()));
118
- });
119
- rl.close();
120
- return this.parseChoice(answer, choices);
121
- }
122
- /**
123
- * 解析用户选择
124
- */
125
- static parseChoice(answer, actions) {
126
- const choice = parseInt(answer);
127
- if (isNaN(choice) || choice < 1 || choice > actions.length) {
128
- return { type: 'continue' };
129
- }
130
- const action = actions[choice - 1];
131
- switch (action.type) {
132
- case 'confirm_continue':
133
- return { type: 'continue' };
134
- case 'auto_sample_pipe':
135
- return {
136
- type: 'sample',
137
- strategy: action.strategy
138
- };
139
- case 'suggest_model_switch':
140
- return {
141
- type: 'switch_model',
142
- targetModel: action.targetModel
143
- };
144
- case 'abort':
145
- return { type: 'abort' };
146
- default:
147
- return { type: 'continue' };
148
- }
149
- }
150
- /**
151
- * 格式化占比
152
- */
153
- static formatRatio(ratio) {
154
- const percentage = (ratio * 100).toFixed(1);
155
- const color = ratio > 1.0
156
- ? chalk_1.default.red
157
- : ratio > 0.8
158
- ? chalk_1.default.yellow
159
- : chalk_1.default.green;
160
- return color(`${percentage}%`);
161
- }
162
- /**
163
- * 获取操作图标
164
- */
165
- static getActionIcon(action) {
166
- switch (action.type) {
167
- case 'confirm_continue':
168
- return '✓';
169
- case 'auto_sample_pipe':
170
- return '✂';
171
- case 'suggest_model_switch':
172
- return '🔄';
173
- case 'abort':
174
- return '✗';
175
- default:
176
- return '•';
177
- }
178
- }
179
- /**
180
- * 计算 suppress key
181
- */
182
- static computeSuppressKey(result) {
183
- return `${result.estimatedTokens}:${result.limit}`;
184
- }
185
- /**
186
- * 清除抑制缓存(用于测试或会话重启)
187
- */
188
- static clearSuppressCache() {
189
- this.suppressCache.clear();
190
- }
191
- }
192
- exports.PolicyPresenter = PolicyPresenter;
193
- //# sourceMappingURL=PolicyPresenter.js.map