tabctl 0.3.0 → 0.4.0
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 +7 -5
- package/dist/cli/lib/client.js +23 -2
- package/dist/cli/lib/commands/index.js +2 -1
- package/dist/cli/lib/commands/meta.js +16 -13
- package/dist/cli/lib/commands/params-groups.js +8 -0
- package/dist/cli/lib/commands/params.js +6 -3
- package/dist/cli/lib/commands/setup.js +104 -126
- package/dist/cli/lib/options-commands.js +13 -1
- package/dist/cli/lib/output.js +36 -1
- package/dist/cli/lib/policy-filter.js +1 -1
- package/dist/cli/lib/policy.js +3 -3
- package/dist/cli/lib/response.js +9 -9
- package/dist/cli/tabctl.js +9 -1
- package/dist/extension/background.js +429 -43
- package/dist/extension/lib/groups.js +89 -3
- package/dist/extension/lib/screenshot.js +2 -2
- package/dist/extension/lib/tabs.js +97 -36
- package/dist/extension/lib/undo-handlers.js +8 -0
- package/dist/extension/manifest.json +2 -2
- package/dist/host/host.bundle.js +669 -0
- package/dist/host/host.js +30 -11
- package/dist/host/launcher/go.mod +3 -0
- package/dist/host/launcher/main.go +109 -0
- package/dist/host/lib/handlers.js +7 -5
- package/dist/host/lib/undo.js +6 -6
- package/dist/shared/config.js +36 -13
- package/dist/shared/extension-sync.js +59 -15
- package/dist/shared/profiles.js +5 -5
- package/dist/shared/version.js +2 -2
- package/package.json +11 -4
package/dist/cli/lib/output.js
CHANGED
|
@@ -6,6 +6,10 @@ exports.setupStdoutErrorHandling = setupStdoutErrorHandling;
|
|
|
6
6
|
exports.emitVersionWarnings = emitVersionWarnings;
|
|
7
7
|
const version_1 = require("../../shared/version");
|
|
8
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 client_1 = require("./client");
|
|
12
|
+
const client_2 = require("./client");
|
|
9
13
|
function printJson(payload, pretty = true) {
|
|
10
14
|
try {
|
|
11
15
|
const active = (0, profiles_1.getActiveProfile)();
|
|
@@ -21,6 +25,20 @@ function printJson(payload, pretty = true) {
|
|
|
21
25
|
process.stdout.write(`${output}\n`);
|
|
22
26
|
}
|
|
23
27
|
function errorOut(message) {
|
|
28
|
+
// On ENOENT (socket missing), try syncing host + extension before showing error
|
|
29
|
+
if (message.includes("ENOENT")) {
|
|
30
|
+
try {
|
|
31
|
+
const config = (0, config_1.resolveConfig)();
|
|
32
|
+
const hostResult = (0, extension_sync_1.syncHost)(config.baseDataDir);
|
|
33
|
+
const extResult = (0, extension_sync_1.syncExtension)(config.baseDataDir);
|
|
34
|
+
if (hostResult.synced || extResult.synced) {
|
|
35
|
+
process.stderr.write(`[tabctl] synced host and extension to ${config.baseDataDir}\n`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// Sync is best-effort
|
|
40
|
+
}
|
|
41
|
+
}
|
|
24
42
|
const hints = {
|
|
25
43
|
"Unknown option: --format": "Use --json for JSON output. --format is only for report.",
|
|
26
44
|
"ENOENT": "Native host not running. Ensure the browser extension is loaded and active. If you recently upgraded, run: tabctl setup",
|
|
@@ -45,8 +63,25 @@ function setupStdoutErrorHandling() {
|
|
|
45
63
|
}
|
|
46
64
|
function emitVersionWarnings(response, fallbackAction) {
|
|
47
65
|
const hostVersion = typeof response.version === "string" ? response.version : null;
|
|
66
|
+
// CLI ↔ host version mismatch: auto-upgrade (sync files + trigger reload)
|
|
48
67
|
if (hostVersion && hostVersion !== version_1.VERSION) {
|
|
49
|
-
|
|
68
|
+
try {
|
|
69
|
+
const config = (0, config_1.resolveConfig)();
|
|
70
|
+
const hostResult = (0, extension_sync_1.syncHost)(config.baseDataDir);
|
|
71
|
+
const extResult = (0, extension_sync_1.syncExtension)(config.baseDataDir);
|
|
72
|
+
const anySynced = hostResult.synced || extResult.synced;
|
|
73
|
+
// Send reload if we synced new files OR if the running host is stale
|
|
74
|
+
if (anySynced) {
|
|
75
|
+
process.stderr.write(`[tabctl] upgraded: ${hostVersion} → ${version_1.BASE_VERSION}. Reloading extension...\n`);
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
process.stderr.write(`[tabctl] host is stale (${hostVersion}), reloading extension...\n`);
|
|
79
|
+
}
|
|
80
|
+
(0, client_1.sendFireAndForget)({ id: (0, client_2.createRequestId)(), action: "reload", params: {} });
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
process.stderr.write(`[tabctl] version mismatch: cli ${version_1.VERSION}, host ${hostVersion}. Run: tabctl setup\n`);
|
|
84
|
+
}
|
|
50
85
|
}
|
|
51
86
|
const data = response.data;
|
|
52
87
|
const extensionVersion = data && typeof data.extensionVersion === "string" ? data.extensionVersion : null;
|
|
@@ -120,7 +120,7 @@ function applyPolicyFilter(command, params, snapshot, policyContext, policySumma
|
|
|
120
120
|
protected: protectedTabs.map(mapProtectedTab),
|
|
121
121
|
};
|
|
122
122
|
}
|
|
123
|
-
else if (command === "group-update" || command === "group-ungroup") {
|
|
123
|
+
else if (command === "group-update" || command === "group-ungroup" || command === "group-gather") {
|
|
124
124
|
if (!eligibleIds.length || protectedTabs.length > 0) {
|
|
125
125
|
earlyResponse = {
|
|
126
126
|
ok: true,
|
package/dist/cli/lib/policy.js
CHANGED
|
@@ -10,7 +10,7 @@ exports.getDomain = getDomain;
|
|
|
10
10
|
exports.evaluateTab = evaluateTab;
|
|
11
11
|
exports.annotateEntry = annotateEntry;
|
|
12
12
|
exports.summarizePolicy = summarizePolicy;
|
|
13
|
-
const
|
|
13
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
14
14
|
const config_1 = require("../../shared/config");
|
|
15
15
|
function defaultPolicyPath() {
|
|
16
16
|
return (0, config_1.resolveConfig)().policyPath;
|
|
@@ -25,10 +25,10 @@ function defaultPolicyTemplate() {
|
|
|
25
25
|
}
|
|
26
26
|
function loadPolicy() {
|
|
27
27
|
const resolvedPath = defaultPolicyPath();
|
|
28
|
-
if (!
|
|
28
|
+
if (!node_fs_1.default.existsSync(resolvedPath)) {
|
|
29
29
|
return { policy: null, path: resolvedPath };
|
|
30
30
|
}
|
|
31
|
-
const raw =
|
|
31
|
+
const raw = node_fs_1.default.readFileSync(resolvedPath, "utf8");
|
|
32
32
|
const parsed = JSON.parse(raw);
|
|
33
33
|
return { policy: parsed, path: resolvedPath };
|
|
34
34
|
}
|
package/dist/cli/lib/response.js
CHANGED
|
@@ -9,8 +9,8 @@ exports.buildDedupeOutput = buildDedupeOutput;
|
|
|
9
9
|
exports.extractDedupePlan = extractDedupePlan;
|
|
10
10
|
exports.formatReport = formatReport;
|
|
11
11
|
exports.writeScreenshots = writeScreenshots;
|
|
12
|
-
const
|
|
13
|
-
const
|
|
12
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
13
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
14
14
|
const report_1 = require("./report");
|
|
15
15
|
const policy_1 = require("./policy");
|
|
16
16
|
const output_1 = require("./output");
|
|
@@ -170,7 +170,7 @@ function formatReport(response, options, prettyOutput) {
|
|
|
170
170
|
(0, output_1.errorOut)(`Unknown report format: ${format}`);
|
|
171
171
|
}
|
|
172
172
|
if (options.out) {
|
|
173
|
-
|
|
173
|
+
node_fs_1.default.writeFileSync(String(options.out), content, "utf8");
|
|
174
174
|
(0, output_1.printJson)({ ok: true, data: { writtenTo: options.out, format, count: entries.length, ...(page ? { page } : {}) } }, prettyOutput);
|
|
175
175
|
return { printed: true };
|
|
176
176
|
}
|
|
@@ -190,13 +190,13 @@ function writeScreenshots(response, options, prettyOutput) {
|
|
|
190
190
|
const page = data && "page" in data ? data.page : undefined;
|
|
191
191
|
const outDir = options.out
|
|
192
192
|
? String(options.out)
|
|
193
|
-
:
|
|
194
|
-
|
|
193
|
+
: node_path_1.default.join(process.cwd(), ".tabctl", "screenshots", String(Date.now()));
|
|
194
|
+
node_fs_1.default.mkdirSync(outDir, { recursive: true });
|
|
195
195
|
let filesWritten = 0;
|
|
196
196
|
const sanitized = entries.map((entry) => {
|
|
197
197
|
const tabId = entry.tabId;
|
|
198
|
-
const tabDir =
|
|
199
|
-
|
|
198
|
+
const tabDir = node_path_1.default.join(outDir, String(tabId ?? "unknown"));
|
|
199
|
+
node_fs_1.default.mkdirSync(tabDir, { recursive: true });
|
|
200
200
|
const tiles = Array.isArray(entry.tiles) ? entry.tiles : [];
|
|
201
201
|
const sanitizedTiles = tiles.map((tile) => {
|
|
202
202
|
const rawUrl = tile.dataUrl;
|
|
@@ -215,9 +215,9 @@ function writeScreenshots(response, options, prettyOutput) {
|
|
|
215
215
|
const total = Number.isFinite(tile.total) ? Number(tile.total) : null;
|
|
216
216
|
const suffix = total && total > 1 ? `-of-${total}` : "";
|
|
217
217
|
const filename = `screenshot-${index}${suffix}.${ext}`;
|
|
218
|
-
const filePath =
|
|
218
|
+
const filePath = node_path_1.default.join(tabDir, filename);
|
|
219
219
|
const buffer = Buffer.from(base64, "base64");
|
|
220
|
-
|
|
220
|
+
node_fs_1.default.writeFileSync(filePath, buffer);
|
|
221
221
|
filesWritten += 1;
|
|
222
222
|
return {
|
|
223
223
|
...rest,
|
package/dist/cli/tabctl.js
CHANGED
|
@@ -242,6 +242,10 @@ async function main() {
|
|
|
242
242
|
action = "group-assign";
|
|
243
243
|
params = (0, commands_2.buildGroupAssignParams)(options);
|
|
244
244
|
break;
|
|
245
|
+
case "group-gather":
|
|
246
|
+
action = "group-gather";
|
|
247
|
+
params = (0, commands_2.buildGroupGatherParams)(options);
|
|
248
|
+
break;
|
|
245
249
|
case "move-tab":
|
|
246
250
|
action = "move-tab";
|
|
247
251
|
params = (0, commands_2.buildMoveTabParams)(options);
|
|
@@ -270,6 +274,10 @@ async function main() {
|
|
|
270
274
|
action = "screenshot";
|
|
271
275
|
params = (0, commands_2.buildScreenshotParams)(options);
|
|
272
276
|
break;
|
|
277
|
+
case "reload":
|
|
278
|
+
action = "reload";
|
|
279
|
+
params = {};
|
|
280
|
+
break;
|
|
273
281
|
default:
|
|
274
282
|
(0, output_1.errorOut)(`Unknown command: ${command}`);
|
|
275
283
|
}
|
|
@@ -299,7 +307,7 @@ async function main() {
|
|
|
299
307
|
(0, output_1.errorOut)("merge-window --close-source requires --confirm");
|
|
300
308
|
}
|
|
301
309
|
}
|
|
302
|
-
if (enforcePolicy && ["analyze", "inspect", "report", "screenshot", "close", "archive", "focus", "refresh", "move-tab", "move-group", "group-assign", "group-update", "group-ungroup", "merge-window"].includes(command)) {
|
|
310
|
+
if (enforcePolicy && ["analyze", "inspect", "report", "screenshot", "close", "archive", "focus", "refresh", "move-tab", "move-group", "group-assign", "group-update", "group-ungroup", "group-gather", "merge-window"].includes(command)) {
|
|
303
311
|
if (command === "close" && options.apply) {
|
|
304
312
|
(0, output_1.errorOut)("Policy blocks close --apply; use explicit tab targets.");
|
|
305
313
|
}
|