mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-02-01 22:48:03 +00:00
interactive shell mode
This commit is contained in:
17
package-lock.json
generated
17
package-lock.json
generated
@@ -6772,6 +6772,12 @@
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/nan": {
|
||||
"version": "2.22.2",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.22.2.tgz",
|
||||
"integrity": "sha512-DANghxFkS1plDdRsX0X9pm0Z6SJNN6gBdtXfanwoZ8hooC5gosGFSBGRYHUVPz1asKA/kMRqDRdHrluZ61SpBQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.11",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
||||
@@ -6901,6 +6907,16 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/node-pty": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.0.0.tgz",
|
||||
"integrity": "sha512-wtBMWWS7dFZm/VgqElrTvtfMq4GzJ6+edFI0Y0zyzygUSZMgZdraDUMUhCIvkjhJjme15qWmbyJbtAx4ot4uZA==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"nan": "^2.17.0"
|
||||
}
|
||||
},
|
||||
"node_modules/nopt": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
|
||||
@@ -10344,6 +10360,7 @@
|
||||
"ink-text-input": "^6.0.0",
|
||||
"lowlight": "^3.3.0",
|
||||
"mime-types": "^2.1.4",
|
||||
"node-pty": "^1.0.0",
|
||||
"open": "^10.1.2",
|
||||
"react": "^18.3.1",
|
||||
"read-package-up": "^11.0.0",
|
||||
|
||||
@@ -41,8 +41,9 @@
|
||||
"ink-spinner": "^5.0.0",
|
||||
"ink-text-input": "^6.0.0",
|
||||
"lowlight": "^3.3.0",
|
||||
"open": "^10.1.2",
|
||||
"mime-types": "^2.1.4",
|
||||
"node-pty": "^1.0.0",
|
||||
"open": "^10.1.2",
|
||||
"react": "^18.3.1",
|
||||
"read-package-up": "^11.0.0",
|
||||
"shell-quote": "^1.8.2",
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { spawn } from 'child_process';
|
||||
import type { HistoryItemWithoutId } from '../types.js';
|
||||
import type { exec as ExecType } from 'child_process';
|
||||
import { useCallback } from 'react';
|
||||
@@ -15,6 +14,7 @@ import crypto from 'crypto';
|
||||
import path from 'path';
|
||||
import os from 'os';
|
||||
import fs from 'fs';
|
||||
import pty from 'node-pty';
|
||||
|
||||
/**
|
||||
* Hook to process shell commands (e.g., !ls, $pwd).
|
||||
@@ -115,35 +115,32 @@ export const useShellCommandProcessor = (
|
||||
},
|
||||
);
|
||||
} else {
|
||||
const child = spawn('bash', ['-c', commandToExecute], {
|
||||
const child = pty.spawn('bash', ['-c', commandToExecute], {
|
||||
name: 'xterm-color',
|
||||
cols: process.stdout.columns,
|
||||
rows: Math.min(20, process.stdout.rows),
|
||||
cwd: targetDir,
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
env: process.env,
|
||||
});
|
||||
|
||||
let output = '';
|
||||
const handleOutput = (data: string) => {
|
||||
child.onData((data: string) => {
|
||||
output += data;
|
||||
setPendingHistoryItem({
|
||||
type: 'info',
|
||||
text: output,
|
||||
});
|
||||
};
|
||||
child.stdout.on('data', handleOutput);
|
||||
child.stderr.on('data', handleOutput);
|
||||
|
||||
let error: Error | null = null;
|
||||
child.on('error', (err: Error) => {
|
||||
error = err;
|
||||
setPendingHistoryItem({ type: 'info', text: output });
|
||||
});
|
||||
|
||||
child.on('close', (code, signal) => {
|
||||
const stdinListener = (data: Buffer) => {
|
||||
child.write(data.toString());
|
||||
};
|
||||
process.stdin.on('data', stdinListener);
|
||||
|
||||
child.onExit(({ exitCode, signal }) => {
|
||||
process.stdin.removeListener('data', stdinListener);
|
||||
setPendingHistoryItem(null);
|
||||
|
||||
output = output.trim() || '(Command produced no output)';
|
||||
if (error) {
|
||||
const text = `${error.message.replace(commandToExecute, rawQuery)}\n${output}`;
|
||||
addItemToHistory({ type: 'error', text }, userMessageTimestamp);
|
||||
} else if (code !== 0) {
|
||||
const text = `Command exited with code ${code}\n${output}`;
|
||||
if (exitCode !== 0) {
|
||||
const text = `Command exited with code ${exitCode}\n${output}`;
|
||||
addItemToHistory({ type: 'error', text }, userMessageTimestamp);
|
||||
} else if (signal) {
|
||||
const text = `Command terminated with signal ${signal}\n${output}`;
|
||||
|
||||
@@ -14,4 +14,10 @@
|
||||
(literal "/dev/stdout")
|
||||
(literal "/dev/stderr")
|
||||
(literal "/dev/null")
|
||||
)
|
||||
)
|
||||
|
||||
;; additional permissions for node-pty
|
||||
(allow file-write*
|
||||
(regex #"^/dev/tty.*")
|
||||
(regex #"^/dev/ptmx$")
|
||||
)
|
||||
|
||||
@@ -72,6 +72,8 @@
|
||||
(literal "/dev/stdout")
|
||||
(literal "/dev/stderr")
|
||||
(literal "/dev/null")
|
||||
(regex #"^/dev/tty.*")
|
||||
(regex #"^/dev/ptmx$")
|
||||
)
|
||||
|
||||
;; allow outbound network connections
|
||||
@@ -86,3 +88,10 @@
|
||||
;; enable terminal access required by ink
|
||||
;; fixes setRawMode EPERM failure (at node:tty:81:24)
|
||||
(allow file-ioctl (regex #"^/dev/tty.*"))
|
||||
|
||||
;; additional permissions for node-pty
|
||||
(allow file-write*
|
||||
(regex #"^/dev/tty.*")
|
||||
(regex #"^/dev/ptmx$")
|
||||
)
|
||||
(allow file-ioctl (regex #"^/dev/ptmx$"))
|
||||
|
||||
Reference in New Issue
Block a user