Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ OpenCLI is not only for websites. It can also:
| `OPENCLI_LIVE` | `false` | Set to `1` to keep the automation lease open after an adapter command finishes (useful for inspection). The `--live` flag sets this. |
| `OPENCLI_BROWSER_CONNECT_TIMEOUT` | `30` | Seconds to wait for browser connection |
| `OPENCLI_BROWSER_COMMAND_TIMEOUT` | `60` | Seconds to wait for a single browser command |
| `OPENCLI_GEMINI_ASK_TIMEOUT` | `60` | Default seconds to wait for `opencli gemini ask` responses |
| `OPENCLI_CDP_ENDPOINT` | — | Chrome DevTools Protocol endpoint for remote browser or Electron apps |
| `OPENCLI_CDP_TARGET` | — | Filter CDP targets by URL substring (e.g. `detail.1688.com`) |
| `OPENCLI_VERBOSE` | `false` | Enable verbose logging (`-v` flag also works) |
Expand Down
1 change: 1 addition & 0 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ OpenCLI 不只是网站 CLI,还可以:
| `OPENCLI_LIVE` | `false` | 设为 `1` 时 adapter 命令执行完后保留 automation 窗口不关闭(适合检查页面)。`--live` 标志会设置此变量 |
| `OPENCLI_BROWSER_CONNECT_TIMEOUT` | `30` | 浏览器连接超时(秒) |
| `OPENCLI_BROWSER_COMMAND_TIMEOUT` | `60` | 单个浏览器命令超时(秒) |
| `OPENCLI_GEMINI_ASK_TIMEOUT` | `60` | `opencli gemini ask` 等待回答的默认秒数 |
| `OPENCLI_CDP_ENDPOINT` | — | Chrome DevTools Protocol 端点,用于远程浏览器或 Electron 应用 |
| `OPENCLI_CDP_TARGET` | — | 按 URL 子串过滤 CDP target(如 `detail.1688.com`) |
| `OPENCLI_VERBOSE` | `false` | 启用详细日志(`-v` 也可以) |
Expand Down
5 changes: 2 additions & 3 deletions cli-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6980,9 +6980,8 @@
{
"name": "timeout",
"type": "str",
"default": "60",
"required": false,
"help": "Max seconds to wait (default: 60)"
"help": "Max seconds to wait (default: OPENCLI_GEMINI_ASK_TIMEOUT or 60)"
},
{
"name": "new",
Expand All @@ -6995,7 +6994,7 @@
"columns": [
"response"
],
"timeout": 180,
"timeout": 3600,
"type": "js",
"modulePath": "gemini/ask.js",
"sourceFile": "gemini/ask.js",
Expand Down
12 changes: 9 additions & 3 deletions clis/gemini/ask.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import { cli, Strategy } from '@jackwener/opencli/registry';
import { GEMINI_DOMAIN, readGeminiSnapshot, sendGeminiMessage, startNewGeminiChat, waitForGeminiResponse, waitForGeminiSubmission } from './utils.js';
export function parseGeminiAskTimeout(value, fallback) {
const parsed = parseInt(String(value ?? ''), 10);
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
}
function normalizeBooleanFlag(value) {
if (typeof value === 'boolean')
return value;
const normalized = String(value ?? '').trim().toLowerCase();
return normalized === 'true' || normalized === '1' || normalized === 'yes' || normalized === 'on';
}
export const DEFAULT_GEMINI_ASK_TIMEOUT_SECONDS = parseGeminiAskTimeout(process.env.OPENCLI_GEMINI_ASK_TIMEOUT, 60);
const GEMINI_ASK_COMMAND_TIMEOUT_SECONDS = 3600;
const NO_RESPONSE_PREFIX = '[NO RESPONSE]';
export const askCommand = cli({
site: 'gemini',
Expand All @@ -16,16 +22,16 @@ export const askCommand = cli({
browser: true,
navigateBefore: false,
defaultFormat: 'plain',
timeoutSeconds: 180,
timeoutSeconds: GEMINI_ASK_COMMAND_TIMEOUT_SECONDS,
args: [
{ name: 'prompt', required: true, positional: true, help: 'Prompt to send' },
{ name: 'timeout', required: false, help: 'Max seconds to wait (default: 60)', default: '60' },
{ name: 'timeout', required: false, help: 'Max seconds to wait (default: OPENCLI_GEMINI_ASK_TIMEOUT or 60)' },
{ name: 'new', required: false, help: 'Start a new chat first (true/false, default: false)', default: 'false' },
],
columns: ['response'],
func: async (page, kwargs) => {
const prompt = kwargs.prompt;
const timeout = parseInt(kwargs.timeout, 10) || 60;
const timeout = parseGeminiAskTimeout(kwargs.timeout, DEFAULT_GEMINI_ASK_TIMEOUT_SECONDS);
const startFresh = normalizeBooleanFlag(kwargs.new);
if (startFresh)
await startNewGeminiChat(page);
Expand Down
25 changes: 24 additions & 1 deletion clis/gemini/ask.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ vi.mock('./utils.js', async () => {
waitForGeminiResponse: mocks.waitForGeminiResponse,
};
});
import { askCommand } from './ask.js';
import { DEFAULT_GEMINI_ASK_TIMEOUT_SECONDS, askCommand, parseGeminiAskTimeout } from './ask.js';
function createPageMock() {
return {
goto: vi.fn().mockResolvedValue(undefined),
Expand Down Expand Up @@ -70,6 +70,29 @@ describe('gemini ask orchestration', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('parses positive timeout values and falls back for invalid values', () => {
expect(parseGeminiAskTimeout('120', 60)).toBe(120);
expect(parseGeminiAskTimeout(300, 60)).toBe(300);
expect(parseGeminiAskTimeout('0', 60)).toBe(60);
expect(parseGeminiAskTimeout('-1', 60)).toBe(60);
expect(parseGeminiAskTimeout('not-a-number', 60)).toBe(60);
});
it('uses the environment-backed default timeout when no timeout argument is provided', async () => {
const page = createPageMock();
mocks.readGeminiSnapshot.mockResolvedValueOnce(baseline);
mocks.sendGeminiMessage.mockResolvedValueOnce('button');
mocks.waitForGeminiSubmission.mockResolvedValueOnce(null);
const result = await askCommand.func(page, { prompt: '请只回复:OK', new: 'false' });
expect(mocks.waitForGeminiSubmission).toHaveBeenCalledWith(page, baseline, DEFAULT_GEMINI_ASK_TIMEOUT_SECONDS);
expect(result).toEqual([{ response: `💬 [NO RESPONSE] No Gemini response within ${DEFAULT_GEMINI_ASK_TIMEOUT_SECONDS}s.` }]);
});
it('keeps the outer browser command timeout high enough for long explicit waits', () => {
expect(askCommand.timeoutSeconds).toBeGreaterThanOrEqual(3600);
});
it('leaves timeout without a static default so the runtime env default can apply', () => {
const timeoutArg = askCommand.args.find((arg) => arg.name === 'timeout');
expect(timeoutArg.default).toBeUndefined();
});
it('captures baseline, sends, waits for confirmed submission, then waits with the remaining timeout', async () => {
vi.spyOn(Date, 'now')
.mockReturnValueOnce(0)
Expand Down