@jackwener/opencli 1.7.15 → 1.7.17
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/README.md +15 -13
- package/README.zh-CN.md +15 -12
- package/cli-manifest.json +165 -209
- package/clis/chatgpt/ask.js +3 -2
- package/clis/chatgpt/commands.test.js +2 -2
- package/clis/chatgpt/detail.js +7 -2
- package/clis/chatgpt/history.js +1 -1
- package/clis/chatgpt/image.js +38 -4
- package/clis/chatgpt/image.test.js +68 -1
- package/clis/chatgpt/new.js +1 -1
- package/clis/chatgpt/read.js +3 -2
- package/clis/chatgpt/send.js +3 -2
- package/clis/chatgpt/status.js +1 -1
- package/clis/chatgpt/utils.js +259 -25
- package/clis/chatgpt/utils.test.js +166 -2
- package/clis/claude/ask.js +23 -8
- package/clis/claude/detail.js +10 -3
- package/clis/claude/history.js +1 -1
- package/clis/claude/new.js +9 -3
- package/clis/claude/read.js +3 -2
- package/clis/claude/send.js +9 -4
- package/clis/claude/status.js +1 -1
- package/clis/claude/utils.js +27 -4
- package/clis/deepseek/ask.js +22 -9
- package/clis/deepseek/detail.js +10 -2
- package/clis/deepseek/history.js +1 -1
- package/clis/deepseek/new.js +14 -3
- package/clis/deepseek/read.js +3 -2
- package/clis/deepseek/send.js +1 -1
- package/clis/deepseek/status.js +1 -1
- package/clis/deepseek/utils.js +8 -1
- package/clis/doubao/ask.js +1 -1
- package/clis/doubao/detail.js +1 -1
- package/clis/doubao/history.js +1 -1
- package/clis/doubao/meeting-summary.js +1 -1
- package/clis/doubao/meeting-transcript.js +1 -1
- package/clis/doubao/new.js +1 -1
- package/clis/doubao/read.js +1 -1
- package/clis/doubao/send.js +1 -1
- package/clis/doubao/status.js +1 -1
- package/clis/gemini/ask.js +1 -1
- package/clis/gemini/deep-research-result.js +1 -1
- package/clis/gemini/deep-research.js +1 -1
- package/clis/gemini/image.js +1 -1
- package/clis/gemini/new.js +1 -1
- package/clis/grok/ask.js +1 -1
- package/clis/grok/detail.js +1 -1
- package/clis/grok/history.js +1 -1
- package/clis/grok/image.js +1 -1
- package/clis/grok/new.js +1 -1
- package/clis/grok/read.js +1 -1
- package/clis/grok/send.js +1 -1
- package/clis/grok/status.js +1 -1
- package/clis/linkedin/search.js +8 -11
- package/clis/maimai/search-talents.js +10 -6
- package/clis/notebooklm/current.js +1 -1
- package/clis/notebooklm/get.js +1 -1
- package/clis/notebooklm/history.js +1 -1
- package/clis/notebooklm/note-list.js +1 -1
- package/clis/notebooklm/notes-get.js +1 -1
- package/clis/notebooklm/open.js +2 -2
- package/clis/notebooklm/open.test.js +1 -1
- package/clis/notebooklm/source-fulltext.js +1 -1
- package/clis/notebooklm/source-get.js +1 -1
- package/clis/notebooklm/source-guide.js +1 -1
- package/clis/notebooklm/source-list.js +1 -1
- package/clis/notebooklm/summary.js +1 -1
- package/clis/openreview/author.js +58 -0
- package/clis/openreview/openreview.test.js +83 -1
- package/clis/openreview/utils.js +14 -0
- package/clis/qwen/ask.js +1 -1
- package/clis/qwen/detail.js +1 -1
- package/clis/qwen/history.js +1 -1
- package/clis/qwen/image.js +1 -1
- package/clis/qwen/new.js +1 -1
- package/clis/qwen/read.js +1 -1
- package/clis/qwen/send.js +1 -1
- package/clis/qwen/status.js +1 -1
- package/clis/reddit/comment.js +1 -0
- package/clis/reddit/frontpage.js +1 -0
- package/clis/reddit/popular.js +1 -0
- package/clis/reddit/read.js +2 -0
- package/clis/reddit/read.test.js +4 -0
- package/clis/reddit/save.js +1 -0
- package/clis/reddit/saved.js +1 -0
- package/clis/reddit/search.js +1 -0
- package/clis/reddit/subreddit.js +1 -0
- package/clis/reddit/subscribe.js +1 -0
- package/clis/reddit/upvote.js +1 -0
- package/clis/reddit/upvoted.js +1 -0
- package/clis/reddit/user-comments.js +1 -0
- package/clis/reddit/user-posts.js +1 -0
- package/clis/reddit/user.js +1 -0
- package/clis/twitter/article.js +7 -4
- package/clis/twitter/bookmark-folder.js +3 -5
- package/clis/twitter/bookmark-folder.test.js +5 -2
- package/clis/twitter/bookmark-folders.js +3 -5
- package/clis/twitter/bookmark-folders.test.js +3 -1
- package/clis/twitter/bookmarks.js +3 -5
- package/clis/twitter/download.js +1 -0
- package/clis/twitter/followers.js +1 -0
- package/clis/twitter/following.js +3 -6
- package/clis/twitter/following.test.js +2 -1
- package/clis/twitter/likes.js +3 -5
- package/clis/twitter/list-add.js +4 -3
- package/clis/twitter/list-add.test.js +23 -1
- package/clis/twitter/list-remove.js +4 -3
- package/clis/twitter/list-remove.test.js +23 -1
- package/clis/twitter/list-tweets.js +3 -5
- package/clis/twitter/lists.js +3 -5
- package/clis/twitter/notifications.js +1 -0
- package/clis/twitter/profile.js +7 -4
- package/clis/twitter/search.js +1 -0
- package/clis/twitter/thread.js +5 -7
- package/clis/twitter/timeline.js +5 -7
- package/clis/twitter/trending.js +4 -4
- package/clis/twitter/tweets.js +3 -6
- package/clis/youtube/like.js +6 -2
- package/clis/youtube/subscribe.js +6 -2
- package/clis/youtube/unlike.js +6 -2
- package/clis/youtube/unsubscribe.js +6 -2
- package/clis/youtube/utils.js +19 -13
- package/clis/youtube/utils.test.js +17 -1
- package/clis/yuanbao/ask.js +1 -1
- package/clis/yuanbao/detail.js +1 -1
- package/clis/yuanbao/history.js +1 -1
- package/clis/yuanbao/new.js +1 -1
- package/clis/yuanbao/read.js +1 -1
- package/clis/yuanbao/send.js +1 -1
- package/clis/yuanbao/status.js +1 -1
- package/dist/src/browser/bridge.d.ts +4 -1
- package/dist/src/browser/bridge.js +3 -1
- package/dist/src/browser/cdp.d.ts +4 -1
- package/dist/src/browser/daemon-client.d.ts +9 -16
- package/dist/src/browser/daemon-client.js +8 -9
- package/dist/src/browser/daemon-client.test.js +10 -0
- package/dist/src/browser/network-cache.d.ts +5 -5
- package/dist/src/browser/network-cache.js +8 -8
- package/dist/src/browser/network-cache.test.js +4 -4
- package/dist/src/browser/page.d.ts +9 -7
- package/dist/src/browser/page.js +27 -16
- package/dist/src/browser/page.test.js +60 -30
- package/dist/src/build-manifest.js +1 -1
- package/dist/src/cli.js +91 -125
- package/dist/src/cli.test.js +293 -180
- package/dist/src/commanderAdapter.js +9 -0
- package/dist/src/discovery.js +1 -1
- package/dist/src/doctor.d.ts +0 -4
- package/dist/src/doctor.js +8 -72
- package/dist/src/doctor.test.js +26 -97
- package/dist/src/execution.d.ts +3 -0
- package/dist/src/execution.js +47 -23
- package/dist/src/execution.test.js +68 -45
- package/dist/src/external-clis.yaml +24 -0
- package/dist/src/help.d.ts +1 -0
- package/dist/src/help.js +36 -1
- package/dist/src/main.js +0 -29
- package/dist/src/manifest-types.d.ts +2 -4
- package/dist/src/observation/artifact.js +1 -1
- package/dist/src/observation/artifact.test.js +3 -3
- package/dist/src/observation/events.d.ts +1 -1
- package/dist/src/observation/manager.js +1 -1
- package/dist/src/observation/manager.test.js +3 -3
- package/dist/src/registry-api.d.ts +1 -1
- package/dist/src/registry.d.ts +3 -12
- package/dist/src/registry.js +6 -10
- package/dist/src/runtime.d.ts +10 -2
- package/dist/src/runtime.js +4 -1
- package/dist/src/serialization.d.ts +1 -1
- package/dist/src/serialization.js +1 -1
- package/dist/src/types.d.ts +0 -15
- package/package.json +1 -1
|
@@ -136,7 +136,7 @@ describe('executeCommand — non-browser timeout', () => {
|
|
|
136
136
|
});
|
|
137
137
|
vi.restoreAllMocks();
|
|
138
138
|
});
|
|
139
|
-
it('reuses a site
|
|
139
|
+
it('reuses a persistent site browser session and keeps the tab lease open', async () => {
|
|
140
140
|
const closeWindow = vi.fn().mockResolvedValue(undefined);
|
|
141
141
|
const mockPage = { closeWindow };
|
|
142
142
|
const sessionOpts = [];
|
|
@@ -147,22 +147,24 @@ describe('executeCommand — non-browser timeout', () => {
|
|
|
147
147
|
});
|
|
148
148
|
const cmd = cli({
|
|
149
149
|
site: 'test-execution',
|
|
150
|
-
name: '
|
|
151
|
-
description: 'test site
|
|
150
|
+
name: 'site-session-persistent', access: 'read',
|
|
151
|
+
description: 'test persistent site session',
|
|
152
152
|
browser: true,
|
|
153
153
|
strategy: Strategy.PUBLIC,
|
|
154
|
-
|
|
154
|
+
siteSession: 'persistent',
|
|
155
155
|
func: async () => [{ ok: true }],
|
|
156
156
|
});
|
|
157
157
|
await executeCommand(cmd, {});
|
|
158
|
-
await executeCommand(cmd, {});
|
|
158
|
+
await executeCommand(cmd, {}, false, { keepTab: 'false' });
|
|
159
159
|
expect(sessionOpts).toHaveLength(2);
|
|
160
|
-
expect(sessionOpts[0]).toMatchObject({
|
|
161
|
-
expect(sessionOpts[1]).toMatchObject({
|
|
160
|
+
expect(sessionOpts[0]).toMatchObject({ session: 'site:test-execution', windowMode: 'background', siteSession: 'persistent' });
|
|
161
|
+
expect(sessionOpts[1]).toMatchObject({ session: 'site:test-execution', windowMode: 'background', siteSession: 'persistent' });
|
|
162
|
+
expect(sessionOpts[0]?.idleTimeout).toBeUndefined();
|
|
163
|
+
expect(sessionOpts[1]?.idleTimeout).toBeUndefined();
|
|
162
164
|
expect(closeWindow).not.toHaveBeenCalled();
|
|
163
165
|
vi.restoreAllMocks();
|
|
164
166
|
});
|
|
165
|
-
it('keeps default browser commands on one-shot
|
|
167
|
+
it('keeps default browser commands on one-shot adapter sessions', async () => {
|
|
166
168
|
const closeWindow = vi.fn().mockResolvedValue(undefined);
|
|
167
169
|
const mockPage = { closeWindow };
|
|
168
170
|
const sessionOpts = [];
|
|
@@ -173,8 +175,8 @@ describe('executeCommand — non-browser timeout', () => {
|
|
|
173
175
|
});
|
|
174
176
|
const cmd = cli({
|
|
175
177
|
site: 'test-execution',
|
|
176
|
-
name: '
|
|
177
|
-
description: 'test default one-shot browser
|
|
178
|
+
name: 'site-session-default', access: 'read',
|
|
179
|
+
description: 'test default one-shot browser session',
|
|
178
180
|
browser: true,
|
|
179
181
|
strategy: Strategy.PUBLIC,
|
|
180
182
|
func: async () => [{ ok: true }],
|
|
@@ -182,15 +184,17 @@ describe('executeCommand — non-browser timeout', () => {
|
|
|
182
184
|
await executeCommand(cmd, {});
|
|
183
185
|
await executeCommand(cmd, {});
|
|
184
186
|
expect(sessionOpts).toHaveLength(2);
|
|
185
|
-
expect(sessionOpts[0]?.
|
|
186
|
-
expect(sessionOpts[1]?.
|
|
187
|
-
expect(sessionOpts[0]?.
|
|
187
|
+
expect(sessionOpts[0]?.session).toMatch(/^site:test-execution:/);
|
|
188
|
+
expect(sessionOpts[1]?.session).toMatch(/^site:test-execution:/);
|
|
189
|
+
expect(sessionOpts[0]?.session).not.toBe(sessionOpts[1]?.session);
|
|
188
190
|
expect(sessionOpts[0]?.idleTimeout).toBeUndefined();
|
|
189
191
|
expect(sessionOpts[1]?.idleTimeout).toBeUndefined();
|
|
192
|
+
expect(sessionOpts[0]?.windowMode).toBe('background');
|
|
193
|
+
expect(sessionOpts[1]?.windowMode).toBe('background');
|
|
190
194
|
expect(closeWindow).toHaveBeenCalledTimes(2);
|
|
191
195
|
vi.restoreAllMocks();
|
|
192
196
|
});
|
|
193
|
-
it('lets user --
|
|
197
|
+
it('lets user --site-session ephemeral override adapter persistent metadata', async () => {
|
|
194
198
|
const closeWindow = vi.fn().mockResolvedValue(undefined);
|
|
195
199
|
const mockPage = { closeWindow };
|
|
196
200
|
const sessionOpts = [];
|
|
@@ -199,33 +203,27 @@ describe('executeCommand — non-browser timeout', () => {
|
|
|
199
203
|
sessionOpts.push(opts ?? {});
|
|
200
204
|
return fn(mockPage);
|
|
201
205
|
});
|
|
202
|
-
const prev = process.env.OPENCLI_BROWSER_REUSE;
|
|
203
|
-
process.env.OPENCLI_BROWSER_REUSE = 'none';
|
|
204
206
|
try {
|
|
205
207
|
const cmd = cli({
|
|
206
208
|
site: 'test-execution',
|
|
207
|
-
name: '
|
|
208
|
-
description: 'test user
|
|
209
|
+
name: 'site-session-override-ephemeral', access: 'read',
|
|
210
|
+
description: 'test user site-session override',
|
|
209
211
|
browser: true,
|
|
210
212
|
strategy: Strategy.PUBLIC,
|
|
211
|
-
|
|
213
|
+
siteSession: 'persistent',
|
|
212
214
|
func: async () => [{ ok: true }],
|
|
213
215
|
});
|
|
214
|
-
await executeCommand(cmd, {});
|
|
216
|
+
await executeCommand(cmd, {}, false, { siteSession: 'ephemeral' });
|
|
215
217
|
expect(sessionOpts).toHaveLength(1);
|
|
216
|
-
expect(sessionOpts[0]?.
|
|
218
|
+
expect(sessionOpts[0]?.session).toMatch(/^site:test-execution:/);
|
|
217
219
|
expect(sessionOpts[0]?.idleTimeout).toBeUndefined();
|
|
218
220
|
expect(closeWindow).toHaveBeenCalledTimes(1);
|
|
219
221
|
}
|
|
220
222
|
finally {
|
|
221
|
-
if (prev === undefined)
|
|
222
|
-
delete process.env.OPENCLI_BROWSER_REUSE;
|
|
223
|
-
else
|
|
224
|
-
process.env.OPENCLI_BROWSER_REUSE = prev;
|
|
225
223
|
vi.restoreAllMocks();
|
|
226
224
|
}
|
|
227
225
|
});
|
|
228
|
-
it('skips repeated domain pre-navigation for site
|
|
226
|
+
it('skips repeated domain pre-navigation for persistent site sessions', async () => {
|
|
229
227
|
const closeWindow = vi.fn().mockResolvedValue(undefined);
|
|
230
228
|
const goto = vi.fn().mockResolvedValue(undefined);
|
|
231
229
|
const mockPage = {
|
|
@@ -237,12 +235,12 @@ describe('executeCommand — non-browser timeout', () => {
|
|
|
237
235
|
vi.spyOn(runtime, 'browserSession').mockImplementation(async (_Factory, fn) => fn(mockPage));
|
|
238
236
|
const cmd = cli({
|
|
239
237
|
site: 'test-execution',
|
|
240
|
-
name: '
|
|
238
|
+
name: 'site-session-skip-prenav', access: 'read',
|
|
241
239
|
description: 'test reused same-domain tabs do not reset conversation state',
|
|
242
240
|
browser: true,
|
|
243
241
|
strategy: Strategy.COOKIE,
|
|
244
242
|
domain: 'grok.com',
|
|
245
|
-
|
|
243
|
+
siteSession: 'persistent',
|
|
246
244
|
func: async () => [{ ok: true }],
|
|
247
245
|
});
|
|
248
246
|
await executeCommand(cmd, {});
|
|
@@ -250,7 +248,7 @@ describe('executeCommand — non-browser timeout', () => {
|
|
|
250
248
|
expect(closeWindow).not.toHaveBeenCalled();
|
|
251
249
|
vi.restoreAllMocks();
|
|
252
250
|
});
|
|
253
|
-
it('keeps explicit path pre-navigation for site
|
|
251
|
+
it('keeps explicit path pre-navigation for persistent site sessions', async () => {
|
|
254
252
|
const closeWindow = vi.fn().mockResolvedValue(undefined);
|
|
255
253
|
const goto = vi.fn().mockResolvedValue(undefined);
|
|
256
254
|
const mockPage = {
|
|
@@ -262,13 +260,13 @@ describe('executeCommand — non-browser timeout', () => {
|
|
|
262
260
|
vi.spyOn(runtime, 'browserSession').mockImplementation(async (_Factory, fn) => fn(mockPage));
|
|
263
261
|
const cmd = cli({
|
|
264
262
|
site: 'test-execution',
|
|
265
|
-
name: '
|
|
263
|
+
name: 'site-session-path-prenav', access: 'read',
|
|
266
264
|
description: 'test explicit path pre-navigation still runs',
|
|
267
265
|
browser: true,
|
|
268
266
|
strategy: Strategy.COOKIE,
|
|
269
267
|
domain: 'example.com',
|
|
270
268
|
navigateBefore: 'https://example.com/dashboard',
|
|
271
|
-
|
|
269
|
+
siteSession: 'persistent',
|
|
272
270
|
func: async () => [{ ok: true }],
|
|
273
271
|
});
|
|
274
272
|
await executeCommand(cmd, {});
|
|
@@ -372,18 +370,18 @@ describe('executeCommand — non-browser timeout', () => {
|
|
|
372
370
|
expect(closeWindow).toHaveBeenCalledTimes(1);
|
|
373
371
|
vi.restoreAllMocks();
|
|
374
372
|
});
|
|
375
|
-
it('skips closeWindow when
|
|
373
|
+
it('skips closeWindow when OPENCLI_KEEP_TAB=true (success path)', async () => {
|
|
376
374
|
const closeWindow = vi.fn().mockResolvedValue(undefined);
|
|
377
375
|
const mockPage = { closeWindow };
|
|
378
376
|
vi.spyOn(capRouting, 'shouldUseBrowserSession').mockReturnValue(true);
|
|
379
377
|
vi.spyOn(runtime, 'browserSession').mockImplementation(async (_Factory, fn) => fn(mockPage));
|
|
380
|
-
const prev = process.env.
|
|
381
|
-
process.env.
|
|
378
|
+
const prev = process.env.OPENCLI_KEEP_TAB;
|
|
379
|
+
process.env.OPENCLI_KEEP_TAB = 'true';
|
|
382
380
|
try {
|
|
383
381
|
const cmd = cli({
|
|
384
382
|
site: 'test-execution',
|
|
385
|
-
name: 'browser-
|
|
386
|
-
description: 'test closeWindow skipped with --
|
|
383
|
+
name: 'browser-keep-tab-success', access: 'read',
|
|
384
|
+
description: 'test closeWindow skipped with --keep-tab on success',
|
|
387
385
|
browser: true,
|
|
388
386
|
strategy: Strategy.PUBLIC,
|
|
389
387
|
func: async () => [{ ok: true }],
|
|
@@ -393,24 +391,24 @@ describe('executeCommand — non-browser timeout', () => {
|
|
|
393
391
|
}
|
|
394
392
|
finally {
|
|
395
393
|
if (prev === undefined)
|
|
396
|
-
delete process.env.
|
|
394
|
+
delete process.env.OPENCLI_KEEP_TAB;
|
|
397
395
|
else
|
|
398
|
-
process.env.
|
|
396
|
+
process.env.OPENCLI_KEEP_TAB = prev;
|
|
399
397
|
vi.restoreAllMocks();
|
|
400
398
|
}
|
|
401
399
|
});
|
|
402
|
-
it('skips closeWindow when
|
|
400
|
+
it('skips closeWindow when OPENCLI_KEEP_TAB=true (failure path)', async () => {
|
|
403
401
|
const closeWindow = vi.fn().mockResolvedValue(undefined);
|
|
404
402
|
const mockPage = { closeWindow };
|
|
405
403
|
vi.spyOn(capRouting, 'shouldUseBrowserSession').mockReturnValue(true);
|
|
406
404
|
vi.spyOn(runtime, 'browserSession').mockImplementation(async (_Factory, fn) => fn(mockPage));
|
|
407
|
-
const prev = process.env.
|
|
408
|
-
process.env.
|
|
405
|
+
const prev = process.env.OPENCLI_KEEP_TAB;
|
|
406
|
+
process.env.OPENCLI_KEEP_TAB = 'true';
|
|
409
407
|
try {
|
|
410
408
|
const cmd = cli({
|
|
411
409
|
site: 'test-execution',
|
|
412
|
-
name: 'browser-
|
|
413
|
-
description: 'test closeWindow skipped with --
|
|
410
|
+
name: 'browser-keep-tab-failure', access: 'read',
|
|
411
|
+
description: 'test closeWindow skipped with --keep-tab on failure',
|
|
414
412
|
browser: true,
|
|
415
413
|
strategy: Strategy.PUBLIC,
|
|
416
414
|
func: async () => { throw new Error('adapter failure'); },
|
|
@@ -420,12 +418,37 @@ describe('executeCommand — non-browser timeout', () => {
|
|
|
420
418
|
}
|
|
421
419
|
finally {
|
|
422
420
|
if (prev === undefined)
|
|
423
|
-
delete process.env.
|
|
421
|
+
delete process.env.OPENCLI_KEEP_TAB;
|
|
424
422
|
else
|
|
425
|
-
process.env.
|
|
423
|
+
process.env.OPENCLI_KEEP_TAB = prev;
|
|
426
424
|
vi.restoreAllMocks();
|
|
427
425
|
}
|
|
428
426
|
});
|
|
427
|
+
it('lets browser common options override adapter window and keep-tab defaults', async () => {
|
|
428
|
+
const closeWindow = vi.fn().mockResolvedValue(undefined);
|
|
429
|
+
const mockPage = { closeWindow };
|
|
430
|
+
const sessionOpts = [];
|
|
431
|
+
vi.spyOn(capRouting, 'shouldUseBrowserSession').mockReturnValue(true);
|
|
432
|
+
vi.spyOn(runtime, 'browserSession').mockImplementation(async (_Factory, fn, opts) => {
|
|
433
|
+
sessionOpts.push(opts ?? {});
|
|
434
|
+
return fn(mockPage);
|
|
435
|
+
});
|
|
436
|
+
const cmd = cli({
|
|
437
|
+
site: 'test-execution',
|
|
438
|
+
name: 'browser-window-options', access: 'read',
|
|
439
|
+
description: 'test browser common options',
|
|
440
|
+
browser: true,
|
|
441
|
+
strategy: Strategy.PUBLIC,
|
|
442
|
+
func: async () => [{ ok: true }],
|
|
443
|
+
});
|
|
444
|
+
await executeCommand(cmd, {}, false, {
|
|
445
|
+
windowMode: 'foreground',
|
|
446
|
+
keepTab: 'true',
|
|
447
|
+
});
|
|
448
|
+
expect(sessionOpts[0]).toMatchObject({ windowMode: 'foreground' });
|
|
449
|
+
expect(closeWindow).not.toHaveBeenCalled();
|
|
450
|
+
vi.restoreAllMocks();
|
|
451
|
+
});
|
|
429
452
|
it('does not re-run custom validation when args are already prepared', async () => {
|
|
430
453
|
const validateArgs = vi.fn();
|
|
431
454
|
const cmd = {
|
|
@@ -54,3 +54,27 @@
|
|
|
54
54
|
tags: [vercel, deployment, serverless, frontend, devops]
|
|
55
55
|
install:
|
|
56
56
|
default: "npm install -g vercel"
|
|
57
|
+
|
|
58
|
+
- name: tg-cli
|
|
59
|
+
binary: tg
|
|
60
|
+
description: "Telegram CLI — local-first sync, search, export via MTProto for AI agents"
|
|
61
|
+
homepage: "https://github.com/jackwener/tg-cli"
|
|
62
|
+
tags: [telegram, messaging, search, export, ai-agent]
|
|
63
|
+
install:
|
|
64
|
+
default: "uv tool install kabi-tg-cli"
|
|
65
|
+
|
|
66
|
+
- name: discord-cli
|
|
67
|
+
binary: discord
|
|
68
|
+
description: "Discord CLI — local-first sync, search, export via SQLite for AI agents"
|
|
69
|
+
homepage: "https://github.com/jackwener/discord-cli"
|
|
70
|
+
tags: [discord, messaging, search, export, ai-agent]
|
|
71
|
+
install:
|
|
72
|
+
default: "uv tool install kabi-discord-cli"
|
|
73
|
+
|
|
74
|
+
- name: wx-cli
|
|
75
|
+
binary: wx
|
|
76
|
+
description: "WeChat local data CLI — sessions, messages, search, contacts, export for AI agents"
|
|
77
|
+
homepage: "https://github.com/jackwener/wx-cli"
|
|
78
|
+
tags: [wechat, messaging, search, export, ai-agent]
|
|
79
|
+
install:
|
|
80
|
+
default: "npm install -g @jackwener/wx-cli"
|
package/dist/src/help.d.ts
CHANGED
|
@@ -66,6 +66,7 @@ export declare function rootHelpData(program: Command, groups: RootAdapterGroups
|
|
|
66
66
|
export declare function siteHelpData(site: string, commands: readonly CliCommand[]): Record<string, unknown>;
|
|
67
67
|
export declare function commandHelpData(cmd: CliCommand): Record<string, unknown>;
|
|
68
68
|
export declare function formatCommonOptionsHelpText(): string;
|
|
69
|
+
export declare function formatBrowserCommonOptionsHelpText(): string;
|
|
69
70
|
export declare function formatSiteHelpText(site: string, commands: readonly CliCommand[]): string;
|
|
70
71
|
export declare function formatCommandHelpText(cmd: CliCommand): string;
|
|
71
72
|
export declare function installStructuredHelp(command: Command, data: () => unknown, textSuffix?: string | (() => string)): void;
|
package/dist/src/help.js
CHANGED
|
@@ -28,6 +28,26 @@ const COMMON_OPTIONS = [
|
|
|
28
28
|
help: 'display help for command',
|
|
29
29
|
},
|
|
30
30
|
];
|
|
31
|
+
const BROWSER_COMMON_OPTIONS = [
|
|
32
|
+
{
|
|
33
|
+
flags: '--window <mode>',
|
|
34
|
+
name: 'window',
|
|
35
|
+
help: 'Browser window mode: foreground or background',
|
|
36
|
+
choices: ['foreground', 'background'],
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
flags: '--site-session <mode>',
|
|
40
|
+
name: 'site-session',
|
|
41
|
+
help: 'Adapter site session lifecycle: ephemeral or persistent',
|
|
42
|
+
choices: ['ephemeral', 'persistent'],
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
flags: '--keep-tab <bool>',
|
|
46
|
+
name: 'keep-tab',
|
|
47
|
+
help: 'Keep the browser tab lease after the command finishes',
|
|
48
|
+
choices: ['true', 'false'],
|
|
49
|
+
},
|
|
50
|
+
];
|
|
31
51
|
function normalizeStructuredHelpFormat(value) {
|
|
32
52
|
const normalized = value?.toLowerCase();
|
|
33
53
|
if (normalized === 'yaml' || normalized === 'yml')
|
|
@@ -307,8 +327,9 @@ function compactCommand(cmd) {
|
|
|
307
327
|
...(cmd.aliases?.length ? { aliases: cmd.aliases } : {}),
|
|
308
328
|
positionals: positionals(cmd).map(compactArg),
|
|
309
329
|
command_options: commandOptions(cmd).map(compactArg),
|
|
330
|
+
...(cmd.browser ? { browser_common_options: BROWSER_COMMON_OPTIONS.map(compactCommonOption) } : {}),
|
|
310
331
|
example: formatCommandExample(cmd),
|
|
311
|
-
...(cmd.
|
|
332
|
+
...(cmd.siteSession ? { siteSession: cmd.siteSession } : {}),
|
|
312
333
|
...(cmd.defaultFormat ? { defaultFormat: cmd.defaultFormat } : {}),
|
|
313
334
|
...(cmd.columns?.length ? { columns: cmd.columns } : {}),
|
|
314
335
|
};
|
|
@@ -353,6 +374,7 @@ export function siteHelpData(site, commands) {
|
|
|
353
374
|
command_count: unique.length,
|
|
354
375
|
commands: unique.map(cmd => compactCommand(cmd)),
|
|
355
376
|
common_options: COMMON_OPTIONS.map(compactCommonOption),
|
|
377
|
+
...(unique.some(cmd => cmd.browser) ? { browser_common_options: BROWSER_COMMON_OPTIONS.map(compactCommonOption) } : {}),
|
|
356
378
|
next: [
|
|
357
379
|
`opencli ${site} <command> --help -f yaml`,
|
|
358
380
|
`opencli ${site} <command> -f yaml`,
|
|
@@ -364,6 +386,7 @@ export function commandHelpData(cmd) {
|
|
|
364
386
|
site: cmd.site,
|
|
365
387
|
...compactCommand(cmd),
|
|
366
388
|
common_options: COMMON_OPTIONS.map(compactCommonOption),
|
|
389
|
+
...(cmd.browser ? { browser_common_options: BROWSER_COMMON_OPTIONS.map(compactCommonOption) } : {}),
|
|
367
390
|
output_formats: ['table', 'plain', 'yaml', 'json', 'md', 'csv'],
|
|
368
391
|
};
|
|
369
392
|
}
|
|
@@ -394,6 +417,15 @@ export function formatCommonOptionsHelpText() {
|
|
|
394
417
|
});
|
|
395
418
|
return ['Common options:', ...formatRows(rows)].join('\n');
|
|
396
419
|
}
|
|
420
|
+
export function formatBrowserCommonOptionsHelpText() {
|
|
421
|
+
const rows = BROWSER_COMMON_OPTIONS.map(option => {
|
|
422
|
+
const details = [option.help];
|
|
423
|
+
if ('choices' in option)
|
|
424
|
+
details.push(`choices: ${option.choices.join(', ')}`);
|
|
425
|
+
return [option.flags, details.join(' ')];
|
|
426
|
+
});
|
|
427
|
+
return ['Browser common options:', ...formatRows(rows)].join('\n');
|
|
428
|
+
}
|
|
397
429
|
export function formatSiteHelpText(site, commands) {
|
|
398
430
|
const unique = [...new Map(commands.map(cmd => [fullName(cmd), cmd])).values()]
|
|
399
431
|
.sort((a, b) => a.name.localeCompare(b.name));
|
|
@@ -406,6 +438,7 @@ export function formatSiteHelpText(site, commands) {
|
|
|
406
438
|
...formatRows(unique.map(cmd => [formatCommandListTerm(cmd), formatSiteCommandDescription(cmd)])),
|
|
407
439
|
'',
|
|
408
440
|
formatCommonOptionsHelpText(),
|
|
441
|
+
...(unique.some(cmd => cmd.browser) ? ['', formatBrowserCommonOptionsHelpText()] : []),
|
|
409
442
|
'',
|
|
410
443
|
`Agent tip: use 'opencli ${site} --help -f yaml' to get all command args/options in one structured response.`,
|
|
411
444
|
'',
|
|
@@ -434,6 +467,8 @@ export function formatCommandHelpText(cmd) {
|
|
|
434
467
|
lines.push('Command options:', ...formatRows(optionRows), '');
|
|
435
468
|
}
|
|
436
469
|
lines.push(formatCommonOptionsHelpText(), '');
|
|
470
|
+
if (cmd.browser)
|
|
471
|
+
lines.push(formatBrowserCommonOptionsHelpText(), '');
|
|
437
472
|
const meta = [];
|
|
438
473
|
meta.push(`Access: ${cmd.access}`);
|
|
439
474
|
meta.push(`Browser: ${cmd.browser ? 'yes' : 'no'}`);
|
package/dist/src/main.js
CHANGED
|
@@ -27,35 +27,6 @@ const __dirname = path.dirname(__filename);
|
|
|
27
27
|
// Use findPackageRoot so the path works both in dev (src/main.ts) and prod (dist/src/main.js).
|
|
28
28
|
const BUILTIN_CLIS = path.join(findPackageRoot(__filename), 'clis');
|
|
29
29
|
const USER_CLIS = path.join(os.homedir(), '.opencli', 'clis');
|
|
30
|
-
// ── Session lifecycle flags ──────────────────────────────────────────────
|
|
31
|
-
// `--live` / `--focus` / `--reuse` are top-level-ish toggles that tweak the automation
|
|
32
|
-
// window's lifecycle. We strip them from argv before Commander runs so they
|
|
33
|
-
// can be placed anywhere and work on any subcommand (adapter or browser).
|
|
34
|
-
{
|
|
35
|
-
const liveIdx = process.argv.indexOf('--live');
|
|
36
|
-
if (liveIdx !== -1) {
|
|
37
|
-
process.env.OPENCLI_LIVE = '1';
|
|
38
|
-
process.argv.splice(liveIdx, 1);
|
|
39
|
-
}
|
|
40
|
-
const focusIdx = process.argv.indexOf('--focus');
|
|
41
|
-
if (focusIdx !== -1) {
|
|
42
|
-
process.env.OPENCLI_WINDOW_FOCUSED = '1';
|
|
43
|
-
process.argv.splice(focusIdx, 1);
|
|
44
|
-
}
|
|
45
|
-
const reuseIdx = process.argv.findIndex(arg => arg === '--reuse' || arg.startsWith('--reuse='));
|
|
46
|
-
if (reuseIdx !== -1) {
|
|
47
|
-
const arg = process.argv[reuseIdx];
|
|
48
|
-
const value = arg.startsWith('--reuse=')
|
|
49
|
-
? arg.slice('--reuse='.length)
|
|
50
|
-
: process.argv[reuseIdx + 1];
|
|
51
|
-
if (value !== 'none' && value !== 'site') {
|
|
52
|
-
process.stderr.write(`--reuse must be one of: none, site. Received: "${value ?? ''}"\n`);
|
|
53
|
-
process.exit(EXIT_CODES.USAGE_ERROR);
|
|
54
|
-
}
|
|
55
|
-
process.env.OPENCLI_BROWSER_REUSE = value;
|
|
56
|
-
process.argv.splice(reuseIdx, arg.startsWith('--reuse=') ? 1 : 2);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
30
|
// ── Ultra-fast path: lightweight commands bypass full discovery ──────────
|
|
60
31
|
// These are high-frequency or trivial paths that must not pay the startup tax.
|
|
61
32
|
const argv = process.argv.slice(2);
|
|
@@ -36,8 +36,6 @@ export interface ManifestEntry {
|
|
|
36
36
|
sourceFile?: string;
|
|
37
37
|
/** Pre-navigation control — see CliCommand.navigateBefore */
|
|
38
38
|
navigateBefore?: boolean | string;
|
|
39
|
-
/**
|
|
40
|
-
|
|
41
|
-
reuse?: 'none' | 'site';
|
|
42
|
-
};
|
|
39
|
+
/** Site session lifecycle defaults — see CliCommand.siteSession */
|
|
40
|
+
siteSession?: 'ephemeral' | 'persistent';
|
|
43
41
|
}
|
|
@@ -141,7 +141,7 @@ function renderSummary(session, events, opts) {
|
|
|
141
141
|
`traceId: ${yamlScalar(session.id)}`,
|
|
142
142
|
`status: ${opts.status}`,
|
|
143
143
|
`contextId: ${yamlScalar(session.scope.contextId ?? 'default')}`,
|
|
144
|
-
`
|
|
144
|
+
`session: ${yamlScalar(session.scope.session)}`,
|
|
145
145
|
...(session.scope.target ? [`target: ${yamlScalar(session.scope.target)}`] : []),
|
|
146
146
|
...(session.scope.site ? [`site: ${yamlScalar(session.scope.site)}`] : []),
|
|
147
147
|
...(session.scope.command ? [`command: ${yamlScalar(session.scope.command)}`] : []),
|
|
@@ -17,7 +17,7 @@ describe('observation artifact', () => {
|
|
|
17
17
|
id: 'trace-1',
|
|
18
18
|
scope: {
|
|
19
19
|
contextId: 'work',
|
|
20
|
-
|
|
20
|
+
session: 'site:demo',
|
|
21
21
|
site: 'demo',
|
|
22
22
|
command: 'demo/run',
|
|
23
23
|
adapterSourcePath: '/tmp/clis/demo/run.js',
|
|
@@ -69,7 +69,7 @@ describe('observation artifact', () => {
|
|
|
69
69
|
expiresAt: expect.any(String),
|
|
70
70
|
scope: {
|
|
71
71
|
contextId: 'work',
|
|
72
|
-
|
|
72
|
+
session: 'site:demo',
|
|
73
73
|
site: 'demo',
|
|
74
74
|
command: 'demo/run',
|
|
75
75
|
adapterSourcePath: '/tmp/clis/demo/run.js',
|
|
@@ -107,7 +107,7 @@ describe('observation artifact', () => {
|
|
|
107
107
|
fs.writeFileSync(path.join(oldTraceDir, 'trace.jsonl'), '{}\n', 'utf-8');
|
|
108
108
|
const session = new ObservationSession({
|
|
109
109
|
id: 'new-trace',
|
|
110
|
-
scope: { contextId: 'work',
|
|
110
|
+
scope: { contextId: 'work', session: 'site:demo' },
|
|
111
111
|
now: () => Date.parse('2026-05-03T00:00:00.000Z'),
|
|
112
112
|
});
|
|
113
113
|
session.record({ stream: 'action', name: 'command', phase: 'start' });
|
|
@@ -19,7 +19,7 @@ export class ObservationManager {
|
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
function scopeMatches(actual, expected) {
|
|
22
|
-
return actual.
|
|
22
|
+
return actual.session === expected.session
|
|
23
23
|
&& (expected.contextId === undefined || actual.contextId === expected.contextId)
|
|
24
24
|
&& (expected.target === undefined || actual.target === expected.target)
|
|
25
25
|
&& (expected.site === undefined || actual.site === expected.site)
|
|
@@ -3,10 +3,10 @@ import { ObservationManager } from './manager.js';
|
|
|
3
3
|
describe('ObservationManager', () => {
|
|
4
4
|
it('indexes sessions by id and scope', () => {
|
|
5
5
|
const manager = new ObservationManager();
|
|
6
|
-
const work = manager.start({ id: 'work-1', scope: { contextId: 'work',
|
|
7
|
-
manager.start({ id: 'personal-1', scope: { contextId: 'personal',
|
|
6
|
+
const work = manager.start({ id: 'work-1', scope: { contextId: 'work', session: 'site:x', target: 'tab-1' } });
|
|
7
|
+
manager.start({ id: 'personal-1', scope: { contextId: 'personal', session: 'site:x', target: 'tab-2' } });
|
|
8
8
|
expect(manager.get('work-1')).toBe(work);
|
|
9
|
-
expect(manager.findByScope({ contextId: 'work',
|
|
9
|
+
expect(manager.findByScope({ contextId: 'work', session: 'site:x' }).map((session) => session.id)).toEqual(['work-1']);
|
|
10
10
|
expect(manager.stop('work-1')).toBe(work);
|
|
11
11
|
expect(manager.get('work-1')).toBeUndefined();
|
|
12
12
|
});
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* plugins are dynamically imported during discoverPlugins().
|
|
8
8
|
*/
|
|
9
9
|
export { cli, Strategy, getRegistry, fullName, registerCommand } from './registry.js';
|
|
10
|
-
export type { CliCommand, Arg, CliOptions, CommandArgs,
|
|
10
|
+
export type { CliCommand, Arg, CliOptions, CommandArgs, SiteSessionMode } from './registry.js';
|
|
11
11
|
export type { IPage } from './types.js';
|
|
12
12
|
export { onStartup, onBeforeExecute, onAfterExecute } from './hooks.js';
|
|
13
13
|
export type { HookFn, HookContext, HookName } from './hooks.js';
|
package/dist/src/registry.d.ts
CHANGED
|
@@ -23,16 +23,7 @@ export type CommandArgs = Record<string, any>;
|
|
|
23
23
|
export type BrowserCommandFunc = (page: IPage, kwargs: CommandArgs, debug?: boolean) => Promise<unknown>;
|
|
24
24
|
export type NonBrowserCommandFunc = (kwargs: CommandArgs, debug?: boolean) => Promise<unknown>;
|
|
25
25
|
export type CommandAccess = 'read' | 'write';
|
|
26
|
-
export type
|
|
27
|
-
export interface BrowserSessionOptions {
|
|
28
|
-
/**
|
|
29
|
-
* Control whether browser-backed adapter commands reuse a stable tab lease.
|
|
30
|
-
*
|
|
31
|
-
* - `none`: one-shot workspace per command execution (default)
|
|
32
|
-
* - `site`: all commands for this site share `site:<site>` until idle expiry
|
|
33
|
-
*/
|
|
34
|
-
reuse?: BrowserSessionReuse;
|
|
35
|
-
}
|
|
26
|
+
export type SiteSessionMode = 'ephemeral' | 'persistent';
|
|
36
27
|
interface BaseCliCommand {
|
|
37
28
|
site: string;
|
|
38
29
|
name: string;
|
|
@@ -66,8 +57,8 @@ interface BaseCliCommand {
|
|
|
66
57
|
* Adapter authors can set this explicitly to override the strategy-based default.
|
|
67
58
|
*/
|
|
68
59
|
navigateBefore?: boolean | string;
|
|
69
|
-
/**
|
|
70
|
-
|
|
60
|
+
/** Site session lifecycle for adapter commands. */
|
|
61
|
+
siteSession?: SiteSessionMode;
|
|
71
62
|
/** Override the default CLI output format when the user does not pass -f/--format. */
|
|
72
63
|
defaultFormat?: 'table' | 'plain' | 'json' | 'yaml' | 'yml' | 'md' | 'markdown' | 'csv';
|
|
73
64
|
}
|
package/dist/src/registry.js
CHANGED
|
@@ -27,7 +27,7 @@ export function cli(opts) {
|
|
|
27
27
|
pipeline: opts.pipeline,
|
|
28
28
|
footerExtra: opts.footerExtra,
|
|
29
29
|
navigateBefore: opts.navigateBefore,
|
|
30
|
-
|
|
30
|
+
siteSession: opts.siteSession,
|
|
31
31
|
defaultFormat: opts.defaultFormat,
|
|
32
32
|
};
|
|
33
33
|
registerCommand(cmd);
|
|
@@ -57,7 +57,7 @@ export function strategyLabel(cmd) {
|
|
|
57
57
|
*/
|
|
58
58
|
function normalizeCommand(cmd) {
|
|
59
59
|
assertCommandAccess(cmd);
|
|
60
|
-
|
|
60
|
+
assertSiteSession(cmd);
|
|
61
61
|
const strategy = cmd.strategy ?? (cmd.browser === false ? Strategy.PUBLIC : Strategy.COOKIE);
|
|
62
62
|
const browser = cmd.browser ?? (strategy !== Strategy.PUBLIC && strategy !== Strategy.LOCAL);
|
|
63
63
|
let navigateBefore = cmd.navigateBefore;
|
|
@@ -82,16 +82,12 @@ function assertCommandAccess(cmd) {
|
|
|
82
82
|
const key = `${cmd.site}/${cmd.name}`;
|
|
83
83
|
throw new Error(`Command ${key} must declare access: 'read' | 'write'`);
|
|
84
84
|
}
|
|
85
|
-
function
|
|
86
|
-
if (cmd.
|
|
85
|
+
function assertSiteSession(cmd) {
|
|
86
|
+
if (cmd.siteSession === undefined)
|
|
87
87
|
return;
|
|
88
88
|
const key = `${cmd.site}/${cmd.name}`;
|
|
89
|
-
if (cmd.
|
|
90
|
-
throw new Error(`Command ${key}
|
|
91
|
-
}
|
|
92
|
-
const reuse = cmd.browserSession.reuse;
|
|
93
|
-
if (reuse !== undefined && reuse !== 'none' && reuse !== 'site') {
|
|
94
|
-
throw new Error(`Command ${key} browserSession.reuse must be one of: none, site`);
|
|
89
|
+
if (cmd.siteSession !== 'ephemeral' && cmd.siteSession !== 'persistent') {
|
|
90
|
+
throw new Error(`Command ${key} siteSession must be one of: ephemeral, persistent`);
|
|
95
91
|
}
|
|
96
92
|
}
|
|
97
93
|
export function registerCommand(cmd) {
|
package/dist/src/runtime.d.ts
CHANGED
|
@@ -6,6 +6,8 @@ import type { IPage } from './types.js';
|
|
|
6
6
|
export declare function getBrowserFactory(site?: string): new () => IBrowserFactory;
|
|
7
7
|
export declare const DEFAULT_BROWSER_CONNECT_TIMEOUT: number;
|
|
8
8
|
export declare const DEFAULT_BROWSER_COMMAND_TIMEOUT: number;
|
|
9
|
+
export type BrowserWindowMode = 'foreground' | 'background';
|
|
10
|
+
export type BrowserSurface = 'browser' | 'adapter';
|
|
9
11
|
/**
|
|
10
12
|
* Timeout with seconds unit. Used for high-level command timeouts.
|
|
11
13
|
*/
|
|
@@ -24,16 +26,22 @@ export declare function withTimeoutMs<T>(promise: Promise<T>, timeoutMs: number,
|
|
|
24
26
|
export interface IBrowserFactory {
|
|
25
27
|
connect(opts?: {
|
|
26
28
|
timeout?: number;
|
|
27
|
-
|
|
29
|
+
session?: string;
|
|
28
30
|
cdpEndpoint?: string;
|
|
29
31
|
contextId?: string;
|
|
30
32
|
idleTimeout?: number;
|
|
33
|
+
windowMode?: BrowserWindowMode;
|
|
34
|
+
surface?: BrowserSurface;
|
|
35
|
+
siteSession?: 'ephemeral' | 'persistent';
|
|
31
36
|
}): Promise<IPage>;
|
|
32
37
|
close(): Promise<void>;
|
|
33
38
|
}
|
|
34
39
|
export declare function browserSession<T>(BrowserFactory: new () => IBrowserFactory, fn: (page: IPage) => Promise<T>, opts?: {
|
|
35
|
-
|
|
40
|
+
session?: string;
|
|
36
41
|
cdpEndpoint?: string;
|
|
37
42
|
contextId?: string;
|
|
38
43
|
idleTimeout?: number;
|
|
44
|
+
windowMode?: BrowserWindowMode;
|
|
45
|
+
surface?: BrowserSurface;
|
|
46
|
+
siteSession?: 'ephemeral' | 'persistent';
|
|
39
47
|
}): Promise<T>;
|
package/dist/src/runtime.js
CHANGED
|
@@ -50,10 +50,13 @@ export async function browserSession(BrowserFactory, fn, opts = {}) {
|
|
|
50
50
|
try {
|
|
51
51
|
const page = await browser.connect({
|
|
52
52
|
timeout: DEFAULT_BROWSER_CONNECT_TIMEOUT,
|
|
53
|
-
|
|
53
|
+
session: opts.session,
|
|
54
54
|
cdpEndpoint: opts.cdpEndpoint,
|
|
55
55
|
contextId: opts.contextId,
|
|
56
56
|
idleTimeout: opts.idleTimeout,
|
|
57
|
+
windowMode: opts.windowMode,
|
|
58
|
+
surface: opts.surface,
|
|
59
|
+
siteSession: opts.siteSession,
|
|
57
60
|
});
|
|
58
61
|
return await fn(page);
|
|
59
62
|
}
|
|
@@ -32,7 +32,7 @@ export declare function serializeCommand(cmd: CliCommand): {
|
|
|
32
32
|
domain: string | null;
|
|
33
33
|
example: string;
|
|
34
34
|
defaultFormat: "table" | "plain" | "json" | "yaml" | "yml" | "md" | "markdown" | "csv" | null;
|
|
35
|
-
|
|
35
|
+
siteSession: import("./registry.js").SiteSessionMode | null;
|
|
36
36
|
};
|
|
37
37
|
/** Human-readable arg summary: `<required> [optional]` style. */
|
|
38
38
|
export declare function formatArgSummary(args: Arg[]): string;
|
|
@@ -34,7 +34,7 @@ export function serializeCommand(cmd) {
|
|
|
34
34
|
domain: cmd.domain ?? null,
|
|
35
35
|
example: formatCommandExample(cmd),
|
|
36
36
|
defaultFormat: cmd.defaultFormat ?? null,
|
|
37
|
-
|
|
37
|
+
siteSession: cmd.siteSession ?? null,
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
40
|
// ── Formatting ──────────────────────────────────────────────────────────────
|