tabctl 0.3.1 → 0.5.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 +9 -7
- package/dist/cli/lib/client.js +3 -3
- package/dist/cli/lib/commands/doctor.js +134 -0
- package/dist/cli/lib/commands/index.js +5 -1
- package/dist/cli/lib/commands/meta.js +12 -12
- package/dist/cli/lib/commands/params-groups.js +8 -0
- package/dist/cli/lib/commands/params.js +6 -6
- package/dist/cli/lib/commands/setup.js +37 -136
- package/dist/cli/lib/constants.js +1 -1
- package/dist/cli/lib/options-commands.js +16 -1
- package/dist/cli/lib/output.js +39 -0
- package/dist/cli/lib/policy-filter.js +2 -2
- package/dist/cli/lib/policy.js +3 -3
- package/dist/cli/lib/response.js +11 -11
- package/dist/cli/tabctl.js +9 -1
- package/dist/extension/background.js +431 -133
- package/dist/extension/lib/content.js +0 -30
- package/dist/extension/lib/groups.js +89 -3
- package/dist/extension/lib/inspect.js +3 -67
- 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 +45 -43
- package/dist/host/host.js +11 -11
- package/dist/host/lib/handlers.js +7 -5
- package/dist/host/lib/undo.js +6 -6
- package/dist/shared/config.js +19 -19
- package/dist/shared/extension-sync.js +20 -20
- package/dist/shared/profiles.js +5 -5
- package/dist/shared/version.js +2 -2
- package/dist/shared/wrapper-health.js +132 -0
- package/package.json +8 -4
package/dist/host/host.bundle.js
CHANGED
|
@@ -17,28 +17,28 @@ var require_config = __commonJS({
|
|
|
17
17
|
exports2.resetConfig = resetConfig;
|
|
18
18
|
exports2.expandEnvVars = expandEnvVars;
|
|
19
19
|
exports2.resolveConfig = resolveConfig;
|
|
20
|
-
var
|
|
21
|
-
var
|
|
22
|
-
var
|
|
23
|
-
var
|
|
20
|
+
var node_os_1 = __importDefault2(require("node:os"));
|
|
21
|
+
var node_path_1 = __importDefault2(require("node:path"));
|
|
22
|
+
var node_crypto_12 = __importDefault2(require("node:crypto"));
|
|
23
|
+
var node_fs_12 = __importDefault2(require("node:fs"));
|
|
24
24
|
function defaultConfigBase() {
|
|
25
25
|
if (process.platform === "win32") {
|
|
26
|
-
return process.env.APPDATA ||
|
|
26
|
+
return process.env.APPDATA || node_path_1.default.join(node_os_1.default.homedir(), "AppData", "Roaming");
|
|
27
27
|
}
|
|
28
|
-
return
|
|
28
|
+
return node_path_1.default.join(node_os_1.default.homedir(), ".config");
|
|
29
29
|
}
|
|
30
30
|
function defaultStateBase() {
|
|
31
31
|
if (process.platform === "win32") {
|
|
32
|
-
return process.env.LOCALAPPDATA ||
|
|
32
|
+
return process.env.LOCALAPPDATA || node_path_1.default.join(node_os_1.default.homedir(), "AppData", "Local");
|
|
33
33
|
}
|
|
34
|
-
return
|
|
34
|
+
return node_path_1.default.join(node_os_1.default.homedir(), ".local", "state");
|
|
35
35
|
}
|
|
36
36
|
function resolveSocketPath(dataDir) {
|
|
37
37
|
if (process.platform === "win32") {
|
|
38
|
-
const hash =
|
|
38
|
+
const hash = node_crypto_12.default.createHash("sha256").update(dataDir).digest("hex").slice(0, 12);
|
|
39
39
|
return `\\\\.\\pipe\\tabctl-${hash}`;
|
|
40
40
|
}
|
|
41
|
-
return
|
|
41
|
+
return node_path_1.default.join(dataDir, "tabctl.sock");
|
|
42
42
|
}
|
|
43
43
|
var cached;
|
|
44
44
|
function resetConfig() {
|
|
@@ -53,23 +53,23 @@ var require_config = __commonJS({
|
|
|
53
53
|
function resolveConfig(profileName) {
|
|
54
54
|
if (!profileName && cached)
|
|
55
55
|
return cached;
|
|
56
|
-
const configDir = process.env.TABCTL_CONFIG_DIR ||
|
|
56
|
+
const configDir = process.env.TABCTL_CONFIG_DIR || node_path_1.default.join(process.env.XDG_CONFIG_HOME || defaultConfigBase(), "tabctl");
|
|
57
57
|
let fileConfig = {};
|
|
58
58
|
try {
|
|
59
|
-
const raw =
|
|
59
|
+
const raw = node_fs_12.default.readFileSync(node_path_1.default.join(configDir, "config.json"), "utf-8");
|
|
60
60
|
fileConfig = JSON.parse(raw);
|
|
61
61
|
} catch {
|
|
62
62
|
}
|
|
63
63
|
let dataDir;
|
|
64
64
|
if (typeof fileConfig.dataDir === "string" && fileConfig.dataDir) {
|
|
65
65
|
dataDir = expandEnvVars(fileConfig.dataDir);
|
|
66
|
-
if (!
|
|
66
|
+
if (!node_path_1.default.isAbsolute(dataDir)) {
|
|
67
67
|
throw new Error(`dataDir in config.json must be an absolute path (got: ${dataDir}). Use $HOME or full paths.`);
|
|
68
68
|
}
|
|
69
69
|
} else if (process.env.TABCTL_CONFIG_DIR) {
|
|
70
|
-
dataDir =
|
|
70
|
+
dataDir = node_path_1.default.join(configDir, "data");
|
|
71
71
|
} else {
|
|
72
|
-
dataDir =
|
|
72
|
+
dataDir = node_path_1.default.join(process.env.XDG_STATE_HOME || defaultStateBase(), "tabctl");
|
|
73
73
|
}
|
|
74
74
|
const baseDataDir = dataDir;
|
|
75
75
|
const explicitProfile = profileName;
|
|
@@ -77,7 +77,7 @@ var require_config = __commonJS({
|
|
|
77
77
|
let activeProfileName;
|
|
78
78
|
if (effectiveProfile) {
|
|
79
79
|
try {
|
|
80
|
-
const raw =
|
|
80
|
+
const raw = node_fs_12.default.readFileSync(node_path_1.default.join(configDir, "profiles.json"), "utf-8");
|
|
81
81
|
const registry = JSON.parse(raw);
|
|
82
82
|
const profile = registry.profiles[effectiveProfile];
|
|
83
83
|
if (profile) {
|
|
@@ -96,7 +96,7 @@ var require_config = __commonJS({
|
|
|
96
96
|
}
|
|
97
97
|
} else {
|
|
98
98
|
try {
|
|
99
|
-
const raw =
|
|
99
|
+
const raw = node_fs_12.default.readFileSync(node_path_1.default.join(configDir, "profiles.json"), "utf-8");
|
|
100
100
|
const registry = JSON.parse(raw);
|
|
101
101
|
if (registry.default && registry.profiles[registry.default]) {
|
|
102
102
|
dataDir = registry.profiles[registry.default].dataDir;
|
|
@@ -110,9 +110,9 @@ var require_config = __commonJS({
|
|
|
110
110
|
dataDir,
|
|
111
111
|
baseDataDir,
|
|
112
112
|
socketPath: process.env.TABCTL_SOCKET || resolveSocketPath(dataDir),
|
|
113
|
-
undoLog:
|
|
113
|
+
undoLog: node_path_1.default.join(dataDir, "undo.jsonl"),
|
|
114
114
|
wrapperDir: dataDir,
|
|
115
|
-
policyPath:
|
|
115
|
+
policyPath: node_path_1.default.join(configDir, "policy.json"),
|
|
116
116
|
activeProfileName
|
|
117
117
|
};
|
|
118
118
|
if (!profileName) {
|
|
@@ -129,8 +129,8 @@ var require_version = __commonJS({
|
|
|
129
129
|
"use strict";
|
|
130
130
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
131
131
|
exports2.DIRTY = exports2.GIT_SHA = exports2.VERSION = exports2.BASE_VERSION = void 0;
|
|
132
|
-
exports2.BASE_VERSION = "0.
|
|
133
|
-
exports2.VERSION = "0.
|
|
132
|
+
exports2.BASE_VERSION = "0.5.0";
|
|
133
|
+
exports2.VERSION = "0.5.0";
|
|
134
134
|
exports2.GIT_SHA = null;
|
|
135
135
|
exports2.DIRTY = false;
|
|
136
136
|
}
|
|
@@ -149,18 +149,18 @@ var require_undo = __commonJS({
|
|
|
149
149
|
exports2.filterByRetention = filterByRetention;
|
|
150
150
|
exports2.findUndoRecord = findUndoRecord;
|
|
151
151
|
exports2.findLatestUndoRecord = findLatestUndoRecord;
|
|
152
|
-
var
|
|
153
|
-
var
|
|
152
|
+
var node_fs_12 = __importDefault2(require("node:fs"));
|
|
153
|
+
var node_path_1 = __importDefault2(require("node:path"));
|
|
154
154
|
var DEFAULT_RETENTION_DAYS = 30;
|
|
155
155
|
function appendUndoRecord(filePath, record) {
|
|
156
|
-
const dir =
|
|
157
|
-
|
|
158
|
-
|
|
156
|
+
const dir = node_path_1.default.dirname(filePath);
|
|
157
|
+
node_fs_12.default.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
158
|
+
node_fs_12.default.appendFileSync(filePath, `${JSON.stringify(record)}
|
|
159
159
|
`, "utf8");
|
|
160
160
|
}
|
|
161
161
|
function readUndoRecords(filePath) {
|
|
162
162
|
try {
|
|
163
|
-
const content =
|
|
163
|
+
const content = node_fs_12.default.readFileSync(filePath, "utf8");
|
|
164
164
|
const lines = content.split("\n").filter(Boolean);
|
|
165
165
|
const records = [];
|
|
166
166
|
for (const line of lines) {
|
|
@@ -205,6 +205,7 @@ var require_handlers = __commonJS({
|
|
|
205
205
|
"dist/host/lib/handlers.js"(exports2) {
|
|
206
206
|
"use strict";
|
|
207
207
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
208
|
+
exports2.LOCAL_ACTIONS = exports2.UNDO_ACTIONS = void 0;
|
|
208
209
|
exports2.respond = respond;
|
|
209
210
|
exports2.refreshTimeout = refreshTimeout;
|
|
210
211
|
exports2.forwardToExtension = forwardToExtension;
|
|
@@ -216,17 +217,18 @@ var require_handlers = __commonJS({
|
|
|
216
217
|
var MAX_RESPONSE_BYTES = 20 * 1024 * 1024;
|
|
217
218
|
var HISTORY_LIMIT_DEFAULT = 20;
|
|
218
219
|
var RETENTION_DAYS = 30;
|
|
219
|
-
|
|
220
|
+
exports2.UNDO_ACTIONS = /* @__PURE__ */ new Set([
|
|
220
221
|
"archive",
|
|
221
222
|
"close",
|
|
222
223
|
"group-update",
|
|
223
224
|
"group-ungroup",
|
|
224
225
|
"group-assign",
|
|
226
|
+
"group-gather",
|
|
225
227
|
"move-tab",
|
|
226
228
|
"move-group",
|
|
227
229
|
"merge-window"
|
|
228
230
|
]);
|
|
229
|
-
|
|
231
|
+
exports2.LOCAL_ACTIONS = /* @__PURE__ */ new Set(["history", "undo", "version"]);
|
|
230
232
|
function respond(socket, payload) {
|
|
231
233
|
const serialized = JSON.stringify(payload);
|
|
232
234
|
if (Buffer.byteLength(serialized, "utf8") > MAX_RESPONSE_BYTES) {
|
|
@@ -265,7 +267,7 @@ var require_handlers = __commonJS({
|
|
|
265
267
|
if (txid) {
|
|
266
268
|
params.txid = txid;
|
|
267
269
|
}
|
|
268
|
-
if (!LOCAL_ACTIONS.has(request.action)) {
|
|
270
|
+
if (!exports2.LOCAL_ACTIONS.has(request.action)) {
|
|
269
271
|
params.client = {
|
|
270
272
|
component: "host",
|
|
271
273
|
version: version_1.VERSION
|
|
@@ -362,7 +364,7 @@ var require_handlers = __commonJS({
|
|
|
362
364
|
});
|
|
363
365
|
return;
|
|
364
366
|
}
|
|
365
|
-
if (UNDO_ACTIONS.has(pendingRequest.action)) {
|
|
367
|
+
if (exports2.UNDO_ACTIONS.has(pendingRequest.action)) {
|
|
366
368
|
const record = {
|
|
367
369
|
txid: pendingRequest.txid,
|
|
368
370
|
createdAt: Date.now(),
|
|
@@ -518,7 +520,7 @@ var require_handlers = __commonJS({
|
|
|
518
520
|
}, { txid });
|
|
519
521
|
return;
|
|
520
522
|
}
|
|
521
|
-
if (UNDO_ACTIONS.has(action)) {
|
|
523
|
+
if (exports2.UNDO_ACTIONS.has(action)) {
|
|
522
524
|
const txid = deps2.createId("tx");
|
|
523
525
|
forwardToExtension(deps2, socket, request, { txid });
|
|
524
526
|
return;
|
|
@@ -533,9 +535,9 @@ var __importDefault = exports && exports.__importDefault || function(mod) {
|
|
|
533
535
|
return mod && mod.__esModule ? mod : { "default": mod };
|
|
534
536
|
};
|
|
535
537
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
536
|
-
var
|
|
537
|
-
var
|
|
538
|
-
var
|
|
538
|
+
var node_fs_1 = __importDefault(require("node:fs"));
|
|
539
|
+
var node_net_1 = __importDefault(require("node:net"));
|
|
540
|
+
var node_crypto_1 = __importDefault(require("node:crypto"));
|
|
539
541
|
var config_1 = require_config();
|
|
540
542
|
var handlers_1 = require_handlers();
|
|
541
543
|
var config;
|
|
@@ -555,10 +557,10 @@ function log(...args) {
|
|
|
555
557
|
`);
|
|
556
558
|
}
|
|
557
559
|
function ensureDir() {
|
|
558
|
-
|
|
560
|
+
node_fs_1.default.mkdirSync(SOCKET_DIR, { recursive: true, mode: 448 });
|
|
559
561
|
}
|
|
560
562
|
function createId(prefix) {
|
|
561
|
-
return `${prefix}-${Date.now()}-${
|
|
563
|
+
return `${prefix}-${Date.now()}-${node_crypto_1.default.randomBytes(4).toString("hex")}`;
|
|
562
564
|
}
|
|
563
565
|
function sendNative(message) {
|
|
564
566
|
const json = JSON.stringify(message);
|
|
@@ -601,10 +603,10 @@ process.stdin.on("end", () => {
|
|
|
601
603
|
});
|
|
602
604
|
function startSocketServer() {
|
|
603
605
|
ensureDir();
|
|
604
|
-
if (process.platform !== "win32" &&
|
|
605
|
-
|
|
606
|
+
if (process.platform !== "win32" && node_fs_1.default.existsSync(SOCKET_PATH)) {
|
|
607
|
+
node_fs_1.default.unlinkSync(SOCKET_PATH);
|
|
606
608
|
}
|
|
607
|
-
const server2 =
|
|
609
|
+
const server2 = node_net_1.default.createServer((socket) => {
|
|
608
610
|
socket.setEncoding("utf8");
|
|
609
611
|
let buffer = "";
|
|
610
612
|
socket.on("data", (data) => {
|
|
@@ -644,7 +646,7 @@ function startSocketServer() {
|
|
|
644
646
|
server2.listen(SOCKET_PATH, () => {
|
|
645
647
|
if (process.platform !== "win32") {
|
|
646
648
|
try {
|
|
647
|
-
|
|
649
|
+
node_fs_1.default.chmodSync(SOCKET_PATH, 384);
|
|
648
650
|
} catch {
|
|
649
651
|
}
|
|
650
652
|
}
|
|
@@ -654,8 +656,8 @@ function startSocketServer() {
|
|
|
654
656
|
}
|
|
655
657
|
function cleanupAndExit(code) {
|
|
656
658
|
try {
|
|
657
|
-
if (process.platform !== "win32" &&
|
|
658
|
-
|
|
659
|
+
if (process.platform !== "win32" && node_fs_1.default.existsSync(SOCKET_PATH)) {
|
|
660
|
+
node_fs_1.default.unlinkSync(SOCKET_PATH);
|
|
659
661
|
}
|
|
660
662
|
} catch {
|
|
661
663
|
}
|
package/dist/host/host.js
CHANGED
|
@@ -4,9 +4,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
5
|
};
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_net_1 = __importDefault(require("node:net"));
|
|
9
|
+
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
10
10
|
const config_1 = require("../shared/config");
|
|
11
11
|
const handlers_1 = require("./lib/handlers");
|
|
12
12
|
let config;
|
|
@@ -25,10 +25,10 @@ function log(...args) {
|
|
|
25
25
|
process.stderr.write(`[tabctl-host] ${args.join(" ")}\n`);
|
|
26
26
|
}
|
|
27
27
|
function ensureDir() {
|
|
28
|
-
|
|
28
|
+
node_fs_1.default.mkdirSync(SOCKET_DIR, { recursive: true, mode: 0o700 });
|
|
29
29
|
}
|
|
30
30
|
function createId(prefix) {
|
|
31
|
-
return `${prefix}-${Date.now()}-${
|
|
31
|
+
return `${prefix}-${Date.now()}-${node_crypto_1.default.randomBytes(4).toString("hex")}`;
|
|
32
32
|
}
|
|
33
33
|
function sendNative(message) {
|
|
34
34
|
const json = JSON.stringify(message);
|
|
@@ -72,10 +72,10 @@ process.stdin.on("end", () => {
|
|
|
72
72
|
function startSocketServer() {
|
|
73
73
|
ensureDir();
|
|
74
74
|
// Named pipes on Windows don't use filesystem paths; skip cleanup
|
|
75
|
-
if (process.platform !== "win32" &&
|
|
76
|
-
|
|
75
|
+
if (process.platform !== "win32" && node_fs_1.default.existsSync(SOCKET_PATH)) {
|
|
76
|
+
node_fs_1.default.unlinkSync(SOCKET_PATH);
|
|
77
77
|
}
|
|
78
|
-
const server =
|
|
78
|
+
const server = node_net_1.default.createServer((socket) => {
|
|
79
79
|
socket.setEncoding("utf8");
|
|
80
80
|
let buffer = "";
|
|
81
81
|
socket.on("data", (data) => {
|
|
@@ -117,7 +117,7 @@ function startSocketServer() {
|
|
|
117
117
|
server.listen(SOCKET_PATH, () => {
|
|
118
118
|
if (process.platform !== "win32") {
|
|
119
119
|
try {
|
|
120
|
-
|
|
120
|
+
node_fs_1.default.chmodSync(SOCKET_PATH, 0o600);
|
|
121
121
|
}
|
|
122
122
|
catch { /* ignore on platforms without chmod */ }
|
|
123
123
|
}
|
|
@@ -128,8 +128,8 @@ function startSocketServer() {
|
|
|
128
128
|
function cleanupAndExit(code) {
|
|
129
129
|
try {
|
|
130
130
|
// Named pipes on Windows don't need filesystem cleanup
|
|
131
|
-
if (process.platform !== "win32" &&
|
|
132
|
-
|
|
131
|
+
if (process.platform !== "win32" && node_fs_1.default.existsSync(SOCKET_PATH)) {
|
|
132
|
+
node_fs_1.default.unlinkSync(SOCKET_PATH);
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
135
|
catch {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LOCAL_ACTIONS = exports.UNDO_ACTIONS = void 0;
|
|
3
4
|
exports.respond = respond;
|
|
4
5
|
exports.refreshTimeout = refreshTimeout;
|
|
5
6
|
exports.forwardToExtension = forwardToExtension;
|
|
@@ -11,17 +12,18 @@ const REQUEST_TIMEOUT_MS = 30000;
|
|
|
11
12
|
const MAX_RESPONSE_BYTES = 20 * 1024 * 1024;
|
|
12
13
|
const HISTORY_LIMIT_DEFAULT = 20;
|
|
13
14
|
const RETENTION_DAYS = 30;
|
|
14
|
-
|
|
15
|
+
exports.UNDO_ACTIONS = new Set([
|
|
15
16
|
"archive",
|
|
16
17
|
"close",
|
|
17
18
|
"group-update",
|
|
18
19
|
"group-ungroup",
|
|
19
20
|
"group-assign",
|
|
21
|
+
"group-gather",
|
|
20
22
|
"move-tab",
|
|
21
23
|
"move-group",
|
|
22
24
|
"merge-window",
|
|
23
25
|
]);
|
|
24
|
-
|
|
26
|
+
exports.LOCAL_ACTIONS = new Set(["history", "undo", "version"]);
|
|
25
27
|
function respond(socket, payload) {
|
|
26
28
|
const serialized = JSON.stringify(payload);
|
|
27
29
|
if (Buffer.byteLength(serialized, "utf8") > MAX_RESPONSE_BYTES) {
|
|
@@ -58,7 +60,7 @@ function forwardToExtension(deps, socket, request, overrides = {}) {
|
|
|
58
60
|
if (txid) {
|
|
59
61
|
params.txid = txid;
|
|
60
62
|
}
|
|
61
|
-
if (!LOCAL_ACTIONS.has(request.action)) {
|
|
63
|
+
if (!exports.LOCAL_ACTIONS.has(request.action)) {
|
|
62
64
|
params.client = {
|
|
63
65
|
component: "host",
|
|
64
66
|
version: version_1.VERSION,
|
|
@@ -156,7 +158,7 @@ function handleNativeMessage(deps, payload) {
|
|
|
156
158
|
});
|
|
157
159
|
return;
|
|
158
160
|
}
|
|
159
|
-
if (UNDO_ACTIONS.has(pendingRequest.action)) {
|
|
161
|
+
if (exports.UNDO_ACTIONS.has(pendingRequest.action)) {
|
|
160
162
|
const record = {
|
|
161
163
|
txid: pendingRequest.txid,
|
|
162
164
|
createdAt: Date.now(),
|
|
@@ -316,7 +318,7 @@ function handleCliRequest(deps, socket, request) {
|
|
|
316
318
|
}, { txid });
|
|
317
319
|
return;
|
|
318
320
|
}
|
|
319
|
-
if (UNDO_ACTIONS.has(action)) {
|
|
321
|
+
if (exports.UNDO_ACTIONS.has(action)) {
|
|
320
322
|
const txid = deps.createId("tx");
|
|
321
323
|
forwardToExtension(deps, socket, request, { txid });
|
|
322
324
|
return;
|
package/dist/host/lib/undo.js
CHANGED
|
@@ -8,17 +8,17 @@ exports.readUndoRecords = readUndoRecords;
|
|
|
8
8
|
exports.filterByRetention = filterByRetention;
|
|
9
9
|
exports.findUndoRecord = findUndoRecord;
|
|
10
10
|
exports.findLatestUndoRecord = findLatestUndoRecord;
|
|
11
|
-
const
|
|
12
|
-
const
|
|
11
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
12
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
13
13
|
const DEFAULT_RETENTION_DAYS = 30;
|
|
14
14
|
function appendUndoRecord(filePath, record) {
|
|
15
|
-
const dir =
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
const dir = node_path_1.default.dirname(filePath);
|
|
16
|
+
node_fs_1.default.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
17
|
+
node_fs_1.default.appendFileSync(filePath, `${JSON.stringify(record)}\n`, "utf8");
|
|
18
18
|
}
|
|
19
19
|
function readUndoRecords(filePath) {
|
|
20
20
|
try {
|
|
21
|
-
const content =
|
|
21
|
+
const content = node_fs_1.default.readFileSync(filePath, "utf8");
|
|
22
22
|
const lines = content.split("\n").filter(Boolean);
|
|
23
23
|
const records = [];
|
|
24
24
|
for (const line of lines) {
|
package/dist/shared/config.js
CHANGED
|
@@ -7,30 +7,30 @@ exports.resolveSocketPath = resolveSocketPath;
|
|
|
7
7
|
exports.resetConfig = resetConfig;
|
|
8
8
|
exports.expandEnvVars = expandEnvVars;
|
|
9
9
|
exports.resolveConfig = resolveConfig;
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
10
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
11
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
12
|
+
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
13
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
14
14
|
function defaultConfigBase() {
|
|
15
15
|
if (process.platform === "win32") {
|
|
16
|
-
return process.env.APPDATA ||
|
|
16
|
+
return process.env.APPDATA || node_path_1.default.join(node_os_1.default.homedir(), "AppData", "Roaming");
|
|
17
17
|
}
|
|
18
|
-
return
|
|
18
|
+
return node_path_1.default.join(node_os_1.default.homedir(), ".config");
|
|
19
19
|
}
|
|
20
20
|
function defaultStateBase() {
|
|
21
21
|
if (process.platform === "win32") {
|
|
22
|
-
return process.env.LOCALAPPDATA ||
|
|
22
|
+
return process.env.LOCALAPPDATA || node_path_1.default.join(node_os_1.default.homedir(), "AppData", "Local");
|
|
23
23
|
}
|
|
24
|
-
return
|
|
24
|
+
return node_path_1.default.join(node_os_1.default.homedir(), ".local", "state");
|
|
25
25
|
}
|
|
26
26
|
/** Resolve the IPC socket/pipe path for the given data directory. */
|
|
27
27
|
function resolveSocketPath(dataDir) {
|
|
28
28
|
if (process.platform === "win32") {
|
|
29
29
|
// Windows: use named pipes (Unix domain sockets are unreliable)
|
|
30
|
-
const hash =
|
|
30
|
+
const hash = node_crypto_1.default.createHash("sha256").update(dataDir).digest("hex").slice(0, 12);
|
|
31
31
|
return `\\\\.\\pipe\\tabctl-${hash}`;
|
|
32
32
|
}
|
|
33
|
-
return
|
|
33
|
+
return node_path_1.default.join(dataDir, "tabctl.sock");
|
|
34
34
|
}
|
|
35
35
|
let cached;
|
|
36
36
|
function resetConfig() {
|
|
@@ -48,11 +48,11 @@ function resolveConfig(profileName) {
|
|
|
48
48
|
return cached;
|
|
49
49
|
// Config dir resolution
|
|
50
50
|
const configDir = process.env.TABCTL_CONFIG_DIR
|
|
51
|
-
||
|
|
51
|
+
|| node_path_1.default.join(process.env.XDG_CONFIG_HOME || defaultConfigBase(), "tabctl");
|
|
52
52
|
// Read optional config.json
|
|
53
53
|
let fileConfig = {};
|
|
54
54
|
try {
|
|
55
|
-
const raw =
|
|
55
|
+
const raw = node_fs_1.default.readFileSync(node_path_1.default.join(configDir, "config.json"), "utf-8");
|
|
56
56
|
fileConfig = JSON.parse(raw);
|
|
57
57
|
}
|
|
58
58
|
catch {
|
|
@@ -62,15 +62,15 @@ function resolveConfig(profileName) {
|
|
|
62
62
|
let dataDir;
|
|
63
63
|
if (typeof fileConfig.dataDir === "string" && fileConfig.dataDir) {
|
|
64
64
|
dataDir = expandEnvVars(fileConfig.dataDir);
|
|
65
|
-
if (!
|
|
65
|
+
if (!node_path_1.default.isAbsolute(dataDir)) {
|
|
66
66
|
throw new Error(`dataDir in config.json must be an absolute path (got: ${dataDir}). Use $HOME or full paths.`);
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
else if (process.env.TABCTL_CONFIG_DIR) {
|
|
70
|
-
dataDir =
|
|
70
|
+
dataDir = node_path_1.default.join(configDir, "data");
|
|
71
71
|
}
|
|
72
72
|
else {
|
|
73
|
-
dataDir =
|
|
73
|
+
dataDir = node_path_1.default.join(process.env.XDG_STATE_HOME || defaultStateBase(), "tabctl");
|
|
74
74
|
}
|
|
75
75
|
const baseDataDir = dataDir;
|
|
76
76
|
// Profile resolution (read profiles.json inline to avoid circular import)
|
|
@@ -79,7 +79,7 @@ function resolveConfig(profileName) {
|
|
|
79
79
|
let activeProfileName;
|
|
80
80
|
if (effectiveProfile) {
|
|
81
81
|
try {
|
|
82
|
-
const raw =
|
|
82
|
+
const raw = node_fs_1.default.readFileSync(node_path_1.default.join(configDir, "profiles.json"), "utf-8");
|
|
83
83
|
const registry = JSON.parse(raw);
|
|
84
84
|
const profile = registry.profiles[effectiveProfile];
|
|
85
85
|
if (profile) {
|
|
@@ -105,7 +105,7 @@ function resolveConfig(profileName) {
|
|
|
105
105
|
else {
|
|
106
106
|
// No explicit profile — check for a default in profiles.json
|
|
107
107
|
try {
|
|
108
|
-
const raw =
|
|
108
|
+
const raw = node_fs_1.default.readFileSync(node_path_1.default.join(configDir, "profiles.json"), "utf-8");
|
|
109
109
|
const registry = JSON.parse(raw);
|
|
110
110
|
if (registry.default && registry.profiles[registry.default]) {
|
|
111
111
|
dataDir = registry.profiles[registry.default].dataDir;
|
|
@@ -121,9 +121,9 @@ function resolveConfig(profileName) {
|
|
|
121
121
|
dataDir,
|
|
122
122
|
baseDataDir,
|
|
123
123
|
socketPath: process.env.TABCTL_SOCKET || resolveSocketPath(dataDir),
|
|
124
|
-
undoLog:
|
|
124
|
+
undoLog: node_path_1.default.join(dataDir, "undo.jsonl"),
|
|
125
125
|
wrapperDir: dataDir,
|
|
126
|
-
policyPath:
|
|
126
|
+
policyPath: node_path_1.default.join(configDir, "policy.json"),
|
|
127
127
|
activeProfileName,
|
|
128
128
|
};
|
|
129
129
|
// Only cache no-arg calls
|
|
@@ -14,9 +14,9 @@ exports.readHostVersion = readHostVersion;
|
|
|
14
14
|
exports.syncExtension = syncExtension;
|
|
15
15
|
exports.syncHost = syncHost;
|
|
16
16
|
exports.checkExtensionSync = checkExtensionSync;
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
const
|
|
17
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
18
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
19
|
+
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
20
20
|
const config_1 = require("./config");
|
|
21
21
|
exports.EXTENSION_DIR_NAME = "extension";
|
|
22
22
|
exports.HOST_BUNDLE_NAME = "host.bundle.js";
|
|
@@ -25,35 +25,35 @@ exports.HOST_BUNDLE_NAME = "host.bundle.js";
|
|
|
25
25
|
* Chromium computes: SHA256(absolute_path) → first 32 hex chars → map 0-f to a-p.
|
|
26
26
|
*/
|
|
27
27
|
function deriveExtensionId(extensionDir) {
|
|
28
|
-
const hash =
|
|
28
|
+
const hash = node_crypto_1.default.createHash("sha256").update(extensionDir).digest("hex").slice(0, 32);
|
|
29
29
|
return hash.split("").map(c => String.fromCharCode("a".charCodeAt(0) + parseInt(c, 16))).join("");
|
|
30
30
|
}
|
|
31
31
|
function resolveBundledExtensionDir() {
|
|
32
|
-
const dir =
|
|
33
|
-
const manifest =
|
|
34
|
-
if (!
|
|
32
|
+
const dir = node_path_1.default.resolve(__dirname, "../extension");
|
|
33
|
+
const manifest = node_path_1.default.join(dir, "manifest.json");
|
|
34
|
+
if (!node_fs_1.default.existsSync(dir) || !node_fs_1.default.existsSync(manifest)) {
|
|
35
35
|
throw new Error(`Bundled extension not found at ${dir}`);
|
|
36
36
|
}
|
|
37
37
|
return dir;
|
|
38
38
|
}
|
|
39
39
|
function resolveBundledHostPath() {
|
|
40
|
-
const p =
|
|
41
|
-
if (!
|
|
40
|
+
const p = node_path_1.default.resolve(__dirname, "../host", exports.HOST_BUNDLE_NAME);
|
|
41
|
+
if (!node_fs_1.default.existsSync(p)) {
|
|
42
42
|
throw new Error(`Bundled host not found at ${p}`);
|
|
43
43
|
}
|
|
44
44
|
return p;
|
|
45
45
|
}
|
|
46
46
|
function resolveInstalledExtensionDir(dataDir) {
|
|
47
47
|
const dir = dataDir ?? (0, config_1.resolveConfig)().baseDataDir;
|
|
48
|
-
return
|
|
48
|
+
return node_path_1.default.join(dir, exports.EXTENSION_DIR_NAME);
|
|
49
49
|
}
|
|
50
50
|
function resolveInstalledHostPath(dataDir) {
|
|
51
51
|
const dir = dataDir ?? (0, config_1.resolveConfig)().baseDataDir;
|
|
52
|
-
return
|
|
52
|
+
return node_path_1.default.join(dir, exports.HOST_BUNDLE_NAME);
|
|
53
53
|
}
|
|
54
54
|
function readExtensionVersion(extensionDir) {
|
|
55
55
|
try {
|
|
56
|
-
const raw =
|
|
56
|
+
const raw = node_fs_1.default.readFileSync(node_path_1.default.join(extensionDir, "manifest.json"), "utf-8");
|
|
57
57
|
const manifest = JSON.parse(raw);
|
|
58
58
|
return typeof manifest.version === "string" ? manifest.version : null;
|
|
59
59
|
}
|
|
@@ -64,7 +64,7 @@ function readExtensionVersion(extensionDir) {
|
|
|
64
64
|
/** Read the BASE_VERSION constant from a bundled host.bundle.js file. */
|
|
65
65
|
function readHostVersion(hostPath) {
|
|
66
66
|
try {
|
|
67
|
-
const content =
|
|
67
|
+
const content = node_fs_1.default.readFileSync(hostPath, "utf-8");
|
|
68
68
|
const match = content.match(/\bBASE_VERSION\s*=\s*"([^"]+)"/);
|
|
69
69
|
return match ? match[1] : null;
|
|
70
70
|
}
|
|
@@ -77,10 +77,10 @@ function syncExtension(dataDir) {
|
|
|
77
77
|
const installedDir = resolveInstalledExtensionDir(dataDir);
|
|
78
78
|
const bundledVersion = readExtensionVersion(bundledDir);
|
|
79
79
|
const installedVersion = readExtensionVersion(installedDir);
|
|
80
|
-
const needsCopy = !
|
|
80
|
+
const needsCopy = !node_fs_1.default.existsSync(installedDir) || bundledVersion !== installedVersion;
|
|
81
81
|
if (needsCopy) {
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
node_fs_1.default.mkdirSync(installedDir, { recursive: true });
|
|
83
|
+
node_fs_1.default.cpSync(bundledDir, installedDir, { recursive: true });
|
|
84
84
|
}
|
|
85
85
|
return {
|
|
86
86
|
synced: needsCopy,
|
|
@@ -94,10 +94,10 @@ function syncHost(dataDir) {
|
|
|
94
94
|
const installedPath = resolveInstalledHostPath(dataDir);
|
|
95
95
|
const bundledVersion = readHostVersion(bundledPath);
|
|
96
96
|
const installedVersion = readHostVersion(installedPath);
|
|
97
|
-
const needsCopy = !
|
|
97
|
+
const needsCopy = !node_fs_1.default.existsSync(installedPath) || bundledVersion !== installedVersion;
|
|
98
98
|
if (needsCopy) {
|
|
99
|
-
|
|
100
|
-
|
|
99
|
+
node_fs_1.default.mkdirSync(node_path_1.default.dirname(installedPath), { recursive: true });
|
|
100
|
+
node_fs_1.default.copyFileSync(bundledPath, installedPath);
|
|
101
101
|
}
|
|
102
102
|
return {
|
|
103
103
|
synced: needsCopy,
|
|
@@ -111,7 +111,7 @@ function checkExtensionSync(dataDir) {
|
|
|
111
111
|
const installedDir = resolveInstalledExtensionDir(dataDir);
|
|
112
112
|
const bundledVersion = readExtensionVersion(bundledDir);
|
|
113
113
|
const installedVersion = readExtensionVersion(installedDir);
|
|
114
|
-
const exists =
|
|
114
|
+
const exists = node_fs_1.default.existsSync(installedDir) && installedVersion !== null;
|
|
115
115
|
const needsSync = !exists || bundledVersion !== installedVersion;
|
|
116
116
|
const needsReload = exists && bundledVersion !== installedVersion;
|
|
117
117
|
return {
|
package/dist/shared/profiles.js
CHANGED
|
@@ -11,8 +11,8 @@ exports.addProfile = addProfile;
|
|
|
11
11
|
exports.removeProfile = removeProfile;
|
|
12
12
|
exports.getActiveProfile = getActiveProfile;
|
|
13
13
|
exports.listProfiles = listProfiles;
|
|
14
|
-
const
|
|
15
|
-
const
|
|
14
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
15
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
16
16
|
const config_1 = require("./config");
|
|
17
17
|
exports.PROFILE_NAME_PATTERN = /^[a-z0-9-]+$/;
|
|
18
18
|
exports.PROFILES_FILE = "profiles.json";
|
|
@@ -24,7 +24,7 @@ function validateProfileName(name) {
|
|
|
24
24
|
function loadProfiles(configDir) {
|
|
25
25
|
const dir = configDir ?? (0, config_1.resolveConfig)().configDir;
|
|
26
26
|
try {
|
|
27
|
-
const raw =
|
|
27
|
+
const raw = node_fs_1.default.readFileSync(node_path_1.default.join(dir, exports.PROFILES_FILE), "utf-8");
|
|
28
28
|
return JSON.parse(raw);
|
|
29
29
|
}
|
|
30
30
|
catch {
|
|
@@ -33,8 +33,8 @@ function loadProfiles(configDir) {
|
|
|
33
33
|
}
|
|
34
34
|
function saveProfiles(registry, configDir) {
|
|
35
35
|
const dir = configDir ?? (0, config_1.resolveConfig)().configDir;
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
node_fs_1.default.mkdirSync(dir, { recursive: true });
|
|
37
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(dir, exports.PROFILES_FILE), JSON.stringify(registry, null, 2) + "\n");
|
|
38
38
|
}
|
|
39
39
|
function addProfile(name, entry, configDir) {
|
|
40
40
|
validateProfileName(name);
|
package/dist/shared/version.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DIRTY = exports.GIT_SHA = exports.VERSION = exports.BASE_VERSION = void 0;
|
|
4
|
-
exports.BASE_VERSION = "0.
|
|
5
|
-
exports.VERSION = "0.
|
|
4
|
+
exports.BASE_VERSION = "0.5.0";
|
|
5
|
+
exports.VERSION = "0.5.0";
|
|
6
6
|
exports.GIT_SHA = null;
|
|
7
7
|
exports.DIRTY = false;
|