Merge feature_extract_toggle_decision_libjs into vibes for human review
Preserves the individual refactor commit as required by the agent git workflow. Written by AI agent working for @jtmorris. Model: grok-build-0.1.
This commit is contained in:
commit
b124a3e6e3
3 changed files with 43 additions and 21 deletions
|
|
@ -16,7 +16,6 @@ PluginComponent {
|
||||||
property var peers: []
|
property var peers: []
|
||||||
property string _copyText: ""
|
property string _copyText: ""
|
||||||
property int _copyIndex: 0
|
property int _copyIndex: 0
|
||||||
property bool _pendingToggle: false // transient one-shot for post-action verification (to be removed in first-principles refactor)
|
|
||||||
|
|
||||||
layerNamespacePlugin: "tailscalectl"
|
layerNamespacePlugin: "tailscalectl"
|
||||||
popoutWidth: 360
|
popoutWidth: 360
|
||||||
|
|
@ -34,11 +33,11 @@ PluginComponent {
|
||||||
|
|
||||||
onExited: (code, status) => {
|
onExited: (code, status) => {
|
||||||
if (code !== 0) {
|
if (code !== 0) {
|
||||||
var action = root.isConnected ? "disconnect" : "connect"
|
var action = root.isConnected ? "disconnect" : "connect";
|
||||||
var detail = toggleProcess.stderr.text.trim()
|
var detail = toggleProcess.stderr.text.trim();
|
||||||
ToastService.showError("tailscalectl", TailscaleLib.formatError(action, detail))
|
ToastService.showError("tailscalectl", TailscaleLib.formatError(action, detail));
|
||||||
}
|
}
|
||||||
statusCheck.running = true
|
root.refreshStatus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -66,10 +65,10 @@ PluginComponent {
|
||||||
|
|
||||||
onExited: (code, status) => {
|
onExited: (code, status) => {
|
||||||
if (code !== 0) {
|
if (code !== 0) {
|
||||||
var detail = exitNodeProcess.stderr.text.trim()
|
var detail = exitNodeProcess.stderr.text.trim();
|
||||||
ToastService.showError("tailscalectl", TailscaleLib.formatError("set", detail))
|
ToastService.showError("tailscalectl", TailscaleLib.formatError("set", detail));
|
||||||
}
|
}
|
||||||
statusCheck.running = true
|
root.refreshStatus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -95,19 +94,22 @@ PluginComponent {
|
||||||
ToastService.showError("tailscalectl", TailscaleLib.formatError("status"))
|
ToastService.showError("tailscalectl", TailscaleLib.formatError("status"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Post-action verification: if a toggle was requested, use the fresh state we just received.
|
const cmd = TailscaleLib.commandForPendingAction(statusCheck._pendingAction, root.isConnected);
|
||||||
if (root._pendingToggle) {
|
if (cmd) {
|
||||||
root._pendingToggle = false
|
toggleProcess.command = cmd;
|
||||||
toggleProcess.command = TailscaleLib.buildToggleCommand(root.isConnected)
|
toggleProcess.running = true;
|
||||||
toggleProcess.running = true
|
|
||||||
}
|
}
|
||||||
|
statusCheck._pendingAction = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleTailscale() {
|
function toggleTailscale() {
|
||||||
// Post-action verification: force a fresh status check, then act on the real ground truth.
|
statusCheck._pendingAction = "toggle";
|
||||||
root._pendingToggle = true
|
statusCheck.running = true;
|
||||||
statusCheck.running = true
|
}
|
||||||
|
|
||||||
|
function refreshStatus() {
|
||||||
|
statusCheck.running = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setExitNode(hostname) {
|
function setExitNode(hostname) {
|
||||||
|
|
|
||||||
|
|
@ -125,7 +125,17 @@ function formatError(action, detail) {
|
||||||
return base;
|
return base;
|
||||||
}
|
}
|
||||||
|
|
||||||
// CommonJS export for Node.js tests (ignored by QML)
|
const PendingAction = Object.freeze({
|
||||||
if (typeof module !== "undefined" && module.exports) {
|
TOGGLE: "toggle"
|
||||||
module.exports = { parsePeers, makeExitNodeCommand, findActiveExitNode, errorMessage, formatError, getStatusCommand, isValidExitNodeHostname, getClipboardCommands, buildToggleCommand, parseStatusResult, getStrings, shouldShowClearExitNode, isActiveExitNode }
|
});
|
||||||
|
|
||||||
|
function commandForPendingAction(pending, freshIsConnected) {
|
||||||
|
if (pending === PendingAction.TOGGLE) {
|
||||||
|
return buildToggleCommand(freshIsConnected);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof module !== "undefined" && module.exports) {
|
||||||
|
module.exports = { parsePeers, makeExitNodeCommand, findActiveExitNode, errorMessage, formatError, getStatusCommand, isValidExitNodeHostname, getClipboardCommands, buildToggleCommand, parseStatusResult, getStrings, shouldShowClearExitNode, isActiveExitNode, PendingAction, commandForPendingAction };
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 } = 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.
|
||||||
|
|
@ -151,7 +151,17 @@ test("buildToggleCommand treats null and undefined as disconnected", () => {
|
||||||
assert.deepStrictEqual(buildToggleCommand(undefined), ["tailscale", "up"])
|
assert.deepStrictEqual(buildToggleCommand(undefined), ["tailscale", "up"])
|
||||||
})
|
})
|
||||||
|
|
||||||
// --- parseStatusResult ---
|
test("commandForPendingAction returns toggle command when pending is TOGGLE and passes through buildToggleCommand logic", () => {
|
||||||
|
assert.deepStrictEqual(commandForPendingAction(PendingAction.TOGGLE, true), ["tailscale", "down"]);
|
||||||
|
assert.deepStrictEqual(commandForPendingAction(PendingAction.TOGGLE, false), ["tailscale", "up"]);
|
||||||
|
assert.deepStrictEqual(commandForPendingAction(PendingAction.TOGGLE, null), ["tailscale", "up"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("commandForPendingAction returns null for no pending action or unknown pending value", () => {
|
||||||
|
assert.strictEqual(commandForPendingAction(null, true), null);
|
||||||
|
assert.strictEqual(commandForPendingAction(undefined, false), null);
|
||||||
|
assert.strictEqual(commandForPendingAction("something-else", true), null);
|
||||||
|
});
|
||||||
|
|
||||||
test("parseStatusResult produces correct state from valid JSON", () => {
|
test("parseStatusResult produces correct state from valid JSON", () => {
|
||||||
const json = JSON.stringify({
|
const json = JSON.stringify({
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue