@standardagents/code 0.13.7 → 0.13.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/README.md +206 -9
- package/dist/index.js +327 -114
- package/dist/index.js.map +1 -1
- package/package.json +7 -3
package/dist/index.js
CHANGED
|
@@ -13,7 +13,8 @@ import * as transcript_delivery_star from '@standardagents/code-network/transcri
|
|
|
13
13
|
import * as approvals_star from '@standardagents/code-network/approvals';
|
|
14
14
|
import net from 'net';
|
|
15
15
|
import { setImmediate } from 'timers';
|
|
16
|
-
import
|
|
16
|
+
import fs12 from 'fs';
|
|
17
|
+
import { isTranscriptMetaTool } from '@standardagents/code-network/tool-activity';
|
|
17
18
|
import { permissionKey, Bridge as Bridge$1 } from '@standardagents/code-network/bridge';
|
|
18
19
|
import readline from 'readline';
|
|
19
20
|
import * as types_star from '@standardagents/code-network/types';
|
|
@@ -103,10 +104,10 @@ var AccountUserStream = class {
|
|
|
103
104
|
options.uniqueConnectionId
|
|
104
105
|
);
|
|
105
106
|
}
|
|
106
|
-
start() {
|
|
107
|
+
async start() {
|
|
107
108
|
if (this.started) return;
|
|
108
109
|
this.started = true;
|
|
109
|
-
this.hub.start();
|
|
110
|
+
await this.hub.start();
|
|
110
111
|
}
|
|
111
112
|
onEvent(listener) {
|
|
112
113
|
this.eventListeners.add(listener);
|
|
@@ -119,7 +120,7 @@ var AccountUserStream = class {
|
|
|
119
120
|
/** Wait for a live socket. The timeout is local and performs no network work. */
|
|
120
121
|
waitUntilConnected(timeoutMs = 15e3) {
|
|
121
122
|
if (this.connected) return Promise.resolve();
|
|
122
|
-
this.start();
|
|
123
|
+
void this.start();
|
|
123
124
|
return new Promise((resolve, reject) => {
|
|
124
125
|
let timer;
|
|
125
126
|
const remove = this.onConnection((state) => {
|
|
@@ -217,6 +218,7 @@ function riskCeiling(state) {
|
|
|
217
218
|
function decide(state, tool, risk, hasPermissionRequest) {
|
|
218
219
|
const effectiveRisk = typeof risk === "number" ? Math.min(5, Math.max(1, risk)) : hasPermissionRequest ? 3 : 1;
|
|
219
220
|
if (state.alwaysAllow.has(tool)) return "allow";
|
|
221
|
+
if (hasPermissionRequest) return "ask";
|
|
220
222
|
return effectiveRisk <= riskCeiling(state) ? "allow" : "ask";
|
|
221
223
|
}
|
|
222
224
|
var CATASTROPHIC_PATTERNS = [
|
|
@@ -420,7 +422,7 @@ var ToolLedger = class {
|
|
|
420
422
|
if (this.loaded) return;
|
|
421
423
|
this.loaded = true;
|
|
422
424
|
try {
|
|
423
|
-
const raw =
|
|
425
|
+
const raw = fs12.readFileSync(this.file, "utf8");
|
|
424
426
|
const parsed = JSON.parse(raw);
|
|
425
427
|
for (const [id, entry] of Object.entries(parsed)) {
|
|
426
428
|
if (entry && typeof entry.ok === "boolean") this.entries.set(id, entry);
|
|
@@ -452,13 +454,13 @@ var ToolLedger = class {
|
|
|
452
454
|
}
|
|
453
455
|
persist() {
|
|
454
456
|
try {
|
|
455
|
-
|
|
457
|
+
fs12.mkdirSync(ledgerDir(), { recursive: true });
|
|
456
458
|
const out = {};
|
|
457
459
|
for (const [id, entry] of this.entries) {
|
|
458
460
|
const size = entry.result ? Buffer.byteLength(entry.result) : 0;
|
|
459
461
|
out[id] = size > MAX_RESULT_BYTES ? { ...entry, result: void 0 } : entry;
|
|
460
462
|
}
|
|
461
|
-
|
|
463
|
+
fs12.writeFileSync(this.file, JSON.stringify(out), { mode: 384 });
|
|
462
464
|
} catch {
|
|
463
465
|
}
|
|
464
466
|
}
|
|
@@ -686,7 +688,10 @@ var Bridge = class extends Bridge$1 {
|
|
|
686
688
|
isCatastrophic: (command) => isCatastrophic(command),
|
|
687
689
|
decide: (permKey, risk, requestPermission) => decide(perm, permKey, risk, requestPermission),
|
|
688
690
|
grantAlways: (permKey) => void perm.alwaysAllow.add(permKey),
|
|
689
|
-
grantRisk: (risk) =>
|
|
691
|
+
grantRisk: (risk) => {
|
|
692
|
+
perm.level = Math.max(perm.level, risk);
|
|
693
|
+
perm.allowRisk.clear();
|
|
694
|
+
},
|
|
690
695
|
persistApprovals: () => void (0, approvals_exports.saveApprovals)(api, threadId, perm)
|
|
691
696
|
},
|
|
692
697
|
{
|
|
@@ -797,7 +802,6 @@ function prettyAgentName(id) {
|
|
|
797
802
|
if (!key) return "subagent";
|
|
798
803
|
return AGENT_NAMES[key] || key.replace(/[_-]+/g, " ").replace(/\b\w/g, (ch) => ch.toUpperCase());
|
|
799
804
|
}
|
|
800
|
-
var META_TOOLS = /* @__PURE__ */ new Set(["set_goal", "update_goal_step", "reasoning"]);
|
|
801
805
|
var APPROVAL_PENDING_MARKER = "Awaiting your approval";
|
|
802
806
|
var ToolActivityFeed = class {
|
|
803
807
|
calls = /* @__PURE__ */ new Map();
|
|
@@ -832,7 +836,7 @@ var ToolActivityFeed = class {
|
|
|
832
836
|
const text = (0, session_state_exports.messageText)(message.content);
|
|
833
837
|
if (text.includes(session_state_exports.WAITING_FOR_CLIENT_MARKER) || text.includes(APPROVAL_PENDING_MARKER)) return null;
|
|
834
838
|
this.settled.add(callId);
|
|
835
|
-
if (
|
|
839
|
+
if (isTranscriptMetaTool(req.tool)) return null;
|
|
836
840
|
const summary = describe(req);
|
|
837
841
|
if (toolResultFailed(message, text)) {
|
|
838
842
|
return { line: `\u2717 ${summary} \u2014 ${text.trim() || "failed"}` };
|
|
@@ -1546,6 +1550,46 @@ var WordMill = class {
|
|
|
1546
1550
|
// src/types.ts
|
|
1547
1551
|
var types_exports = {};
|
|
1548
1552
|
__reExport(types_exports, types_star);
|
|
1553
|
+
|
|
1554
|
+
// src/subprocess-env.ts
|
|
1555
|
+
var INHERITED_ENV_ALLOWLIST = /* @__PURE__ */ new Set([
|
|
1556
|
+
"APPDATA",
|
|
1557
|
+
"COMSPEC",
|
|
1558
|
+
"HOME",
|
|
1559
|
+
"LANG",
|
|
1560
|
+
"LC_ALL",
|
|
1561
|
+
"LC_CTYPE",
|
|
1562
|
+
"LOCALAPPDATA",
|
|
1563
|
+
"LOGNAME",
|
|
1564
|
+
"PATH",
|
|
1565
|
+
"PATHEXT",
|
|
1566
|
+
"PROGRAMDATA",
|
|
1567
|
+
"SHELL",
|
|
1568
|
+
"SYSTEMROOT",
|
|
1569
|
+
"TEMP",
|
|
1570
|
+
"TERM",
|
|
1571
|
+
"TMP",
|
|
1572
|
+
"TMPDIR",
|
|
1573
|
+
"USER",
|
|
1574
|
+
"USERPROFILE",
|
|
1575
|
+
"WINDIR"
|
|
1576
|
+
]);
|
|
1577
|
+
function inheritedSubprocessEnv(ambient = process.env) {
|
|
1578
|
+
const child = { ...ambient };
|
|
1579
|
+
delete child.NODE_TLS_REJECT_UNAUTHORIZED;
|
|
1580
|
+
return child;
|
|
1581
|
+
}
|
|
1582
|
+
function restrictedSubprocessEnv(explicit = {}, ambient = process.env) {
|
|
1583
|
+
const inherited = {};
|
|
1584
|
+
for (const [name, value] of Object.entries(ambient)) {
|
|
1585
|
+
if (value !== void 0 && INHERITED_ENV_ALLOWLIST.has(name.toUpperCase())) {
|
|
1586
|
+
inherited[name] = value;
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
return { ...inherited, ...explicit };
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
// src/clipboard.ts
|
|
1549
1593
|
var FILE_MIMES = {
|
|
1550
1594
|
".png": "image/png",
|
|
1551
1595
|
".jpg": "image/jpeg",
|
|
@@ -1556,7 +1600,11 @@ var FILE_MIMES = {
|
|
|
1556
1600
|
var MAX_IMAGE_BYTES = 8 * 1024 * 1024;
|
|
1557
1601
|
function run(cmd, args, maxBuffer = MAX_IMAGE_BYTES * 2) {
|
|
1558
1602
|
return new Promise((resolve) => {
|
|
1559
|
-
execFile(cmd, args, {
|
|
1603
|
+
execFile(cmd, args, {
|
|
1604
|
+
encoding: "buffer",
|
|
1605
|
+
maxBuffer,
|
|
1606
|
+
env: inheritedSubprocessEnv()
|
|
1607
|
+
}, (err, stdout) => {
|
|
1560
1608
|
resolve({ ok: !err, stdout: stdout ?? Buffer.alloc(0) });
|
|
1561
1609
|
});
|
|
1562
1610
|
});
|
|
@@ -1565,9 +1613,9 @@ function fromFile(filePath) {
|
|
|
1565
1613
|
const mime = FILE_MIMES[path9.extname(filePath).toLowerCase()];
|
|
1566
1614
|
if (!mime) return null;
|
|
1567
1615
|
try {
|
|
1568
|
-
const stat =
|
|
1616
|
+
const stat = fs12.statSync(filePath);
|
|
1569
1617
|
if (!stat.isFile() || stat.size === 0 || stat.size > MAX_IMAGE_BYTES) return null;
|
|
1570
|
-
return { data:
|
|
1618
|
+
return { data: fs12.readFileSync(filePath).toString("base64"), mime };
|
|
1571
1619
|
} catch {
|
|
1572
1620
|
return null;
|
|
1573
1621
|
}
|
|
@@ -1585,7 +1633,7 @@ async function readDarwin() {
|
|
|
1585
1633
|
if (png.ok) {
|
|
1586
1634
|
const img = fromFile(tmp);
|
|
1587
1635
|
try {
|
|
1588
|
-
|
|
1636
|
+
fs12.unlinkSync(tmp);
|
|
1589
1637
|
} catch {
|
|
1590
1638
|
}
|
|
1591
1639
|
if (img) return img;
|
|
@@ -1619,7 +1667,7 @@ async function readWindows() {
|
|
|
1619
1667
|
await run("powershell", ["-NoProfile", "-STA", "-Command", ps]);
|
|
1620
1668
|
const img = fromFile(tmp);
|
|
1621
1669
|
try {
|
|
1622
|
-
|
|
1670
|
+
fs12.unlinkSync(tmp);
|
|
1623
1671
|
} catch {
|
|
1624
1672
|
}
|
|
1625
1673
|
return img;
|
|
@@ -3816,6 +3864,18 @@ __reExport(shared_messaging_exports, shared_messaging_star);
|
|
|
3816
3864
|
var history_exports = {};
|
|
3817
3865
|
__reExport(history_exports, history_star);
|
|
3818
3866
|
var LOG_DIR = path9.join(os6.homedir(), ".standardagents", "process-logs");
|
|
3867
|
+
function openPrivateProcessLog(logPath) {
|
|
3868
|
+
const directory = path9.dirname(logPath);
|
|
3869
|
+
fs12.mkdirSync(directory, { recursive: true, mode: 448 });
|
|
3870
|
+
fs12.chmodSync(directory, 448);
|
|
3871
|
+
const descriptor = fs12.openSync(
|
|
3872
|
+
logPath,
|
|
3873
|
+
fs12.constants.O_WRONLY | fs12.constants.O_CREAT | fs12.constants.O_APPEND,
|
|
3874
|
+
384
|
|
3875
|
+
);
|
|
3876
|
+
fs12.fchmodSync(descriptor, 384);
|
|
3877
|
+
return descriptor;
|
|
3878
|
+
}
|
|
3819
3879
|
function isAlive(pid) {
|
|
3820
3880
|
try {
|
|
3821
3881
|
process.kill(pid, 0);
|
|
@@ -3834,7 +3894,7 @@ function configFile() {
|
|
|
3834
3894
|
}
|
|
3835
3895
|
function loadMcpConfig() {
|
|
3836
3896
|
try {
|
|
3837
|
-
const raw =
|
|
3897
|
+
const raw = fs12.readFileSync(configFile(), "utf8");
|
|
3838
3898
|
const parsed = JSON.parse(raw);
|
|
3839
3899
|
if (!parsed.servers || typeof parsed.servers !== "object") parsed.servers = {};
|
|
3840
3900
|
return parsed;
|
|
@@ -3865,8 +3925,8 @@ function setMcpServerEnabled(name, enabled) {
|
|
|
3865
3925
|
}
|
|
3866
3926
|
function write(cfg) {
|
|
3867
3927
|
const file2 = configFile();
|
|
3868
|
-
|
|
3869
|
-
|
|
3928
|
+
fs12.mkdirSync(path9.dirname(file2), { recursive: true });
|
|
3929
|
+
fs12.writeFileSync(file2, JSON.stringify(cfg, null, 2), { mode: 384 });
|
|
3870
3930
|
}
|
|
3871
3931
|
function parseServerSpec(spec) {
|
|
3872
3932
|
const trimmed = spec.trim();
|
|
@@ -3937,7 +3997,11 @@ function openUrl(url) {
|
|
|
3937
3997
|
const cmd = platform === "darwin" ? "open" : platform === "win32" ? "cmd" : "xdg-open";
|
|
3938
3998
|
const args = platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
3939
3999
|
try {
|
|
3940
|
-
const child = spawn(cmd, args, {
|
|
4000
|
+
const child = spawn(cmd, args, {
|
|
4001
|
+
stdio: "ignore",
|
|
4002
|
+
detached: true,
|
|
4003
|
+
env: inheritedSubprocessEnv()
|
|
4004
|
+
});
|
|
3941
4005
|
child.unref();
|
|
3942
4006
|
} catch {
|
|
3943
4007
|
}
|
|
@@ -3949,11 +4013,11 @@ __reExport(recorder_exports, recorder_star);
|
|
|
3949
4013
|
__reExport(recorder_exports, recorder_node_star);
|
|
3950
4014
|
function readVersion() {
|
|
3951
4015
|
try {
|
|
3952
|
-
const pkg = JSON.parse(
|
|
4016
|
+
const pkg = JSON.parse(fs12.readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
3953
4017
|
if (typeof pkg.version === "string" && pkg.version) return pkg.version;
|
|
3954
4018
|
} catch {
|
|
3955
4019
|
}
|
|
3956
|
-
return "0.13.
|
|
4020
|
+
return "0.13.9" ;
|
|
3957
4021
|
}
|
|
3958
4022
|
function isLocalHost(host) {
|
|
3959
4023
|
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);
|
|
@@ -3979,7 +4043,7 @@ var dir = () => path9.join(os6.homedir(), ".standardagents");
|
|
|
3979
4043
|
var file = () => path9.join(dir(), "machine.json");
|
|
3980
4044
|
function loadMachineIdentity() {
|
|
3981
4045
|
try {
|
|
3982
|
-
const parsed = JSON.parse(
|
|
4046
|
+
const parsed = JSON.parse(fs12.readFileSync(file(), "utf8"));
|
|
3983
4047
|
if (typeof parsed.machine_id === "string" && parsed.machine_id.length > 0) {
|
|
3984
4048
|
return { machine_id: parsed.machine_id, created_at: parsed.created_at ?? Date.now() };
|
|
3985
4049
|
}
|
|
@@ -3993,8 +4057,8 @@ function loadMachineIdentity() {
|
|
|
3993
4057
|
return identity;
|
|
3994
4058
|
}
|
|
3995
4059
|
function saveMachineIdentity(identity) {
|
|
3996
|
-
|
|
3997
|
-
|
|
4060
|
+
fs12.mkdirSync(dir(), { recursive: true });
|
|
4061
|
+
fs12.writeFileSync(file(), JSON.stringify(identity, null, 2), { mode: 384 });
|
|
3998
4062
|
}
|
|
3999
4063
|
function daemonClientId(identity) {
|
|
4000
4064
|
return `daemon:${identity.machine_id}`;
|
|
@@ -4325,7 +4389,8 @@ function projectRepository(projectDir) {
|
|
|
4325
4389
|
const url = execFileSync("git", ["-C", projectDir, "remote", "get-url", "origin"], {
|
|
4326
4390
|
encoding: "utf8",
|
|
4327
4391
|
timeout: 3e3,
|
|
4328
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
4392
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
4393
|
+
env: inheritedSubprocessEnv()
|
|
4329
4394
|
}).trim();
|
|
4330
4395
|
return url || null;
|
|
4331
4396
|
} catch {
|
|
@@ -4504,7 +4569,7 @@ function markers(dirPath) {
|
|
|
4504
4569
|
for (const marker of PROJECT_MARKERS) {
|
|
4505
4570
|
let hit = false;
|
|
4506
4571
|
try {
|
|
4507
|
-
hit =
|
|
4572
|
+
hit = fs12.existsSync(path9.join(dirPath, marker));
|
|
4508
4573
|
} catch {
|
|
4509
4574
|
hit = false;
|
|
4510
4575
|
}
|
|
@@ -4526,11 +4591,11 @@ function browseDirectory(input3, opts = {}) {
|
|
|
4526
4591
|
};
|
|
4527
4592
|
let dirents;
|
|
4528
4593
|
try {
|
|
4529
|
-
const stat =
|
|
4594
|
+
const stat = fs12.statSync(abs);
|
|
4530
4595
|
if (!stat.isDirectory()) {
|
|
4531
4596
|
return { ...base, entries: [], truncated: false, error: "Not a directory" };
|
|
4532
4597
|
}
|
|
4533
|
-
dirents =
|
|
4598
|
+
dirents = fs12.readdirSync(abs, { withFileTypes: true });
|
|
4534
4599
|
} catch (err) {
|
|
4535
4600
|
const code = err?.code;
|
|
4536
4601
|
const message = code === "EACCES" || code === "EPERM" ? "Permission denied" : code === "ENOENT" ? "Folder not found" : "Could not read this folder";
|
|
@@ -4545,7 +4610,7 @@ function browseDirectory(input3, opts = {}) {
|
|
|
4545
4610
|
let isDir = d.isDirectory();
|
|
4546
4611
|
if (d.isSymbolicLink()) {
|
|
4547
4612
|
try {
|
|
4548
|
-
isDir =
|
|
4613
|
+
isDir = fs12.statSync(path9.join(abs, name)).isDirectory();
|
|
4549
4614
|
} catch {
|
|
4550
4615
|
isDir = false;
|
|
4551
4616
|
}
|
|
@@ -4576,7 +4641,7 @@ function mkdirDirectory(parentInput, name, opts = {}) {
|
|
|
4576
4641
|
return { ...listParent(), error: "Invalid folder name." };
|
|
4577
4642
|
}
|
|
4578
4643
|
try {
|
|
4579
|
-
|
|
4644
|
+
fs12.mkdirSync(target, { recursive: false });
|
|
4580
4645
|
} catch (err) {
|
|
4581
4646
|
const code = err?.code;
|
|
4582
4647
|
const message = code === "EEXIST" ? "A folder with that name already exists." : code === "EACCES" || code === "EPERM" ? "Permission denied." : code === "ENOENT" ? "The parent folder no longer exists." : "Could not create the folder.";
|
|
@@ -4791,7 +4856,10 @@ function probe(cmd) {
|
|
|
4791
4856
|
return new Promise((resolve) => {
|
|
4792
4857
|
let child;
|
|
4793
4858
|
try {
|
|
4794
|
-
child = spawn(cmd, ["--version"], {
|
|
4859
|
+
child = spawn(cmd, ["--version"], {
|
|
4860
|
+
stdio: "ignore",
|
|
4861
|
+
env: inheritedSubprocessEnv()
|
|
4862
|
+
});
|
|
4795
4863
|
} catch {
|
|
4796
4864
|
resolve(false);
|
|
4797
4865
|
return;
|
|
@@ -4827,7 +4895,7 @@ function download(url, dest, redirects = 5) {
|
|
|
4827
4895
|
reject(new Error(`HTTP ${status} for ${url}`));
|
|
4828
4896
|
return;
|
|
4829
4897
|
}
|
|
4830
|
-
const out =
|
|
4898
|
+
const out = fs12.createWriteStream(dest);
|
|
4831
4899
|
res.pipe(out);
|
|
4832
4900
|
out.on("finish", () => out.close(() => resolve()));
|
|
4833
4901
|
out.on("error", reject);
|
|
@@ -4838,7 +4906,7 @@ function download(url, dest, redirects = 5) {
|
|
|
4838
4906
|
async function sha256File(file2) {
|
|
4839
4907
|
const hash = crypto.createHash("sha256");
|
|
4840
4908
|
await new Promise((resolve, reject) => {
|
|
4841
|
-
const stream =
|
|
4909
|
+
const stream = fs12.createReadStream(file2);
|
|
4842
4910
|
stream.on("data", (chunk) => hash.update(chunk));
|
|
4843
4911
|
stream.on("end", () => resolve());
|
|
4844
4912
|
stream.on("error", reject);
|
|
@@ -4849,7 +4917,11 @@ function runOk(cmd, args, cwd) {
|
|
|
4849
4917
|
return new Promise((resolve) => {
|
|
4850
4918
|
let child;
|
|
4851
4919
|
try {
|
|
4852
|
-
child = spawn(cmd, args, {
|
|
4920
|
+
child = spawn(cmd, args, {
|
|
4921
|
+
cwd,
|
|
4922
|
+
stdio: "ignore",
|
|
4923
|
+
env: inheritedSubprocessEnv()
|
|
4924
|
+
});
|
|
4853
4925
|
} catch {
|
|
4854
4926
|
resolve(false);
|
|
4855
4927
|
return;
|
|
@@ -4876,7 +4948,7 @@ async function provision(target) {
|
|
|
4876
4948
|
let extracted = null;
|
|
4877
4949
|
for (const entry of entries) {
|
|
4878
4950
|
const candidate = entry.isDirectory() ? path9.join(work, entry.name, binName) : entry.name === binName ? path9.join(work, entry.name) : null;
|
|
4879
|
-
if (candidate &&
|
|
4951
|
+
if (candidate && fs12.existsSync(candidate)) {
|
|
4880
4952
|
extracted = candidate;
|
|
4881
4953
|
break;
|
|
4882
4954
|
}
|
|
@@ -4901,7 +4973,7 @@ function resolveRg(log) {
|
|
|
4901
4973
|
const override = process.env.STANDARDCODE_RG_PATH;
|
|
4902
4974
|
if (override && await probe(override)) return override;
|
|
4903
4975
|
if (await probe("rg")) return "rg";
|
|
4904
|
-
if (
|
|
4976
|
+
if (fs12.existsSync(RG_BIN) && await probe(RG_BIN)) return RG_BIN;
|
|
4905
4977
|
const target = RG_TARGETS[`${process.platform}-${process.arch}`];
|
|
4906
4978
|
if (!target) return null;
|
|
4907
4979
|
try {
|
|
@@ -4960,16 +5032,36 @@ var HostTools = class {
|
|
|
4960
5032
|
mcp;
|
|
4961
5033
|
onMcpCatalogChange;
|
|
4962
5034
|
api;
|
|
4963
|
-
/** Resolve a user/model-supplied path against the project directory.
|
|
5035
|
+
/** Resolve a user/model-supplied path against the project directory.
|
|
5036
|
+
* A leading `~` expands to the host home directory — without this, a
|
|
5037
|
+
* model-supplied `~/Desktop/x` silently lands at `<project>/~/Desktop/x`.
|
|
5038
|
+
* Outside-project containment checks run on the resolved result, so an
|
|
5039
|
+
* expanded home path still requires a fresh human decision. */
|
|
4964
5040
|
resolve(p) {
|
|
4965
5041
|
if (!p || p === ".") return this.projectDir;
|
|
5042
|
+
if (p === "~" || p.startsWith("~/")) {
|
|
5043
|
+
p = path9.join(os6.homedir(), p.slice(1));
|
|
5044
|
+
}
|
|
4966
5045
|
return path9.resolve(this.projectDir, p);
|
|
4967
5046
|
}
|
|
4968
|
-
/**
|
|
5047
|
+
/** Resolve symlinks in an existing target or its nearest existing ancestor. */
|
|
5048
|
+
realPathForContainment(target) {
|
|
5049
|
+
const missing = [];
|
|
5050
|
+
let existing = path9.resolve(target);
|
|
5051
|
+
while (!fs12.existsSync(existing)) {
|
|
5052
|
+
const parent = path9.dirname(existing);
|
|
5053
|
+
if (parent === existing) return path9.resolve(target);
|
|
5054
|
+
missing.unshift(path9.basename(existing));
|
|
5055
|
+
existing = parent;
|
|
5056
|
+
}
|
|
5057
|
+
return path9.join(fs12.realpathSync.native(existing), ...missing);
|
|
5058
|
+
}
|
|
5059
|
+
/** True when the resolved path escapes the real project directory. */
|
|
4969
5060
|
isOutsideProject(p) {
|
|
4970
|
-
const
|
|
4971
|
-
const
|
|
4972
|
-
|
|
5061
|
+
const project = this.realPathForContainment(this.projectDir);
|
|
5062
|
+
const candidate = this.realPathForContainment(this.resolve(p));
|
|
5063
|
+
const rel = path9.relative(project, candidate);
|
|
5064
|
+
return rel === ".." || rel.startsWith(`..${path9.sep}`) || path9.isAbsolute(rel);
|
|
4973
5065
|
}
|
|
4974
5066
|
async execute(tool, args) {
|
|
4975
5067
|
try {
|
|
@@ -5065,7 +5157,7 @@ var HostTools = class {
|
|
|
5065
5157
|
const file2 = this.resolve(String(args.path || ""));
|
|
5066
5158
|
const content = String(args.content ?? "");
|
|
5067
5159
|
await fsp2.mkdir(path9.dirname(file2), { recursive: true });
|
|
5068
|
-
const existed =
|
|
5160
|
+
const existed = fs12.existsSync(file2);
|
|
5069
5161
|
await fsp2.writeFile(file2, content, "utf8");
|
|
5070
5162
|
return {
|
|
5071
5163
|
ok: true,
|
|
@@ -5107,7 +5199,7 @@ var HostTools = class {
|
|
|
5107
5199
|
if (!bytes) return { ok: false, error: `Could not download ${source} from the thread filesystem.` };
|
|
5108
5200
|
const dest = this.resolve(destArg);
|
|
5109
5201
|
await fsp2.mkdir(path9.dirname(dest), { recursive: true });
|
|
5110
|
-
const existed =
|
|
5202
|
+
const existed = fs12.existsSync(dest);
|
|
5111
5203
|
await fsp2.writeFile(dest, bytes);
|
|
5112
5204
|
return {
|
|
5113
5205
|
ok: true,
|
|
@@ -5184,7 +5276,7 @@ var HostTools = class {
|
|
|
5184
5276
|
return await new Promise((resolvePromise) => {
|
|
5185
5277
|
const child = spawn(cmd, argv, {
|
|
5186
5278
|
cwd: this.projectDir,
|
|
5187
|
-
env: {
|
|
5279
|
+
env: restrictedSubprocessEnv({ SKILL_DIR: skillDir }),
|
|
5188
5280
|
stdio: ["ignore", "pipe", "pipe"]
|
|
5189
5281
|
});
|
|
5190
5282
|
let out = "";
|
|
@@ -5237,30 +5329,34 @@ ${truncated}`
|
|
|
5237
5329
|
const command = String(args.command || "");
|
|
5238
5330
|
if (!command.trim()) return { ok: false, error: "command is required" };
|
|
5239
5331
|
const cwd = args.cwd ? this.resolve(String(args.cwd)) : this.projectDir;
|
|
5240
|
-
if (!
|
|
5332
|
+
if (!fs12.existsSync(cwd)) {
|
|
5241
5333
|
return { ok: false, error: `cwd does not exist: ${cwd}` };
|
|
5242
5334
|
}
|
|
5243
5335
|
const id = crypto.randomUUID().slice(0, 8);
|
|
5244
5336
|
const logPath = path9.join(LOG_DIR, `${id}.log`);
|
|
5245
5337
|
let out;
|
|
5246
5338
|
try {
|
|
5247
|
-
|
|
5248
|
-
out = fs11.openSync(logPath, "a");
|
|
5339
|
+
out = openPrivateProcessLog(logPath);
|
|
5249
5340
|
} catch (err) {
|
|
5250
5341
|
return { ok: false, error: `Could not open log file: ${err instanceof Error ? err.message : String(err)}` };
|
|
5251
5342
|
}
|
|
5252
5343
|
let child;
|
|
5253
5344
|
try {
|
|
5254
|
-
child = spawn("bash", ["-lc", command], {
|
|
5345
|
+
child = spawn("bash", ["-lc", command], {
|
|
5346
|
+
cwd,
|
|
5347
|
+
detached: true,
|
|
5348
|
+
stdio: ["ignore", out, out],
|
|
5349
|
+
env: inheritedSubprocessEnv()
|
|
5350
|
+
});
|
|
5255
5351
|
} catch (err) {
|
|
5256
|
-
|
|
5352
|
+
fs12.closeSync(out);
|
|
5257
5353
|
return { ok: false, error: `Failed to start: ${err instanceof Error ? err.message : String(err)}` };
|
|
5258
5354
|
}
|
|
5259
5355
|
let spawnError = null;
|
|
5260
5356
|
child.on("error", (err) => {
|
|
5261
5357
|
spawnError = err;
|
|
5262
5358
|
});
|
|
5263
|
-
|
|
5359
|
+
fs12.closeSync(out);
|
|
5264
5360
|
const pid = child.pid;
|
|
5265
5361
|
let earlyExit;
|
|
5266
5362
|
const onEarlyExit = (code) => {
|
|
@@ -5466,7 +5562,11 @@ ${tail2}` : " No output was captured.")
|
|
|
5466
5562
|
const MAX_OUTPUT = 256 * 1024;
|
|
5467
5563
|
let child;
|
|
5468
5564
|
try {
|
|
5469
|
-
child = spawn(cmd, args, {
|
|
5565
|
+
child = spawn(cmd, args, {
|
|
5566
|
+
cwd,
|
|
5567
|
+
detached: true,
|
|
5568
|
+
env: inheritedSubprocessEnv()
|
|
5569
|
+
});
|
|
5470
5570
|
} catch {
|
|
5471
5571
|
resolve({ stdout: "", stderr: "", code: 127, timedOut: false });
|
|
5472
5572
|
return;
|
|
@@ -5566,7 +5666,7 @@ var McpClient = class {
|
|
|
5566
5666
|
async connect(defaultCwd) {
|
|
5567
5667
|
const child = spawn(this.config.command, this.config.args, {
|
|
5568
5668
|
cwd: this.config.cwd || defaultCwd,
|
|
5569
|
-
env:
|
|
5669
|
+
env: restrictedSubprocessEnv(this.config.env),
|
|
5570
5670
|
stdio: ["pipe", "pipe", "pipe"]
|
|
5571
5671
|
});
|
|
5572
5672
|
this.child = child;
|
|
@@ -6030,7 +6130,7 @@ var DaemonUserStream = class {
|
|
|
6030
6130
|
if (hooks.onConnection) this.stream.onConnection(hooks.onConnection);
|
|
6031
6131
|
}
|
|
6032
6132
|
start() {
|
|
6033
|
-
this.stream.start();
|
|
6133
|
+
return this.stream.start();
|
|
6034
6134
|
}
|
|
6035
6135
|
close() {
|
|
6036
6136
|
this.stream.close();
|
|
@@ -6073,7 +6173,7 @@ function cachePath() {
|
|
|
6073
6173
|
}
|
|
6074
6174
|
function readCache() {
|
|
6075
6175
|
try {
|
|
6076
|
-
const raw =
|
|
6176
|
+
const raw = fs12.readFileSync(cachePath(), "utf-8");
|
|
6077
6177
|
return JSON.parse(raw);
|
|
6078
6178
|
} catch {
|
|
6079
6179
|
return null;
|
|
@@ -6082,14 +6182,14 @@ function readCache() {
|
|
|
6082
6182
|
function writeCache(latest) {
|
|
6083
6183
|
try {
|
|
6084
6184
|
const dir2 = cacheDir();
|
|
6085
|
-
if (!
|
|
6086
|
-
|
|
6185
|
+
if (!fs12.existsSync(dir2)) fs12.mkdirSync(dir2, { recursive: true });
|
|
6186
|
+
fs12.writeFileSync(cachePath(), JSON.stringify({ latest, timestamp: Date.now() }));
|
|
6087
6187
|
} catch {
|
|
6088
6188
|
}
|
|
6089
6189
|
}
|
|
6090
6190
|
function readAutoUpdateState(dir2 = cacheDir()) {
|
|
6091
6191
|
try {
|
|
6092
|
-
const raw =
|
|
6192
|
+
const raw = fs12.readFileSync(path9.join(dir2, STATE_FILE), "utf-8");
|
|
6093
6193
|
const state = JSON.parse(raw);
|
|
6094
6194
|
return typeof state?.version === "string" ? state : null;
|
|
6095
6195
|
} catch {
|
|
@@ -6098,14 +6198,14 @@ function readAutoUpdateState(dir2 = cacheDir()) {
|
|
|
6098
6198
|
}
|
|
6099
6199
|
function writeAutoUpdateState(state, dir2 = cacheDir()) {
|
|
6100
6200
|
try {
|
|
6101
|
-
if (!
|
|
6102
|
-
|
|
6201
|
+
if (!fs12.existsSync(dir2)) fs12.mkdirSync(dir2, { recursive: true });
|
|
6202
|
+
fs12.writeFileSync(path9.join(dir2, STATE_FILE), JSON.stringify(state));
|
|
6103
6203
|
} catch {
|
|
6104
6204
|
}
|
|
6105
6205
|
}
|
|
6106
6206
|
function clearAutoUpdateState(dir2 = cacheDir()) {
|
|
6107
6207
|
try {
|
|
6108
|
-
|
|
6208
|
+
fs12.unlinkSync(path9.join(dir2, STATE_FILE));
|
|
6109
6209
|
} catch {
|
|
6110
6210
|
}
|
|
6111
6211
|
}
|
|
@@ -6152,7 +6252,11 @@ function startBackgroundUpdate(latest, pm, dir2 = cacheDir()) {
|
|
|
6152
6252
|
const { cmd, args } = updateCommand(pm);
|
|
6153
6253
|
const script = `const cp=require('child_process');const fs=require('fs');const r=cp.spawnSync(${JSON.stringify(cmd)},${JSON.stringify(args)},{shell:process.platform==='win32',encoding:'utf8'});const out=((r.stdout||'')+(r.stderr||'')).slice(-2000);fs.writeFileSync(${JSON.stringify(stateFile)},JSON.stringify({version:${JSON.stringify(latest)},startedAt:${startedAt},exitCode:r.status==null?-1:r.status,finishedAt:Date.now(),output:out}));`;
|
|
6154
6254
|
try {
|
|
6155
|
-
const child = spawn(process.execPath, ["-e", script], {
|
|
6255
|
+
const child = spawn(process.execPath, ["-e", script], {
|
|
6256
|
+
detached: true,
|
|
6257
|
+
stdio: "ignore",
|
|
6258
|
+
env: inheritedSubprocessEnv()
|
|
6259
|
+
});
|
|
6156
6260
|
child.unref();
|
|
6157
6261
|
return true;
|
|
6158
6262
|
} catch {
|
|
@@ -6224,7 +6328,8 @@ function runUpdate(pm) {
|
|
|
6224
6328
|
const { cmd, args } = updateCommand(pm);
|
|
6225
6329
|
const child = spawn(cmd, args, {
|
|
6226
6330
|
shell: process.platform === "win32",
|
|
6227
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
6331
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
6332
|
+
env: inheritedSubprocessEnv()
|
|
6228
6333
|
});
|
|
6229
6334
|
let out = "";
|
|
6230
6335
|
child.stdout?.on("data", (d) => out += d);
|
|
@@ -6240,22 +6345,30 @@ var UPDATE_CHECK_MS = 6 * 60 * 6e4;
|
|
|
6240
6345
|
var MAX_WORKERS = 30;
|
|
6241
6346
|
var BRIDGE_ATTACH_GRACE_MS = 15e3;
|
|
6242
6347
|
var BRIDGE_IDLE_MS = 2e3;
|
|
6348
|
+
var LIVE_FRAME_APPROVAL_TIMEOUT_MS = 45e3;
|
|
6243
6349
|
var LOG_MAX_BYTES = 1e6;
|
|
6244
6350
|
var LOG_FILE = path9.join(os6.homedir(), ".standardagents", "daemon.log");
|
|
6245
|
-
function
|
|
6351
|
+
function appendPrivateLog(file2, line, maxBytes) {
|
|
6246
6352
|
try {
|
|
6247
|
-
|
|
6353
|
+
const directory = path9.dirname(file2);
|
|
6354
|
+
fs12.mkdirSync(directory, { recursive: true, mode: 448 });
|
|
6355
|
+
fs12.chmodSync(directory, 448);
|
|
6248
6356
|
try {
|
|
6249
|
-
|
|
6250
|
-
|
|
6357
|
+
fs12.chmodSync(file2, 384);
|
|
6358
|
+
if (fs12.statSync(file2).size > maxBytes) {
|
|
6359
|
+
fs12.renameSync(file2, `${file2}.old`);
|
|
6251
6360
|
}
|
|
6252
6361
|
} catch {
|
|
6253
6362
|
}
|
|
6254
|
-
|
|
6255
|
-
|
|
6363
|
+
fs12.appendFileSync(file2, line, { mode: 384 });
|
|
6364
|
+
fs12.chmodSync(file2, 384);
|
|
6256
6365
|
} catch {
|
|
6257
6366
|
}
|
|
6258
6367
|
}
|
|
6368
|
+
function daemonLog(line) {
|
|
6369
|
+
appendPrivateLog(LOG_FILE, `${(/* @__PURE__ */ new Date()).toISOString()} ${line}
|
|
6370
|
+
`, LOG_MAX_BYTES);
|
|
6371
|
+
}
|
|
6259
6372
|
function pathFromTags(tags) {
|
|
6260
6373
|
const tag = tags.find((t) => t.startsWith("path:"));
|
|
6261
6374
|
if (!tag) return null;
|
|
@@ -6320,7 +6433,8 @@ var ThreadWorker = class {
|
|
|
6320
6433
|
const permKey = permissionKey(req);
|
|
6321
6434
|
const fresh = decide(this.perm, permKey, effectiveRisk, !!req.requestPermission);
|
|
6322
6435
|
if (fresh === "allow") return { choice: "allow" };
|
|
6323
|
-
|
|
6436
|
+
const relayId = req.toolCallId ?? req.id;
|
|
6437
|
+
if (!relayId) {
|
|
6324
6438
|
return { choice: "deny", reason: "No one is available to approve this right now." };
|
|
6325
6439
|
}
|
|
6326
6440
|
daemonLog(`[${threadId.slice(0, 8)}] relaying approval: ${summary} (risk ${effectiveRisk})`);
|
|
@@ -6331,7 +6445,7 @@ var ThreadWorker = class {
|
|
|
6331
6445
|
api,
|
|
6332
6446
|
threadId,
|
|
6333
6447
|
{
|
|
6334
|
-
tool_call_id:
|
|
6448
|
+
tool_call_id: relayId,
|
|
6335
6449
|
tool: req.tool,
|
|
6336
6450
|
summary,
|
|
6337
6451
|
permission: req.requestPermission,
|
|
@@ -6339,13 +6453,24 @@ var ThreadWorker = class {
|
|
|
6339
6453
|
machine: machineName,
|
|
6340
6454
|
requested_at: Date.now()
|
|
6341
6455
|
},
|
|
6342
|
-
{
|
|
6456
|
+
{
|
|
6457
|
+
subscribe: (wake) => approvalWakes.subscribe(threadId, wake),
|
|
6458
|
+
// A live frame is answered over the socket and the server fails
|
|
6459
|
+
// the call on its own frame timeout — bound the human wait so a
|
|
6460
|
+
// late "allow" can never execute a side effect into a frame the
|
|
6461
|
+
// server already gave up on. Durable calls park server-side and
|
|
6462
|
+
// keep the long default.
|
|
6463
|
+
timeoutMs: req.toolCallId ? void 0 : LIVE_FRAME_APPROVAL_TIMEOUT_MS
|
|
6464
|
+
}
|
|
6343
6465
|
);
|
|
6344
6466
|
if (!response) {
|
|
6345
6467
|
return { choice: "deny", reason: "The approval request timed out with no one to approve it." };
|
|
6346
6468
|
}
|
|
6347
6469
|
if (response.choice === "always") this.perm.alwaysAllow.add(permKey);
|
|
6348
|
-
if (response.choice === "always_risk")
|
|
6470
|
+
if (response.choice === "always_risk") {
|
|
6471
|
+
this.perm.level = Math.max(this.perm.level, effectiveRisk);
|
|
6472
|
+
this.perm.allowRisk.clear();
|
|
6473
|
+
}
|
|
6349
6474
|
if (response.choice === "always" || response.choice === "always_risk") {
|
|
6350
6475
|
(0, approvals_exports.saveApprovals)(api, threadId, this.perm);
|
|
6351
6476
|
}
|
|
@@ -6514,7 +6639,7 @@ Run \`standardcode daemon install\` (or plain \`standardcode\`) once to sign in.
|
|
|
6514
6639
|
daemonLog(`detached ${oldest.threadId.slice(0, 8)} (worker cap)`);
|
|
6515
6640
|
}
|
|
6516
6641
|
try {
|
|
6517
|
-
|
|
6642
|
+
fs12.mkdirSync(projectDir, { recursive: true });
|
|
6518
6643
|
} catch (e) {
|
|
6519
6644
|
daemonLog(`cannot prepare project dir ${projectDir}: ${e instanceof Error ? e.message : String(e)}`);
|
|
6520
6645
|
return;
|
|
@@ -6817,7 +6942,7 @@ function resolveDaemonCommand(extraArgs = []) {
|
|
|
6817
6942
|
let tsx = null;
|
|
6818
6943
|
for (let i = 0; i < 6 && dir2 !== path9.dirname(dir2); i++) {
|
|
6819
6944
|
const candidate = path9.join(dir2, "node_modules", "tsx", "dist", "cli.mjs");
|
|
6820
|
-
if (
|
|
6945
|
+
if (fs12.existsSync(candidate)) {
|
|
6821
6946
|
tsx = candidate;
|
|
6822
6947
|
break;
|
|
6823
6948
|
}
|
|
@@ -6847,13 +6972,25 @@ function servicePath() {
|
|
|
6847
6972
|
return [.../* @__PURE__ */ new Set([...current, ...parts])].join(":");
|
|
6848
6973
|
}
|
|
6849
6974
|
function run2(cmd, args) {
|
|
6850
|
-
const res = spawnSync(cmd, args, {
|
|
6975
|
+
const res = spawnSync(cmd, args, {
|
|
6976
|
+
encoding: "utf8",
|
|
6977
|
+
env: inheritedSubprocessEnv()
|
|
6978
|
+
});
|
|
6851
6979
|
const output4 = `${res.stdout ?? ""}${res.stderr ?? ""}`.trim();
|
|
6852
6980
|
return { ok: res.status === 0, output: output4 };
|
|
6853
6981
|
}
|
|
6854
6982
|
var plistPath = () => path9.join(os6.homedir(), "Library", "LaunchAgents", `${SERVICE_LABEL}.plist`);
|
|
6855
6983
|
var unitPath = () => path9.join(os6.homedir(), ".config", "systemd", "user", SYSTEMD_UNIT);
|
|
6856
6984
|
var xmlEscape = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
6985
|
+
function restartServiceInvocation(platform, uid = 501) {
|
|
6986
|
+
if (platform === "darwin") {
|
|
6987
|
+
return { cmd: "launchctl", args: ["kickstart", "-k", `gui/${uid}/${SERVICE_LABEL}`] };
|
|
6988
|
+
}
|
|
6989
|
+
if (platform === "linux") {
|
|
6990
|
+
return { cmd: "systemctl", args: ["--user", "restart", SYSTEMD_UNIT] };
|
|
6991
|
+
}
|
|
6992
|
+
return null;
|
|
6993
|
+
}
|
|
6857
6994
|
function installService(command, endpoint) {
|
|
6858
6995
|
if (process.platform === "darwin") return installLaunchd(command, endpoint);
|
|
6859
6996
|
if (process.platform === "linux") return installSystemd(command, endpoint);
|
|
@@ -6865,8 +7002,8 @@ function installService(command, endpoint) {
|
|
|
6865
7002
|
}
|
|
6866
7003
|
function installLaunchd(command, endpoint) {
|
|
6867
7004
|
const logDir = path9.join(os6.homedir(), ".standardagents");
|
|
6868
|
-
|
|
6869
|
-
|
|
7005
|
+
fs12.mkdirSync(logDir, { recursive: true });
|
|
7006
|
+
fs12.mkdirSync(path9.dirname(plistPath()), { recursive: true });
|
|
6870
7007
|
const envEntries = [
|
|
6871
7008
|
` <key>PATH</key><string>${xmlEscape(servicePath())}</string>`,
|
|
6872
7009
|
...endpoint ? [` <key>STANDARD_CODE_DAEMON_ENDPOINT</key><string>${xmlEscape(endpoint)}</string>`] : []
|
|
@@ -6892,7 +7029,7 @@ ${envEntries}
|
|
|
6892
7029
|
</dict>
|
|
6893
7030
|
</plist>
|
|
6894
7031
|
`;
|
|
6895
|
-
|
|
7032
|
+
fs12.writeFileSync(plistPath(), plist);
|
|
6896
7033
|
const uid = typeof process.getuid === "function" ? process.getuid() : 501;
|
|
6897
7034
|
run2("launchctl", ["bootout", `gui/${uid}`, plistPath()]);
|
|
6898
7035
|
const boot = run2("launchctl", ["bootstrap", `gui/${uid}`, plistPath()]);
|
|
@@ -6909,7 +7046,7 @@ ${envEntries}
|
|
|
6909
7046
|
return { ok: true, detail: `LaunchAgent installed (${plistPath()})` };
|
|
6910
7047
|
}
|
|
6911
7048
|
function installSystemd(command, endpoint) {
|
|
6912
|
-
|
|
7049
|
+
fs12.mkdirSync(path9.dirname(unitPath()), { recursive: true });
|
|
6913
7050
|
const unit = `[Unit]
|
|
6914
7051
|
Description=Standard Code daemon (headless coding-agent execution client)
|
|
6915
7052
|
After=network-online.target
|
|
@@ -6924,7 +7061,7 @@ ${endpoint ? `Environment=STANDARD_CODE_DAEMON_ENDPOINT=${endpoint}
|
|
|
6924
7061
|
[Install]
|
|
6925
7062
|
WantedBy=default.target
|
|
6926
7063
|
`;
|
|
6927
|
-
|
|
7064
|
+
fs12.writeFileSync(unitPath(), unit);
|
|
6928
7065
|
const reload = run2("systemctl", ["--user", "daemon-reload"]);
|
|
6929
7066
|
if (!reload.ok) {
|
|
6930
7067
|
return {
|
|
@@ -6948,7 +7085,7 @@ function uninstallService() {
|
|
|
6948
7085
|
const uid = typeof process.getuid === "function" ? process.getuid() : 501;
|
|
6949
7086
|
run2("launchctl", ["bootout", `gui/${uid}`, plistPath()]);
|
|
6950
7087
|
try {
|
|
6951
|
-
|
|
7088
|
+
fs12.unlinkSync(plistPath());
|
|
6952
7089
|
} catch {
|
|
6953
7090
|
}
|
|
6954
7091
|
return { ok: true, detail: "LaunchAgent removed" };
|
|
@@ -6956,7 +7093,7 @@ function uninstallService() {
|
|
|
6956
7093
|
if (process.platform === "linux") {
|
|
6957
7094
|
run2("systemctl", ["--user", "disable", "--now", SYSTEMD_UNIT]);
|
|
6958
7095
|
try {
|
|
6959
|
-
|
|
7096
|
+
fs12.unlinkSync(unitPath());
|
|
6960
7097
|
} catch {
|
|
6961
7098
|
}
|
|
6962
7099
|
run2("systemctl", ["--user", "daemon-reload"]);
|
|
@@ -6964,9 +7101,42 @@ function uninstallService() {
|
|
|
6964
7101
|
}
|
|
6965
7102
|
return { ok: false, detail: `Unsupported platform: ${process.platform}` };
|
|
6966
7103
|
}
|
|
7104
|
+
function restartService() {
|
|
7105
|
+
if (process.platform === "darwin") {
|
|
7106
|
+
if (!fs12.existsSync(plistPath())) {
|
|
7107
|
+
return {
|
|
7108
|
+
ok: false,
|
|
7109
|
+
detail: "The daemon service is not installed. Run `standardcode daemon install` first."
|
|
7110
|
+
};
|
|
7111
|
+
}
|
|
7112
|
+
const uid = typeof process.getuid === "function" ? process.getuid() : 501;
|
|
7113
|
+
const invocation = restartServiceInvocation(process.platform, uid);
|
|
7114
|
+
const restart = run2(invocation.cmd, invocation.args);
|
|
7115
|
+
if (restart.ok) return { ok: true, detail: "LaunchAgent restarted" };
|
|
7116
|
+
const boot = run2("launchctl", ["bootstrap", `gui/${uid}`, plistPath()]);
|
|
7117
|
+
if (boot.ok) return { ok: true, detail: "LaunchAgent loaded and started" };
|
|
7118
|
+
return {
|
|
7119
|
+
ok: false,
|
|
7120
|
+
detail: `launchctl failed: ${restart.output || boot.output}`,
|
|
7121
|
+
manualHint: `Load it manually: launchctl bootstrap gui/${uid} ${plistPath()}`
|
|
7122
|
+
};
|
|
7123
|
+
}
|
|
7124
|
+
if (process.platform === "linux") {
|
|
7125
|
+
if (!fs12.existsSync(unitPath())) {
|
|
7126
|
+
return {
|
|
7127
|
+
ok: false,
|
|
7128
|
+
detail: "The daemon service is not installed. Run `standardcode daemon install` first."
|
|
7129
|
+
};
|
|
7130
|
+
}
|
|
7131
|
+
const invocation = restartServiceInvocation(process.platform);
|
|
7132
|
+
const restart = run2(invocation.cmd, invocation.args);
|
|
7133
|
+
return restart.ok ? { ok: true, detail: "systemd user service restarted" } : { ok: false, detail: `systemctl restart failed: ${restart.output}` };
|
|
7134
|
+
}
|
|
7135
|
+
return { ok: false, detail: `Unsupported platform: ${process.platform}` };
|
|
7136
|
+
}
|
|
6967
7137
|
function serviceStatus() {
|
|
6968
7138
|
if (process.platform === "darwin") {
|
|
6969
|
-
const installed =
|
|
7139
|
+
const installed = fs12.existsSync(plistPath());
|
|
6970
7140
|
const list = run2("launchctl", ["list", SERVICE_LABEL]);
|
|
6971
7141
|
const pidMatch = list.output.match(/"PID"\s*=\s*(\d+)/);
|
|
6972
7142
|
return {
|
|
@@ -6976,7 +7146,7 @@ function serviceStatus() {
|
|
|
6976
7146
|
};
|
|
6977
7147
|
}
|
|
6978
7148
|
if (process.platform === "linux") {
|
|
6979
|
-
const installed =
|
|
7149
|
+
const installed = fs12.existsSync(unitPath());
|
|
6980
7150
|
const active = run2("systemctl", ["--user", "is-active", SYSTEMD_UNIT]);
|
|
6981
7151
|
return {
|
|
6982
7152
|
installed,
|
|
@@ -7011,6 +7181,8 @@ function usage() {
|
|
|
7011
7181
|
" e.g. when another app drives the install).",
|
|
7012
7182
|
" uninstall Stop and remove the service.",
|
|
7013
7183
|
" status Service + registry status for this machine.",
|
|
7184
|
+
" restart Restart the installed service immediately.",
|
|
7185
|
+
" update [--endpoint url] Install the latest CLI, then restart the service.",
|
|
7014
7186
|
" run [--endpoint url] Run the daemon in the foreground (what the service runs).",
|
|
7015
7187
|
" add-project <path> Register a project directory for remote sessions.",
|
|
7016
7188
|
" remove-project <path> Remove a registered project directory.",
|
|
@@ -7140,9 +7312,10 @@ async function installCommand(endpointFlag, opts = {}) {
|
|
|
7140
7312
|
stdout.write(
|
|
7141
7313
|
`${c3.green}\u2713${c3.reset} ${c3.bold}${displayName}${c3.reset} is online.
|
|
7142
7314
|
|
|
7143
|
-
|
|
7144
|
-
${c3.dim}Projects
|
|
7145
|
-
|
|
7315
|
+
Standard Code sessions can now edit files and run tools on this machine while you drive them from any device signed in to your account.
|
|
7316
|
+
${c3.dim}Projects are added automatically when you start Standard Code from their directory.
|
|
7317
|
+
To add a project manually, run:${c3.reset}
|
|
7318
|
+
${c3.bold}standardcode daemon add-project /absolute/path/to/project${c3.reset}
|
|
7146
7319
|
`
|
|
7147
7320
|
);
|
|
7148
7321
|
} else {
|
|
@@ -7192,6 +7365,55 @@ async function statusCommand() {
|
|
|
7192
7365
|
for (const p of projects.sort()) stdout.write(` ${c3.dim}${p}${c3.reset}
|
|
7193
7366
|
`);
|
|
7194
7367
|
}
|
|
7368
|
+
async function updateDaemonCommand(endpointFlag) {
|
|
7369
|
+
const status = serviceStatus();
|
|
7370
|
+
if (!status.installed) {
|
|
7371
|
+
stdout.write(
|
|
7372
|
+
`${c3.red}\u2717${c3.reset} The daemon service is not installed. Run \`standardcode daemon install\` first.
|
|
7373
|
+
`
|
|
7374
|
+
);
|
|
7375
|
+
process.exit(1);
|
|
7376
|
+
}
|
|
7377
|
+
const endpoint = resolveEndpoint(endpointFlag);
|
|
7378
|
+
const api = await ensureSignedIn(endpoint);
|
|
7379
|
+
const identity = loadMachineIdentity();
|
|
7380
|
+
const record = await loadMachine(api, identity.machine_id).catch(() => null);
|
|
7381
|
+
if (status.running && record && daemonOnline(record)) {
|
|
7382
|
+
await enqueueMachineCommand(api, identity.machine_id, "update");
|
|
7383
|
+
stdout.write(
|
|
7384
|
+
`${c3.green}\u2713${c3.reset} Update requested. The daemon will finish active tool calls, install the latest Standard Code, and restart.
|
|
7385
|
+
`
|
|
7386
|
+
);
|
|
7387
|
+
return;
|
|
7388
|
+
}
|
|
7389
|
+
const pm = detectPackageManager();
|
|
7390
|
+
if (!pm) {
|
|
7391
|
+
stdout.write(
|
|
7392
|
+
`${c3.red}\u2717${c3.reset} This source checkout cannot update the installed CLI. Run \`standardcode daemon update\` through your global Standard Code command.
|
|
7393
|
+
`
|
|
7394
|
+
);
|
|
7395
|
+
process.exit(1);
|
|
7396
|
+
}
|
|
7397
|
+
const { display } = updateCommand(pm);
|
|
7398
|
+
stdout.write(`${c3.dim}Running ${display}\u2026${c3.reset}
|
|
7399
|
+
`);
|
|
7400
|
+
const result = await runUpdate(pm);
|
|
7401
|
+
if (!result.ok) {
|
|
7402
|
+
stdout.write(`${c3.red}\u2717${c3.reset} Update failed.
|
|
7403
|
+
`);
|
|
7404
|
+
for (const line of result.output.trim().split("\n").slice(-6)) {
|
|
7405
|
+
if (line) stdout.write(`${c3.dim}${line}${c3.reset}
|
|
7406
|
+
`);
|
|
7407
|
+
}
|
|
7408
|
+
process.exit(1);
|
|
7409
|
+
}
|
|
7410
|
+
const restarted = restartService();
|
|
7411
|
+
stdout.write(
|
|
7412
|
+
`${restarted.ok ? c3.green + "\u2713" : c3.red + "\u2717"}${c3.reset} ${restarted.ok ? "Standard Code updated. " : "Standard Code updated, but the daemon restart failed. "}${restarted.detail}
|
|
7413
|
+
`
|
|
7414
|
+
);
|
|
7415
|
+
if (!restarted.ok) process.exit(1);
|
|
7416
|
+
}
|
|
7195
7417
|
async function projectCommand(action, target) {
|
|
7196
7418
|
if (!target) {
|
|
7197
7419
|
stdout.write(`${c3.red}\u2717${c3.reset} Expected a project path.
|
|
@@ -7199,7 +7421,7 @@ async function projectCommand(action, target) {
|
|
|
7199
7421
|
process.exit(1);
|
|
7200
7422
|
}
|
|
7201
7423
|
const dir2 = path9.resolve(target);
|
|
7202
|
-
if (action === "add" && !
|
|
7424
|
+
if (action === "add" && !fs12.existsSync(dir2)) {
|
|
7203
7425
|
stdout.write(`${c3.red}\u2717${c3.reset} ${dir2} does not exist on this machine.
|
|
7204
7426
|
`);
|
|
7205
7427
|
process.exit(1);
|
|
@@ -7247,6 +7469,18 @@ async function runDaemonCommand(argv) {
|
|
|
7247
7469
|
case "status":
|
|
7248
7470
|
await statusCommand();
|
|
7249
7471
|
return;
|
|
7472
|
+
case "restart": {
|
|
7473
|
+
const result = restartService();
|
|
7474
|
+
stdout.write(`${result.ok ? c3.green + "\u2713" : c3.red + "\u2717"}${c3.reset} ${result.detail}
|
|
7475
|
+
`);
|
|
7476
|
+
if (result.manualHint) stdout.write(`${c3.dim}${result.manualHint}${c3.reset}
|
|
7477
|
+
`);
|
|
7478
|
+
if (!result.ok) process.exit(1);
|
|
7479
|
+
return;
|
|
7480
|
+
}
|
|
7481
|
+
case "update":
|
|
7482
|
+
await updateDaemonCommand(endpoint);
|
|
7483
|
+
return;
|
|
7250
7484
|
case "add-project":
|
|
7251
7485
|
await projectCommand("add", rest[0]);
|
|
7252
7486
|
return;
|
|
@@ -7268,15 +7502,15 @@ var DIR = path9.join(os6.homedir(), ".standardagents");
|
|
|
7268
7502
|
var FILE = path9.join(DIR, "prefs.json");
|
|
7269
7503
|
function loadPrefs(file2 = FILE) {
|
|
7270
7504
|
try {
|
|
7271
|
-
return JSON.parse(
|
|
7505
|
+
return JSON.parse(fs12.readFileSync(file2, "utf8"));
|
|
7272
7506
|
} catch {
|
|
7273
7507
|
return {};
|
|
7274
7508
|
}
|
|
7275
7509
|
}
|
|
7276
7510
|
function savePrefs(update, file2 = FILE) {
|
|
7277
7511
|
const merged = { ...loadPrefs(file2), ...update };
|
|
7278
|
-
|
|
7279
|
-
|
|
7512
|
+
fs12.mkdirSync(path9.dirname(file2), { recursive: true });
|
|
7513
|
+
fs12.writeFileSync(file2, JSON.stringify(merged, null, 2));
|
|
7280
7514
|
}
|
|
7281
7515
|
function shouldOfferDaemonInstall(facts) {
|
|
7282
7516
|
if (facts.optedOutEnv) return false;
|
|
@@ -8464,13 +8698,9 @@ async function runInteractive(tui, api, accountStream, threadId, projectDir, mac
|
|
|
8464
8698
|
const pendingSent = /* @__PURE__ */ new Map();
|
|
8465
8699
|
let lastSent = null;
|
|
8466
8700
|
let upgradeInFlight = false;
|
|
8467
|
-
let tokensIn = 0;
|
|
8468
|
-
let tokensOut = 0;
|
|
8469
8701
|
let liveOut = 0;
|
|
8470
|
-
const countedLogs = /* @__PURE__ */ new Set();
|
|
8471
8702
|
const activeSteps = /* @__PURE__ */ new Map();
|
|
8472
8703
|
const refreshStatus = () => {
|
|
8473
|
-
tui.setTokens(tokensIn, tokensOut + liveOut);
|
|
8474
8704
|
let label = null;
|
|
8475
8705
|
for (const v of activeSteps.values()) label = v;
|
|
8476
8706
|
tui.setStep(label, liveOut);
|
|
@@ -9310,24 +9540,6 @@ ${c5.dim}runs on ${request.machine || runnerName}${c5.reset}`,
|
|
|
9310
9540
|
refreshBgCount();
|
|
9311
9541
|
void relayApprovals().catch(() => {
|
|
9312
9542
|
});
|
|
9313
|
-
try {
|
|
9314
|
-
const logs = await api.getLogs(threadId, 100);
|
|
9315
|
-
let landed = 0;
|
|
9316
|
-
for (const l of logs) {
|
|
9317
|
-
if (!l.is_complete) continue;
|
|
9318
|
-
const id = l.id ?? `${l.created_at}:${l.total_tokens}`;
|
|
9319
|
-
if (countedLogs.has(id)) continue;
|
|
9320
|
-
countedLogs.add(id);
|
|
9321
|
-
const inT = Number(l.input_tokens) || 0;
|
|
9322
|
-
const outT = Number(l.output_tokens) || 0;
|
|
9323
|
-
tokensIn += inT;
|
|
9324
|
-
tokensOut += outT;
|
|
9325
|
-
landed += outT;
|
|
9326
|
-
}
|
|
9327
|
-
if (landed > 0) liveOut = 0;
|
|
9328
|
-
refreshStatus();
|
|
9329
|
-
} catch {
|
|
9330
|
-
}
|
|
9331
9543
|
const COMPACTION_TRIGGER_FRACTION = 0.5;
|
|
9332
9544
|
const COMPACTION_TRIGGER_CAP = 5e4;
|
|
9333
9545
|
try {
|
|
@@ -9611,7 +9823,8 @@ function showDaemonInfo(tui, session) {
|
|
|
9611
9823
|
`${c5.dim}The daemon is the executor on every machine: it runs the agent's tools, keeps working after you close the terminal, self-restarts, self-updates, and picks up sessions you start from anywhere.${c5.reset}`
|
|
9612
9824
|
);
|
|
9613
9825
|
} else {
|
|
9614
|
-
tui.print(`${c5.dim}
|
|
9826
|
+
tui.print(`${c5.dim}Daemon commands: standardcode daemon status | restart | update | uninstall.${c5.reset}`);
|
|
9827
|
+
tui.print(`${c5.dim}Add a project: standardcode daemon add-project /absolute/path/to/project${c5.reset}`);
|
|
9615
9828
|
}
|
|
9616
9829
|
}
|
|
9617
9830
|
function showKeybindings(tui) {
|