osai-agent 4.2.11 → 4.2.13
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 +1 -1
- package/src/agent/loop/tool-executor.js +45 -1
- package/src/agent/react-loop.js +2 -0
- package/src/tools/local.js +14 -2
package/package.json
CHANGED
|
@@ -235,6 +235,14 @@ export default {
|
|
|
235
235
|
}
|
|
236
236
|
}
|
|
237
237
|
|
|
238
|
+
// Sudo password prompt
|
|
239
|
+
if (tool === TOOLS.LOCAL_CMD && /^sudo\b/.test(toolCall.cmd?.trim()) && !this.isSubagent) {
|
|
240
|
+
if (!this.sudoPassword) {
|
|
241
|
+
this.onMarkdown('\n_This command requires sudo. Enter your password:_\n');
|
|
242
|
+
this.sudoPassword = await this._readPassword(' sudo password: ');
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
238
246
|
const safety = checkSafety(toolCall, this.mode);
|
|
239
247
|
|
|
240
248
|
if ((safety.tier === SAFETY_TIERS.READ && this.noConfirm) || !safety.requiresConfirmation) {
|
|
@@ -410,7 +418,7 @@ export default {
|
|
|
410
418
|
|
|
411
419
|
case TOOLS.LOCAL_CMD:
|
|
412
420
|
default:
|
|
413
|
-
return await executeLocal(toolCall.cmd || '');
|
|
421
|
+
return await executeLocal(toolCall.cmd || '', this.sudoPassword || null);
|
|
414
422
|
}
|
|
415
423
|
} catch (error) {
|
|
416
424
|
logger.error('Tool dispatch error', { tool, error: error.message });
|
|
@@ -428,6 +436,42 @@ export default {
|
|
|
428
436
|
});
|
|
429
437
|
},
|
|
430
438
|
|
|
439
|
+
_readPassword(prompt) {
|
|
440
|
+
return new Promise((resolve) => {
|
|
441
|
+
const input = this.readline?.input || process.stdin;
|
|
442
|
+
const output = this.readline?.output || process.stdout;
|
|
443
|
+
|
|
444
|
+
output.write(prompt);
|
|
445
|
+
let password = '';
|
|
446
|
+
|
|
447
|
+
const onData = (data) => {
|
|
448
|
+
const char = String(data);
|
|
449
|
+
|
|
450
|
+
if (char === '\r' || char === '\n') {
|
|
451
|
+
input.removeListener('data', onData);
|
|
452
|
+
output.write('\n');
|
|
453
|
+
resolve(password);
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
if (char === '\x7f' || char === '\b') {
|
|
458
|
+
if (password.length > 0) {
|
|
459
|
+
password = password.slice(0, -1);
|
|
460
|
+
output.write('\b \b');
|
|
461
|
+
}
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
if (char.charCodeAt(0) < 32 && char !== '\t') return;
|
|
466
|
+
|
|
467
|
+
password += char;
|
|
468
|
+
output.write('*');
|
|
469
|
+
};
|
|
470
|
+
|
|
471
|
+
input.on('data', onData);
|
|
472
|
+
});
|
|
473
|
+
},
|
|
474
|
+
|
|
431
475
|
async _confirmExecution(toolCall, safety) {
|
|
432
476
|
const tier = safety.tier || (safety.isDangerous ? SAFETY_TIERS.DANGEROUS : SAFETY_TIERS.WRITE);
|
|
433
477
|
|
package/src/agent/react-loop.js
CHANGED
|
@@ -216,6 +216,7 @@ export class AgentLoop {
|
|
|
216
216
|
|
|
217
217
|
cancel() {
|
|
218
218
|
this._cancelled = true;
|
|
219
|
+
this.sudoPassword = null;
|
|
219
220
|
if (this.abortController) {
|
|
220
221
|
try { this.abortController.abort(); } catch {}
|
|
221
222
|
}
|
|
@@ -236,6 +237,7 @@ export class AgentLoop {
|
|
|
236
237
|
}
|
|
237
238
|
|
|
238
239
|
async cleanup() {
|
|
240
|
+
this.sudoPassword = null;
|
|
239
241
|
if (this.ws) { try { this.ws.close(); } catch {} this.ws = null; }
|
|
240
242
|
if (this.mode === MODES.NETWORK) closeAllConnections();
|
|
241
243
|
}
|
package/src/tools/local.js
CHANGED
|
@@ -194,7 +194,7 @@ export const getSystemInfo = () => ({
|
|
|
194
194
|
/**
|
|
195
195
|
* Execute a local shell command with timeout and safety checks.
|
|
196
196
|
*/
|
|
197
|
-
export const executeLocal = async (command) => {
|
|
197
|
+
export const executeLocal = async (command, sudoPassword = null) => {
|
|
198
198
|
const trimmedCmd = (command || '').trim();
|
|
199
199
|
if (!trimmedCmd) {
|
|
200
200
|
return { success: false, output: '', error: 'Empty command' };
|
|
@@ -214,7 +214,11 @@ export const executeLocal = async (command) => {
|
|
|
214
214
|
logger.debug('Executing local command', { cmd: trimmedCmd, timeout: COMMAND_TIMEOUT });
|
|
215
215
|
|
|
216
216
|
const isWindows = detectOS() === 'windows';
|
|
217
|
-
|
|
217
|
+
let finalCommand = isWindows ? `chcp 65001 >nul 2>&1 && ${trimmedCmd}` : trimmedCmd;
|
|
218
|
+
|
|
219
|
+
if (sudoPassword && /^sudo\b/.test(finalCommand)) {
|
|
220
|
+
finalCommand = finalCommand.replace(/^sudo\b/, 'sudo -S');
|
|
221
|
+
}
|
|
218
222
|
|
|
219
223
|
return await new Promise((resolve) => {
|
|
220
224
|
let output = '';
|
|
@@ -235,6 +239,14 @@ export const executeLocal = async (command) => {
|
|
|
235
239
|
env: { ...process.env, PYTHONIOENCODING: 'utf-8', LANG: 'en_US.UTF-8' },
|
|
236
240
|
});
|
|
237
241
|
|
|
242
|
+
if (sudoPassword) {
|
|
243
|
+
const writePassword = () => {
|
|
244
|
+
child.stdin.write(sudoPassword + '\n');
|
|
245
|
+
child.stdin.end();
|
|
246
|
+
};
|
|
247
|
+
setTimeout(writePassword, 200);
|
|
248
|
+
}
|
|
249
|
+
|
|
238
250
|
const timeoutId = setTimeout(() => {
|
|
239
251
|
timedOut = true;
|
|
240
252
|
try { child.kill('SIGTERM'); } catch {}
|