tabctl 0.1.4 → 0.2.1

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.
Files changed (48) hide show
  1. package/{cli → dist/cli}/lib/commands/index.js +4 -2
  2. package/dist/cli/lib/commands/meta.js +226 -0
  3. package/dist/cli/lib/commands/params-groups.js +40 -0
  4. package/dist/cli/lib/commands/params-move.js +44 -0
  5. package/{cli → dist/cli}/lib/commands/params.js +61 -125
  6. package/{cli/lib/commands/meta.js → dist/cli/lib/commands/setup.js} +26 -222
  7. package/{cli/lib/options.js → dist/cli/lib/options-commands.js} +3 -155
  8. package/dist/cli/lib/options-groups.js +41 -0
  9. package/dist/cli/lib/options.js +125 -0
  10. package/{cli → dist/cli}/lib/output.js +5 -4
  11. package/dist/cli/lib/policy-filter.js +202 -0
  12. package/dist/cli/lib/response.js +235 -0
  13. package/{cli → dist/cli}/lib/scope.js +3 -31
  14. package/dist/cli/tabctl.js +463 -0
  15. package/dist/extension/background.js +3398 -0
  16. package/dist/extension/lib/archive.js +444 -0
  17. package/dist/extension/lib/content.js +320 -0
  18. package/dist/extension/lib/deps.js +4 -0
  19. package/dist/extension/lib/groups.js +443 -0
  20. package/dist/extension/lib/inspect.js +316 -0
  21. package/dist/extension/lib/move.js +342 -0
  22. package/dist/extension/lib/screenshot.js +367 -0
  23. package/dist/extension/lib/tabs.js +395 -0
  24. package/dist/extension/lib/undo-handlers.js +439 -0
  25. package/{extension → dist/extension}/manifest.json +2 -2
  26. package/dist/host/host.js +124 -0
  27. package/{host/host.js → dist/host/lib/handlers.js} +84 -187
  28. package/{shared → dist/shared}/version.js +2 -2
  29. package/package.json +12 -10
  30. package/cli/tabctl.js +0 -841
  31. package/extension/background.js +0 -3372
  32. package/extension/manifest.template.json +0 -22
  33. /package/{cli → dist/cli}/lib/args.js +0 -0
  34. /package/{cli → dist/cli}/lib/client.js +0 -0
  35. /package/{cli → dist/cli}/lib/commands/list.js +0 -0
  36. /package/{cli → dist/cli}/lib/commands/profile.js +0 -0
  37. /package/{cli → dist/cli}/lib/constants.js +0 -0
  38. /package/{cli → dist/cli}/lib/help.js +0 -0
  39. /package/{cli → dist/cli}/lib/pagination.js +0 -0
  40. /package/{cli → dist/cli}/lib/policy.js +0 -0
  41. /package/{cli → dist/cli}/lib/report.js +0 -0
  42. /package/{cli → dist/cli}/lib/snapshot.js +0 -0
  43. /package/{cli → dist/cli}/lib/types.js +0 -0
  44. /package/{host → dist/host}/host.sh +0 -0
  45. /package/{host → dist/host}/lib/undo.js +0 -0
  46. /package/{shared → dist/shared}/config.js +0 -0
  47. /package/{shared → dist/shared}/extension-sync.js +0 -0
  48. /package/{shared → dist/shared}/profiles.js +0 -0
@@ -1,19 +1,19 @@
1
1
  "use strict";
2
2
  /**
3
- * Meta command handlers: version, ping, setup, skill, policy, history, undo
4
- * These commands don't operate on tab data and are relatively self-contained.
3
+ * Setup command handler: interactive browser profile configuration.
4
+ * Extracted from meta.ts for modularity.
5
5
  */
6
6
  var __importDefault = (this && this.__importDefault) || function (mod) {
7
7
  return (mod && mod.__esModule) ? mod : { "default": mod };
8
8
  };
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.resolveBrowser = resolveBrowser;
11
+ exports.resolveExtensionId = resolveExtensionId;
12
+ exports.promptExtensionId = promptExtensionId;
13
+ exports.resolveNodePath = resolveNodePath;
14
+ exports.resolveManifestDir = resolveManifestDir;
15
+ exports.writeWrapper = writeWrapper;
10
16
  exports.runSetup = runSetup;
11
- exports.runSkillInstall = runSkillInstall;
12
- exports.runVersion = runVersion;
13
- exports.runPolicy = runPolicy;
14
- exports.runHistory = runHistory;
15
- exports.runUndo = runUndo;
16
- exports.runPing = runPing;
17
17
  const fs_1 = __importDefault(require("fs"));
18
18
  const os_1 = __importDefault(require("os"));
19
19
  const path_1 = __importDefault(require("path"));
@@ -21,13 +21,9 @@ const readline_1 = __importDefault(require("readline"));
21
21
  const node_child_process_1 = require("node:child_process");
22
22
  const constants_1 = require("../constants");
23
23
  const output_1 = require("../output");
24
- const client_1 = require("../client");
25
- const policy_1 = require("../policy");
26
24
  const profiles_1 = require("../../../shared/profiles");
25
+ const config_1 = require("../../../shared/config");
27
26
  const extension_sync_1 = require("../../../shared/extension-sync");
28
- // ============================================================================
29
- // Setup Command
30
- // ============================================================================
31
27
  function resolveBrowser(value) {
32
28
  if (typeof value !== "string") {
33
29
  return null;
@@ -263,6 +259,9 @@ async function runSetup(options, prettyOutput) {
263
259
  profileEntry.userDataDir = userDataDir;
264
260
  }
265
261
  const registry = (0, profiles_1.addProfile)(profileName, profileEntry);
262
+ // Ensure printJson footer reflects the newly-created profile
263
+ (0, config_1.resetConfig)();
264
+ process.env.TABCTL_PROFILE = profileName;
266
265
  (0, output_1.printJson)({
267
266
  ok: true,
268
267
  action: "setup",
@@ -281,213 +280,18 @@ async function runSetup(options, prettyOutput) {
281
280
  extensionSynced: extensionSync?.synced || false,
282
281
  },
283
282
  }, prettyOutput);
284
- }
285
- // ============================================================================
286
- // Skill Command
287
- // ============================================================================
288
- function formatCliArgValue(value) {
289
- const raw = String(value);
290
- if (!raw) {
291
- return raw;
292
- }
293
- if (/[\s"]/g.test(raw)) {
294
- const escaped = raw.replace(/"/g, "\\\"");
295
- return `"${escaped}"`;
296
- }
297
- return raw;
298
- }
299
- function resolveProjectRoot() {
300
- try {
301
- return fs_1.default.realpathSync(process.cwd());
302
- }
303
- catch {
304
- return path_1.default.resolve(process.cwd());
305
- }
306
- }
307
- function resolveConfigHome() {
308
- return process.env.XDG_CONFIG_HOME || path_1.default.join(os_1.default.homedir(), ".config");
309
- }
310
- function resolveSkillTargetDir(globalInstall) {
311
- if (globalInstall) {
312
- return path_1.default.join(resolveConfigHome(), "opencode", "skills", constants_1.SKILL_NAME);
313
- }
314
- return path_1.default.join(resolveProjectRoot(), ".opencode", "skills", constants_1.SKILL_NAME);
315
- }
316
- function runSkillsCli(args) {
317
- const result = (0, node_child_process_1.spawnSync)("npx", ["skills", ...args], { stdio: "pipe" });
318
- if (result.error) {
319
- (0, output_1.errorOut)(`Failed to run skills CLI: ${result.error.message}`);
320
- }
321
- if (typeof result.status === "number" && result.status !== 0) {
322
- const stderr = result.stderr ? result.stderr.toString().trim() : "";
323
- const stdout = result.stdout ? result.stdout.toString().trim() : "";
324
- const detail = stderr || stdout;
325
- const message = detail ? `skills CLI failed: ${detail}` : `skills CLI exited with status ${result.status}`;
326
- (0, output_1.errorOut)(message);
327
- }
328
- }
329
- function runSkillInstall(options, prettyOutput) {
330
- const globalInstall = options.global === true;
331
- const installTarget = resolveSkillTargetDir(globalInstall);
332
- const agents = Array.isArray(options.agent)
333
- ? options.agent.filter((value) => typeof value === "string" && value.trim())
334
- : [];
335
- const args = ["add", constants_1.SKILL_REPO, "--skill", constants_1.SKILL_NAME];
336
- if (agents.length > 0) {
337
- for (const agent of agents) {
338
- args.push("-a", agent);
339
- }
340
- }
341
- if (globalInstall) {
342
- args.push("-g");
343
- }
344
- const hintAgents = agents.length > 0 ? agents.map((agent) => `-a ${formatCliArgValue(agent)}`).join(" ") : "";
345
- const installHintParts = ["npx skills add", formatCliArgValue(constants_1.SKILL_REPO), "--skill", constants_1.SKILL_NAME];
346
- if (hintAgents) {
347
- installHintParts.push(hintAgents);
348
- }
349
- if (globalInstall) {
350
- installHintParts.push("-g");
351
- }
352
- const installHint = installHintParts.join(" ").trim();
353
- runSkillsCli(args);
354
- (0, output_1.printJson)({
355
- ok: true,
356
- data: {
357
- name: constants_1.SKILL_NAME,
358
- targetDir: installTarget,
359
- scope: globalInstall ? "global" : "project",
360
- installHint,
361
- tool: "skills",
362
- },
363
- }, prettyOutput);
364
- }
365
- // ============================================================================
366
- // Version Command
367
- // ============================================================================
368
- function runVersion(prettyOutput) {
369
- (0, output_1.printJson)({
370
- ok: true,
371
- data: {
372
- version: constants_1.VERSION,
373
- baseVersion: constants_1.BASE_VERSION,
374
- gitSha: constants_1.GIT_SHA,
375
- dirty: constants_1.DIRTY,
376
- component: "cli",
377
- },
378
- }, prettyOutput);
379
- }
380
- // ============================================================================
381
- // Policy Command
382
- // ============================================================================
383
- function runPolicy(options, policyContext, prettyOutput) {
384
- const policyPath = (0, policy_1.defaultPolicyPath)();
385
- if (options.init) {
386
- if (fs_1.default.existsSync(policyPath)) {
387
- (0, output_1.printJson)({
388
- ok: true,
389
- data: {
390
- status: "exists",
391
- path: policyPath,
392
- },
393
- }, prettyOutput);
394
- return;
395
- }
396
- const dir = path_1.default.dirname(policyPath);
397
- fs_1.default.mkdirSync(dir, { recursive: true });
398
- fs_1.default.writeFileSync(policyPath, JSON.stringify((0, policy_1.defaultPolicyTemplate)(), null, 2), "utf8");
399
- (0, output_1.printJson)({
400
- ok: true,
401
- data: {
402
- status: "created",
403
- path: policyPath,
404
- },
405
- }, prettyOutput);
406
- return;
407
- }
408
- const policySummary = (0, policy_1.summarizePolicy)(policyContext.policy, policyContext.path);
409
- (0, output_1.printJson)({
410
- ok: true,
411
- data: {
412
- ...policySummary,
413
- path: policyPath,
414
- },
415
- }, prettyOutput);
416
- }
417
- // ============================================================================
418
- // History Command
419
- // ============================================================================
420
- async function runHistory(options, prettyOutput) {
421
- const params = {
422
- limit: options.limit ? Number(options.limit) : undefined,
423
- };
424
- const response = await (0, client_1.sendRequest)({
425
- id: (0, client_1.createRequestId)(),
426
- action: "history",
427
- params,
428
- client: {
429
- component: "cli",
430
- version: constants_1.VERSION,
431
- baseVersion: constants_1.BASE_VERSION,
432
- gitSha: constants_1.GIT_SHA,
433
- dirty: constants_1.DIRTY,
434
- },
435
- });
436
- (0, output_1.printJson)(response, prettyOutput);
437
- if (!response.ok) {
438
- process.exit(1);
439
- }
440
- }
441
- // ============================================================================
442
- // Undo Command
443
- // ============================================================================
444
- async function runUndo(options, prettyOutput) {
445
- const positionalTxid = options._[0];
446
- const flagTxid = options.txid;
447
- const useLatest = options.latest === true;
448
- if (useLatest && (positionalTxid || flagTxid)) {
449
- (0, output_1.errorOut)("--latest cannot be combined with a txid argument or --txid");
450
- }
451
- const txid = positionalTxid || flagTxid;
452
- const params = {
453
- txid,
454
- latest: useLatest,
455
- };
456
- const response = await (0, client_1.sendRequest)({
457
- id: (0, client_1.createRequestId)(),
458
- action: "undo",
459
- params,
460
- client: {
461
- component: "cli",
462
- version: constants_1.VERSION,
463
- baseVersion: constants_1.BASE_VERSION,
464
- gitSha: constants_1.GIT_SHA,
465
- dirty: constants_1.DIRTY,
466
- },
467
- });
468
- (0, output_1.printJson)(response, prettyOutput);
469
- if (!response.ok) {
470
- process.exit(1);
471
- }
472
- }
473
- // ============================================================================
474
- // Ping Command
475
- // ============================================================================
476
- async function runPing(prettyOutput) {
477
- const response = await (0, client_1.sendRequest)({
478
- id: (0, client_1.createRequestId)(),
479
- action: "ping",
480
- params: {},
481
- client: {
482
- component: "cli",
483
- version: constants_1.VERSION,
484
- baseVersion: constants_1.BASE_VERSION,
485
- gitSha: constants_1.GIT_SHA,
486
- dirty: constants_1.DIRTY,
487
- },
488
- });
489
- (0, output_1.printJson)(response, prettyOutput);
490
- if (!response.ok) {
491
- process.exit(1);
492
- }
283
+ if (registry.default !== profileName) {
284
+ process.stderr.write([
285
+ "",
286
+ `Profile "${profileName}" created (current default: "${registry.default}").`,
287
+ ` To use: tabctl --profile ${profileName} <command>`,
288
+ ` To make default: tabctl profile-switch ${profileName}`,
289
+ "",
290
+ ].join("\n"));
291
+ }
292
+ process.stderr.write([
293
+ `Verify connection: tabctl --profile ${profileName} ping`,
294
+ `If ping fails, ensure the ${browser === "edge" ? "Edge" : "Chrome"} extension is active.`,
295
+ "",
296
+ ].join("\n"));
493
297
  }
@@ -1,61 +1,7 @@
1
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
2
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.COMMANDS = exports.SCREENSHOT_OPTIONS = exports.OPTION_GROUPS = void 0;
8
- exports.getBooleanFlags = getBooleanFlags;
9
- exports.getAllowedFlags = getAllowedFlags;
10
- exports.getCommandAllowedFlags = getCommandAllowedFlags;
11
- exports.getCommandGroups = getCommandGroups;
12
- exports.getCommandOptions = getCommandOptions;
13
- exports.commandSupportsGroup = commandSupportsGroup;
14
- const GLOBAL_FLAGS = ["help", "json", "pretty"];
15
- // ============================================================================
16
- // Option Group Definitions
17
- // ============================================================================
18
- exports.OPTION_GROUPS = {
19
- scope: {
20
- name: "Scope Options",
21
- description: "Filter which tabs/groups to operate on",
22
- options: [
23
- { flag: "--tab <id>", desc: "Target specific tab(s) by ID", repeatable: true },
24
- { flag: "--group <name>", desc: "Target tabs in group by title" },
25
- { flag: "--group-id <id>", desc: "Target group by ID (use -1 for ungrouped)" },
26
- { flag: "--ungrouped", desc: "Alias for --group-id -1" },
27
- { flag: "--window <id|active|last-focused>", desc: "Target tabs in specific window" },
28
- { flag: "--all", desc: "Target all eligible tabs" },
29
- ],
30
- },
31
- pagination: {
32
- name: "Pagination Options",
33
- description: "Control result paging (default limit: 100)",
34
- options: [
35
- { flag: "--limit <n>", desc: "Maximum items to return" },
36
- { flag: "--offset <n>", desc: "Skip first n items" },
37
- { flag: "--no-page", desc: "Disable pagination, return all results" },
38
- ],
39
- },
40
- };
41
- exports.SCREENSHOT_OPTIONS = {
42
- name: "Screenshot Options",
43
- description: "Control screenshot capture",
44
- options: [
45
- { flag: "--mode viewport|full", desc: "Capture mode" },
46
- { flag: "--format png|jpeg", desc: "Image format" },
47
- { flag: "--quality <n>", desc: "JPEG quality (0-100)" },
48
- { flag: "--tile-max-dim <px>", desc: "Max tile dimension in pixels" },
49
- { flag: "--max-bytes <n>", desc: "Max bytes per tile" },
50
- { flag: "--wait-for load|dom|settle|none", desc: "Wait for page readiness before capture" },
51
- { flag: "--wait-timeout-ms <ms>", desc: "Timeout for page readiness wait" },
52
- { flag: "--out <dir>", desc: "Write files to directory" },
53
- { flag: "--progress", desc: "Show progress during capture" },
54
- ],
55
- };
56
- // ============================================================================
57
- // Command Metadata
58
- // ============================================================================
3
+ exports.COMMANDS = void 0;
4
+ const options_groups_1 = require("./options-groups");
59
5
  exports.COMMANDS = {
60
6
  help: {
61
7
  description: "Show help information",
@@ -111,7 +57,7 @@ exports.COMMANDS = {
111
57
  screenshot: {
112
58
  description: "Capture screenshots from tabs",
113
59
  groups: ["scope"],
114
- options: exports.SCREENSHOT_OPTIONS.options,
60
+ options: options_groups_1.SCREENSHOT_OPTIONS.options,
115
61
  },
116
62
  focus: {
117
63
  description: "Focus a specific tab",
@@ -308,101 +254,3 @@ exports.COMMANDS = {
308
254
  description: "Test connection to browser extension",
309
255
  },
310
256
  };
311
- // ============================================================================
312
- // Helper Functions
313
- // ============================================================================
314
- /**
315
- * Get all allowed flags for argument parsing validation.
316
- */
317
- function getBooleanFlags() {
318
- const flags = new Set();
319
- GLOBAL_FLAGS.forEach((flag) => flags.add(flag));
320
- const addFromOptions = (options) => {
321
- for (const opt of options) {
322
- if (!/^--[a-z-]+$/.test(opt.flag.trim())) {
323
- continue;
324
- }
325
- const match = opt.flag.match(/^--([a-z-]+)/);
326
- if (match) {
327
- flags.add(match[1]);
328
- }
329
- }
330
- };
331
- for (const group of Object.values(exports.OPTION_GROUPS)) {
332
- addFromOptions(group.options);
333
- }
334
- for (const cmd of Object.values(exports.COMMANDS)) {
335
- if (cmd.options) {
336
- addFromOptions(cmd.options);
337
- }
338
- }
339
- return flags;
340
- }
341
- function getAllowedFlags() {
342
- const flags = new Set();
343
- for (const flag of getBooleanFlags()) {
344
- flags.add(flag);
345
- }
346
- // Add value flags from option groups
347
- for (const group of Object.values(exports.OPTION_GROUPS)) {
348
- for (const opt of group.options) {
349
- const match = opt.flag.match(/^--([a-z-]+)/);
350
- if (match)
351
- flags.add(match[1]);
352
- }
353
- }
354
- // Add value flags from command options
355
- for (const cmd of Object.values(exports.COMMANDS)) {
356
- for (const opt of cmd.options || []) {
357
- const match = opt.flag.match(/^--([a-z-]+)/);
358
- if (match)
359
- flags.add(match[1]);
360
- }
361
- }
362
- flags.add("profile");
363
- return flags;
364
- }
365
- function getCommandAllowedFlags(command) {
366
- const flags = new Set(GLOBAL_FLAGS);
367
- flags.add("profile");
368
- const meta = exports.COMMANDS[command];
369
- const addOptions = (options) => {
370
- if (!options) {
371
- return;
372
- }
373
- for (const opt of options) {
374
- const match = opt.flag.match(/^--([a-z-]+)/);
375
- if (match) {
376
- flags.add(match[1]);
377
- }
378
- }
379
- };
380
- if (meta?.groups) {
381
- for (const groupKey of meta.groups) {
382
- addOptions(exports.OPTION_GROUPS[groupKey]?.options);
383
- }
384
- }
385
- addOptions(meta?.options);
386
- return flags;
387
- }
388
- /**
389
- * Get the option groups for a command.
390
- */
391
- function getCommandGroups(command) {
392
- const meta = exports.COMMANDS[command];
393
- return meta?.groups ? [...meta.groups] : [];
394
- }
395
- /**
396
- * Get command-specific options (not from groups).
397
- */
398
- function getCommandOptions(command) {
399
- const meta = exports.COMMANDS[command];
400
- return meta?.options ? [...meta.options] : [];
401
- }
402
- /**
403
- * Check if a command supports a specific option group.
404
- */
405
- function commandSupportsGroup(command, group) {
406
- const meta = exports.COMMANDS[command];
407
- return meta?.groups?.includes(group) ?? false;
408
- }
@@ -0,0 +1,41 @@
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
+ };
@@ -0,0 +1,125 @@
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
+ }
@@ -23,8 +23,9 @@ function printJson(payload, pretty = true) {
23
23
  function errorOut(message) {
24
24
  const hints = {
25
25
  "Unknown option: --format": "Use --json for JSON output. --format is only for report.",
26
+ "ENOENT": "Native host not running. Ensure the browser extension is loaded and active. If you recently upgraded, run: tabctl setup",
26
27
  };
27
- const hint = hints[message];
28
+ const hint = Object.entries(hints).find(([key]) => message.includes(key))?.[1];
28
29
  if (hint) {
29
30
  printJson({ ok: false, error: { message, hint } });
30
31
  }
@@ -45,13 +46,13 @@ function setupStdoutErrorHandling() {
45
46
  function emitVersionWarnings(response, fallbackAction) {
46
47
  const hostVersion = typeof response.version === "string" ? response.version : null;
47
48
  if (hostVersion && hostVersion !== version_1.VERSION) {
48
- process.stderr.write(`[tabctl] version mismatch: cli ${version_1.VERSION}, host ${hostVersion}\n`);
49
+ process.stderr.write(`[tabctl] version mismatch: cli ${version_1.VERSION}, host ${hostVersion}. Run: tabctl setup\n`);
49
50
  }
50
51
  const data = response.data;
51
52
  const extensionVersion = data && typeof data.extensionVersion === "string" ? data.extensionVersion : null;
52
53
  const extensionComponent = data && typeof data.extensionComponent === "string" ? data.extensionComponent : null;
53
54
  if (extensionVersion && hostVersion && extensionVersion !== hostVersion) {
54
- process.stderr.write(`[tabctl] version mismatch: host ${hostVersion}, extension ${extensionVersion}\n`);
55
+ process.stderr.write(`[tabctl] version mismatch: host ${hostVersion}, extension ${extensionVersion}. Reload the extension in your browser\n`);
55
56
  }
56
57
  if (extensionComponent && extensionComponent !== "extension") {
57
58
  process.stderr.write(`[tabctl] unexpected extension component: ${extensionComponent}\n`);
@@ -59,6 +60,6 @@ function emitVersionWarnings(response, fallbackAction) {
59
60
  const action = response.action || fallbackAction;
60
61
  const extensionExpected = !["history", "version"].includes(action);
61
62
  if (extensionExpected && !extensionVersion) {
62
- process.stderr.write("[tabctl] extension version unavailable; reload the extension to validate version match\n");
63
+ process.stderr.write("[tabctl] extension version unavailable. Reload the extension in your browser\n");
63
64
  }
64
65
  }