orquesta-cli 0.2.112 → 0.2.116
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/dist/agents/planner/index.js +2 -1
- package/dist/cli.js +8 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/core/git-auto-updater.d.ts +58 -0
- package/dist/core/git-auto-updater.js +374 -0
- package/dist/core/slash-command-handler.js +2 -1
- package/dist/index.js +8 -1
- package/dist/tools/mcp/mcp-client.js +8 -1
- package/dist/ui/TodoPanel.js +1 -1
- package/dist/ui/components/ActivityIndicator.js +5 -5
- package/dist/ui/components/CustomTextInput.d.ts +1 -0
- package/dist/ui/components/CustomTextInput.js +7 -1
- package/dist/ui/components/PlanExecuteApp.js +52 -34
- package/dist/ui/components/StreamingOutput.d.ts +1 -0
- package/dist/ui/components/StreamingOutput.js +5 -3
- package/dist/ui/components/dialogs/SettingsDialog.js +2 -1
- package/dist/ui/ink-entry.js +17 -0
- package/package.json +1 -1
|
@@ -71,10 +71,11 @@ Choose one of your 3 tools now.`,
|
|
|
71
71
|
});
|
|
72
72
|
logger.warn(`Planning LLM retry attempt ${attempt}/${MAX_RETRIES}`, { lastError: lastError?.message });
|
|
73
73
|
}
|
|
74
|
+
const toolChoiceAuto = /^(1|true|yes|on)$/i.test(process.env['ORQUESTA_TOOL_CHOICE_AUTO'] || '');
|
|
74
75
|
const response = await this.llmClient.chatCompletion({
|
|
75
76
|
messages,
|
|
76
77
|
tools: planningTools,
|
|
77
|
-
tool_choice: 'required',
|
|
78
|
+
tool_choice: toolChoiceAuto ? 'auto' : 'required',
|
|
78
79
|
temperature: 0.7,
|
|
79
80
|
max_tokens: 2000,
|
|
80
81
|
...(this.modelOverride ? { model: this.modelOverride } : {}),
|
package/dist/cli.js
CHANGED
|
@@ -30,8 +30,15 @@ import { syncOrquestaConfigs } from './orquesta/config-sync.js';
|
|
|
30
30
|
import { scanProviders, scanProvider, toEndpointConfig } from './core/config/auto-detect.js';
|
|
31
31
|
import { getProviders, refreshCatalogFromServer } from './core/config/providers.js';
|
|
32
32
|
import { shouldShowOnboarding, runOnboarding } from './core/onboarding.js';
|
|
33
|
+
import { APP_VERSION } from './constants.js';
|
|
33
34
|
const require = createRequire(import.meta.url);
|
|
34
|
-
|
|
35
|
+
let packageJson;
|
|
36
|
+
try {
|
|
37
|
+
packageJson = require('../package.json');
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
packageJson = { version: APP_VERSION };
|
|
41
|
+
}
|
|
35
42
|
const program = new Command();
|
|
36
43
|
async function readPromptFromStdin() {
|
|
37
44
|
if (process.stdin.isTTY)
|
package/dist/constants.d.ts
CHANGED
|
@@ -5,5 +5,5 @@ export declare const DOCS_DIR: string;
|
|
|
5
5
|
export declare const BACKUPS_DIR: string;
|
|
6
6
|
export declare const PROJECTS_DIR: string;
|
|
7
7
|
export declare function cwdToProjectSegment(cwd?: string): string;
|
|
8
|
-
export declare const APP_VERSION = "0.2.
|
|
8
|
+
export declare const APP_VERSION = "0.2.115";
|
|
9
9
|
//# sourceMappingURL=constants.d.ts.map
|
package/dist/constants.js
CHANGED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export type UpdateStatus = {
|
|
2
|
+
type: 'checking';
|
|
3
|
+
} | {
|
|
4
|
+
type: 'no_update';
|
|
5
|
+
} | {
|
|
6
|
+
type: 'first_run';
|
|
7
|
+
step: number;
|
|
8
|
+
totalSteps: number;
|
|
9
|
+
message: string;
|
|
10
|
+
} | {
|
|
11
|
+
type: 'updating';
|
|
12
|
+
step: number;
|
|
13
|
+
totalSteps: number;
|
|
14
|
+
message: string;
|
|
15
|
+
} | {
|
|
16
|
+
type: 'complete';
|
|
17
|
+
needsRestart: boolean;
|
|
18
|
+
message: string;
|
|
19
|
+
} | {
|
|
20
|
+
type: 'error';
|
|
21
|
+
message: string;
|
|
22
|
+
} | {
|
|
23
|
+
type: 'skipped';
|
|
24
|
+
reason: string;
|
|
25
|
+
};
|
|
26
|
+
export type StatusCallback = (status: UpdateStatus) => void;
|
|
27
|
+
export declare class GitAutoUpdater {
|
|
28
|
+
private repoUrl;
|
|
29
|
+
private repoDir;
|
|
30
|
+
private commitFile;
|
|
31
|
+
private enabled;
|
|
32
|
+
private onStatus;
|
|
33
|
+
constructor(options?: {
|
|
34
|
+
repoUrl?: string;
|
|
35
|
+
enabled?: boolean;
|
|
36
|
+
onStatus?: StatusCallback;
|
|
37
|
+
});
|
|
38
|
+
setStatusCallback(callback: StatusCallback): void;
|
|
39
|
+
private emitStatus;
|
|
40
|
+
run(options?: {
|
|
41
|
+
noUpdate?: boolean;
|
|
42
|
+
}): Promise<boolean>;
|
|
43
|
+
private runBinaryMode;
|
|
44
|
+
private getRemoteCommit;
|
|
45
|
+
private getSavedCommit;
|
|
46
|
+
private saveCommit;
|
|
47
|
+
private updateBinary;
|
|
48
|
+
private initialSetup;
|
|
49
|
+
private pullAndUpdate;
|
|
50
|
+
private freshClone;
|
|
51
|
+
private rebuildAndLink;
|
|
52
|
+
private copyBinariesInternal;
|
|
53
|
+
private ensurePathConfigured;
|
|
54
|
+
private unlinkNpm;
|
|
55
|
+
private cleanupRepo;
|
|
56
|
+
}
|
|
57
|
+
export default GitAutoUpdater;
|
|
58
|
+
//# sourceMappingURL=git-auto-updater.d.ts.map
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
import fs, { createReadStream, createWriteStream } from 'fs';
|
|
3
|
+
import { rm, copyFile, chmod } from 'fs/promises';
|
|
4
|
+
import { pipeline } from 'stream/promises';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import os from 'os';
|
|
7
|
+
import zlib from 'zlib';
|
|
8
|
+
import { logger } from '../utils/logger.js';
|
|
9
|
+
function isRunningAsBinary() {
|
|
10
|
+
const execPath = process.execPath;
|
|
11
|
+
const isNodeRuntime = execPath.includes('node') || execPath.includes('nodejs');
|
|
12
|
+
return !isNodeRuntime;
|
|
13
|
+
}
|
|
14
|
+
function execAsync(command, options = {}) {
|
|
15
|
+
return new Promise((resolve, reject) => {
|
|
16
|
+
const [cmd, ...args] = command.split(' ');
|
|
17
|
+
const child = spawn(cmd, args, {
|
|
18
|
+
cwd: options.cwd,
|
|
19
|
+
shell: true,
|
|
20
|
+
stdio: 'pipe',
|
|
21
|
+
});
|
|
22
|
+
let stdout = '';
|
|
23
|
+
let stderr = '';
|
|
24
|
+
child.stdout?.on('data', (data) => {
|
|
25
|
+
stdout += data.toString();
|
|
26
|
+
});
|
|
27
|
+
child.stderr?.on('data', (data) => {
|
|
28
|
+
stderr += data.toString();
|
|
29
|
+
});
|
|
30
|
+
child.on('close', (code) => {
|
|
31
|
+
if (code === 0) {
|
|
32
|
+
resolve({ stdout, stderr });
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
const error = new Error(`Command failed: ${command}`);
|
|
36
|
+
error.stdout = stdout;
|
|
37
|
+
error.stderr = stderr;
|
|
38
|
+
error.code = code;
|
|
39
|
+
reject(error);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
child.on('error', (err) => {
|
|
43
|
+
reject(err);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
export class GitAutoUpdater {
|
|
48
|
+
repoUrl = 'https://github.com/A2G-Dev-Space/Local-CLI.git';
|
|
49
|
+
repoDir;
|
|
50
|
+
commitFile;
|
|
51
|
+
enabled = true;
|
|
52
|
+
onStatus = null;
|
|
53
|
+
constructor(options) {
|
|
54
|
+
this.repoDir = path.join(os.homedir(), '.local-cli', 'repo');
|
|
55
|
+
this.commitFile = path.join(os.homedir(), '.local-cli', 'current-commit');
|
|
56
|
+
if (options?.repoUrl) {
|
|
57
|
+
this.repoUrl = options.repoUrl;
|
|
58
|
+
}
|
|
59
|
+
if (options?.enabled !== undefined) {
|
|
60
|
+
this.enabled = options.enabled;
|
|
61
|
+
}
|
|
62
|
+
if (options?.onStatus) {
|
|
63
|
+
this.onStatus = options.onStatus;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
setStatusCallback(callback) {
|
|
67
|
+
this.onStatus = callback;
|
|
68
|
+
}
|
|
69
|
+
emitStatus(status) {
|
|
70
|
+
if (this.onStatus) {
|
|
71
|
+
this.onStatus(status);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async run(options = {}) {
|
|
75
|
+
logger.enter('GitAutoUpdater.run', {
|
|
76
|
+
noUpdate: options.noUpdate,
|
|
77
|
+
enabled: this.enabled,
|
|
78
|
+
repoDir: this.repoDir
|
|
79
|
+
});
|
|
80
|
+
if (options.noUpdate || !this.enabled) {
|
|
81
|
+
logger.flow('Git auto-update disabled - skipping');
|
|
82
|
+
this.emitStatus({ type: 'skipped', reason: 'disabled' });
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
this.emitStatus({ type: 'checking' });
|
|
86
|
+
try {
|
|
87
|
+
if (isRunningAsBinary() && fs.existsSync(this.repoDir)) {
|
|
88
|
+
logger.flow('Cleaning up leftover repo from previous version');
|
|
89
|
+
await this.cleanupRepo();
|
|
90
|
+
}
|
|
91
|
+
if (isRunningAsBinary()) {
|
|
92
|
+
return await this.runBinaryMode();
|
|
93
|
+
}
|
|
94
|
+
logger.flow('Checking repository directory');
|
|
95
|
+
if (!fs.existsSync(this.repoDir)) {
|
|
96
|
+
logger.flow('First run detected - need initial setup');
|
|
97
|
+
return await this.initialSetup();
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
return await this.pullAndUpdate();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
logger.error('Git auto-update failed', error);
|
|
105
|
+
this.emitStatus({ type: 'error', message: 'Auto-update failed, continuing with current version' });
|
|
106
|
+
}
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
async runBinaryMode() {
|
|
110
|
+
logger.flow('Running in binary mode - using ls-remote for update check');
|
|
111
|
+
try {
|
|
112
|
+
const remoteCommit = await this.getRemoteCommit();
|
|
113
|
+
if (!remoteCommit) {
|
|
114
|
+
logger.error('Failed to get remote commit');
|
|
115
|
+
this.emitStatus({ type: 'error', message: 'Failed to check for updates' });
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
const savedCommit = this.getSavedCommit();
|
|
119
|
+
logger.debug('Version check', { remote: remoteCommit.slice(0, 7), saved: savedCommit?.slice(0, 7) || 'none' });
|
|
120
|
+
if (savedCommit === remoteCommit) {
|
|
121
|
+
logger.flow('Already up to date');
|
|
122
|
+
this.emitStatus({ type: 'no_update' });
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
logger.flow('Update available, starting update process');
|
|
126
|
+
return await this.updateBinary(remoteCommit);
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
logger.error('Binary mode update failed', error);
|
|
130
|
+
this.emitStatus({ type: 'error', message: 'Update check failed' });
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
async getRemoteCommit() {
|
|
135
|
+
try {
|
|
136
|
+
const result = await execAsync(`git ls-remote ${this.repoUrl} refs/heads/main`);
|
|
137
|
+
const match = result.stdout.match(/^([a-f0-9]+)/);
|
|
138
|
+
return match && match[1] ? match[1] : null;
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
logger.error('Failed to get remote commit via ls-remote', error);
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
getSavedCommit() {
|
|
146
|
+
try {
|
|
147
|
+
if (fs.existsSync(this.commitFile)) {
|
|
148
|
+
return fs.readFileSync(this.commitFile, 'utf-8').trim();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
logger.debug('Failed to read saved commit: ' + (error instanceof Error ? error.message : String(error)));
|
|
153
|
+
}
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
saveCommit(commit) {
|
|
157
|
+
try {
|
|
158
|
+
const dir = path.dirname(this.commitFile);
|
|
159
|
+
if (!fs.existsSync(dir)) {
|
|
160
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
161
|
+
}
|
|
162
|
+
fs.writeFileSync(this.commitFile, commit);
|
|
163
|
+
logger.debug('Saved commit hash: ' + commit.slice(0, 7));
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
logger.debug('Failed to save commit: ' + (error instanceof Error ? error.message : String(error)));
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
async updateBinary(remoteCommit) {
|
|
170
|
+
const totalSteps = 3;
|
|
171
|
+
const isFirstRun = !this.getSavedCommit();
|
|
172
|
+
try {
|
|
173
|
+
const statusType = isFirstRun ? 'first_run' : 'updating';
|
|
174
|
+
this.emitStatus({ type: statusType, step: 1, totalSteps, message: 'Downloading update...' });
|
|
175
|
+
const parentDir = path.dirname(this.repoDir);
|
|
176
|
+
if (!fs.existsSync(parentDir)) {
|
|
177
|
+
fs.mkdirSync(parentDir, { recursive: true });
|
|
178
|
+
}
|
|
179
|
+
await execAsync(`git clone --depth 1 ${this.repoUrl} ${this.repoDir}`);
|
|
180
|
+
this.emitStatus({ type: statusType, step: 2, totalSteps, message: 'Installing update...' });
|
|
181
|
+
const success = await this.copyBinariesInternal();
|
|
182
|
+
if (!success) {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
this.emitStatus({ type: statusType, step: 3, totalSteps, message: 'Finalizing...' });
|
|
186
|
+
await this.cleanupRepo();
|
|
187
|
+
this.saveCommit(remoteCommit);
|
|
188
|
+
const shell = process.env['SHELL'] || '/bin/bash';
|
|
189
|
+
const rcFile = shell.includes('zsh') ? '~/.zshrc' : '~/.bashrc';
|
|
190
|
+
const restartMsg = isFirstRun
|
|
191
|
+
? `Setup complete! Run: source ${rcFile} && lcli`
|
|
192
|
+
: 'Update complete! Please restart.';
|
|
193
|
+
this.emitStatus({ type: 'complete', needsRestart: true, message: restartMsg });
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
logger.error('Binary update failed', error);
|
|
198
|
+
await this.cleanupRepo();
|
|
199
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
200
|
+
this.emitStatus({ type: 'error', message: `Update failed: ${message}` });
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
async initialSetup() {
|
|
205
|
+
logger.enter('initialSetup', {
|
|
206
|
+
repoDir: this.repoDir,
|
|
207
|
+
repoUrl: this.repoUrl
|
|
208
|
+
});
|
|
209
|
+
const totalSteps = 4;
|
|
210
|
+
try {
|
|
211
|
+
this.emitStatus({ type: 'first_run', step: 1, totalSteps, message: 'Cloning repository...' });
|
|
212
|
+
const parentDir = path.dirname(this.repoDir);
|
|
213
|
+
if (!fs.existsSync(parentDir)) {
|
|
214
|
+
fs.mkdirSync(parentDir, { recursive: true });
|
|
215
|
+
}
|
|
216
|
+
await execAsync(`git clone ${this.repoUrl} ${this.repoDir}`);
|
|
217
|
+
this.emitStatus({ type: 'first_run', step: 2, totalSteps, message: 'Installing dependencies...' });
|
|
218
|
+
await execAsync('npm install', { cwd: this.repoDir });
|
|
219
|
+
this.emitStatus({ type: 'first_run', step: 3, totalSteps, message: 'Building project...' });
|
|
220
|
+
await execAsync('npm run build', { cwd: this.repoDir });
|
|
221
|
+
this.emitStatus({ type: 'first_run', step: 4, totalSteps, message: 'Creating global link...' });
|
|
222
|
+
await execAsync('npm link', { cwd: this.repoDir });
|
|
223
|
+
this.emitStatus({ type: 'complete', needsRestart: true, message: 'Setup complete! Please restart.' });
|
|
224
|
+
return true;
|
|
225
|
+
}
|
|
226
|
+
catch (error) {
|
|
227
|
+
logger.error('Initial setup failed', error);
|
|
228
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
229
|
+
this.emitStatus({ type: 'error', message: `Setup failed: ${message}` });
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
async pullAndUpdate() {
|
|
234
|
+
logger.debug('Checking for updates', { repoDir: this.repoDir });
|
|
235
|
+
try {
|
|
236
|
+
await execAsync('git fetch origin main', { cwd: this.repoDir });
|
|
237
|
+
const currentResult = await execAsync('git rev-parse HEAD', { cwd: this.repoDir });
|
|
238
|
+
const latestResult = await execAsync('git rev-parse origin/main', { cwd: this.repoDir });
|
|
239
|
+
const currentCommit = currentResult.stdout.trim();
|
|
240
|
+
const latestCommit = latestResult.stdout.trim();
|
|
241
|
+
if (currentCommit === latestCommit) {
|
|
242
|
+
logger.debug('Already up to date, no rebuild needed');
|
|
243
|
+
this.emitStatus({ type: 'no_update' });
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
logger.debug('Resetting to latest commit...', { from: currentCommit.slice(0, 7), to: latestCommit.slice(0, 7) });
|
|
247
|
+
await execAsync('git reset --hard origin/main', { cwd: this.repoDir });
|
|
248
|
+
return await this.rebuildAndLink();
|
|
249
|
+
}
|
|
250
|
+
catch (error) {
|
|
251
|
+
logger.error('Pull/reset failed, attempting fresh clone', error);
|
|
252
|
+
return await this.freshClone();
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
async freshClone() {
|
|
256
|
+
logger.flow('Performing fresh clone');
|
|
257
|
+
try {
|
|
258
|
+
this.emitStatus({ type: 'updating', step: 1, totalSteps: 4, message: 'Removing old repository...' });
|
|
259
|
+
await rm(this.repoDir, { recursive: true, force: true });
|
|
260
|
+
return await this.initialSetup();
|
|
261
|
+
}
|
|
262
|
+
catch (error) {
|
|
263
|
+
logger.error('Fresh clone failed', error);
|
|
264
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
265
|
+
this.emitStatus({ type: 'error', message: `Fresh clone failed: ${message}` });
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
async rebuildAndLink() {
|
|
270
|
+
const totalSteps = 3;
|
|
271
|
+
try {
|
|
272
|
+
this.emitStatus({ type: 'updating', step: 1, totalSteps, message: 'Updating dependencies...' });
|
|
273
|
+
await execAsync('npm install', { cwd: this.repoDir });
|
|
274
|
+
this.emitStatus({ type: 'updating', step: 2, totalSteps, message: 'Building project...' });
|
|
275
|
+
await execAsync('npm run build', { cwd: this.repoDir });
|
|
276
|
+
this.emitStatus({ type: 'updating', step: 3, totalSteps, message: 'Updating global link...' });
|
|
277
|
+
await execAsync('npm link', { cwd: this.repoDir });
|
|
278
|
+
this.emitStatus({ type: 'complete', needsRestart: true, message: 'Update complete! Please restart.' });
|
|
279
|
+
return true;
|
|
280
|
+
}
|
|
281
|
+
catch (buildError) {
|
|
282
|
+
logger.error('Build/link failed', buildError);
|
|
283
|
+
const message = buildError instanceof Error ? buildError.message : 'Unknown error';
|
|
284
|
+
this.emitStatus({ type: 'error', message: `Build failed: ${message}` });
|
|
285
|
+
return false;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
async copyBinariesInternal() {
|
|
289
|
+
try {
|
|
290
|
+
const repoBinDir = path.join(this.repoDir, 'bin');
|
|
291
|
+
const installDir = path.join(os.homedir(), '.local', 'bin');
|
|
292
|
+
const lcliGzSrc = path.join(repoBinDir, 'lcli.gz');
|
|
293
|
+
const yogaSrc = path.join(repoBinDir, 'yoga.wasm');
|
|
294
|
+
const lcliDest = path.join(installDir, 'lcli');
|
|
295
|
+
const yogaDest = path.join(installDir, 'yoga.wasm');
|
|
296
|
+
if (!fs.existsSync(lcliGzSrc)) {
|
|
297
|
+
this.emitStatus({ type: 'error', message: 'Binary not found in repository (bin/lcli.gz)' });
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
if (!fs.existsSync(installDir)) {
|
|
301
|
+
fs.mkdirSync(installDir, { recursive: true });
|
|
302
|
+
}
|
|
303
|
+
await rm(lcliDest, { force: true });
|
|
304
|
+
await pipeline(createReadStream(lcliGzSrc), zlib.createGunzip(), createWriteStream(lcliDest));
|
|
305
|
+
await chmod(lcliDest, 0o755);
|
|
306
|
+
if (fs.existsSync(yogaSrc)) {
|
|
307
|
+
await copyFile(yogaSrc, yogaDest);
|
|
308
|
+
}
|
|
309
|
+
await this.ensurePathConfigured(installDir);
|
|
310
|
+
await this.unlinkNpm();
|
|
311
|
+
logger.debug('Binaries copied successfully');
|
|
312
|
+
return true;
|
|
313
|
+
}
|
|
314
|
+
catch (error) {
|
|
315
|
+
logger.error('Copy binaries internal failed', error);
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
async ensurePathConfigured(binDir) {
|
|
320
|
+
const pathExport = `export PATH="${binDir}:$PATH"`;
|
|
321
|
+
const marker = '# local-cli binary';
|
|
322
|
+
const shell = process.env['SHELL'] || '/bin/bash';
|
|
323
|
+
let rcFile;
|
|
324
|
+
if (shell.includes('zsh')) {
|
|
325
|
+
rcFile = path.join(os.homedir(), '.zshrc');
|
|
326
|
+
}
|
|
327
|
+
else {
|
|
328
|
+
rcFile = path.join(os.homedir(), '.bashrc');
|
|
329
|
+
}
|
|
330
|
+
try {
|
|
331
|
+
let content = '';
|
|
332
|
+
if (fs.existsSync(rcFile)) {
|
|
333
|
+
content = fs.readFileSync(rcFile, 'utf-8');
|
|
334
|
+
}
|
|
335
|
+
if (content.includes(marker)) {
|
|
336
|
+
logger.debug('PATH already configured (marker found)');
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
const currentPath = process.env['PATH'] || '';
|
|
340
|
+
if (currentPath.split(':').includes(binDir)) {
|
|
341
|
+
logger.debug('PATH already contains binDir, adding marker for future reference');
|
|
342
|
+
fs.appendFileSync(rcFile, `\n${marker}\n# PATH already configured elsewhere\n`);
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
const addition = `\n${marker}\n${pathExport}\n`;
|
|
346
|
+
fs.appendFileSync(rcFile, addition);
|
|
347
|
+
logger.debug('PATH configuration added to ' + rcFile);
|
|
348
|
+
}
|
|
349
|
+
catch (error) {
|
|
350
|
+
logger.debug('Failed to configure PATH: ' + (error instanceof Error ? error.message : String(error)));
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
async unlinkNpm() {
|
|
354
|
+
try {
|
|
355
|
+
await execAsync('npm unlink -g local-cli');
|
|
356
|
+
}
|
|
357
|
+
catch (error) {
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
async cleanupRepo() {
|
|
361
|
+
try {
|
|
362
|
+
if (fs.existsSync(this.repoDir)) {
|
|
363
|
+
logger.debug('Cleaning up repo directory to prevent source code exposure');
|
|
364
|
+
await rm(this.repoDir, { recursive: true, force: true });
|
|
365
|
+
logger.debug('Repo directory removed successfully');
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
catch (error) {
|
|
369
|
+
logger.debug('Failed to cleanup repo: ' + (error instanceof Error ? error.message : String(error)));
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
export default GitAutoUpdater;
|
|
374
|
+
//# sourceMappingURL=git-auto-updater.js.map
|
|
@@ -6,6 +6,7 @@ import { fullSync } from '../orquesta/config-sync.js';
|
|
|
6
6
|
import { readHookConfig, writeHookFiles, disableHooks } from '../orquesta/hook-init.js';
|
|
7
7
|
import { checkForCliUpdate, runCliUpdate, setSkippedVersion } from '../utils/update-checker.js';
|
|
8
8
|
import { createRequire } from 'module';
|
|
9
|
+
import { APP_VERSION } from '../constants.js';
|
|
9
10
|
import { configManager } from './config/config-manager.js';
|
|
10
11
|
import { getForcedTier, setForcedTier } from './routing-state.js';
|
|
11
12
|
import { auditLog } from '../orchestration/audit-log.js';
|
|
@@ -491,7 +492,7 @@ ${executorLines}
|
|
|
491
492
|
context.setMessages(updatedMessages);
|
|
492
493
|
return { handled: true, shouldContinue: false, updatedContext: { messages: updatedMessages } };
|
|
493
494
|
};
|
|
494
|
-
let currentVersion =
|
|
495
|
+
let currentVersion = APP_VERSION;
|
|
495
496
|
try {
|
|
496
497
|
const require = createRequire(import.meta.url);
|
|
497
498
|
currentVersion = require('../../package.json').version;
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { createRequire } from 'module';
|
|
2
|
+
import { APP_VERSION } from './constants.js';
|
|
2
3
|
const require = createRequire(import.meta.url);
|
|
3
|
-
|
|
4
|
+
let packageJson;
|
|
5
|
+
try {
|
|
6
|
+
packageJson = require('../package.json');
|
|
7
|
+
}
|
|
8
|
+
catch {
|
|
9
|
+
packageJson = { version: APP_VERSION, name: 'orquesta-cli' };
|
|
10
|
+
}
|
|
4
11
|
export const version = packageJson.version;
|
|
5
12
|
export const name = packageJson.name;
|
|
6
13
|
//# sourceMappingURL=index.js.map
|
|
@@ -5,7 +5,14 @@ import { createRequire } from 'module';
|
|
|
5
5
|
import { toolRegistry } from '../registry.js';
|
|
6
6
|
import { loadMcpServerConfigs } from './mcp-config.js';
|
|
7
7
|
import { logger } from '../../utils/logger.js';
|
|
8
|
-
|
|
8
|
+
import { APP_VERSION } from '../../constants.js';
|
|
9
|
+
let pkg;
|
|
10
|
+
try {
|
|
11
|
+
pkg = createRequire(import.meta.url)('../../../package.json');
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
pkg = { version: APP_VERSION };
|
|
15
|
+
}
|
|
9
16
|
const CONNECT_TIMEOUT_MS = Number(process.env['ORQUESTA_MCP_TIMEOUT_MS']) || 15000;
|
|
10
17
|
const CALL_TIMEOUT_MS = Number(process.env['ORQUESTA_MCP_CALL_TIMEOUT_MS']) || 120000;
|
|
11
18
|
const connected = [];
|
package/dist/ui/TodoPanel.js
CHANGED
|
@@ -91,7 +91,7 @@ export const TodoPanel = React.memo(({ todos, currentTodoId, isProcessing = fals
|
|
|
91
91
|
React.createElement(Box, null,
|
|
92
92
|
React.createElement(Box, { width: 2 }, isInProgress && isProcessing ? (React.createElement(Text, { color: "blueBright" },
|
|
93
93
|
React.createElement(Spinner, { type: "dots2" }))) : (React.createElement(Text, { color: config.color }, config.icon))),
|
|
94
|
-
React.createElement(Text, { color: isCompleted ? 'gray' : isInProgress ? 'white' : 'gray', bold: isInProgress,
|
|
94
|
+
React.createElement(Text, { color: isCompleted ? 'gray' : isInProgress ? 'white' : 'gray', bold: isInProgress, strikethrough: isCompleted }, todo.title),
|
|
95
95
|
isInProgress && isProcessing && (React.createElement(Text, { color: "blueBright" }, " \u2190"))),
|
|
96
96
|
todo.error && (React.createElement(Box, { marginLeft: 2 },
|
|
97
97
|
React.createElement(Text, { color: "red", dimColor: true },
|
|
@@ -86,9 +86,9 @@ export const ActivityIndicator = ({ activity, startTime, detail, subActivities =
|
|
|
86
86
|
formatTime(elapsedSeconds)),
|
|
87
87
|
renderProgressBar()),
|
|
88
88
|
detail && (React.createElement(Box, { marginLeft: 2 },
|
|
89
|
-
React.createElement(Text, { color: "
|
|
89
|
+
React.createElement(Text, { color: "cyan" }, detail))),
|
|
90
90
|
stepName && (React.createElement(Box, { marginLeft: 2 },
|
|
91
|
-
React.createElement(Text, { color: "gray"
|
|
91
|
+
React.createElement(Text, { color: "gray" },
|
|
92
92
|
currentStep,
|
|
93
93
|
"/",
|
|
94
94
|
totalSteps,
|
|
@@ -98,15 +98,15 @@ export const ActivityIndicator = ({ activity, startTime, detail, subActivities =
|
|
|
98
98
|
const subInfo = ACTIVITY_INFO[sub.type];
|
|
99
99
|
return (React.createElement(Box, { key: idx },
|
|
100
100
|
getStatusIcon(sub.status),
|
|
101
|
-
React.createElement(Text, { color: "gray"
|
|
101
|
+
React.createElement(Text, { color: "gray" },
|
|
102
102
|
" ",
|
|
103
103
|
subInfo.label),
|
|
104
|
-
sub.detail && React.createElement(Text, { color: "
|
|
104
|
+
sub.detail && React.createElement(Text, { color: "white" },
|
|
105
105
|
": ",
|
|
106
106
|
sub.detail)));
|
|
107
107
|
}))),
|
|
108
108
|
tokenCount !== undefined && (React.createElement(Box, { marginLeft: 2 },
|
|
109
|
-
React.createElement(Text, { color: "gray"
|
|
109
|
+
React.createElement(Text, { color: "gray" },
|
|
110
110
|
formatTokens(tokenCount),
|
|
111
111
|
" tokens",
|
|
112
112
|
tokensPerSecond !== undefined && tokensPerSecond > 0 && ` · ${tokensPerSecond.toFixed(0)} tok/s`)))));
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useState, useEffect, useRef } from 'react';
|
|
2
2
|
import { Box, Text, useStdin } from 'ink';
|
|
3
3
|
const MULTILINE_COLLAPSE_THRESHOLD = 10;
|
|
4
|
-
export const CustomTextInput = ({ value, onChange, onSubmit, onHistoryPrev, onHistoryNext, placeholder = '', focus = true, }) => {
|
|
4
|
+
export const CustomTextInput = ({ value, onChange, onSubmit, onCtrlS, onHistoryPrev, onHistoryNext, placeholder = '', focus = true, }) => {
|
|
5
5
|
const { stdin, setRawMode } = useStdin();
|
|
6
6
|
const [cursorPosition, setCursorPosition] = useState(value.length);
|
|
7
7
|
const previousValueLength = useRef(value.length);
|
|
@@ -10,6 +10,7 @@ export const CustomTextInput = ({ value, onChange, onSubmit, onHistoryPrev, onHi
|
|
|
10
10
|
const cursorPositionRef = useRef(cursorPosition);
|
|
11
11
|
const onChangeRef = useRef(onChange);
|
|
12
12
|
const onSubmitRef = useRef(onSubmit);
|
|
13
|
+
const onCtrlSRef = useRef(onCtrlS);
|
|
13
14
|
const onHistoryPrevRef = useRef(onHistoryPrev);
|
|
14
15
|
const onHistoryNextRef = useRef(onHistoryNext);
|
|
15
16
|
const setIsCollapsedViewRef = useRef(setIsCollapsedView);
|
|
@@ -19,6 +20,7 @@ export const CustomTextInput = ({ value, onChange, onSubmit, onHistoryPrev, onHi
|
|
|
19
20
|
cursorPositionRef.current = cursorPosition;
|
|
20
21
|
onChangeRef.current = onChange;
|
|
21
22
|
onSubmitRef.current = onSubmit;
|
|
23
|
+
onCtrlSRef.current = onCtrlS;
|
|
22
24
|
onHistoryPrevRef.current = onHistoryPrev;
|
|
23
25
|
onHistoryNextRef.current = onHistoryNext;
|
|
24
26
|
setIsCollapsedViewRef.current = setIsCollapsedView;
|
|
@@ -49,6 +51,10 @@ export const CustomTextInput = ({ value, onChange, onSubmit, onHistoryPrev, onHi
|
|
|
49
51
|
const str = data.toString();
|
|
50
52
|
const currentValue = valueRef.current;
|
|
51
53
|
const currentCursor = cursorPositionRef.current;
|
|
54
|
+
if (str === '\x13') {
|
|
55
|
+
onCtrlSRef.current?.(currentValue);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
52
58
|
const deleteWordBeforeCursor = (value, cursor) => {
|
|
53
59
|
if (cursor > 0) {
|
|
54
60
|
const beforeCursor = value.slice(0, cursor);
|
|
@@ -46,11 +46,17 @@ import { UpdateNotification } from '../UpdateNotification.js';
|
|
|
46
46
|
import { checkForCliUpdate, runCliUpdate, setSkippedVersion } from '../../utils/update-checker.js';
|
|
47
47
|
import { setToolExecutionCallback, setTellToUserCallback, setToolResponseCallback, setPlanCreatedCallback, setTodoStartCallback, setTodoCompleteCallback, setTodoFailCallback, setCompactCallback, setAssistantResponseCallback, setToolApprovalCallback, setReasoningCallback, } from '../../tools/llm/simple/file-tools.js';
|
|
48
48
|
import { createRequire } from 'module';
|
|
49
|
+
import { APP_VERSION } from '../../constants.js';
|
|
49
50
|
const require = createRequire(import.meta.url);
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
let VERSION;
|
|
52
|
+
try {
|
|
53
|
+
VERSION = require('../../../package.json').version;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
VERSION = APP_VERSION;
|
|
57
|
+
}
|
|
52
58
|
const TOOLS_REQUIRING_APPROVAL = new Set(['create_file', 'edit_file', 'bash']);
|
|
53
|
-
const STARTUP_TIP = 'Type /help for
|
|
59
|
+
const STARTUP_TIP = 'Type /help for commands, /model to switch models. Press Ctrl+S to send now (or steer the AI mid-task).';
|
|
54
60
|
function shortenPath(fullPath) {
|
|
55
61
|
const homeDir = os.homedir();
|
|
56
62
|
if (fullPath.startsWith(homeDir)) {
|
|
@@ -185,6 +191,7 @@ export const PlanExecuteApp = ({ llmClient: initialLlmClient, modelInfo, resumeL
|
|
|
185
191
|
const fileBrowserState = useFileBrowserState(input, isProcessing);
|
|
186
192
|
const commandBrowserState = useCommandBrowserState(input, isProcessing);
|
|
187
193
|
const planExecutionState = usePlanExecution(pendingMessageCallbacks);
|
|
194
|
+
const dialogAwaitingInput = !!planExecutionState.askUserRequest || !!pendingToolApproval;
|
|
188
195
|
useEffect(() => {
|
|
189
196
|
sessionManager.setTodos(planExecutionState.todos);
|
|
190
197
|
}, [planExecutionState.todos]);
|
|
@@ -457,6 +464,9 @@ export const PlanExecuteApp = ({ llmClient: initialLlmClient, modelInfo, resumeL
|
|
|
457
464
|
if (!isProcessing) {
|
|
458
465
|
return;
|
|
459
466
|
}
|
|
467
|
+
if (dialogAwaitingInput) {
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
460
470
|
const interval = setInterval(() => {
|
|
461
471
|
const sessionUsage = usageTracker.getSessionUsage();
|
|
462
472
|
setSessionTokens(prev => (prev === sessionUsage.totalTokens ? prev : sessionUsage.totalTokens));
|
|
@@ -464,7 +474,7 @@ export const PlanExecuteApp = ({ llmClient: initialLlmClient, modelInfo, resumeL
|
|
|
464
474
|
setSessionElapsed(prev => (prev === elapsed ? prev : elapsed));
|
|
465
475
|
}, 1000);
|
|
466
476
|
return () => clearInterval(interval);
|
|
467
|
-
}, [isProcessing]);
|
|
477
|
+
}, [isProcessing, dialogAwaitingInput]);
|
|
468
478
|
useEffect(() => {
|
|
469
479
|
const initialize = async () => {
|
|
470
480
|
logger.flow('Starting initialization');
|
|
@@ -640,30 +650,33 @@ export const PlanExecuteApp = ({ llmClient: initialLlmClient, modelInfo, resumeL
|
|
|
640
650
|
if (key.ctrl && inputChar === 'o') {
|
|
641
651
|
setShowLogFiles(prev => !prev);
|
|
642
652
|
}
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
}
|
|
653
|
-
const text = input.trim();
|
|
654
|
-
if (text) {
|
|
655
|
-
if (isProcessing) {
|
|
656
|
-
addLog({ type: 'user_input', content: `(⚡ steering now — picked up at next step) ${text}` });
|
|
657
|
-
setPendingUserMessage(text);
|
|
658
|
-
setInput('');
|
|
659
|
-
}
|
|
660
|
-
else {
|
|
661
|
-
handleSubmitRef.current?.(text);
|
|
662
|
-
}
|
|
663
|
-
}
|
|
653
|
+
});
|
|
654
|
+
const handleCtrlS = useCallback((rawValue) => {
|
|
655
|
+
if (showSessionBrowser ||
|
|
656
|
+
showSettings ||
|
|
657
|
+
showSetupWizard ||
|
|
658
|
+
fileBrowserState.showFileBrowser ||
|
|
659
|
+
commandBrowserState.showCommandBrowser ||
|
|
660
|
+
planExecutionState.askUserRequest ||
|
|
661
|
+
pendingToolApproval) {
|
|
664
662
|
return;
|
|
665
663
|
}
|
|
666
|
-
|
|
664
|
+
const text = (rawValue ?? '').trim();
|
|
665
|
+
if (!text)
|
|
666
|
+
return;
|
|
667
|
+
if (isProcessing) {
|
|
668
|
+
addLog({ type: 'user_input', content: `(⚡ Ctrl+S — steering now, picked up at next step) ${text}` });
|
|
669
|
+
setPendingUserMessage(text);
|
|
670
|
+
setInput('');
|
|
671
|
+
}
|
|
672
|
+
else {
|
|
673
|
+
handleSubmitRef.current?.(text);
|
|
674
|
+
}
|
|
675
|
+
}, [
|
|
676
|
+
showSessionBrowser, showSettings, showSetupWizard,
|
|
677
|
+
fileBrowserState.showFileBrowser, commandBrowserState.showCommandBrowser,
|
|
678
|
+
planExecutionState.askUserRequest, pendingToolApproval, isProcessing, addLog,
|
|
679
|
+
]);
|
|
667
680
|
const handleFileSelect = useCallback((filePaths) => {
|
|
668
681
|
logger.debug('File selected', { filePaths });
|
|
669
682
|
const newInput = fileBrowserState.handleFileSelect(filePaths, input);
|
|
@@ -1348,6 +1361,7 @@ export const PlanExecuteApp = ({ llmClient: initialLlmClient, modelInfo, resumeL
|
|
|
1348
1361
|
: reason;
|
|
1349
1362
|
return (React.createElement(Box, { key: entry.id, flexDirection: "column", marginTop: 1 },
|
|
1350
1363
|
React.createElement(Box, null,
|
|
1364
|
+
React.createElement(Text, { color: "gray", dimColor: true }, "\u2502 "),
|
|
1351
1365
|
React.createElement(Text, { color: "cyan", bold: true },
|
|
1352
1366
|
icon,
|
|
1353
1367
|
" ",
|
|
@@ -1421,9 +1435,11 @@ export const PlanExecuteApp = ({ llmClient: initialLlmClient, modelInfo, resumeL
|
|
|
1421
1435
|
". ",
|
|
1422
1436
|
item))))));
|
|
1423
1437
|
case 'todo_start':
|
|
1424
|
-
return (React.createElement(Box, { key: entry.id, marginTop: 1 },
|
|
1425
|
-
React.createElement(Text, { color: "
|
|
1426
|
-
React.createElement(
|
|
1438
|
+
return (React.createElement(Box, { key: entry.id, flexDirection: "column", marginTop: 1 },
|
|
1439
|
+
React.createElement(Text, { color: "gray", dimColor: true }, '─'.repeat(44)),
|
|
1440
|
+
React.createElement(Box, null,
|
|
1441
|
+
React.createElement(Text, { color: "blue", bold: true }, "\uD83C\uDFB5 \u25B6 "),
|
|
1442
|
+
React.createElement(Text, { bold: true }, entry.content))));
|
|
1427
1443
|
case 'todo_complete':
|
|
1428
1444
|
return (React.createElement(Box, { key: entry.id, marginLeft: 2 },
|
|
1429
1445
|
React.createElement(Text, { color: "white" }, "\u23BF "),
|
|
@@ -1485,11 +1501,11 @@ export const PlanExecuteApp = ({ llmClient: initialLlmClient, modelInfo, resumeL
|
|
|
1485
1501
|
React.createElement(Static, { key: resizeTick, items: logEntries }, (entry) => renderLogEntry(entry)),
|
|
1486
1502
|
pendingToolApproval && (React.createElement(Box, { marginY: 1 },
|
|
1487
1503
|
React.createElement(ApprovalDialog, { toolName: pendingToolApproval.toolName, args: pendingToolApproval.args, reason: pendingToolApproval.reason, onResponse: handleApprovalResponse }))),
|
|
1488
|
-
isProcessing && !pendingToolApproval && !isDocsSearching && (React.createElement(Box, { marginY: 1, flexDirection: "column" },
|
|
1504
|
+
isProcessing && !pendingToolApproval && !isDocsSearching && !planExecutionState.askUserRequest && (React.createElement(Box, { marginY: 1, flexDirection: "column" },
|
|
1489
1505
|
React.createElement(ActivityIndicator, { activity: getCurrentActivityType(), startTime: activityStartTime, detail: activityDetail || currentToolName || '', subActivities: subActivities, modelName: currentModelInfo.model }),
|
|
1490
1506
|
streamingText && (React.createElement(StreamingOutput, { text: streamingText })))),
|
|
1491
1507
|
isDocsSearching && (React.createElement(DocsSearchProgress, { logs: docsSearchLogs, isSearching: isDocsSearching })),
|
|
1492
|
-
planExecutionState.todos.length > 0 && (React.createElement(Box, { marginTop:
|
|
1508
|
+
planExecutionState.todos.length > 0 && (React.createElement(Box, { marginTop: 1 },
|
|
1493
1509
|
React.createElement(TodoPanel, { todos: planExecutionState.todos, currentTodoId: planExecutionState.currentTodoId, isProcessing: isProcessing }))),
|
|
1494
1510
|
updatePhase === 'available' && (React.createElement(UpdateNotification, { currentVersion: VERSION, latestVersion: updateLatest })),
|
|
1495
1511
|
updatePhase === 'updating' && (React.createElement(UpdateNotification, { currentVersion: VERSION, latestVersion: updateLatest, isUpdating: true, updateProgress: updateProgress })),
|
|
@@ -1508,7 +1524,7 @@ export const PlanExecuteApp = ({ llmClient: initialLlmClient, modelInfo, resumeL
|
|
|
1508
1524
|
return;
|
|
1509
1525
|
}
|
|
1510
1526
|
setInput(value);
|
|
1511
|
-
}, onSubmit: handleSubmit, onHistoryPrev: isAnyPanelOpen ? undefined : handleHistoryPrev, onHistoryNext: isAnyPanelOpen ? undefined : handleHistoryNext, placeholder: isProcessing
|
|
1527
|
+
}, onSubmit: handleSubmit, onCtrlS: handleCtrlS, onHistoryPrev: isAnyPanelOpen ? undefined : handleHistoryPrev, onHistoryNext: isAnyPanelOpen ? undefined : handleHistoryNext, placeholder: isProcessing
|
|
1512
1528
|
? "AI is working... (type + Ctrl+S to steer now)"
|
|
1513
1529
|
: showSessionBrowser
|
|
1514
1530
|
? "Select a session or press ESC..."
|
|
@@ -1518,6 +1534,9 @@ export const PlanExecuteApp = ({ llmClient: initialLlmClient, modelInfo, resumeL
|
|
|
1518
1534
|
? "Select a doc source or press ESC..."
|
|
1519
1535
|
: "Type your message... (@ files, / commands, Alt+Enter newline, Ctrl+S send)", focus: !showSessionBrowser && !showSettings && !showDocsBrowser && !planExecutionState.askUserRequest && updatePhase === 'hidden' })),
|
|
1520
1536
|
input.length > 0 && (React.createElement(Text, { color: input.length > 4000 ? 'red' : input.length > 2000 ? 'yellow' : 'gray', dimColor: true }, input.length.toLocaleString())))),
|
|
1537
|
+
isProcessing && input.trim().length > 0 && (React.createElement(Box, { paddingX: 1 },
|
|
1538
|
+
React.createElement(Text, { color: "yellow", bold: true }, "\u26A1 Ctrl+S"),
|
|
1539
|
+
React.createElement(Text, { color: "gray" }, " to send now, or Enter to queue for the next step"))),
|
|
1521
1540
|
fileBrowserState.showFileBrowser && !isProcessing && (React.createElement(Box, { marginTop: 0 }, fileBrowserState.isLoadingFiles ? (React.createElement(Box, { borderStyle: "single", borderColor: "yellow", paddingX: 1 },
|
|
1522
1541
|
React.createElement(Spinner, { type: "dots" }),
|
|
1523
1542
|
React.createElement(Text, { color: "yellow" }, " Loading files..."))) : (React.createElement(FileBrowser, { filter: fileBrowserState.filterText, onSelect: handleFileSelect, onCancel: fileBrowserState.handleFileBrowserCancel, cachedFiles: fileBrowserState.cachedFileList })))),
|
|
@@ -1541,8 +1560,7 @@ export const PlanExecuteApp = ({ llmClient: initialLlmClient, modelInfo, resumeL
|
|
|
1541
1560
|
React.createElement(Text, { color: "cyan" },
|
|
1542
1561
|
React.createElement(Spinner, { type: "shark" })))) : null,
|
|
1543
1562
|
React.createElement(Box, null,
|
|
1544
|
-
React.createElement(Text, { color: "magenta" },
|
|
1545
|
-
React.createElement(Spinner, { type: "star" })),
|
|
1563
|
+
React.createElement(Text, { color: "magenta" }, dialogAwaitingInput ? '✶' : React.createElement(Spinner, { type: "star" })),
|
|
1546
1564
|
React.createElement(Text, { color: "white" },
|
|
1547
1565
|
' ',
|
|
1548
1566
|
getStatusText({
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Box, Text } from 'ink';
|
|
3
|
-
export const StreamingOutput = ({ text }) => {
|
|
3
|
+
export const StreamingOutput = ({ text, height = 3 }) => {
|
|
4
4
|
if (!text)
|
|
5
5
|
return null;
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
const tail = text.split('\n').slice(-height);
|
|
7
|
+
const pad = Array(Math.max(0, height - tail.length)).fill('');
|
|
8
|
+
const rows = [...pad, ...tail];
|
|
9
|
+
return (React.createElement(Box, { paddingX: 1, flexDirection: "column", height: height }, rows.map((line, i) => (React.createElement(Text, { key: i, wrap: "truncate-end" }, line || ' ')))));
|
|
8
10
|
};
|
|
9
11
|
//# sourceMappingURL=StreamingOutput.js.map
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { useState, useEffect, useCallback } from 'react';
|
|
2
2
|
import { Box, Text, useInput } from 'ink';
|
|
3
3
|
import TextInput from 'ink-text-input';
|
|
4
|
+
import { APP_VERSION } from '../../../constants.js';
|
|
4
5
|
import SelectInput from 'ink-select-input';
|
|
5
6
|
import { configManager } from '../../../core/config/config-manager.js';
|
|
6
7
|
import { sessionManager } from '../../../core/session/session-manager.js';
|
|
@@ -60,7 +61,7 @@ export const SettingsBrowser = ({ currentPlanningMode: _currentPlanningMode, onP
|
|
|
60
61
|
const endpoint = configManager.getCurrentEndpoint();
|
|
61
62
|
const model = configManager.getCurrentModel();
|
|
62
63
|
const cwd = process.cwd();
|
|
63
|
-
let version =
|
|
64
|
+
let version = APP_VERSION;
|
|
64
65
|
try {
|
|
65
66
|
const { readFile } = await import('fs/promises');
|
|
66
67
|
const { fileURLToPath } = await import('url');
|
package/dist/ui/ink-entry.js
CHANGED
|
@@ -1,13 +1,30 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import React from 'react';
|
|
3
|
+
import { execSync } from 'child_process';
|
|
3
4
|
import { render } from 'ink';
|
|
4
5
|
import { PlanExecuteApp } from './components/PlanExecuteApp.js';
|
|
5
6
|
import { createLLMClient } from '../core/llm/llm-client.js';
|
|
6
7
|
import { configManager } from '../core/config/config-manager.js';
|
|
7
8
|
import { initializeOptionalTools } from '../tools/registry.js';
|
|
8
9
|
import { logger } from '../utils/logger.js';
|
|
10
|
+
function disableFlowControl() {
|
|
11
|
+
if (process.platform === 'win32' || !process.stdin.isTTY)
|
|
12
|
+
return;
|
|
13
|
+
try {
|
|
14
|
+
execSync('stty -ixon', { stdio: 'inherit' });
|
|
15
|
+
process.on('exit', () => {
|
|
16
|
+
try {
|
|
17
|
+
execSync('stty ixon', { stdio: 'inherit' });
|
|
18
|
+
}
|
|
19
|
+
catch { }
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
}
|
|
24
|
+
}
|
|
9
25
|
(async () => {
|
|
10
26
|
try {
|
|
27
|
+
disableFlowControl();
|
|
11
28
|
await configManager.initialize();
|
|
12
29
|
await initializeOptionalTools();
|
|
13
30
|
const llmClient = createLLMClient();
|