@standardagents/code 0.11.7 → 0.11.9
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/index.js +92 -15
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import os8, { homedir } from 'os';
|
|
3
3
|
import path4 from 'path';
|
|
4
|
-
import
|
|
4
|
+
import readline3 from 'readline/promises';
|
|
5
5
|
import { stdout, stdin } from 'process';
|
|
6
6
|
import net from 'net';
|
|
7
7
|
import fs5 from 'fs';
|
|
@@ -938,6 +938,7 @@ var TunnelManager = class {
|
|
|
938
938
|
let opened = false;
|
|
939
939
|
const socket = net.connect({ host, port });
|
|
940
940
|
socket.setNoDelay(true);
|
|
941
|
+
socket.setKeepAlive(true, 15e3);
|
|
941
942
|
this.tunnels.set(id, { socket });
|
|
942
943
|
socket.on("connect", () => {
|
|
943
944
|
opened = true;
|
|
@@ -6490,7 +6491,7 @@ function readVersion() {
|
|
|
6490
6491
|
if (typeof pkg.version === "string" && pkg.version) return pkg.version;
|
|
6491
6492
|
} catch {
|
|
6492
6493
|
}
|
|
6493
|
-
return "0.11.
|
|
6494
|
+
return "0.11.9" ;
|
|
6494
6495
|
}
|
|
6495
6496
|
function isLocalHost(host) {
|
|
6496
6497
|
return host === "localhost" || host === "127.0.0.1" || host === "::1" || host.endsWith(".local") || host.endsWith(".localhost") || /^10\./.test(host) || /^192\.168\./.test(host) || /^172\.(1[6-9]|2\d|3[01])\./.test(host);
|
|
@@ -6605,6 +6606,13 @@ async function readFsResponse(api, machineId) {
|
|
|
6605
6606
|
async function readFsRequest(api, machineId) {
|
|
6606
6607
|
return parseFsRequest(await api.userKvGet(fsRequestKey(machineId)));
|
|
6607
6608
|
}
|
|
6609
|
+
var FS_POLL_HOT_MS = 1e3;
|
|
6610
|
+
var FS_POLL_IDLE_MS = 5e3;
|
|
6611
|
+
var FS_HOT_WINDOW_MS = 5 * 6e4;
|
|
6612
|
+
function fsPollDelay(lastRequestSeenAt, now) {
|
|
6613
|
+
if (lastRequestSeenAt !== null && now - lastRequestSeenAt < FS_HOT_WINDOW_MS) return FS_POLL_HOT_MS;
|
|
6614
|
+
return FS_POLL_IDLE_MS;
|
|
6615
|
+
}
|
|
6608
6616
|
async function writeFsResponse(api, machineId, res) {
|
|
6609
6617
|
await api.userKvSet(fsResponseKey(machineId), { ...res, responded_at: Date.now() });
|
|
6610
6618
|
}
|
|
@@ -6879,16 +6887,16 @@ async function clearMachineCommands(api, machineId, appliedIds) {
|
|
|
6879
6887
|
async function applyMachineCommand(api, identity, cmd) {
|
|
6880
6888
|
switch (cmd.kind) {
|
|
6881
6889
|
case "add_project": {
|
|
6882
|
-
const
|
|
6883
|
-
if (!
|
|
6884
|
-
await registerProject(api, identity,
|
|
6885
|
-
return `added project ${
|
|
6890
|
+
const path16 = typeof cmd.args?.path === "string" ? cmd.args.path : "";
|
|
6891
|
+
if (!path16) return "add_project: ignored (no path)";
|
|
6892
|
+
await registerProject(api, identity, path16);
|
|
6893
|
+
return `added project ${path16}`;
|
|
6886
6894
|
}
|
|
6887
6895
|
case "remove_project": {
|
|
6888
|
-
const
|
|
6889
|
-
if (!
|
|
6890
|
-
await unregisterProject(api, identity,
|
|
6891
|
-
return `removed project ${
|
|
6896
|
+
const path16 = typeof cmd.args?.path === "string" ? cmd.args.path : "";
|
|
6897
|
+
if (!path16) return "remove_project: ignored (no path)";
|
|
6898
|
+
await unregisterProject(api, identity, path16);
|
|
6899
|
+
return `removed project ${path16}`;
|
|
6892
6900
|
}
|
|
6893
6901
|
case "update":
|
|
6894
6902
|
return "update requested";
|
|
@@ -7516,7 +7524,6 @@ function runUpdate(pm) {
|
|
|
7516
7524
|
// src/daemon.ts
|
|
7517
7525
|
var HEARTBEAT_MS = 3e4;
|
|
7518
7526
|
var COMMAND_POLL_MS = 8e3;
|
|
7519
|
-
var FS_POLL_MS = 1e3;
|
|
7520
7527
|
var RECLAIM_PROBE_MS = 2 * 6e4;
|
|
7521
7528
|
var UPDATE_CHECK_MS = 6 * 60 * 6e4;
|
|
7522
7529
|
var SWEEP_MS = 10 * 6e4;
|
|
@@ -7867,12 +7874,14 @@ Run \`standardcode daemon install\` (or plain \`standardcode\`) once to sign in.
|
|
|
7867
7874
|
void drainCommands();
|
|
7868
7875
|
const commandTimer = setInterval(() => void drainCommands(), COMMAND_POLL_MS);
|
|
7869
7876
|
let lastFsNonce = null;
|
|
7877
|
+
let lastFsRequestSeenAt = null;
|
|
7870
7878
|
let fsBusy = false;
|
|
7871
7879
|
const drainFsRequests = async () => {
|
|
7872
7880
|
if (fsBusy) return;
|
|
7873
7881
|
fsBusy = true;
|
|
7874
7882
|
try {
|
|
7875
7883
|
const req = await readFsRequest(api, identity.machine_id).catch(() => null);
|
|
7884
|
+
if (req) lastFsRequestSeenAt = Math.max(lastFsRequestSeenAt ?? 0, req.requested_at);
|
|
7876
7885
|
if (!req || req.nonce === lastFsNonce) return;
|
|
7877
7886
|
lastFsNonce = req.nonce;
|
|
7878
7887
|
let ok = true;
|
|
@@ -7890,7 +7899,15 @@ Run \`standardcode daemon install\` (or plain \`standardcode\`) once to sign in.
|
|
|
7890
7899
|
fsBusy = false;
|
|
7891
7900
|
}
|
|
7892
7901
|
};
|
|
7893
|
-
|
|
7902
|
+
let fsTimer = null;
|
|
7903
|
+
let fsStopped = false;
|
|
7904
|
+
const scheduleFsPoll = () => {
|
|
7905
|
+
if (fsStopped) return;
|
|
7906
|
+
fsTimer = setTimeout(() => {
|
|
7907
|
+
void drainFsRequests().finally(scheduleFsPoll);
|
|
7908
|
+
}, fsPollDelay(lastFsRequestSeenAt, Date.now()));
|
|
7909
|
+
};
|
|
7910
|
+
scheduleFsPoll();
|
|
7894
7911
|
const sweeper = setInterval(() => {
|
|
7895
7912
|
if (updateReady && ![...workers.values()].some((w) => w.busy)) {
|
|
7896
7913
|
daemonLog("restarting to apply the installed update");
|
|
@@ -7904,7 +7921,8 @@ Run \`standardcode daemon install\` (or plain \`standardcode\`) once to sign in.
|
|
|
7904
7921
|
clearInterval(reclaim);
|
|
7905
7922
|
clearInterval(updateTimer);
|
|
7906
7923
|
clearInterval(commandTimer);
|
|
7907
|
-
|
|
7924
|
+
fsStopped = true;
|
|
7925
|
+
if (fsTimer) clearTimeout(fsTimer);
|
|
7908
7926
|
clearInterval(sweeper);
|
|
7909
7927
|
for (const worker of workers.values()) worker.stop();
|
|
7910
7928
|
events.close();
|
|
@@ -8181,7 +8199,7 @@ async function installCommand(endpointFlag) {
|
|
|
8181
8199
|
const identity = loadMachineIdentity();
|
|
8182
8200
|
const existing = await loadMachine(api, identity.machine_id).catch(() => null);
|
|
8183
8201
|
const suggested = machineDisplayName(existing ?? { hostname: os8.hostname(), id: identity.machine_id });
|
|
8184
|
-
const rl =
|
|
8202
|
+
const rl = readline3.createInterface({ input: stdin, output: stdout });
|
|
8185
8203
|
const answer = (await rl.question(
|
|
8186
8204
|
`${c3.bold}Machine name${c3.reset} ${c3.dim}(shown in the session picker)${c3.reset} [${suggested}]: `
|
|
8187
8205
|
)).trim();
|
|
@@ -8339,6 +8357,29 @@ async function runDaemonCommand(argv) {
|
|
|
8339
8357
|
}
|
|
8340
8358
|
}
|
|
8341
8359
|
}
|
|
8360
|
+
var DIR2 = path4.join(os8.homedir(), ".standardagents");
|
|
8361
|
+
var FILE2 = path4.join(DIR2, "prefs.json");
|
|
8362
|
+
function loadPrefs(file2 = FILE2) {
|
|
8363
|
+
try {
|
|
8364
|
+
return JSON.parse(fs5.readFileSync(file2, "utf8"));
|
|
8365
|
+
} catch {
|
|
8366
|
+
return {};
|
|
8367
|
+
}
|
|
8368
|
+
}
|
|
8369
|
+
function savePrefs(update, file2 = FILE2) {
|
|
8370
|
+
const merged = { ...loadPrefs(file2), ...update };
|
|
8371
|
+
fs5.mkdirSync(path4.dirname(file2), { recursive: true });
|
|
8372
|
+
fs5.writeFileSync(file2, JSON.stringify(merged, null, 2));
|
|
8373
|
+
}
|
|
8374
|
+
function shouldOfferDaemonInstall(facts) {
|
|
8375
|
+
if (facts.optedOutEnv) return false;
|
|
8376
|
+
if (!facts.interactive) return false;
|
|
8377
|
+
if (facts.platform === "win32") return false;
|
|
8378
|
+
if (facts.serviceInstalled) return false;
|
|
8379
|
+
if (facts.daemonOnline) return false;
|
|
8380
|
+
if (facts.declinedAt !== void 0) return false;
|
|
8381
|
+
return true;
|
|
8382
|
+
}
|
|
8342
8383
|
|
|
8343
8384
|
// src/auth-cli.ts
|
|
8344
8385
|
var c4 = {
|
|
@@ -8729,7 +8770,7 @@ ${c5.dim}Press Control-C again to exit${c5.reset}
|
|
|
8729
8770
|
};
|
|
8730
8771
|
const ask = async (question) => {
|
|
8731
8772
|
if (!reader.rl) {
|
|
8732
|
-
reader.rl =
|
|
8773
|
+
reader.rl = readline3.createInterface({ input: stdin, output: stdout });
|
|
8733
8774
|
reader.rl.on("SIGINT", onPreflightSigint);
|
|
8734
8775
|
reader.rl.on("close", () => {
|
|
8735
8776
|
if (handoffClosing) return;
|
|
@@ -8912,6 +8953,42 @@ ${c5.dim}Press Control-C again to exit${c5.reset}
|
|
|
8912
8953
|
session.suggestDaemonInstall = machines.every((m) => !m.daemon);
|
|
8913
8954
|
const remoteTargets = machines.filter((m) => m.id !== identity.machine_id && daemonOnline(m));
|
|
8914
8955
|
const self = machines.find((m) => m.id === identity.machine_id);
|
|
8956
|
+
if (shouldOfferDaemonInstall({
|
|
8957
|
+
platform: process.platform,
|
|
8958
|
+
interactive: Boolean(stdin.isTTY && stdout.isTTY),
|
|
8959
|
+
serviceInstalled: serviceStatus().installed,
|
|
8960
|
+
daemonOnline: Boolean(self && daemonOnline(self)),
|
|
8961
|
+
declinedAt: loadPrefs().daemon_install_declined_at,
|
|
8962
|
+
optedOutEnv: Boolean(process.env.STANDARD_CODE_NO_DAEMON_PROMPT)
|
|
8963
|
+
})) {
|
|
8964
|
+
stdout.write(
|
|
8965
|
+
`
|
|
8966
|
+
${c5.bold}This machine has no always-on daemon${c5.reset} ${c5.dim}\u2014 sessions stop when this terminal closes.${c5.reset}
|
|
8967
|
+
`
|
|
8968
|
+
);
|
|
8969
|
+
const promptRl = readline3.createInterface({ input: stdin, output: stdout });
|
|
8970
|
+
let answer = "";
|
|
8971
|
+
try {
|
|
8972
|
+
answer = (await promptRl.question(
|
|
8973
|
+
`${c5.white}Install it now so sessions keep running and start from anywhere?${c5.reset} ${c5.dim}[Y/n]${c5.reset} `
|
|
8974
|
+
)).trim().toLowerCase();
|
|
8975
|
+
} catch {
|
|
8976
|
+
answer = "n";
|
|
8977
|
+
} finally {
|
|
8978
|
+
promptRl.close();
|
|
8979
|
+
}
|
|
8980
|
+
if (answer === "" || answer === "y" || answer === "yes") {
|
|
8981
|
+
await runDaemonCommand(endpointOverride ? ["install", "--endpoint", endpoint] : ["install"]);
|
|
8982
|
+
stdout.write("\n");
|
|
8983
|
+
} else {
|
|
8984
|
+
savePrefs({ daemon_install_declined_at: Date.now() });
|
|
8985
|
+
stdout.write(
|
|
8986
|
+
`${c5.dim}Okay \u2014 \`standardcode daemon install\` sets it up any time.${c5.reset}
|
|
8987
|
+
|
|
8988
|
+
`
|
|
8989
|
+
);
|
|
8990
|
+
}
|
|
8991
|
+
}
|
|
8915
8992
|
const launchDir = projectDir;
|
|
8916
8993
|
let tags = [];
|
|
8917
8994
|
let resumeTags = [];
|