Merge feature_enforce_semicolon_rule into vibes

Semicolon + brace style enforcement is now part of the vibes history for human review.

Written by AI agent working for @jtmorris. Model: grok-build-0.1.
This commit is contained in:
Vybe (Coding Agent) 2026-05-23 04:57:51 +00:00
commit bc864bb9d8
2 changed files with 72 additions and 78 deletions

View file

@ -1,15 +1,15 @@
function parsePeers(peerMap) { function parsePeers(peerMap) {
if (!peerMap) return [] if (!peerMap) { return []; }
return Object.keys(peerMap).map(function (key) { return Object.keys(peerMap).map(function (key) {
if (!Object.prototype.hasOwnProperty.call(peerMap, key)) return null if (!Object.prototype.hasOwnProperty.call(peerMap, key)) { return null; }
var p = peerMap[key] var p = peerMap[key];
return { return {
hostname: p.HostName || key, hostname: p.HostName || key,
ip: (p.TailscaleIPs && p.TailscaleIPs.length) ? p.TailscaleIPs[0] : "", ip: (p.TailscaleIPs && p.TailscaleIPs.length) ? p.TailscaleIPs[0] : "",
online: p.Online || false, online: p.Online || false,
exitNode: p.ExitNodeOption || false exitNode: p.ExitNodeOption || false
} };
}).filter(function (peer) { return peer !== null }) }).filter(function (peer) { return peer !== null; });
} }
function makeExitNodeCommand(hostname) { function makeExitNodeCommand(hostname) {
@ -23,15 +23,15 @@ function makeExitNodeCommand(hostname) {
} }
function findActiveExitNode(peerMap) { function findActiveExitNode(peerMap) {
if (!peerMap) return "" if (!peerMap) { return ""; }
for (const key in peerMap) { for (const key in peerMap) {
if (!Object.prototype.hasOwnProperty.call(peerMap, key)) continue if (!Object.prototype.hasOwnProperty.call(peerMap, key)) { continue; }
const p = peerMap[key] const p = peerMap[key];
if (p.ExitNode) { if (p.ExitNode) {
return p.HostName || key return p.HostName || key;
} }
} }
return "" return "";
} }
const clipboardTools = [ const clipboardTools = [
@ -52,53 +52,51 @@ function getStrings() {
disconnected: "Disconnected", disconnected: "Disconnected",
exitNodePrefix: "Exit node: ", exitNodePrefix: "Exit node: ",
none: "None", none: "None",
copied: function (text) { return "Copied " + text + " to clipboard" }, copied: function (text) { return "Copied " + text + " to clipboard"; },
clearExitNode: "×", clearExitNode: "×",
setExitNode: "↗", setExitNode: "↗",
invalidExitNodeHostname: "Invalid exit node hostname" invalidExitNodeHostname: "Invalid exit node hostname"
} };
} }
// Light UI predicates — keep the view thin. // Light UI predicates — keep the view thin.
function shouldShowClearExitNode(currentExitNode) { function shouldShowClearExitNode(currentExitNode) {
return currentExitNode !== "" return currentExitNode !== "";
} }
function isActiveExitNode(currentExitNode, hostname) { function isActiveExitNode(currentExitNode, hostname) {
return currentExitNode === hostname return currentExitNode === hostname;
} }
// Security: validate hostnames coming from tailscale status JSON. // Security: validate hostnames coming from tailscale status JSON.
// Fail closed on obviously malicious input. // Fail closed on obviously malicious input.
function isValidExitNodeHostname(hostname) { function isValidExitNodeHostname(hostname) {
if (typeof hostname !== "string") return false; if (typeof hostname !== "string") { return false; }
if (hostname === "") return true; // clear command if (hostname === "") { return true; }
// Permissive enough for real Tailscale hostnames while rejecting
// classic shell metacharacters and control characters.
return /^[a-zA-Z0-9]([a-zA-Z0-9-_.]{0,62}[a-zA-Z0-9])?$/.test(hostname); return /^[a-zA-Z0-9]([a-zA-Z0-9-_.]{0,62}[a-zA-Z0-9])?$/.test(hostname);
} }
function parseStatusResult(jsonText) { function parseStatusResult(jsonText) {
try { try {
const data = JSON.parse(jsonText) const data = JSON.parse(jsonText);
return { return {
isConnected: data.BackendState === "Running", isConnected: data.BackendState === "Running",
tailscaleIP: (data.Self && data.Self.TailscaleIPs && data.Self.TailscaleIPs[0]) || "", tailscaleIP: (data.Self && data.Self.TailscaleIPs && data.Self.TailscaleIPs[0]) || "",
currentExitNode: findActiveExitNode(data.Peer || {}), currentExitNode: findActiveExitNode(data.Peer || {}),
peers: parsePeers(data.Peer || {}) peers: parsePeers(data.Peer || {})
} };
} catch (e) { } catch (e) {
return { isConnected: false, tailscaleIP: "", currentExitNode: "", peers: [] } return { isConnected: false, tailscaleIP: "", currentExitNode: "", peers: [] };
} }
} }
function buildToggleCommand(isConnected) { function buildToggleCommand(isConnected) {
return isConnected ? ["tailscale", "down"] : ["tailscale", "up"] return isConnected ? ["tailscale", "down"] : ["tailscale", "up"];
} }
// Single source of truth for the status command used for on-demand and post-action verification. // Single source of truth for the status command used for on-demand and post-action verification.
function getStatusCommand() { function getStatusCommand() {
return ["tailscale", "status", "--json"] return ["tailscale", "status", "--json"];
} }
function errorMessage(cmd) { function errorMessage(cmd) {

View file

@ -1,7 +1,7 @@
import { test } from "node:test" import { test } from "node:test";
import assert from "node:assert" import assert from "node:assert";
import lib from "../tailscalectl/lib.js" import lib from "../tailscalectl/lib.js";
const { parsePeers, makeExitNodeCommand, findActiveExitNode, errorMessage, formatError, getStatusCommand, isValidExitNodeHostname, getClipboardCommands, buildToggleCommand, parseStatusResult, getStrings, shouldShowClearExitNode, isActiveExitNode, PendingAction, commandForPendingAction } = lib const { parsePeers, makeExitNodeCommand, findActiveExitNode, errorMessage, formatError, getStatusCommand, isValidExitNodeHostname, getClipboardCommands, buildToggleCommand, parseStatusResult, getStrings, shouldShowClearExitNode, isActiveExitNode, PendingAction, commandForPendingAction } = lib;
/* /*
* Unit tests for the pure functions exported from lib.js. * Unit tests for the pure functions exported from lib.js.
@ -27,91 +27,87 @@ test("parsePeers extracts exitNode from ExitNodeOption", () => {
Online: true, Online: true,
ExitNodeOption: false ExitNodeOption: false
} }
} };
const peers = parsePeers(peerMap) const peers = parsePeers(peerMap);
assert.strictEqual(peers[0].exitNode, true) assert.strictEqual(peers[0].exitNode, true);
assert.strictEqual(peers[1].exitNode, false) assert.strictEqual(peers[1].exitNode, false);
}) });
test("makeExitNodeCommand returns tailscale set command for hostname", () => { test("makeExitNodeCommand returns tailscale set command for hostname", () => {
const cmd = makeExitNodeCommand("router") const cmd = makeExitNodeCommand("router");
assert.deepStrictEqual(cmd, ["tailscale", "set", "--exit-node=router"]) assert.deepStrictEqual(cmd, ["tailscale", "set", "--exit-node=router"]);
}) });
test("makeExitNodeCommand with empty string clears exit node", () => { test("makeExitNodeCommand with empty string clears exit node", () => {
const cmd = makeExitNodeCommand("") const cmd = makeExitNodeCommand("");
assert.deepStrictEqual(cmd, ["tailscale", "set", "--exit-node="]) assert.deepStrictEqual(cmd, ["tailscale", "set", "--exit-node="]);
}) });
test("findActiveExitNode returns hostname of peer with ExitNode=true", () => { test("findActiveExitNode returns hostname of peer with ExitNode=true", () => {
const peerMap = { const peerMap = {
"peer-1": { HostName: "gluetun-sjc", ExitNode: true, ExitNodeOption: true }, "peer-1": { HostName: "gluetun-sjc", ExitNode: true, ExitNodeOption: true },
"peer-2": { HostName: "gluetun-den", ExitNode: false, ExitNodeOption: true } "peer-2": { HostName: "gluetun-den", ExitNode: false, ExitNodeOption: true }
} };
assert.strictEqual(findActiveExitNode(peerMap), "gluetun-sjc") assert.strictEqual(findActiveExitNode(peerMap), "gluetun-sjc");
}) });
test("findActiveExitNode returns empty string when no exit node", () => { test("findActiveExitNode returns empty string when no exit node", () => {
const peerMap = { const peerMap = {
"peer-1": { HostName: "laptop", ExitNode: false, ExitNodeOption: false } "peer-1": { HostName: "laptop", ExitNode: false, ExitNodeOption: false }
} };
assert.strictEqual(findActiveExitNode(peerMap), "") assert.strictEqual(findActiveExitNode(peerMap), "");
}) });
test("errorMessage returns user-friendly message for tailscale up failure", () => { test("errorMessage returns user-friendly message for tailscale up failure", () => {
const msg = errorMessage("up", 1) const msg = errorMessage("up", 1);
assert.strictEqual(msg, "Failed to connect to Tailscale") assert.strictEqual(msg, "Failed to connect to Tailscale");
}) })
test("errorMessage returns user-friendly message for tailscale down failure", () => { test("errorMessage returns user-friendly message for tailscale down failure", () => {
const msg = errorMessage("down", 1) const msg = errorMessage("down", 1);
assert.strictEqual(msg, "Failed to disconnect from Tailscale") assert.strictEqual(msg, "Failed to disconnect from Tailscale");
}) });
test("errorMessage returns user-friendly message for tailscale set failure", () => { test("errorMessage returns user-friendly message for tailscale set failure", () => {
const msg = errorMessage("set", 1) const msg = errorMessage("set", 1);
assert.strictEqual(msg, "Failed to set exit node") assert.strictEqual(msg, "Failed to set exit node");
}) });
test("errorMessage returns user-friendly message for tailscale status failure", () => { test("errorMessage returns user-friendly message for tailscale status failure", () => {
const msg = errorMessage("status", 1) const msg = errorMessage("status", 1);
assert.strictEqual(msg, "Failed to read Tailscale status") assert.strictEqual(msg, "Failed to read Tailscale status");
}) });
test("errorMessage returns generic message for unknown command", () => { test("errorMessage returns generic message for unknown command", () => {
const msg = errorMessage("unknown", 1) const msg = errorMessage("unknown", 1);
assert.strictEqual(msg, "Tailscale command failed") assert.strictEqual(msg, "Tailscale command failed");
}) });
// --- getClipboardCommands ---
test("getClipboardCommands returns ordered argv arrays with text appended", () => { test("getClipboardCommands returns ordered argv arrays with text appended", () => {
const cmds = getClipboardCommands("1.2.3.4") const cmds = getClipboardCommands("1.2.3.4");
assert.ok(Array.isArray(cmds)) assert.ok(Array.isArray(cmds));
assert.strictEqual(cmds.length, 2) assert.strictEqual(cmds.length, 2);
assert.deepStrictEqual(cmds[0], ["dms", "cl", "copy", "1.2.3.4"]) assert.deepStrictEqual(cmds[0], ["dms", "cl", "copy", "1.2.3.4"]);
assert.deepStrictEqual(cmds[1], ["wl-copy", "1.2.3.4"]) assert.deepStrictEqual(cmds[1], ["wl-copy", "1.2.3.4"]);
}) });
test("getClipboardCommands handles text with special characters safely (direct argv)", () => { test("getClipboardCommands handles text with special characters safely (direct argv)", () => {
const cmds = getClipboardCommands("it's a 'test' with \"quotes\" and\nnewlines") const cmds = getClipboardCommands("it's a 'test' with \"quotes\" and\nnewlines");
assert.ok(Array.isArray(cmds)) assert.ok(Array.isArray(cmds));
assert.strictEqual(cmds.length, 2) assert.strictEqual(cmds.length, 2);
assert.ok(cmds[0].includes("it's a 'test' with \"quotes\" and\nnewlines")) assert.ok(cmds[0].includes("it's a 'test' with \"quotes\" and\nnewlines"));
}) });
test("getClipboardCommands is deterministic and open for future tools", () => { test("getClipboardCommands is deterministic and open for future tools", () => {
const cmds = getClipboardCommands("foo") const cmds = getClipboardCommands("foo");
assert.ok(Array.isArray(cmds[0])) assert.ok(Array.isArray(cmds[0]));
assert.ok(Array.isArray(cmds[1])) assert.ok(Array.isArray(cmds[1]));
}) });
// --- getStrings ---
test("getStrings returns canonical UI strings for the widget", () => { test("getStrings returns canonical UI strings for the widget", () => {
const s = getStrings() const s = getStrings();
assert.ok(s.header) assert.ok(s.header)
assert.ok(s.connected) assert.ok(s.connected)
assert.ok(s.disconnected) assert.ok(s.disconnected)