tabctl 0.5.3 → 0.6.0-alpha.2
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 +98 -35
- package/dist/extension/background.js +2 -0
- package/dist/extension/manifest.json +2 -2
- package/package.json +13 -5
- package/dist/cli/lib/args.js +0 -141
- package/dist/cli/lib/client.js +0 -83
- package/dist/cli/lib/commands/doctor.js +0 -134
- package/dist/cli/lib/commands/index.js +0 -51
- package/dist/cli/lib/commands/list.js +0 -159
- package/dist/cli/lib/commands/meta.js +0 -229
- package/dist/cli/lib/commands/params-groups.js +0 -48
- package/dist/cli/lib/commands/params-move.js +0 -44
- package/dist/cli/lib/commands/params.js +0 -314
- package/dist/cli/lib/commands/profile.js +0 -91
- package/dist/cli/lib/commands/setup.js +0 -294
- package/dist/cli/lib/constants.js +0 -30
- package/dist/cli/lib/help.js +0 -205
- package/dist/cli/lib/options-commands.js +0 -274
- package/dist/cli/lib/options-groups.js +0 -41
- package/dist/cli/lib/options.js +0 -125
- package/dist/cli/lib/output.js +0 -147
- package/dist/cli/lib/pagination.js +0 -55
- package/dist/cli/lib/policy-filter.js +0 -202
- package/dist/cli/lib/policy.js +0 -91
- package/dist/cli/lib/report.js +0 -61
- package/dist/cli/lib/response.js +0 -235
- package/dist/cli/lib/scope.js +0 -250
- package/dist/cli/lib/snapshot.js +0 -216
- package/dist/cli/lib/types.js +0 -2
- package/dist/cli/tabctl.js +0 -475
- package/dist/host/host.bundle.js +0 -670
- package/dist/host/host.js +0 -143
- package/dist/host/host.sh +0 -5
- package/dist/host/launcher/go.mod +0 -3
- package/dist/host/launcher/main.go +0 -109
- package/dist/host/lib/handlers.js +0 -327
- package/dist/host/lib/undo.js +0 -60
- package/dist/shared/config.js +0 -134
- package/dist/shared/extension-sync.js +0 -170
- package/dist/shared/profiles.js +0 -78
- package/dist/shared/version.js +0 -8
- package/dist/shared/wrapper-health.js +0 -132
|
@@ -1,274 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.COMMANDS = void 0;
|
|
4
|
-
const options_groups_1 = require("./options-groups");
|
|
5
|
-
exports.COMMANDS = {
|
|
6
|
-
help: {
|
|
7
|
-
description: "Show help information",
|
|
8
|
-
},
|
|
9
|
-
list: {
|
|
10
|
-
description: "List browser tabs",
|
|
11
|
-
groups: ["scope", "pagination"],
|
|
12
|
-
options: [
|
|
13
|
-
{ flag: "--groups", desc: "Alias for group-list command" },
|
|
14
|
-
],
|
|
15
|
-
},
|
|
16
|
-
analyze: {
|
|
17
|
-
description: "Analyze tabs for duplicates and stale content",
|
|
18
|
-
groups: ["scope"],
|
|
19
|
-
options: [
|
|
20
|
-
{ flag: "--stale-days <n>", desc: "Days threshold for stale tabs" },
|
|
21
|
-
{ flag: "--github", desc: "Enable GitHub PR/issue status checking" },
|
|
22
|
-
{ flag: "--github-concurrency <n>", desc: "Max concurrent GitHub API requests" },
|
|
23
|
-
{ flag: "--github-timeout-ms <ms>", desc: "Timeout for GitHub API requests" },
|
|
24
|
-
{ flag: "--window-title", desc: "Include active window title in output" },
|
|
25
|
-
{ flag: "--progress", desc: "Show progress during analysis" },
|
|
26
|
-
],
|
|
27
|
-
},
|
|
28
|
-
dedupe: {
|
|
29
|
-
description: "Interactively deduplicate tabs",
|
|
30
|
-
groups: ["scope"],
|
|
31
|
-
options: [
|
|
32
|
-
{ flag: "--stale-days <n>", desc: "Days threshold for stale tabs" },
|
|
33
|
-
{ flag: "--github", desc: "Enable GitHub PR/issue status checking" },
|
|
34
|
-
{ flag: "--github-concurrency <n>", desc: "Max concurrent GitHub API requests" },
|
|
35
|
-
{ flag: "--github-timeout-ms <ms>", desc: "Timeout for GitHub API requests" },
|
|
36
|
-
{ flag: "--include-stale", desc: "Include stale tabs in deduplication" },
|
|
37
|
-
{ flag: "--window-title", desc: "Include active window title in output" },
|
|
38
|
-
{ flag: "--progress", desc: "Show progress during analysis" },
|
|
39
|
-
{ flag: "--confirm", desc: "Execute changes without prompting" },
|
|
40
|
-
],
|
|
41
|
-
},
|
|
42
|
-
inspect: {
|
|
43
|
-
description: "Extract signals from tab content",
|
|
44
|
-
groups: ["scope", "pagination"],
|
|
45
|
-
options: [
|
|
46
|
-
{ flag: "--signal-config <path>", desc: "Path to signal configuration file" },
|
|
47
|
-
{ flag: "--signal <id>", desc: "Signal ID to extract", repeatable: true },
|
|
48
|
-
{ flag: "--selector <name=css|json>", desc: "Custom selector definition (attr: href-url/src-url supported; text/textMode supported)", repeatable: true },
|
|
49
|
-
{ flag: "--selector-attr <attr>", desc: "Default selector attr (text|href|src|href-url|src-url)" },
|
|
50
|
-
{ flag: "--signal-concurrency <n>", desc: "Max concurrent signal extractions" },
|
|
51
|
-
{ flag: "--signal-timeout-ms <ms>", desc: "Timeout for signal extraction" },
|
|
52
|
-
{ flag: "--wait-for load|dom|settle|none", desc: "Wait for page readiness before inspection" },
|
|
53
|
-
{ flag: "--wait-timeout-ms <ms>", desc: "Timeout for page readiness wait" },
|
|
54
|
-
{ flag: "--progress", desc: "Show progress during inspection" },
|
|
55
|
-
],
|
|
56
|
-
},
|
|
57
|
-
screenshot: {
|
|
58
|
-
description: "Capture screenshots from tabs",
|
|
59
|
-
groups: ["scope"],
|
|
60
|
-
options: options_groups_1.SCREENSHOT_OPTIONS.options,
|
|
61
|
-
},
|
|
62
|
-
focus: {
|
|
63
|
-
description: "Focus a specific tab",
|
|
64
|
-
options: [
|
|
65
|
-
{ flag: "--tab <id>", desc: "Tab ID to focus" },
|
|
66
|
-
],
|
|
67
|
-
},
|
|
68
|
-
refresh: {
|
|
69
|
-
description: "Refresh a specific tab",
|
|
70
|
-
options: [
|
|
71
|
-
{ flag: "--tab <id>", desc: "Tab ID to refresh" },
|
|
72
|
-
],
|
|
73
|
-
},
|
|
74
|
-
open: {
|
|
75
|
-
description: "Open new tabs with URLs",
|
|
76
|
-
options: [
|
|
77
|
-
{ flag: "--url <url>", desc: "URL to open", repeatable: true },
|
|
78
|
-
{ flag: "--group <name>", desc: "Add tabs to group by name" },
|
|
79
|
-
{ flag: "--color <name>", desc: "Group color (if creating)" },
|
|
80
|
-
{ flag: "--before-tab <id>", desc: "Position before this tab" },
|
|
81
|
-
{ flag: "--after-tab <id>", desc: "Position after this tab" },
|
|
82
|
-
{ flag: "--after-group <name>", desc: "Position after this group" },
|
|
83
|
-
{ flag: "--window <id|active|last-focused|new>", desc: "Target window ID" },
|
|
84
|
-
{ flag: "--new-window", desc: "Open in new window" },
|
|
85
|
-
{ flag: "--window-group <name>", desc: "Find window containing group" },
|
|
86
|
-
{ flag: "--window-tab <id>", desc: "Find window containing tab" },
|
|
87
|
-
{ flag: "--window-url <substring>", desc: "Find window containing URL" },
|
|
88
|
-
{ flag: "--new-group", desc: "Force new group even if one exists" },
|
|
89
|
-
{ flag: "--allow-duplicates", desc: "Open duplicate URLs" },
|
|
90
|
-
],
|
|
91
|
-
},
|
|
92
|
-
"group-list": {
|
|
93
|
-
description: "List tab groups",
|
|
94
|
-
groups: ["scope", "pagination"],
|
|
95
|
-
},
|
|
96
|
-
group: {
|
|
97
|
-
description: "Alias for group-list",
|
|
98
|
-
aliases: ["group-list"],
|
|
99
|
-
groups: ["scope", "pagination"],
|
|
100
|
-
},
|
|
101
|
-
"group-update": {
|
|
102
|
-
description: "Update tab group properties",
|
|
103
|
-
options: [
|
|
104
|
-
{ flag: "--group <name>", desc: "Target group by title" },
|
|
105
|
-
{ flag: "--group-id <id>", desc: "Target group by ID" },
|
|
106
|
-
{ flag: "--window <id|active|last-focused>", desc: "Target window ID" },
|
|
107
|
-
{ flag: "--title <name>", desc: "New group title" },
|
|
108
|
-
{ flag: "--color <name>", desc: "New group color" },
|
|
109
|
-
{ flag: "--collapsed", desc: "Collapse the group" },
|
|
110
|
-
{ flag: "--expanded", desc: "Expand the group" },
|
|
111
|
-
],
|
|
112
|
-
},
|
|
113
|
-
"group-ungroup": {
|
|
114
|
-
description: "Remove tabs from a group",
|
|
115
|
-
options: [
|
|
116
|
-
{ flag: "--group <name>", desc: "Target group by title" },
|
|
117
|
-
{ flag: "--group-id <id>", desc: "Target group by ID" },
|
|
118
|
-
{ flag: "--window <id|active|last-focused>", desc: "Target window ID" },
|
|
119
|
-
],
|
|
120
|
-
},
|
|
121
|
-
"group-assign": {
|
|
122
|
-
description: "Assign tabs to a group",
|
|
123
|
-
options: [
|
|
124
|
-
{ flag: "--tab <id>", desc: "Tab ID(s) to assign", repeatable: true },
|
|
125
|
-
{ flag: "--group <name>", desc: "Target group by title" },
|
|
126
|
-
{ flag: "--group-id <id>", desc: "Target group by ID" },
|
|
127
|
-
{ flag: "--window <id|active|last-focused>", desc: "Target window ID" },
|
|
128
|
-
{ flag: "--create", desc: "Create group if not exists" },
|
|
129
|
-
{ flag: "--color <name>", desc: "Group color (if creating)" },
|
|
130
|
-
{ flag: "--collapsed", desc: "Collapse group after assign" },
|
|
131
|
-
{ flag: "--expanded", desc: "Expand group after assign" },
|
|
132
|
-
],
|
|
133
|
-
},
|
|
134
|
-
"group-gather": {
|
|
135
|
-
description: "Merge duplicate groups with the same name",
|
|
136
|
-
options: [
|
|
137
|
-
{ flag: "--window <id|active|last-focused>", desc: "Target window" },
|
|
138
|
-
{ flag: "--group <name>", desc: "Group name to gather (optional, gathers all if omitted)" },
|
|
139
|
-
],
|
|
140
|
-
},
|
|
141
|
-
"move-tab": {
|
|
142
|
-
description: "Move a tab to a new position",
|
|
143
|
-
options: [
|
|
144
|
-
{ flag: "--tab <id>", desc: "Tab ID to move" },
|
|
145
|
-
{ flag: "--before-tab <id>", desc: "Position before this tab" },
|
|
146
|
-
{ flag: "--after-tab <id>", desc: "Position after this tab" },
|
|
147
|
-
{ flag: "--before-group <name>", desc: "Position before this group" },
|
|
148
|
-
{ flag: "--after-group <name>", desc: "Position after this group" },
|
|
149
|
-
{ flag: "--window <id|active|last-focused>", desc: "Target window ID" },
|
|
150
|
-
{ flag: "--new-window", desc: "Move to new window" },
|
|
151
|
-
],
|
|
152
|
-
},
|
|
153
|
-
"move-group": {
|
|
154
|
-
description: "Move a tab group to a new position",
|
|
155
|
-
options: [
|
|
156
|
-
{ flag: "--group <name>", desc: "Target group by title" },
|
|
157
|
-
{ flag: "--group-id <id>", desc: "Target group by ID" },
|
|
158
|
-
{ flag: "--before-tab <id>", desc: "Position before this tab" },
|
|
159
|
-
{ flag: "--after-tab <id>", desc: "Position after this tab" },
|
|
160
|
-
{ flag: "--before-group <name>", desc: "Position before this group" },
|
|
161
|
-
{ flag: "--after-group <name>", desc: "Position after this group" },
|
|
162
|
-
{ flag: "--window <id|active|last-focused>", desc: "Target window ID" },
|
|
163
|
-
{ flag: "--new-window", desc: "Move to new window" },
|
|
164
|
-
],
|
|
165
|
-
},
|
|
166
|
-
"merge-window": {
|
|
167
|
-
description: "Merge tabs from one window to another",
|
|
168
|
-
options: [
|
|
169
|
-
{ flag: "--from <id>", desc: "Source window ID" },
|
|
170
|
-
{ flag: "--to <id>", desc: "Destination window ID" },
|
|
171
|
-
{ flag: "--close-source", desc: "Close source window after merge" },
|
|
172
|
-
{ flag: "--confirm", desc: "Execute without prompting" },
|
|
173
|
-
],
|
|
174
|
-
},
|
|
175
|
-
setup: {
|
|
176
|
-
description: "Configure tabctl connection",
|
|
177
|
-
options: [
|
|
178
|
-
{ flag: "--browser edge|chrome", desc: "Browser type" },
|
|
179
|
-
{ flag: "--extension-id <id>", desc: "Override auto-derived extension ID" },
|
|
180
|
-
{ flag: "--node <path>", desc: "Path to Node.js executable" },
|
|
181
|
-
{ flag: "--name <name>", desc: "Profile name (default: browser type)" },
|
|
182
|
-
{ flag: "--user-data-dir <path>", desc: "Chrome/Edge user data directory for custom profiles" },
|
|
183
|
-
],
|
|
184
|
-
},
|
|
185
|
-
policy: {
|
|
186
|
-
description: "Manage browser policies",
|
|
187
|
-
options: [
|
|
188
|
-
{ flag: "--init", desc: "Initialize policy configuration" },
|
|
189
|
-
],
|
|
190
|
-
},
|
|
191
|
-
archive: {
|
|
192
|
-
description: "Archive tabs to storage",
|
|
193
|
-
groups: ["scope"],
|
|
194
|
-
},
|
|
195
|
-
close: {
|
|
196
|
-
description: "Close tabs",
|
|
197
|
-
options: [
|
|
198
|
-
{ flag: "--apply <analysisId>", desc: "Apply analysis results" },
|
|
199
|
-
{ flag: "--tab <id>", desc: "Tab ID(s) to close", repeatable: true },
|
|
200
|
-
{ flag: "--group <name>", desc: "Close tabs in group by title" },
|
|
201
|
-
{ flag: "--group-id <id>", desc: "Close tabs in group by ID" },
|
|
202
|
-
{ flag: "--ungrouped", desc: "Close ungrouped tabs" },
|
|
203
|
-
{ flag: "--window <id|active|last-focused>", desc: "Target window ID" },
|
|
204
|
-
{ flag: "--confirm", desc: "Execute without prompting" },
|
|
205
|
-
{ flag: "--dry-run", desc: "Show what would be closed" },
|
|
206
|
-
],
|
|
207
|
-
},
|
|
208
|
-
report: {
|
|
209
|
-
description: "Generate tab reports",
|
|
210
|
-
groups: ["scope", "pagination"],
|
|
211
|
-
options: [
|
|
212
|
-
{ flag: "--format json|md|csv", desc: "Output format" },
|
|
213
|
-
{ flag: "--out <path>", desc: "Output file path" },
|
|
214
|
-
],
|
|
215
|
-
},
|
|
216
|
-
undo: {
|
|
217
|
-
description: "Undo a previous operation",
|
|
218
|
-
options: [
|
|
219
|
-
{ flag: "<txid>", desc: "Transaction ID (positional)" },
|
|
220
|
-
{ flag: "--txid <id>", desc: "Transaction ID" },
|
|
221
|
-
{ flag: "--latest", desc: "Undo most recent transaction" },
|
|
222
|
-
],
|
|
223
|
-
},
|
|
224
|
-
history: {
|
|
225
|
-
description: "Show operation history",
|
|
226
|
-
options: [
|
|
227
|
-
{ flag: "--limit <n>", desc: "Maximum entries to show" },
|
|
228
|
-
],
|
|
229
|
-
},
|
|
230
|
-
skill: {
|
|
231
|
-
description: "Show AI agent skill documentation",
|
|
232
|
-
options: [
|
|
233
|
-
{ flag: "--agent <name>", desc: "Target agent(s)", repeatable: true },
|
|
234
|
-
{ flag: "--global", desc: "Show global skill info" },
|
|
235
|
-
],
|
|
236
|
-
},
|
|
237
|
-
"profile-list": {
|
|
238
|
-
description: "List configured profiles",
|
|
239
|
-
},
|
|
240
|
-
profile: {
|
|
241
|
-
description: "Alias for profile-list",
|
|
242
|
-
aliases: ["profile-list"],
|
|
243
|
-
},
|
|
244
|
-
"profile-show": {
|
|
245
|
-
description: "Show active profile details",
|
|
246
|
-
},
|
|
247
|
-
"profile-switch": {
|
|
248
|
-
description: "Switch the default profile",
|
|
249
|
-
options: [
|
|
250
|
-
{ flag: "<name>", desc: "Profile name (positional)" },
|
|
251
|
-
],
|
|
252
|
-
},
|
|
253
|
-
"profile-remove": {
|
|
254
|
-
description: "Remove a profile",
|
|
255
|
-
options: [
|
|
256
|
-
{ flag: "<name>", desc: "Profile name (positional)" },
|
|
257
|
-
],
|
|
258
|
-
},
|
|
259
|
-
doctor: {
|
|
260
|
-
description: "Diagnose and repair profile health",
|
|
261
|
-
options: [
|
|
262
|
-
{ flag: "--fix", desc: "Auto-repair broken wrappers" },
|
|
263
|
-
],
|
|
264
|
-
},
|
|
265
|
-
version: {
|
|
266
|
-
description: "Show version information",
|
|
267
|
-
},
|
|
268
|
-
ping: {
|
|
269
|
-
description: "Test connection to browser extension",
|
|
270
|
-
},
|
|
271
|
-
reload: {
|
|
272
|
-
description: "Reload the browser extension (internal, used for upgrades)",
|
|
273
|
-
},
|
|
274
|
-
};
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SCREENSHOT_OPTIONS = exports.OPTION_GROUPS = void 0;
|
|
4
|
-
exports.OPTION_GROUPS = {
|
|
5
|
-
scope: {
|
|
6
|
-
name: "Scope Options",
|
|
7
|
-
description: "Filter which tabs/groups to operate on",
|
|
8
|
-
options: [
|
|
9
|
-
{ flag: "--tab <id>", desc: "Target specific tab(s) by ID", repeatable: true },
|
|
10
|
-
{ flag: "--group <name>", desc: "Target tabs in group by title" },
|
|
11
|
-
{ flag: "--group-id <id>", desc: "Target group by ID (use -1 for ungrouped)" },
|
|
12
|
-
{ flag: "--ungrouped", desc: "Alias for --group-id -1" },
|
|
13
|
-
{ flag: "--window <id|active|last-focused>", desc: "Target tabs in specific window" },
|
|
14
|
-
{ flag: "--all", desc: "Target all eligible tabs" },
|
|
15
|
-
],
|
|
16
|
-
},
|
|
17
|
-
pagination: {
|
|
18
|
-
name: "Pagination Options",
|
|
19
|
-
description: "Control result paging (default limit: 100)",
|
|
20
|
-
options: [
|
|
21
|
-
{ flag: "--limit <n>", desc: "Maximum items to return" },
|
|
22
|
-
{ flag: "--offset <n>", desc: "Skip first n items" },
|
|
23
|
-
{ flag: "--no-page", desc: "Disable pagination, return all results" },
|
|
24
|
-
],
|
|
25
|
-
},
|
|
26
|
-
};
|
|
27
|
-
exports.SCREENSHOT_OPTIONS = {
|
|
28
|
-
name: "Screenshot Options",
|
|
29
|
-
description: "Control screenshot capture",
|
|
30
|
-
options: [
|
|
31
|
-
{ flag: "--mode viewport|full", desc: "Capture mode" },
|
|
32
|
-
{ flag: "--format png|jpeg", desc: "Image format" },
|
|
33
|
-
{ flag: "--quality <n>", desc: "JPEG quality (0-100)" },
|
|
34
|
-
{ flag: "--tile-max-dim <px>", desc: "Max tile dimension in pixels" },
|
|
35
|
-
{ flag: "--max-bytes <n>", desc: "Max bytes per tile" },
|
|
36
|
-
{ flag: "--wait-for load|dom|settle|none", desc: "Wait for page readiness before capture" },
|
|
37
|
-
{ flag: "--wait-timeout-ms <ms>", desc: "Timeout for page readiness wait" },
|
|
38
|
-
{ flag: "--out <dir>", desc: "Write files to directory" },
|
|
39
|
-
{ flag: "--progress", desc: "Show progress during capture" },
|
|
40
|
-
],
|
|
41
|
-
};
|
package/dist/cli/lib/options.js
DELETED
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Central source of truth for CLI options and command metadata.
|
|
4
|
-
* This eliminates duplication between argument parsing and help display.
|
|
5
|
-
*
|
|
6
|
-
* Data definitions live in options-commands.ts and options-groups.ts;
|
|
7
|
-
* this module re-exports them and provides validation helpers.
|
|
8
|
-
*/
|
|
9
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
-
exports.COMMANDS = exports.SCREENSHOT_OPTIONS = exports.OPTION_GROUPS = void 0;
|
|
11
|
-
exports.getBooleanFlags = getBooleanFlags;
|
|
12
|
-
exports.getAllowedFlags = getAllowedFlags;
|
|
13
|
-
exports.getCommandAllowedFlags = getCommandAllowedFlags;
|
|
14
|
-
exports.getCommandGroups = getCommandGroups;
|
|
15
|
-
exports.getCommandOptions = getCommandOptions;
|
|
16
|
-
exports.commandSupportsGroup = commandSupportsGroup;
|
|
17
|
-
// ============================================================================
|
|
18
|
-
// Re-exports from split modules
|
|
19
|
-
// ============================================================================
|
|
20
|
-
var options_groups_1 = require("./options-groups");
|
|
21
|
-
Object.defineProperty(exports, "OPTION_GROUPS", { enumerable: true, get: function () { return options_groups_1.OPTION_GROUPS; } });
|
|
22
|
-
Object.defineProperty(exports, "SCREENSHOT_OPTIONS", { enumerable: true, get: function () { return options_groups_1.SCREENSHOT_OPTIONS; } });
|
|
23
|
-
var options_commands_1 = require("./options-commands");
|
|
24
|
-
Object.defineProperty(exports, "COMMANDS", { enumerable: true, get: function () { return options_commands_1.COMMANDS; } });
|
|
25
|
-
const options_groups_2 = require("./options-groups");
|
|
26
|
-
const options_commands_2 = require("./options-commands");
|
|
27
|
-
const GLOBAL_FLAGS = ["help", "json", "pretty"];
|
|
28
|
-
// ============================================================================
|
|
29
|
-
// Helper Functions
|
|
30
|
-
// ============================================================================
|
|
31
|
-
/**
|
|
32
|
-
* Get all allowed flags for argument parsing validation.
|
|
33
|
-
*/
|
|
34
|
-
function getBooleanFlags() {
|
|
35
|
-
const flags = new Set();
|
|
36
|
-
GLOBAL_FLAGS.forEach((flag) => flags.add(flag));
|
|
37
|
-
const addFromOptions = (options) => {
|
|
38
|
-
for (const opt of options) {
|
|
39
|
-
if (!/^--[a-z-]+$/.test(opt.flag.trim())) {
|
|
40
|
-
continue;
|
|
41
|
-
}
|
|
42
|
-
const match = opt.flag.match(/^--([a-z-]+)/);
|
|
43
|
-
if (match) {
|
|
44
|
-
flags.add(match[1]);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
for (const group of Object.values(options_groups_2.OPTION_GROUPS)) {
|
|
49
|
-
addFromOptions(group.options);
|
|
50
|
-
}
|
|
51
|
-
for (const cmd of Object.values(options_commands_2.COMMANDS)) {
|
|
52
|
-
if (cmd.options) {
|
|
53
|
-
addFromOptions(cmd.options);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
return flags;
|
|
57
|
-
}
|
|
58
|
-
function getAllowedFlags() {
|
|
59
|
-
const flags = new Set();
|
|
60
|
-
for (const flag of getBooleanFlags()) {
|
|
61
|
-
flags.add(flag);
|
|
62
|
-
}
|
|
63
|
-
// Add value flags from option groups
|
|
64
|
-
for (const group of Object.values(options_groups_2.OPTION_GROUPS)) {
|
|
65
|
-
for (const opt of group.options) {
|
|
66
|
-
const match = opt.flag.match(/^--([a-z-]+)/);
|
|
67
|
-
if (match)
|
|
68
|
-
flags.add(match[1]);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
// Add value flags from command options
|
|
72
|
-
for (const cmd of Object.values(options_commands_2.COMMANDS)) {
|
|
73
|
-
for (const opt of cmd.options || []) {
|
|
74
|
-
const match = opt.flag.match(/^--([a-z-]+)/);
|
|
75
|
-
if (match)
|
|
76
|
-
flags.add(match[1]);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
flags.add("profile");
|
|
80
|
-
return flags;
|
|
81
|
-
}
|
|
82
|
-
function getCommandAllowedFlags(command) {
|
|
83
|
-
const flags = new Set(GLOBAL_FLAGS);
|
|
84
|
-
flags.add("profile");
|
|
85
|
-
const meta = options_commands_2.COMMANDS[command];
|
|
86
|
-
const addOptions = (options) => {
|
|
87
|
-
if (!options) {
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
for (const opt of options) {
|
|
91
|
-
const match = opt.flag.match(/^--([a-z-]+)/);
|
|
92
|
-
if (match) {
|
|
93
|
-
flags.add(match[1]);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
};
|
|
97
|
-
if (meta?.groups) {
|
|
98
|
-
for (const groupKey of meta.groups) {
|
|
99
|
-
addOptions(options_groups_2.OPTION_GROUPS[groupKey]?.options);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
addOptions(meta?.options);
|
|
103
|
-
return flags;
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* Get the option groups for a command.
|
|
107
|
-
*/
|
|
108
|
-
function getCommandGroups(command) {
|
|
109
|
-
const meta = options_commands_2.COMMANDS[command];
|
|
110
|
-
return meta?.groups ? [...meta.groups] : [];
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* Get command-specific options (not from groups).
|
|
114
|
-
*/
|
|
115
|
-
function getCommandOptions(command) {
|
|
116
|
-
const meta = options_commands_2.COMMANDS[command];
|
|
117
|
-
return meta?.options ? [...meta.options] : [];
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* Check if a command supports a specific option group.
|
|
121
|
-
*/
|
|
122
|
-
function commandSupportsGroup(command, group) {
|
|
123
|
-
const meta = options_commands_2.COMMANDS[command];
|
|
124
|
-
return meta?.groups?.includes(group) ?? false;
|
|
125
|
-
}
|
package/dist/cli/lib/output.js
DELETED
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.printJson = printJson;
|
|
4
|
-
exports.errorOut = errorOut;
|
|
5
|
-
exports.setupStdoutErrorHandling = setupStdoutErrorHandling;
|
|
6
|
-
exports.emitVersionWarnings = emitVersionWarnings;
|
|
7
|
-
const version_1 = require("../../shared/version");
|
|
8
|
-
const profiles_1 = require("../../shared/profiles");
|
|
9
|
-
const extension_sync_1 = require("../../shared/extension-sync");
|
|
10
|
-
const config_1 = require("../../shared/config");
|
|
11
|
-
const wrapper_health_1 = require("../../shared/wrapper-health");
|
|
12
|
-
const client_1 = require("./client");
|
|
13
|
-
const client_2 = require("./client");
|
|
14
|
-
const setup_1 = require("./commands/setup");
|
|
15
|
-
function printJson(payload, pretty = true) {
|
|
16
|
-
try {
|
|
17
|
-
const active = (0, profiles_1.getActiveProfile)();
|
|
18
|
-
if (active) {
|
|
19
|
-
payload.profile = active.name;
|
|
20
|
-
payload.browser = active.profile.browser;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
catch {
|
|
24
|
-
// Don't let profile errors break output
|
|
25
|
-
}
|
|
26
|
-
const output = pretty ? JSON.stringify(payload, null, 2) : JSON.stringify(payload);
|
|
27
|
-
process.stdout.write(`${output}\n`);
|
|
28
|
-
}
|
|
29
|
-
function errorOut(message) {
|
|
30
|
-
// On ENOENT (socket missing), try syncing host + extension before showing error
|
|
31
|
-
if (message.includes("ENOENT")) {
|
|
32
|
-
try {
|
|
33
|
-
const config = (0, config_1.resolveConfig)();
|
|
34
|
-
const hostResult = (0, extension_sync_1.syncHost)(config.baseDataDir);
|
|
35
|
-
const extResult = (0, extension_sync_1.syncExtension)(config.baseDataDir);
|
|
36
|
-
if (hostResult.synced || extResult.synced) {
|
|
37
|
-
process.stderr.write(`[tabctl] synced host and extension to ${config.baseDataDir}\n`);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
catch {
|
|
41
|
-
// Sync is best-effort
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
const hints = {
|
|
45
|
-
"Unknown option: --format": "Use --json for JSON output. --format is only for report.",
|
|
46
|
-
"ENOENT": "Native host not running. Ensure the browser extension is loaded and active. If you recently upgraded, run: tabctl setup",
|
|
47
|
-
};
|
|
48
|
-
const hint = Object.entries(hints).find(([key]) => message.includes(key))?.[1];
|
|
49
|
-
if (hint) {
|
|
50
|
-
printJson({ ok: false, error: { message, hint } });
|
|
51
|
-
}
|
|
52
|
-
else {
|
|
53
|
-
printJson({ ok: false, error: { message } });
|
|
54
|
-
}
|
|
55
|
-
process.exit(1);
|
|
56
|
-
throw new Error(message);
|
|
57
|
-
}
|
|
58
|
-
function setupStdoutErrorHandling() {
|
|
59
|
-
process.stdout.on("error", (error) => {
|
|
60
|
-
if (error.code === "EPIPE") {
|
|
61
|
-
process.exit(0);
|
|
62
|
-
}
|
|
63
|
-
throw error;
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
function emitVersionWarnings(response, fallbackAction) {
|
|
67
|
-
const hostVersion = typeof response.version === "string" ? response.version : null;
|
|
68
|
-
const data = response.data;
|
|
69
|
-
const hostBaseVersion = data && typeof data.hostBaseVersion === "string" ? data.hostBaseVersion : null;
|
|
70
|
-
const isDevCli = version_1.DEV_BUILD;
|
|
71
|
-
// CLI ↔ host BASE_VERSION mismatch: auto-upgrade (sync files + trigger reload).
|
|
72
|
-
// Dev builds never sync — they use whatever host is already installed.
|
|
73
|
-
const effectiveHostBase = hostBaseVersion ?? hostVersion;
|
|
74
|
-
if (effectiveHostBase && effectiveHostBase !== version_1.BASE_VERSION && !isDevCli) {
|
|
75
|
-
// Downgrade: host is newer than CLI — warn but don't sync/reload
|
|
76
|
-
if ((0, extension_sync_1.compareBaseVersions)(version_1.BASE_VERSION, effectiveHostBase) < 0) {
|
|
77
|
-
process.stderr.write(`[tabctl] cli (${version_1.BASE_VERSION}) is older than host (${effectiveHostBase}). Consider upgrading: npm install -g tabctl\n`);
|
|
78
|
-
}
|
|
79
|
-
else {
|
|
80
|
-
try {
|
|
81
|
-
const config = (0, config_1.resolveConfig)();
|
|
82
|
-
const hostResult = (0, extension_sync_1.syncHost)(config.baseDataDir);
|
|
83
|
-
const extResult = (0, extension_sync_1.syncExtension)(config.baseDataDir);
|
|
84
|
-
const anySynced = hostResult.synced || extResult.synced;
|
|
85
|
-
if (anySynced) {
|
|
86
|
-
process.stderr.write(`[tabctl] upgraded: ${effectiveHostBase} → ${version_1.BASE_VERSION}. Reloading extension...\n`);
|
|
87
|
-
}
|
|
88
|
-
else {
|
|
89
|
-
process.stderr.write(`[tabctl] host is stale (${effectiveHostBase}), reloading extension...\n`);
|
|
90
|
-
}
|
|
91
|
-
(0, client_1.sendFireAndForget)({ id: (0, client_2.createRequestId)(), action: "reload", params: {} });
|
|
92
|
-
try {
|
|
93
|
-
repairActiveWrapper(config.baseDataDir);
|
|
94
|
-
}
|
|
95
|
-
catch {
|
|
96
|
-
// Wrapper repair is best-effort
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
catch {
|
|
100
|
-
process.stderr.write(`[tabctl] version mismatch: cli ${version_1.BASE_VERSION}, host ${effectiveHostBase}. Run: tabctl setup\n`);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
const extensionVersion = data && typeof data.extensionVersion === "string" ? data.extensionVersion : null;
|
|
105
|
-
const extensionComponent = data && typeof data.extensionComponent === "string" ? data.extensionComponent : null;
|
|
106
|
-
if (extensionVersion && hostVersion && extensionVersion !== hostVersion) {
|
|
107
|
-
process.stderr.write(`[tabctl] version mismatch: host ${hostVersion}, extension ${extensionVersion}. Reload the extension in your browser\n`);
|
|
108
|
-
}
|
|
109
|
-
if (extensionComponent && extensionComponent !== "extension") {
|
|
110
|
-
process.stderr.write(`[tabctl] unexpected extension component: ${extensionComponent}\n`);
|
|
111
|
-
}
|
|
112
|
-
const action = response.action || fallbackAction;
|
|
113
|
-
const extensionExpected = !["history", "version"].includes(action);
|
|
114
|
-
if (extensionExpected && !extensionVersion) {
|
|
115
|
-
process.stderr.write("[tabctl] extension version unavailable. Reload the extension in your browser\n");
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Repair the active profile's wrapper if the Node path is broken.
|
|
120
|
-
* Conservative: only fixes paths that are confirmed missing.
|
|
121
|
-
*/
|
|
122
|
-
function repairActiveWrapper(baseDataDir) {
|
|
123
|
-
const active = (0, profiles_1.getActiveProfile)();
|
|
124
|
-
if (!active)
|
|
125
|
-
return;
|
|
126
|
-
const wrapperPath = (0, wrapper_health_1.resolveWrapperPath)(active.profile.dataDir);
|
|
127
|
-
const check = (0, wrapper_health_1.checkWrapper)(wrapperPath);
|
|
128
|
-
if (check.ok || !check.info)
|
|
129
|
-
return;
|
|
130
|
-
const fs = require("node:fs");
|
|
131
|
-
const path = require("node:path");
|
|
132
|
-
const needsNodeFix = !fs.existsSync(check.info.nodePath);
|
|
133
|
-
const needsHostFix = !fs.existsSync(check.info.hostPath);
|
|
134
|
-
if (!needsNodeFix && !needsHostFix)
|
|
135
|
-
return;
|
|
136
|
-
const newNodePath = needsNodeFix ? process.execPath : check.info.nodePath;
|
|
137
|
-
const newHostPath = needsHostFix
|
|
138
|
-
? (0, extension_sync_1.resolveInstalledHostPath)(baseDataDir)
|
|
139
|
-
: check.info.hostPath;
|
|
140
|
-
(0, setup_1.writeWrapper)(newNodePath, newHostPath, check.info.profileName, path.dirname(wrapperPath));
|
|
141
|
-
if (needsNodeFix) {
|
|
142
|
-
process.stderr.write(`[tabctl] fixed wrapper Node path: ${check.info.nodePath} → ${newNodePath}\n`);
|
|
143
|
-
}
|
|
144
|
-
if (needsHostFix) {
|
|
145
|
-
process.stderr.write(`[tabctl] fixed wrapper host path: ${check.info.hostPath} → ${newHostPath}\n`);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.parseNumberOption = parseNumberOption;
|
|
4
|
-
exports.buildNextCommand = buildNextCommand;
|
|
5
|
-
exports.resolvePagination = resolvePagination;
|
|
6
|
-
const constants_1 = require("./constants");
|
|
7
|
-
const output_1 = require("./output");
|
|
8
|
-
function parseNumberOption(options, key) {
|
|
9
|
-
if (!Object.prototype.hasOwnProperty.call(options, key)) {
|
|
10
|
-
return null;
|
|
11
|
-
}
|
|
12
|
-
const value = Number(options[key]);
|
|
13
|
-
if (!Number.isFinite(value)) {
|
|
14
|
-
(0, output_1.errorOut)(`Invalid --${key} value`);
|
|
15
|
-
}
|
|
16
|
-
return value;
|
|
17
|
-
}
|
|
18
|
-
function buildNextCommand(command, scopeArgs, offset, limit) {
|
|
19
|
-
const parts = ["tabctl", command, ...scopeArgs, "--offset", String(offset), "--limit", String(limit)];
|
|
20
|
-
return parts.join(" ");
|
|
21
|
-
}
|
|
22
|
-
function resolvePagination(options, total, command, scopeArgs) {
|
|
23
|
-
const noPage = options["no-page"] === true;
|
|
24
|
-
if (noPage) {
|
|
25
|
-
return { offset: 0, limit: total, page: null };
|
|
26
|
-
}
|
|
27
|
-
const limitRaw = parseNumberOption(options, "limit");
|
|
28
|
-
const offsetRaw = parseNumberOption(options, "offset");
|
|
29
|
-
const limit = limitRaw != null ? Math.floor(limitRaw) : constants_1.DEFAULT_PAGE_LIMIT;
|
|
30
|
-
const offset = offsetRaw != null ? Math.floor(offsetRaw) : 0;
|
|
31
|
-
if (!Number.isFinite(limit) || limit <= 0) {
|
|
32
|
-
(0, output_1.errorOut)("--limit must be a positive number");
|
|
33
|
-
}
|
|
34
|
-
if (!Number.isFinite(offset) || offset < 0) {
|
|
35
|
-
(0, output_1.errorOut)("--offset must be a non-negative number");
|
|
36
|
-
}
|
|
37
|
-
const remaining = total - offset;
|
|
38
|
-
const returned = remaining > 0 ? Math.min(limit, remaining) : 0;
|
|
39
|
-
const hasMore = offset + limit < total;
|
|
40
|
-
const nextOffset = hasMore ? offset + limit : null;
|
|
41
|
-
const hint = hasMore ? `Partial results. Next: ${buildNextCommand(command, scopeArgs, nextOffset, limit)}` : null;
|
|
42
|
-
return {
|
|
43
|
-
offset,
|
|
44
|
-
limit,
|
|
45
|
-
page: {
|
|
46
|
-
offset,
|
|
47
|
-
limit,
|
|
48
|
-
returned,
|
|
49
|
-
total,
|
|
50
|
-
hasMore,
|
|
51
|
-
nextOffset,
|
|
52
|
-
hint,
|
|
53
|
-
},
|
|
54
|
-
};
|
|
55
|
-
}
|