@aayambansal/squint 0.8.2 → 0.9.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/dist/{App-C3LOYRTR.js → App-22JCZNP6.js} +2 -2
- package/dist/{chunk-PPC2KM7P.js → chunk-IANXZQ6Z.js} +1 -1
- package/dist/{chunk-MDLNTOGX.js → chunk-RXVMB47Q.js} +53 -2
- package/dist/{chunk-B356UR44.js → chunk-ZPX5FADM.js} +5 -5
- package/dist/cli.js +16 -10
- package/dist/{receipts-7PERXBJO.js → receipts-JE6WVFYH.js} +1 -1
- package/dist/{remote-ICNK2PPK.js → remote-WHMW4CGK.js} +4 -3
- package/dist/{server-WOYEQLAA.js → server-IIIH4OZZ.js} +1 -1
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
Session
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-ZPX5FADM.js";
|
|
5
5
|
|
|
6
6
|
// src/daemon/client.ts
|
|
7
7
|
import net from "net";
|
|
@@ -57,7 +57,7 @@ function socketPath(cwd) {
|
|
|
57
57
|
const hash = crypto.createHash("sha256").update(cwd).digest("hex").slice(0, 12);
|
|
58
58
|
return path.join(os.tmpdir(), `squint-${hash}.sock`);
|
|
59
59
|
}
|
|
60
|
-
function startDaemon(opts) {
|
|
60
|
+
async function startDaemon(opts) {
|
|
61
61
|
const session = new Session(opts);
|
|
62
62
|
const sock = socketPath(opts.cwd);
|
|
63
63
|
fs.mkdirSync(path.dirname(sock), { recursive: true });
|
|
@@ -132,6 +132,54 @@ ${failures.join("\n")}`);
|
|
|
132
132
|
}
|
|
133
133
|
}, opts.intervalSweepMs ?? 6e4);
|
|
134
134
|
sweep.unref?.();
|
|
135
|
+
let relayServer = null;
|
|
136
|
+
let relayUrl;
|
|
137
|
+
let relayUnsub = null;
|
|
138
|
+
if (opts.approvalWebhook) {
|
|
139
|
+
const http = await import("http");
|
|
140
|
+
const crypto2 = await import("crypto");
|
|
141
|
+
const tokens = /* @__PURE__ */ new Map();
|
|
142
|
+
relayServer = http.createServer((req, res) => {
|
|
143
|
+
const token = (req.url ?? "").replace(/^\//, "");
|
|
144
|
+
const verdict = tokens.get(token);
|
|
145
|
+
if (!verdict) {
|
|
146
|
+
res.writeHead(404).end("unknown or already-used token");
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
tokens.clear();
|
|
150
|
+
session.command(verdict === "yes" ? "/yes approved via webhook" : "/no rejected via webhook");
|
|
151
|
+
res.writeHead(200, { "content-type": "text/plain" });
|
|
152
|
+
res.end(verdict === "yes" ? "approved \u2014 the engine proceeds" : "rejected \u2014 the engine stands down");
|
|
153
|
+
});
|
|
154
|
+
await new Promise((resolve) => relayServer?.listen(opts.relayPort ?? 0, "127.0.0.1", resolve));
|
|
155
|
+
const addr = relayServer.address();
|
|
156
|
+
relayUrl = `http://127.0.0.1:${addr.port}`;
|
|
157
|
+
let lastSeen = null;
|
|
158
|
+
relayUnsub = session.subscribe(() => {
|
|
159
|
+
const pending = session.getState().pendingApproval;
|
|
160
|
+
if (!pending || pending === lastSeen) {
|
|
161
|
+
if (!pending) lastSeen = null;
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
lastSeen = pending;
|
|
165
|
+
const approve = crypto2.randomBytes(16).toString("hex");
|
|
166
|
+
const reject = crypto2.randomBytes(16).toString("hex");
|
|
167
|
+
tokens.clear();
|
|
168
|
+
tokens.set(approve, "yes");
|
|
169
|
+
tokens.set(reject, "no");
|
|
170
|
+
fetch(opts.approvalWebhook, {
|
|
171
|
+
method: "POST",
|
|
172
|
+
headers: { "content-type": "application/json" },
|
|
173
|
+
body: JSON.stringify({
|
|
174
|
+
text: `squint approval requested: ${pending}`,
|
|
175
|
+
summary: pending,
|
|
176
|
+
approveUrl: `${relayUrl}/${approve}`,
|
|
177
|
+
rejectUrl: `${relayUrl}/${reject}`
|
|
178
|
+
}),
|
|
179
|
+
signal: AbortSignal.timeout(5e3)
|
|
180
|
+
}).catch(() => null);
|
|
181
|
+
});
|
|
182
|
+
}
|
|
135
183
|
const unsubscribe = session.subscribe(() => {
|
|
136
184
|
const payload = serialize();
|
|
137
185
|
for (const client of clients) {
|
|
@@ -143,9 +191,12 @@ ${failures.join("\n")}`);
|
|
|
143
191
|
server.listen(sock, () => {
|
|
144
192
|
resolve({
|
|
145
193
|
session,
|
|
194
|
+
relayUrl,
|
|
146
195
|
clientCount: () => clients.length,
|
|
147
196
|
close: () => {
|
|
148
197
|
clearInterval(sweep);
|
|
198
|
+
relayUnsub?.();
|
|
199
|
+
relayServer?.close();
|
|
149
200
|
unsubscribe();
|
|
150
201
|
for (const client of clients) client.destroy();
|
|
151
202
|
server.close();
|
|
@@ -117,6 +117,7 @@ var Session = class {
|
|
|
117
117
|
engineId: opts.engineId,
|
|
118
118
|
model: opts.model,
|
|
119
119
|
devState: "stopped",
|
|
120
|
+
pendingApproval: null,
|
|
120
121
|
devUrl: null,
|
|
121
122
|
totals: { costUsd: 0, turns: 0 },
|
|
122
123
|
queue: [],
|
|
@@ -155,7 +156,6 @@ var Session = class {
|
|
|
155
156
|
checkpoints = [];
|
|
156
157
|
fixAttempts = 0;
|
|
157
158
|
reviewTipShown = false;
|
|
158
|
-
pendingApproval = null;
|
|
159
159
|
goal = null;
|
|
160
160
|
laneEnabled = false;
|
|
161
161
|
inLane = false;
|
|
@@ -496,7 +496,7 @@ ${question}`,
|
|
|
496
496
|
const req = JSON.parse(fs2.readFileSync(reqPath, "utf8"));
|
|
497
497
|
fs2.rmSync(reqPath, { force: true });
|
|
498
498
|
if (typeof req?.summary === "string" && req.summary.length > 0) {
|
|
499
|
-
this.pendingApproval
|
|
499
|
+
this.notify({ pendingApproval: req.summary });
|
|
500
500
|
const shot = typeof req.screenshot === "string" ? path2.resolve(this.execCwd(), req.screenshot) : null;
|
|
501
501
|
if (shot && fs2.existsSync(shot)) this.push("image", shot);
|
|
502
502
|
this.push("status", `\u23F8 approval requested: ${req.summary}
|
|
@@ -1069,12 +1069,12 @@ every ask now carries it; auto-fix presses to ${MAX_GOAL_FIX_ATTEMPTS} attempts
|
|
|
1069
1069
|
}
|
|
1070
1070
|
case "yes":
|
|
1071
1071
|
case "no": {
|
|
1072
|
-
if (!this.pendingApproval) {
|
|
1072
|
+
if (!this.state.pendingApproval) {
|
|
1073
1073
|
this.push("status", `nothing awaiting approval \u2014 /${name} answers an engine's approval request`);
|
|
1074
1074
|
break;
|
|
1075
1075
|
}
|
|
1076
|
-
const summary = this.pendingApproval;
|
|
1077
|
-
this.pendingApproval
|
|
1076
|
+
const summary = this.state.pendingApproval;
|
|
1077
|
+
this.notify({ pendingApproval: null });
|
|
1078
1078
|
const approved = name === "yes";
|
|
1079
1079
|
appendDecision(this.opts.cwd, {
|
|
1080
1080
|
decision: `${approved ? "approved" : "rejected"}: ${summary}${arg ? ` \u2014 ${arg}` : ""}`,
|
package/dist/cli.js
CHANGED
|
@@ -3,11 +3,11 @@ import {
|
|
|
3
3
|
connectDaemon,
|
|
4
4
|
socketPath,
|
|
5
5
|
startDaemon
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-RXVMB47Q.js";
|
|
7
7
|
import {
|
|
8
8
|
App
|
|
9
|
-
} from "./chunk-
|
|
10
|
-
import "./chunk-
|
|
9
|
+
} from "./chunk-IANXZQ6Z.js";
|
|
10
|
+
import "./chunk-ZPX5FADM.js";
|
|
11
11
|
import "./chunk-YKEAKVIS.js";
|
|
12
12
|
import "./chunk-LNJ7O5O4.js";
|
|
13
13
|
import "./chunk-3ZHETLUC.js";
|
|
@@ -66,7 +66,9 @@ var ConfigSchema = z.object({
|
|
|
66
66
|
/** Cheaper model used for auto-fix and /fix turns (mechanical work). */
|
|
67
67
|
fixModel: z.string().optional(),
|
|
68
68
|
/** TUI theme name (amber, ocean, moss, rose, mono). */
|
|
69
|
-
theme: z.string().optional()
|
|
69
|
+
theme: z.string().optional(),
|
|
70
|
+
/** POSTed when an engine requests visual approval under squint serve. */
|
|
71
|
+
approvalWebhook: z.string().url().optional()
|
|
70
72
|
});
|
|
71
73
|
function defaultPaths(cwd) {
|
|
72
74
|
const xdg = process.env.XDG_CONFIG_HOME;
|
|
@@ -114,6 +116,8 @@ function setConfigValue(file, key, value) {
|
|
|
114
116
|
throw new Error(`"${key}" must be true or false`);
|
|
115
117
|
}
|
|
116
118
|
next = { ...current, [key]: value === "true" };
|
|
119
|
+
} else if (key === "approvalWebhook") {
|
|
120
|
+
next = { ...current, approvalWebhook: value };
|
|
117
121
|
} else if (key === "budgetUsd") {
|
|
118
122
|
const budget = Number.parseFloat(value);
|
|
119
123
|
if (!Number.isFinite(budget) || budget <= 0) {
|
|
@@ -126,7 +130,7 @@ function setConfigValue(file, key, value) {
|
|
|
126
130
|
next = { ...current, models: { ...current.models, [engineId]: value } };
|
|
127
131
|
} else {
|
|
128
132
|
throw new Error(
|
|
129
|
-
`Unknown config key "${key}". Supported: engine, theme, autoDev, autoFix, autoProbe, autoCheck, autoReview, bell, budgetUsd, fixModel, models.<engineId>`
|
|
133
|
+
`Unknown config key "${key}". Supported: engine, theme, autoDev, autoFix, autoProbe, autoCheck, autoReview, bell, budgetUsd, fixModel, approvalWebhook, models.<engineId>`
|
|
130
134
|
);
|
|
131
135
|
}
|
|
132
136
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
@@ -173,6 +177,7 @@ function registerDaemon(program2) {
|
|
|
173
177
|
autoReview: config.autoReview,
|
|
174
178
|
fixModel: config.fixModel,
|
|
175
179
|
budgetUsd: config.budgetUsd,
|
|
180
|
+
approvalWebhook: config.approvalWebhook,
|
|
176
181
|
onQuit: () => {
|
|
177
182
|
daemon.close();
|
|
178
183
|
process.exit(0);
|
|
@@ -180,6 +185,7 @@ function registerDaemon(program2) {
|
|
|
180
185
|
});
|
|
181
186
|
console.log(`squint daemon on ${socketPath(cwd)} (engine: ${engineId})`);
|
|
182
187
|
console.log("attach from another terminal with: squint attach");
|
|
188
|
+
if (daemon.relayUrl) console.log(`approval relay on ${daemon.relayUrl} \u2192 ${config.approvalWebhook}`);
|
|
183
189
|
const stop = () => {
|
|
184
190
|
daemon.close();
|
|
185
191
|
process.exit(0);
|
|
@@ -188,18 +194,18 @@ function registerDaemon(program2) {
|
|
|
188
194
|
process.on("SIGTERM", stop);
|
|
189
195
|
});
|
|
190
196
|
program2.command("mcp").description("serve the gates as MCP tools over stdio (check, shot, flows, context)").action(async () => {
|
|
191
|
-
const { runMcpServer } = await import("./server-
|
|
197
|
+
const { runMcpServer } = await import("./server-IIIH4OZZ.js");
|
|
192
198
|
runMcpServer(process.cwd());
|
|
193
199
|
});
|
|
194
200
|
program2.command("attach").description("attach this terminal to a running squint daemon (full TUI; --plain for line mode)").option("--plain", "line-mode attach instead of the full TUI").action(async (opts) => {
|
|
195
201
|
const cwd = process.cwd();
|
|
196
202
|
if (!opts.plain) {
|
|
197
203
|
try {
|
|
198
|
-
const { RemoteSession } = await import("./remote-
|
|
204
|
+
const { RemoteSession } = await import("./remote-WHMW4CGK.js");
|
|
199
205
|
const remote = await RemoteSession.connect(cwd);
|
|
200
206
|
const config = loadConfig(defaultPaths(cwd));
|
|
201
207
|
const { render: render2 } = await import("ink");
|
|
202
|
-
const { App: App2 } = await import("./App-
|
|
208
|
+
const { App: App2 } = await import("./App-22JCZNP6.js");
|
|
203
209
|
const React = await import("react");
|
|
204
210
|
render2(
|
|
205
211
|
React.createElement(App2, {
|
|
@@ -605,7 +611,7 @@ function registerQuality(program2) {
|
|
|
605
611
|
report.ok = !failed;
|
|
606
612
|
report.finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
607
613
|
try {
|
|
608
|
-
const { writeReceipt } = await import("./receipts-
|
|
614
|
+
const { writeReceipt } = await import("./receipts-JE6WVFYH.js");
|
|
609
615
|
console.log(pc5.dim(`receipt \u2192 ${writeReceipt(cwd, report)}`));
|
|
610
616
|
} catch {
|
|
611
617
|
}
|
|
@@ -804,7 +810,7 @@ function registerTui(program2) {
|
|
|
804
810
|
}
|
|
805
811
|
|
|
806
812
|
// src/cli.tsx
|
|
807
|
-
var VERSION = true ? "0.
|
|
813
|
+
var VERSION = true ? "0.9.0" : "0.0.0-dev";
|
|
808
814
|
var program = new Command();
|
|
809
815
|
program.name("squint").description("Lovable for your terminal \u2014 a frontend harness on top of Claude Code, Codex, and friends.").version(VERSION);
|
|
810
816
|
registerRun(program);
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
import {
|
|
3
3
|
connectDaemon,
|
|
4
4
|
socketPath
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-RXVMB47Q.js";
|
|
6
|
+
import "./chunk-ZPX5FADM.js";
|
|
7
7
|
import "./chunk-YKEAKVIS.js";
|
|
8
8
|
import "./chunk-3ZHETLUC.js";
|
|
9
9
|
import "./chunk-UBADCBPT.js";
|
|
@@ -38,7 +38,8 @@ var RemoteSession = class _RemoteSession {
|
|
|
38
38
|
queue: [],
|
|
39
39
|
mode: "safe",
|
|
40
40
|
problems: [],
|
|
41
|
-
sandbox: false
|
|
41
|
+
sandbox: false,
|
|
42
|
+
pendingApproval: null
|
|
42
43
|
};
|
|
43
44
|
client.onMessage((msg) => {
|
|
44
45
|
if (msg.type === "state" && msg.state) {
|
|
@@ -112,7 +112,7 @@ ${result.leaks.map((l) => ` ${l}`).join("\n")}` : ""}` : `\u2717 ${flow.name} a
|
|
|
112
112
|
async run(args, cwd) {
|
|
113
113
|
const fs = await import("fs");
|
|
114
114
|
const path = await import("path");
|
|
115
|
-
const { verifyReceipt } = await import("./receipts-
|
|
115
|
+
const { verifyReceipt } = await import("./receipts-JE6WVFYH.js");
|
|
116
116
|
const file = path.isAbsolute(String(args.path)) ? String(args.path) : path.join(cwd, String(args.path));
|
|
117
117
|
let receipt;
|
|
118
118
|
try {
|
package/package.json
CHANGED