grain-tools 0.10.16 → 0.10.17
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/connection-setup.html +79 -9
- package/dist/credential-page.html +54 -2
- package/dist/mcp-app.html +0 -21
- package/dist/web-app.html +407 -197
- package/out/server/standalone.js +327 -198
- package/package.json +1 -1
- package/server.json +2 -2
package/out/server/standalone.js
CHANGED
|
@@ -19663,10 +19663,10 @@ function toolRuleFor(profile, tool) {
|
|
|
19663
19663
|
return profile.policy?.rules?.[TOOL_RESOURCE_CLASS[tool]] ?? "allow";
|
|
19664
19664
|
}
|
|
19665
19665
|
function refusalNextStep(tool) {
|
|
19666
|
-
return REFUSAL_NEXT_STEP[TOOL_RESOURCE_CLASS[tool]
|
|
19666
|
+
return REFUSAL_NEXT_STEP[tool] ?? `The '${tool}' call requires a profile that permits '${TOOL_RESOURCE_CLASS[tool]}' actions.`;
|
|
19667
19667
|
}
|
|
19668
19668
|
function toolRefusalMessage(profile, tool) {
|
|
19669
|
-
return `Tool '${tool}' is not permitted for the '${profile.name}' profile: its resource class '${TOOL_RESOURCE_CLASS[tool]}' is refused by policy. The tool exists and is advertised; this call was declined, not lost.
|
|
19669
|
+
return `Tool '${tool}' is not permitted for the '${profile.name}' profile: its resource class '${TOOL_RESOURCE_CLASS[tool]}' is refused by policy. The tool exists and is advertised; this call was declined, not lost. ${refusalNextStep(tool)}`;
|
|
19670
19670
|
}
|
|
19671
19671
|
function toolRefusalResult(profile, tool) {
|
|
19672
19672
|
return {
|
|
@@ -19765,7 +19765,7 @@ function createToolProfileResolver(explicit, onUnknownEnvValue) {
|
|
|
19765
19765
|
}
|
|
19766
19766
|
};
|
|
19767
19767
|
}
|
|
19768
|
-
var ALL_TOOLS, READ_ONLY_TOOLS, TOOL_RESOURCE_CLASS, TOOL_PROFILES, REFUSAL_NEXT_STEP,
|
|
19768
|
+
var ALL_TOOLS, READ_ONLY_TOOLS, TOOL_RESOURCE_CLASS, TOOL_PROFILES, REFUSAL_NEXT_STEP, WEB_PROXIED_TOOLS, toolNameSets;
|
|
19769
19769
|
var init_toolProfiles = __esm({
|
|
19770
19770
|
"src/common/toolProfiles.ts"() {
|
|
19771
19771
|
"use strict";
|
|
@@ -19855,11 +19855,11 @@ var init_toolProfiles = __esm({
|
|
|
19855
19855
|
}
|
|
19856
19856
|
};
|
|
19857
19857
|
REFUSAL_NEXT_STEP = {
|
|
19858
|
-
|
|
19859
|
-
|
|
19860
|
-
|
|
19858
|
+
save_connection: "Draft connections are available through create_connection_draft; setup is completed in Grain using details supplied by the user. Grain does not read connection details from local files or other repositories.",
|
|
19859
|
+
delete_connection: "Deleting connections is only available in the Grain workspace (open the connection, then Delete).",
|
|
19860
|
+
request_connection_credentials: "Connection credentials are entered in Grain (VS Code: Grain panel > Settings > Connections > the connection), never in chat.",
|
|
19861
|
+
save_chart: "Charts can be saved from the Grain workspace."
|
|
19861
19862
|
};
|
|
19862
|
-
DEFAULT_REFUSAL_NEXT_STEP = "Tell the user this action is not available to agents in Grain.";
|
|
19863
19863
|
WEB_PROXIED_TOOLS = [
|
|
19864
19864
|
"list_connectors",
|
|
19865
19865
|
"list_connections",
|
|
@@ -22031,6 +22031,121 @@ var init_applicationPayload = __esm({
|
|
|
22031
22031
|
}
|
|
22032
22032
|
});
|
|
22033
22033
|
|
|
22034
|
+
// src/common/ipcEndpoint.ts
|
|
22035
|
+
function posixSocketPathFits(directory) {
|
|
22036
|
+
return Buffer.byteLength(path22.posix.join(directory, SOCKET_BASENAME)) <= UNIX_SOCKET_PATH_MAX_BYTES && Buffer.byteLength(
|
|
22037
|
+
path22.posix.join(directory, `.${"9".repeat(PRIVATE_SOCKET_BASENAME_LENGTH)}`)
|
|
22038
|
+
) <= UNIX_SOCKET_PATH_MAX_BYTES;
|
|
22039
|
+
}
|
|
22040
|
+
function relocatedSocketDirectory(configDir) {
|
|
22041
|
+
const key = (0, import_crypto3.createHash)("sha1").update(path22.resolve(configDir)).digest("hex").slice(0, 16);
|
|
22042
|
+
return path22.join(os4.homedir(), ".grain", "s", key);
|
|
22043
|
+
}
|
|
22044
|
+
function isWindowsNamedPipe(endpoint) {
|
|
22045
|
+
return endpoint.startsWith("\\\\.\\pipe\\") || endpoint.startsWith("\\\\?\\pipe\\");
|
|
22046
|
+
}
|
|
22047
|
+
function daemonIpcEndpoint(configDir) {
|
|
22048
|
+
if (process.platform === "win32") {
|
|
22049
|
+
const key = (0, import_crypto3.createHash)("sha1").update(path22.resolve(configDir).toLowerCase()).digest("hex").slice(0, 8);
|
|
22050
|
+
return `\\\\.\\pipe\\sql-preview-${key}`;
|
|
22051
|
+
}
|
|
22052
|
+
if (posixSocketPathFits(configDir)) {
|
|
22053
|
+
return path22.posix.join(configDir, SOCKET_BASENAME);
|
|
22054
|
+
}
|
|
22055
|
+
const directory = relocatedSocketDirectory(configDir);
|
|
22056
|
+
const endpoint = path22.posix.join(directory, SOCKET_BASENAME);
|
|
22057
|
+
if (!posixSocketPathFits(directory)) {
|
|
22058
|
+
throw new Error(
|
|
22059
|
+
`IPC socket path exceeds the ${UNIX_SOCKET_PATH_MAX_BYTES}-byte limit (${Buffer.byteLength(endpoint)} bytes) for ${configDir}.`
|
|
22060
|
+
);
|
|
22061
|
+
}
|
|
22062
|
+
return endpoint;
|
|
22063
|
+
}
|
|
22064
|
+
function daemonControlEndpoint(configDir) {
|
|
22065
|
+
const key = (0, import_crypto3.createHash)("sha1").update(path22.resolve(configDir).toLowerCase()).digest("hex").slice(0, 16);
|
|
22066
|
+
return `\\\\.\\pipe\\grain-control-${key}`;
|
|
22067
|
+
}
|
|
22068
|
+
function isRelocatedDaemonIpcEndpoint(configDir, endpoint) {
|
|
22069
|
+
return process.platform !== "win32" && endpoint === path22.posix.join(relocatedSocketDirectory(configDir), SOCKET_BASENAME);
|
|
22070
|
+
}
|
|
22071
|
+
function endpointExists(endpoint) {
|
|
22072
|
+
if (isWindowsNamedPipe(endpoint)) {
|
|
22073
|
+
return true;
|
|
22074
|
+
}
|
|
22075
|
+
return fs18.existsSync(endpoint);
|
|
22076
|
+
}
|
|
22077
|
+
var fs18, os4, path22, import_crypto3, UNIX_SOCKET_PATH_MAX_BYTES, SOCKET_BASENAME, PRIVATE_SOCKET_BASENAME_LENGTH;
|
|
22078
|
+
var init_ipcEndpoint = __esm({
|
|
22079
|
+
"src/common/ipcEndpoint.ts"() {
|
|
22080
|
+
"use strict";
|
|
22081
|
+
fs18 = __toESM(require("fs"));
|
|
22082
|
+
os4 = __toESM(require("os"));
|
|
22083
|
+
path22 = __toESM(require("path"));
|
|
22084
|
+
import_crypto3 = require("crypto");
|
|
22085
|
+
UNIX_SOCKET_PATH_MAX_BYTES = 104;
|
|
22086
|
+
SOCKET_BASENAME = "srv.sock";
|
|
22087
|
+
PRIVATE_SOCKET_BASENAME_LENGTH = 24;
|
|
22088
|
+
}
|
|
22089
|
+
});
|
|
22090
|
+
|
|
22091
|
+
// src/common/requestDaemonControlStop.ts
|
|
22092
|
+
function requestDaemonControlStop(endpoint, token) {
|
|
22093
|
+
return new Promise((resolve12) => {
|
|
22094
|
+
const socket = net.createConnection(endpoint);
|
|
22095
|
+
const timeout = setTimeout(() => settle2(false), 2e3);
|
|
22096
|
+
const settle2 = (accepted) => {
|
|
22097
|
+
clearTimeout(timeout);
|
|
22098
|
+
socket.destroy();
|
|
22099
|
+
resolve12(accepted);
|
|
22100
|
+
};
|
|
22101
|
+
socket.once("error", () => settle2(false));
|
|
22102
|
+
socket.once(
|
|
22103
|
+
"connect",
|
|
22104
|
+
() => socket.write(JSON.stringify({ method: "graceful-stop", token }) + "\n")
|
|
22105
|
+
);
|
|
22106
|
+
let buffer = "";
|
|
22107
|
+
socket.on("data", (chunk) => {
|
|
22108
|
+
buffer += chunk.toString();
|
|
22109
|
+
if (Buffer.byteLength(buffer) > CONTROL_MESSAGE_LIMIT) {
|
|
22110
|
+
settle2(false);
|
|
22111
|
+
return;
|
|
22112
|
+
}
|
|
22113
|
+
const newline = buffer.indexOf("\n");
|
|
22114
|
+
if (newline === -1) {
|
|
22115
|
+
return;
|
|
22116
|
+
}
|
|
22117
|
+
try {
|
|
22118
|
+
settle2(JSON.parse(buffer.slice(0, newline)).accepted === true);
|
|
22119
|
+
} catch {
|
|
22120
|
+
settle2(false);
|
|
22121
|
+
}
|
|
22122
|
+
});
|
|
22123
|
+
});
|
|
22124
|
+
}
|
|
22125
|
+
var net, CONTROL_MESSAGE_LIMIT;
|
|
22126
|
+
var init_requestDaemonControlStop = __esm({
|
|
22127
|
+
"src/common/requestDaemonControlStop.ts"() {
|
|
22128
|
+
"use strict";
|
|
22129
|
+
net = __toESM(require("net"));
|
|
22130
|
+
CONTROL_MESSAGE_LIMIT = 8 * 1024;
|
|
22131
|
+
}
|
|
22132
|
+
});
|
|
22133
|
+
|
|
22134
|
+
// src/server/config/drainStopBudget.ts
|
|
22135
|
+
function drainStopBudgetMs(configDir) {
|
|
22136
|
+
const drainTimeoutMs = configDir ? new ConfigLoader({ configDir, createDefaultFile: false }).load().config.server.drainTimeoutMs : DEFAULT_CONFIG.server.drainTimeoutMs;
|
|
22137
|
+
return drainTimeoutMs + DRAIN_STOP_MARGIN_MS;
|
|
22138
|
+
}
|
|
22139
|
+
var DRAIN_STOP_MARGIN_MS;
|
|
22140
|
+
var init_drainStopBudget = __esm({
|
|
22141
|
+
"src/server/config/drainStopBudget.ts"() {
|
|
22142
|
+
"use strict";
|
|
22143
|
+
init_ConfigLoader();
|
|
22144
|
+
init_DaemonConfig();
|
|
22145
|
+
DRAIN_STOP_MARGIN_MS = 2e3;
|
|
22146
|
+
}
|
|
22147
|
+
});
|
|
22148
|
+
|
|
22034
22149
|
// src/server/install/supervisorAdapter.ts
|
|
22035
22150
|
function createCommandRunner() {
|
|
22036
22151
|
return {
|
|
@@ -22062,6 +22177,14 @@ function isUserServicesForbidden(platform2, output) {
|
|
|
22062
22177
|
}
|
|
22063
22178
|
return /access is denied|0x80070005|error_access_denied/.test(normalized);
|
|
22064
22179
|
}
|
|
22180
|
+
function isPidAlive(pid) {
|
|
22181
|
+
try {
|
|
22182
|
+
process.kill(pid, 0);
|
|
22183
|
+
return true;
|
|
22184
|
+
} catch (error52) {
|
|
22185
|
+
return error52.code !== "ESRCH";
|
|
22186
|
+
}
|
|
22187
|
+
}
|
|
22065
22188
|
async function runRegistration(platform2, runner, command, args) {
|
|
22066
22189
|
const result = await runner.run(command, args);
|
|
22067
22190
|
if (result.exitCode === 0) {
|
|
@@ -22085,7 +22208,7 @@ async function probeRegistration(runner, command, args) {
|
|
|
22085
22208
|
function isCommandMissing(error52) {
|
|
22086
22209
|
return error52?.code === "ENOENT";
|
|
22087
22210
|
}
|
|
22088
|
-
function createSupervisorAdapter(platform2, runner, uid) {
|
|
22211
|
+
function createSupervisorAdapter(platform2, runner, uid, configHome) {
|
|
22089
22212
|
if (platform2 === "darwin") {
|
|
22090
22213
|
const target = (domain2, identifier) => `${domain2}/${uid}/${identifier}`;
|
|
22091
22214
|
const registeredTarget = async (identifier) => {
|
|
@@ -22173,7 +22296,28 @@ function createSupervisorAdapter(platform2, runner, uid) {
|
|
|
22173
22296
|
"/F"
|
|
22174
22297
|
]),
|
|
22175
22298
|
unregister: async (identifier) => {
|
|
22176
|
-
|
|
22299
|
+
let exited = false;
|
|
22300
|
+
const pid = configHome ? readDaemonEndpoint(configHome)?.pid : void 0;
|
|
22301
|
+
if (configHome) {
|
|
22302
|
+
const token = readDaemonAuthToken(configHome);
|
|
22303
|
+
if (pid && token && await requestDaemonControlStop(daemonControlEndpoint(configHome), token)) {
|
|
22304
|
+
const deadline = Date.now() + drainStopBudgetMs(configHome);
|
|
22305
|
+
while (Date.now() < deadline) {
|
|
22306
|
+
if (!isPidAlive(pid)) {
|
|
22307
|
+
exited = true;
|
|
22308
|
+
break;
|
|
22309
|
+
}
|
|
22310
|
+
await new Promise((resolve12) => setTimeout(resolve12, 250));
|
|
22311
|
+
}
|
|
22312
|
+
}
|
|
22313
|
+
}
|
|
22314
|
+
if (!exited) {
|
|
22315
|
+
const args = ["/End", "/TN", identifier];
|
|
22316
|
+
const result = await runner.run("schtasks", args);
|
|
22317
|
+
if (result.exitCode !== 0 && pid && isPidAlive(pid)) {
|
|
22318
|
+
throw new RegistrationError("schtasks", args, result.exitCode, result.output);
|
|
22319
|
+
}
|
|
22320
|
+
}
|
|
22177
22321
|
await runRegistration(platform2, runner, "schtasks", ["/Delete", "/TN", identifier, "/F"]);
|
|
22178
22322
|
},
|
|
22179
22323
|
start: (identifier) => runStart(runner, "schtasks", ["/Run", "/TN", identifier])
|
|
@@ -22184,6 +22328,11 @@ var init_supervisorAdapter = __esm({
|
|
|
22184
22328
|
"src/server/install/supervisorAdapter.ts"() {
|
|
22185
22329
|
"use strict";
|
|
22186
22330
|
import_child_process3 = require("child_process");
|
|
22331
|
+
init_daemonEndpointFile();
|
|
22332
|
+
init_DaemonAuthToken();
|
|
22333
|
+
init_ipcEndpoint();
|
|
22334
|
+
init_requestDaemonControlStop();
|
|
22335
|
+
init_drainStopBudget();
|
|
22187
22336
|
UserServicesForbiddenError = class extends Error {
|
|
22188
22337
|
};
|
|
22189
22338
|
ServiceManagerMissingError = class extends UserServicesForbiddenError {
|
|
@@ -22205,7 +22354,7 @@ var require_package = __commonJS({
|
|
|
22205
22354
|
name: "grain",
|
|
22206
22355
|
displayName: "Grain SQL",
|
|
22207
22356
|
description: "Run SQL in VS Code and expose safe, visible MCP database tools for Claude, Cursor, and other AI agents.",
|
|
22208
|
-
version: "0.10.
|
|
22357
|
+
version: "0.10.17",
|
|
22209
22358
|
license: "UNLICENSED",
|
|
22210
22359
|
mcpName: "io.github.pattrnlabs/grain",
|
|
22211
22360
|
publisher: "pattrnlabs",
|
|
@@ -24690,7 +24839,7 @@ function readRegistry(configDir) {
|
|
|
24690
24839
|
}
|
|
24691
24840
|
}
|
|
24692
24841
|
function tokenDigest(token) {
|
|
24693
|
-
return
|
|
24842
|
+
return import_crypto4.default.createHash("sha256").update(token, "utf8").digest("base64url");
|
|
24694
24843
|
}
|
|
24695
24844
|
function digestsMatch(provided, expected) {
|
|
24696
24845
|
const a = Buffer.from(provided, "utf8");
|
|
@@ -24698,7 +24847,7 @@ function digestsMatch(provided, expected) {
|
|
|
24698
24847
|
if (a.length !== b.length) {
|
|
24699
24848
|
return false;
|
|
24700
24849
|
}
|
|
24701
|
-
return
|
|
24850
|
+
return import_crypto4.default.timingSafeEqual(a, b);
|
|
24702
24851
|
}
|
|
24703
24852
|
function assertIdAvailable(registry2, id) {
|
|
24704
24853
|
if (registry2.clients.some((client) => client.id === id)) {
|
|
@@ -24727,7 +24876,7 @@ function registerClient2(configDir, registration) {
|
|
|
24727
24876
|
ensureConfigDirMode2(configDir);
|
|
24728
24877
|
let registry2 = readRegistry(configDir);
|
|
24729
24878
|
assertIdAvailable(registry2, registration.id);
|
|
24730
|
-
const token =
|
|
24879
|
+
const token = import_crypto4.default.randomBytes(TOKEN_BYTES3).toString("base64url");
|
|
24731
24880
|
const entry = { ...registration, tokenDigest: tokenDigest(token) };
|
|
24732
24881
|
const registryPath = clientRegistryPath(configDir);
|
|
24733
24882
|
try {
|
|
@@ -24782,11 +24931,11 @@ function removeClient(configDir, id) {
|
|
|
24782
24931
|
writeExistingRegistry(configDir, registry2);
|
|
24783
24932
|
return true;
|
|
24784
24933
|
}
|
|
24785
|
-
var
|
|
24934
|
+
var import_crypto4, import_fs4, import_path5, CLIENT_REGISTRY_FILENAME, TOKEN_BYTES3, REGISTRY_FILE_MODE, CONFIG_DIR_MODE3;
|
|
24786
24935
|
var init_ClientRegistry = __esm({
|
|
24787
24936
|
"src/server/auth/ClientRegistry.ts"() {
|
|
24788
24937
|
"use strict";
|
|
24789
|
-
|
|
24938
|
+
import_crypto4 = __toESM(require("crypto"));
|
|
24790
24939
|
import_fs4 = __toESM(require("fs"));
|
|
24791
24940
|
import_path5 = __toESM(require("path"));
|
|
24792
24941
|
init_toolProfiles();
|
|
@@ -24853,45 +25002,45 @@ async function startCommand(configDir = resolveGrainHome(), spawner = import_chi
|
|
|
24853
25002
|
console.log(`Grain is already running at ${formatUserFacingLocalUrl(host, port)}`);
|
|
24854
25003
|
return 0;
|
|
24855
25004
|
}
|
|
24856
|
-
const lockPath =
|
|
25005
|
+
const lockPath = path28.join(configDir, "start.lock");
|
|
24857
25006
|
let claimedLock = false;
|
|
24858
25007
|
try {
|
|
24859
|
-
const content =
|
|
25008
|
+
const content = fs23.readFileSync(lockPath, "utf8");
|
|
24860
25009
|
const parts = content.split("\n");
|
|
24861
25010
|
const pidStr = parts[0];
|
|
24862
25011
|
const tsStr = parts[1];
|
|
24863
25012
|
if (pidStr === void 0 || tsStr === void 0) {
|
|
24864
|
-
|
|
25013
|
+
fs23.unlinkSync(lockPath);
|
|
24865
25014
|
} else {
|
|
24866
25015
|
const lockPid = parseInt(pidStr, 10);
|
|
24867
25016
|
const lockTs = parseInt(tsStr, 10);
|
|
24868
25017
|
if (!isNaN(lockTs) && clock.now() - lockTs > 2e4) {
|
|
24869
|
-
|
|
25018
|
+
fs23.unlinkSync(lockPath);
|
|
24870
25019
|
} else if (!isNaN(lockPid)) {
|
|
24871
25020
|
try {
|
|
24872
25021
|
process.kill(lockPid, 0);
|
|
24873
25022
|
} catch {
|
|
24874
|
-
|
|
25023
|
+
fs23.unlinkSync(lockPath);
|
|
24875
25024
|
}
|
|
24876
25025
|
}
|
|
24877
25026
|
}
|
|
24878
25027
|
} catch {
|
|
24879
25028
|
}
|
|
24880
25029
|
try {
|
|
24881
|
-
const fd =
|
|
24882
|
-
|
|
25030
|
+
const fd = fs23.openSync(lockPath, "wx");
|
|
25031
|
+
fs23.writeSync(fd, `${process.pid}
|
|
24883
25032
|
${clock.now()}`);
|
|
24884
|
-
|
|
25033
|
+
fs23.closeSync(fd);
|
|
24885
25034
|
claimedLock = true;
|
|
24886
25035
|
} catch (e2) {
|
|
24887
25036
|
if (e2.code !== "EEXIST") {
|
|
24888
25037
|
throw e2;
|
|
24889
25038
|
}
|
|
24890
25039
|
}
|
|
24891
|
-
const logPath =
|
|
25040
|
+
const logPath = path28.join(configDir, "daemon.log");
|
|
24892
25041
|
if (claimedLock) {
|
|
24893
|
-
const out2 =
|
|
24894
|
-
const err =
|
|
25042
|
+
const out2 = fs23.openSync(logPath, "a");
|
|
25043
|
+
const err = fs23.openSync(logPath, "a");
|
|
24895
25044
|
const child = spawner(process.execPath, [process.argv[1] ?? ""], {
|
|
24896
25045
|
detached: true,
|
|
24897
25046
|
stdio: ["ignore", out2, err],
|
|
@@ -24926,20 +25075,20 @@ ${clock.now()}`);
|
|
|
24926
25075
|
} finally {
|
|
24927
25076
|
if (claimedLock) {
|
|
24928
25077
|
try {
|
|
24929
|
-
|
|
25078
|
+
fs23.unlinkSync(lockPath);
|
|
24930
25079
|
} catch {
|
|
24931
25080
|
}
|
|
24932
25081
|
}
|
|
24933
25082
|
}
|
|
24934
25083
|
}
|
|
24935
|
-
var import_child_process4,
|
|
25084
|
+
var import_child_process4, fs23, path28, http5, OWNERSHIP_PROBE_PATH;
|
|
24936
25085
|
var init_startCommand = __esm({
|
|
24937
25086
|
"src/server/cli/startCommand.ts"() {
|
|
24938
25087
|
"use strict";
|
|
24939
25088
|
import_child_process4 = require("child_process");
|
|
24940
25089
|
init_onboardingHandoff();
|
|
24941
|
-
|
|
24942
|
-
|
|
25090
|
+
fs23 = __toESM(require("fs"));
|
|
25091
|
+
path28 = __toESM(require("path"));
|
|
24943
25092
|
http5 = __toESM(require("http"));
|
|
24944
25093
|
init_grainHome();
|
|
24945
25094
|
init_statusCommand();
|
|
@@ -25019,10 +25168,10 @@ var init_localBrowserPaths = __esm({
|
|
|
25019
25168
|
function findPackageRoot(startDir) {
|
|
25020
25169
|
let dir3 = startDir;
|
|
25021
25170
|
for (let i = 0; i < 8; i++) {
|
|
25022
|
-
if (
|
|
25171
|
+
if (fs24.existsSync(path29.join(dir3, "package.json"))) {
|
|
25023
25172
|
return dir3;
|
|
25024
25173
|
}
|
|
25025
|
-
const parent =
|
|
25174
|
+
const parent = path29.dirname(dir3);
|
|
25026
25175
|
if (parent === dir3) {
|
|
25027
25176
|
break;
|
|
25028
25177
|
}
|
|
@@ -25032,19 +25181,19 @@ function findPackageRoot(startDir) {
|
|
|
25032
25181
|
}
|
|
25033
25182
|
function readDaemonVersion() {
|
|
25034
25183
|
try {
|
|
25035
|
-
const pkgPath =
|
|
25036
|
-
const pkg = JSON.parse(
|
|
25184
|
+
const pkgPath = path29.join(findPackageRoot(__dirname), "package.json");
|
|
25185
|
+
const pkg = JSON.parse(fs24.readFileSync(pkgPath, "utf8"));
|
|
25037
25186
|
return pkg.version ?? null;
|
|
25038
25187
|
} catch {
|
|
25039
25188
|
return null;
|
|
25040
25189
|
}
|
|
25041
25190
|
}
|
|
25042
|
-
var
|
|
25191
|
+
var fs24, path29;
|
|
25043
25192
|
var init_daemonVersion = __esm({
|
|
25044
25193
|
"src/server/daemonVersion.ts"() {
|
|
25045
25194
|
"use strict";
|
|
25046
|
-
|
|
25047
|
-
|
|
25195
|
+
fs24 = __toESM(require("fs"));
|
|
25196
|
+
path29 = __toESM(require("path"));
|
|
25048
25197
|
}
|
|
25049
25198
|
});
|
|
25050
25199
|
|
|
@@ -33703,7 +33852,7 @@ async function stopCommand(configDir = resolveGrainHome(), clock = Date, process
|
|
|
33703
33852
|
process.platform,
|
|
33704
33853
|
createCommandRunner(),
|
|
33705
33854
|
process.getuid?.() ?? 0
|
|
33706
|
-
), isProcessAlive = defaultIsProcessAlive) {
|
|
33855
|
+
), isProcessAlive = defaultIsProcessAlive, platform2 = process.platform, requestStop = requestDaemonControlStop) {
|
|
33707
33856
|
if (await adapter2.isRegistered(DAEMON_SERVICE_IDENTITY)) {
|
|
33708
33857
|
console.error(
|
|
33709
33858
|
"Grain is installed as a background service. Run `grain uninstall` to remove it."
|
|
@@ -33720,8 +33869,12 @@ async function stopCommand(configDir = resolveGrainHome(), clock = Date, process
|
|
|
33720
33869
|
console.error("Could not determine Grain daemon PID.");
|
|
33721
33870
|
return 1;
|
|
33722
33871
|
}
|
|
33872
|
+
const token = platform2 === "win32" ? readDaemonAuthToken(configDir) : void 0;
|
|
33873
|
+
const accepted = token ? await requestStop(daemonControlEndpoint(configDir), token) : false;
|
|
33723
33874
|
try {
|
|
33724
|
-
|
|
33875
|
+
if (!accepted) {
|
|
33876
|
+
processKiller(pid, "SIGTERM");
|
|
33877
|
+
}
|
|
33725
33878
|
} catch (error52) {
|
|
33726
33879
|
const code = error52.code;
|
|
33727
33880
|
if (code !== "ESRCH") {
|
|
@@ -33730,7 +33883,7 @@ async function stopCommand(configDir = resolveGrainHome(), clock = Date, process
|
|
|
33730
33883
|
}
|
|
33731
33884
|
}
|
|
33732
33885
|
const startMs = clock.now();
|
|
33733
|
-
const timeoutMs = 1e4;
|
|
33886
|
+
const timeoutMs = accepted ? drainStopBudgetMs(configDir) : 1e4;
|
|
33734
33887
|
while (clock.now() - startMs < timeoutMs) {
|
|
33735
33888
|
const currentStatus = await readGrainServiceStatus(configDir);
|
|
33736
33889
|
if (!isRunningGrainService(currentStatus) && !isProcessAlive(pid)) {
|
|
@@ -33739,7 +33892,7 @@ async function stopCommand(configDir = resolveGrainHome(), clock = Date, process
|
|
|
33739
33892
|
}
|
|
33740
33893
|
await new Promise((resolve12) => setTimeout(resolve12, 250));
|
|
33741
33894
|
}
|
|
33742
|
-
console.error(
|
|
33895
|
+
console.error(`Grain did not stop within ${timeoutMs / 1e3} seconds.`);
|
|
33743
33896
|
return 1;
|
|
33744
33897
|
}
|
|
33745
33898
|
var init_stopCommand = __esm({
|
|
@@ -33750,6 +33903,10 @@ var init_stopCommand = __esm({
|
|
|
33750
33903
|
init_statusCommand();
|
|
33751
33904
|
init_supervisorAdapter();
|
|
33752
33905
|
init_daemonRecordInstance();
|
|
33906
|
+
init_ipcEndpoint();
|
|
33907
|
+
init_DaemonAuthToken();
|
|
33908
|
+
init_requestDaemonControlStop();
|
|
33909
|
+
init_drainStopBudget();
|
|
33753
33910
|
}
|
|
33754
33911
|
});
|
|
33755
33912
|
|
|
@@ -89674,67 +89831,10 @@ var require_express2 = __commonJS({
|
|
|
89674
89831
|
}
|
|
89675
89832
|
});
|
|
89676
89833
|
|
|
89677
|
-
// src/common/ipcEndpoint.ts
|
|
89678
|
-
function posixSocketPathFits(directory) {
|
|
89679
|
-
return Buffer.byteLength(path30.posix.join(directory, SOCKET_BASENAME)) <= UNIX_SOCKET_PATH_MAX_BYTES && Buffer.byteLength(
|
|
89680
|
-
path30.posix.join(directory, `.${"9".repeat(PRIVATE_SOCKET_BASENAME_LENGTH)}`)
|
|
89681
|
-
) <= UNIX_SOCKET_PATH_MAX_BYTES;
|
|
89682
|
-
}
|
|
89683
|
-
function relocatedSocketDirectory(configDir) {
|
|
89684
|
-
const key = (0, import_crypto5.createHash)("sha1").update(path30.resolve(configDir)).digest("hex").slice(0, 16);
|
|
89685
|
-
return path30.join(os7.homedir(), ".grain", "s", key);
|
|
89686
|
-
}
|
|
89687
|
-
function isWindowsNamedPipe(endpoint) {
|
|
89688
|
-
return endpoint.startsWith("\\\\.\\pipe\\") || endpoint.startsWith("\\\\?\\pipe\\");
|
|
89689
|
-
}
|
|
89690
|
-
function daemonIpcEndpoint(configDir) {
|
|
89691
|
-
if (process.platform === "win32") {
|
|
89692
|
-
const key = (0, import_crypto5.createHash)("sha1").update(path30.resolve(configDir).toLowerCase()).digest("hex").slice(0, 8);
|
|
89693
|
-
return `\\\\.\\pipe\\sql-preview-${key}`;
|
|
89694
|
-
}
|
|
89695
|
-
if (posixSocketPathFits(configDir)) {
|
|
89696
|
-
return path30.posix.join(configDir, SOCKET_BASENAME);
|
|
89697
|
-
}
|
|
89698
|
-
const directory = relocatedSocketDirectory(configDir);
|
|
89699
|
-
const endpoint = path30.posix.join(directory, SOCKET_BASENAME);
|
|
89700
|
-
if (!posixSocketPathFits(directory)) {
|
|
89701
|
-
throw new Error(
|
|
89702
|
-
`IPC socket path exceeds the ${UNIX_SOCKET_PATH_MAX_BYTES}-byte limit (${Buffer.byteLength(endpoint)} bytes) for ${configDir}.`
|
|
89703
|
-
);
|
|
89704
|
-
}
|
|
89705
|
-
return endpoint;
|
|
89706
|
-
}
|
|
89707
|
-
function daemonControlEndpoint(configDir) {
|
|
89708
|
-
const key = (0, import_crypto5.createHash)("sha1").update(path30.resolve(configDir).toLowerCase()).digest("hex").slice(0, 16);
|
|
89709
|
-
return `\\\\.\\pipe\\grain-control-${key}`;
|
|
89710
|
-
}
|
|
89711
|
-
function isRelocatedDaemonIpcEndpoint(configDir, endpoint) {
|
|
89712
|
-
return process.platform !== "win32" && endpoint === path30.posix.join(relocatedSocketDirectory(configDir), SOCKET_BASENAME);
|
|
89713
|
-
}
|
|
89714
|
-
function endpointExists(endpoint) {
|
|
89715
|
-
if (isWindowsNamedPipe(endpoint)) {
|
|
89716
|
-
return true;
|
|
89717
|
-
}
|
|
89718
|
-
return fs26.existsSync(endpoint);
|
|
89719
|
-
}
|
|
89720
|
-
var fs26, os7, path30, import_crypto5, UNIX_SOCKET_PATH_MAX_BYTES, SOCKET_BASENAME, PRIVATE_SOCKET_BASENAME_LENGTH;
|
|
89721
|
-
var init_ipcEndpoint = __esm({
|
|
89722
|
-
"src/common/ipcEndpoint.ts"() {
|
|
89723
|
-
"use strict";
|
|
89724
|
-
fs26 = __toESM(require("fs"));
|
|
89725
|
-
os7 = __toESM(require("os"));
|
|
89726
|
-
path30 = __toESM(require("path"));
|
|
89727
|
-
import_crypto5 = require("crypto");
|
|
89728
|
-
UNIX_SOCKET_PATH_MAX_BYTES = 104;
|
|
89729
|
-
SOCKET_BASENAME = "srv.sock";
|
|
89730
|
-
PRIVATE_SOCKET_BASENAME_LENGTH = 24;
|
|
89731
|
-
}
|
|
89732
|
-
});
|
|
89733
|
-
|
|
89734
89834
|
// src/common/tcpPortAvailability.ts
|
|
89735
89835
|
function isTcpPortAvailable(port, host = "127.0.0.1") {
|
|
89736
89836
|
return new Promise((resolve12) => {
|
|
89737
|
-
const server =
|
|
89837
|
+
const server = net2.createServer();
|
|
89738
89838
|
const finish = (available) => {
|
|
89739
89839
|
server.removeAllListeners();
|
|
89740
89840
|
resolve12(available);
|
|
@@ -89745,11 +89845,11 @@ function isTcpPortAvailable(port, host = "127.0.0.1") {
|
|
|
89745
89845
|
});
|
|
89746
89846
|
});
|
|
89747
89847
|
}
|
|
89748
|
-
var
|
|
89848
|
+
var net2;
|
|
89749
89849
|
var init_tcpPortAvailability = __esm({
|
|
89750
89850
|
"src/common/tcpPortAvailability.ts"() {
|
|
89751
89851
|
"use strict";
|
|
89752
|
-
|
|
89852
|
+
net2 = __toESM(require("net"));
|
|
89753
89853
|
}
|
|
89754
89854
|
});
|
|
89755
89855
|
|
|
@@ -112272,7 +112372,11 @@ var init_QueryOperationRunner = __esm({
|
|
|
112272
112372
|
try {
|
|
112273
112373
|
const hooks = {
|
|
112274
112374
|
...enforceReadOnly !== void 0 ? { enforceReadOnly } : {},
|
|
112275
|
-
onProfileResolved: (profileType) => {
|
|
112375
|
+
onProfileResolved: (profileType, profile) => {
|
|
112376
|
+
if (profile) {
|
|
112377
|
+
tab.connectionId = profile.id;
|
|
112378
|
+
tab.connectionName = profile.name;
|
|
112379
|
+
}
|
|
112276
112380
|
connector = normalizeTelemetryConnector(profileType);
|
|
112277
112381
|
emitQueryRequested();
|
|
112278
112382
|
}
|
|
@@ -112522,14 +112626,16 @@ var init_CredentialRequestService = __esm({
|
|
|
112522
112626
|
"src/server/CredentialRequestService.ts"() {
|
|
112523
112627
|
"use strict";
|
|
112524
112628
|
import_crypto8 = __toESM(require("crypto"));
|
|
112629
|
+
init_AppError();
|
|
112525
112630
|
DEFAULT_TTL_MS = 10 * 60 * 1e3;
|
|
112526
112631
|
SECRET_KEY_PATTERN = /password|secret|credential|privatekeypassphrase|^privatekey$|authheader|token|apikey/i;
|
|
112527
112632
|
CredentialRequestService = class {
|
|
112528
|
-
constructor(connectionManager, getBaseUrl, ttlMs = DEFAULT_TTL_MS, connectorRegistry) {
|
|
112633
|
+
constructor(connectionManager, getBaseUrl, ttlMs = DEFAULT_TTL_MS, connectorRegistry, telemetry) {
|
|
112529
112634
|
this.connectionManager = connectionManager;
|
|
112530
112635
|
this.getBaseUrl = getBaseUrl;
|
|
112531
112636
|
this.ttlMs = ttlMs;
|
|
112532
112637
|
this.connectorRegistry = connectorRegistry;
|
|
112638
|
+
this.telemetry = telemetry;
|
|
112533
112639
|
this.requests = /* @__PURE__ */ new Map();
|
|
112534
112640
|
this.requestConsents = /* @__PURE__ */ new Map();
|
|
112535
112641
|
}
|
|
@@ -112632,12 +112738,36 @@ var init_CredentialRequestService = __esm({
|
|
|
112632
112738
|
)) {
|
|
112633
112739
|
throw new Error("Setup fields include a protected field.");
|
|
112634
112740
|
}
|
|
112635
|
-
|
|
112636
|
-
|
|
112637
|
-
|
|
112638
|
-
|
|
112639
|
-
|
|
112640
|
-
|
|
112741
|
+
try {
|
|
112742
|
+
await this.connectionManager.saveProfile({
|
|
112743
|
+
...profile,
|
|
112744
|
+
...fields,
|
|
112745
|
+
draft: false,
|
|
112746
|
+
...input.password !== void 0 ? { password: input.password } : {}
|
|
112747
|
+
});
|
|
112748
|
+
if (request2.kind === "draft") {
|
|
112749
|
+
this.telemetry?.capture(
|
|
112750
|
+
"first_value_step",
|
|
112751
|
+
{ step: "connection_saved", outcome: "success", state: "mcp_server" },
|
|
112752
|
+
clientConsent
|
|
112753
|
+
);
|
|
112754
|
+
}
|
|
112755
|
+
} catch (e2) {
|
|
112756
|
+
if (request2.kind !== "draft") {
|
|
112757
|
+
throw e2;
|
|
112758
|
+
}
|
|
112759
|
+
this.telemetry?.capture(
|
|
112760
|
+
"first_value_step",
|
|
112761
|
+
{
|
|
112762
|
+
step: "connection_saved",
|
|
112763
|
+
outcome: "failure",
|
|
112764
|
+
error_id: errorIdOf(e2) ?? "connection_save_unknown",
|
|
112765
|
+
state: "mcp_server"
|
|
112766
|
+
},
|
|
112767
|
+
clientConsent
|
|
112768
|
+
);
|
|
112769
|
+
throw e2;
|
|
112770
|
+
}
|
|
112641
112771
|
this.requests.delete(id);
|
|
112642
112772
|
this.requestConsents.delete(id);
|
|
112643
112773
|
return request2;
|
|
@@ -112713,7 +112843,7 @@ async function createConnectionDraft(deps, args, clientConsent) {
|
|
|
112713
112843
|
content: [
|
|
112714
112844
|
{
|
|
112715
112845
|
type: "text",
|
|
112716
|
-
text: `
|
|
112846
|
+
text: `Draft connection '${name}' (id ${connectionId}) created. Setup page: ${request2.url} (expires ${new Date(request2.expiresAt).toISOString()}).`
|
|
112717
112847
|
}
|
|
112718
112848
|
]
|
|
112719
112849
|
};
|
|
@@ -113431,7 +113561,7 @@ function buildSetupRequiredResult(request2, browserOpened) {
|
|
|
113431
113561
|
content: [
|
|
113432
113562
|
{
|
|
113433
113563
|
type: "text",
|
|
113434
|
-
text: `Connection '${request2.connectionName}'
|
|
113564
|
+
text: `Connection '${request2.connectionName}' (id ${request2.connectionId}) is a draft and has no credentials yet. Setup page: ${request2.url} (expires ${expiresAt}). Queries on this connection will work once setup is saved.`
|
|
113435
113565
|
}
|
|
113436
113566
|
],
|
|
113437
113567
|
structuredContent: {
|
|
@@ -113640,6 +113770,22 @@ function isWhitespace(code) {
|
|
|
113640
113770
|
function splitSqlQueries(text, options = {}) {
|
|
113641
113771
|
return [...iterateSqlStatements(text, options)].map(({ statement }) => statement);
|
|
113642
113772
|
}
|
|
113773
|
+
function firstExecutableOffset(text, options = {}) {
|
|
113774
|
+
for (const token of iterateSqlTokens(text, options)) {
|
|
113775
|
+
if (token.kind === "line_comment" || token.kind === "block_comment" || token.kind === "statement_separator") {
|
|
113776
|
+
continue;
|
|
113777
|
+
}
|
|
113778
|
+
const raw = text.slice(token.start, token.end);
|
|
113779
|
+
if (raw.trim().length === 0) {
|
|
113780
|
+
continue;
|
|
113781
|
+
}
|
|
113782
|
+
return token.start + (raw.length - raw.trimStart().length);
|
|
113783
|
+
}
|
|
113784
|
+
return -1;
|
|
113785
|
+
}
|
|
113786
|
+
function hasExecutableSql(text, options = {}) {
|
|
113787
|
+
return firstExecutableOffset(text, options) !== -1;
|
|
113788
|
+
}
|
|
113643
113789
|
var CHAR_NEWLINE, CHAR_ASTERISK, CHAR_SLASH, CHAR_DASH, CHAR_SINGLE_QUOTE, CHAR_DOUBLE_QUOTE, CHAR_DOLLAR, CHAR_SEMICOLON, CHAR_BACKSLASH, CHAR_TAB, CHAR_CR, CHAR_SPACE, CHAR_NBSP, CHAR_HASH, CHAR_UPPER_E, CHAR_LOWER_E;
|
|
113644
113790
|
var init_querySplitter = __esm({
|
|
113645
113791
|
"src/utils/querySplitter.ts"() {
|
|
@@ -194647,9 +194793,7 @@ ${query.sql}`).join("\n\n")
|
|
|
194647
194793
|
content: [
|
|
194648
194794
|
{
|
|
194649
194795
|
type: "text",
|
|
194650
|
-
text:
|
|
194651
|
-
request2.expiresAt
|
|
194652
|
-
).toISOString()}.`
|
|
194796
|
+
text: `One-time Grain setup page for '${request2.connectionName}' (id ${request2.connectionId}): ${request2.url} (expires ${new Date(request2.expiresAt).toISOString()}). ${browserOpened ? "The page was opened in the browser." : "The page was not opened in the browser."} Connection setup takes effect once saved.`
|
|
194653
194797
|
}
|
|
194654
194798
|
]
|
|
194655
194799
|
};
|
|
@@ -195765,6 +195909,10 @@ var init_ConnectorProcessPool = __esm({
|
|
|
195765
195909
|
});
|
|
195766
195910
|
|
|
195767
195911
|
// src/server/DaemonQueryExecutor.ts
|
|
195912
|
+
function withoutTerminalSeparator(query) {
|
|
195913
|
+
const statements = splitSqlQueries(query).filter((statement) => hasExecutableSql(statement));
|
|
195914
|
+
return statements.length === 1 && statements[0] ? statements[0] : query;
|
|
195915
|
+
}
|
|
195768
195916
|
var path39, DaemonQueryExecutor;
|
|
195769
195917
|
var init_DaemonQueryExecutor = __esm({
|
|
195770
195918
|
"src/server/DaemonQueryExecutor.ts"() {
|
|
@@ -195776,6 +195924,7 @@ var init_DaemonQueryExecutor = __esm({
|
|
|
195776
195924
|
init_AppError();
|
|
195777
195925
|
init_optionalDrivers();
|
|
195778
195926
|
init_sqlPolicy();
|
|
195927
|
+
init_querySplitter();
|
|
195779
195928
|
init_classifyQueryError();
|
|
195780
195929
|
init_TelemetrySchema();
|
|
195781
195930
|
init_DaemonConfig();
|
|
@@ -196007,7 +196156,7 @@ var init_DaemonQueryExecutor = __esm({
|
|
|
196007
196156
|
"config"
|
|
196008
196157
|
);
|
|
196009
196158
|
}
|
|
196010
|
-
hooks?.onProfileResolved?.(profile.type);
|
|
196159
|
+
hooks?.onProfileResolved?.(profile.type, profile);
|
|
196011
196160
|
const maxRows = maxRowsOverride ?? this.queryConfig.maxRows;
|
|
196012
196161
|
const connectorConfig = {
|
|
196013
196162
|
...profile,
|
|
@@ -196037,7 +196186,13 @@ var init_DaemonQueryExecutor = __esm({
|
|
|
196037
196186
|
});
|
|
196038
196187
|
const querySignal = this.createQuerySignal(abortSignal);
|
|
196039
196188
|
try {
|
|
196040
|
-
const
|
|
196189
|
+
const executableQuery = withoutTerminalSeparator(query);
|
|
196190
|
+
const generator = connector.runQuery(
|
|
196191
|
+
executableQuery,
|
|
196192
|
+
connectorConfig,
|
|
196193
|
+
authHeader,
|
|
196194
|
+
querySignal.signal
|
|
196195
|
+
);
|
|
196041
196196
|
for await (const page of generator) {
|
|
196042
196197
|
yield {
|
|
196043
196198
|
...page,
|
|
@@ -203546,12 +203701,14 @@ function registerConnectionSetupRoutes(app, context2) {
|
|
|
203546
203701
|
res.status(400).json({ error: "Setup fields include a protected field." });
|
|
203547
203702
|
return;
|
|
203548
203703
|
}
|
|
203704
|
+
const profile = { ...state.profile };
|
|
203705
|
+
delete profile["draft"];
|
|
203549
203706
|
const result = await context2.toolManager.handleToolCall(
|
|
203550
203707
|
"test_connection",
|
|
203551
203708
|
{
|
|
203552
203709
|
type: state.profile["type"],
|
|
203553
203710
|
connectionProfile: {
|
|
203554
|
-
...
|
|
203711
|
+
...profile,
|
|
203555
203712
|
...fields,
|
|
203556
203713
|
...typeof body.password === "string" ? { password: body.password } : {}
|
|
203557
203714
|
}
|
|
@@ -204069,7 +204226,7 @@ function registerDaemonHttpRoutes(context2, mcpHttpSurface) {
|
|
|
204069
204226
|
await context2.credentialRequestService.fulfill(id, token, password);
|
|
204070
204227
|
res.send(
|
|
204071
204228
|
renderCredentialPage({
|
|
204072
|
-
success: "Saved. Go back to your agent
|
|
204229
|
+
success: "Saved. Go back to your agent, and it will retry."
|
|
204073
204230
|
})
|
|
204074
204231
|
);
|
|
204075
204232
|
} catch (err) {
|
|
@@ -204214,7 +204371,9 @@ data: ${Date.now()}
|
|
|
204214
204371
|
sourceFileUri: t.sourceFileUri,
|
|
204215
204372
|
wasTruncated: t.wasTruncated,
|
|
204216
204373
|
totalRowsInFirstBatch: t.totalRowsInFirstBatch,
|
|
204217
|
-
executedAt: t.executedAt
|
|
204374
|
+
executedAt: t.executedAt,
|
|
204375
|
+
connectionId: t.connectionId,
|
|
204376
|
+
connectionName: t.connectionName
|
|
204218
204377
|
}))
|
|
204219
204378
|
);
|
|
204220
204379
|
} catch (err) {
|
|
@@ -235242,12 +235401,12 @@ var init_ExtensionConnectionTracker = __esm({
|
|
|
235242
235401
|
});
|
|
235243
235402
|
|
|
235244
235403
|
// src/server/surfaces/DaemonSocketSurface.ts
|
|
235245
|
-
var fs41,
|
|
235404
|
+
var fs41, net3, path41, bindSequence, DaemonSocketSurface;
|
|
235246
235405
|
var init_DaemonSocketSurface = __esm({
|
|
235247
235406
|
"src/server/surfaces/DaemonSocketSurface.ts"() {
|
|
235248
235407
|
"use strict";
|
|
235249
235408
|
fs41 = __toESM(require("fs"));
|
|
235250
|
-
|
|
235409
|
+
net3 = __toESM(require("net"));
|
|
235251
235410
|
path41 = __toESM(require("path"));
|
|
235252
235411
|
init_ipcEndpoint();
|
|
235253
235412
|
init_ConsoleLogger();
|
|
@@ -235306,7 +235465,7 @@ var init_DaemonSocketSurface = __esm({
|
|
|
235306
235465
|
logger.warn("Failed to clean up socket:", e2);
|
|
235307
235466
|
}
|
|
235308
235467
|
}
|
|
235309
|
-
this.server =
|
|
235468
|
+
this.server = net3.createServer(async (socket) => {
|
|
235310
235469
|
this.sockets.add(socket);
|
|
235311
235470
|
logger.info("Client connected via Socket");
|
|
235312
235471
|
this.options.refreshActivity();
|
|
@@ -235451,45 +235610,14 @@ var init_DaemonSocketSurface = __esm({
|
|
|
235451
235610
|
});
|
|
235452
235611
|
|
|
235453
235612
|
// src/server/surfaces/DaemonControlSurface.ts
|
|
235454
|
-
|
|
235455
|
-
return new Promise((resolve12) => {
|
|
235456
|
-
const socket = net3.createConnection(endpoint);
|
|
235457
|
-
const timeout = setTimeout(() => settle2(false), 2e3);
|
|
235458
|
-
const settle2 = (accepted) => {
|
|
235459
|
-
clearTimeout(timeout);
|
|
235460
|
-
socket.destroy();
|
|
235461
|
-
resolve12(accepted);
|
|
235462
|
-
};
|
|
235463
|
-
socket.once("error", () => settle2(false));
|
|
235464
|
-
socket.once(
|
|
235465
|
-
"connect",
|
|
235466
|
-
() => socket.write(JSON.stringify({ method: "graceful-stop", token }) + "\n")
|
|
235467
|
-
);
|
|
235468
|
-
let buffer = "";
|
|
235469
|
-
socket.on("data", (chunk) => {
|
|
235470
|
-
buffer += chunk.toString();
|
|
235471
|
-
if (Buffer.byteLength(buffer) > CONTROL_MESSAGE_LIMIT) {
|
|
235472
|
-
settle2(false);
|
|
235473
|
-
return;
|
|
235474
|
-
}
|
|
235475
|
-
const newline = buffer.indexOf("\n");
|
|
235476
|
-
if (newline === -1)
|
|
235477
|
-
return;
|
|
235478
|
-
try {
|
|
235479
|
-
settle2(JSON.parse(buffer.slice(0, newline)).accepted === true);
|
|
235480
|
-
} catch {
|
|
235481
|
-
settle2(false);
|
|
235482
|
-
}
|
|
235483
|
-
});
|
|
235484
|
-
});
|
|
235485
|
-
}
|
|
235486
|
-
var net3, CONTROL_MESSAGE_LIMIT, DaemonControlSurface;
|
|
235613
|
+
var net4, CONTROL_MESSAGE_LIMIT2, DaemonControlSurface;
|
|
235487
235614
|
var init_DaemonControlSurface = __esm({
|
|
235488
235615
|
"src/server/surfaces/DaemonControlSurface.ts"() {
|
|
235489
235616
|
"use strict";
|
|
235490
|
-
|
|
235617
|
+
net4 = __toESM(require("net"));
|
|
235491
235618
|
init_DaemonAuthToken();
|
|
235492
|
-
|
|
235619
|
+
init_requestDaemonControlStop();
|
|
235620
|
+
CONTROL_MESSAGE_LIMIT2 = 8 * 1024;
|
|
235493
235621
|
DaemonControlSurface = class {
|
|
235494
235622
|
constructor(options) {
|
|
235495
235623
|
this.options = options;
|
|
@@ -235499,13 +235627,13 @@ var init_DaemonControlSurface = __esm({
|
|
|
235499
235627
|
const enabled = this.options.platformEnabled ?? process.platform === "win32";
|
|
235500
235628
|
if (!enabled)
|
|
235501
235629
|
return;
|
|
235502
|
-
this.server =
|
|
235630
|
+
this.server = net4.createServer((socket) => {
|
|
235503
235631
|
this.sockets.add(socket);
|
|
235504
235632
|
socket.once("close", () => this.sockets.delete(socket));
|
|
235505
235633
|
let buffer = "";
|
|
235506
235634
|
socket.on("data", (chunk) => {
|
|
235507
235635
|
buffer += chunk.toString();
|
|
235508
|
-
if (Buffer.byteLength(buffer) >
|
|
235636
|
+
if (Buffer.byteLength(buffer) > CONTROL_MESSAGE_LIMIT2) {
|
|
235509
235637
|
socket.destroy();
|
|
235510
235638
|
return;
|
|
235511
235639
|
}
|
|
@@ -252899,7 +253027,8 @@ var init_Daemon = __esm({
|
|
|
252899
253027
|
this.connectionManager,
|
|
252900
253028
|
() => this.getHttpBaseUrl(),
|
|
252901
253029
|
void 0,
|
|
252902
|
-
this.connectorRegistry
|
|
253030
|
+
this.connectorRegistry,
|
|
253031
|
+
this.telemetryService
|
|
252903
253032
|
);
|
|
252904
253033
|
this.toolManager = new DaemonMcpToolManager(
|
|
252905
253034
|
this.sessionManager,
|
|
@@ -254366,9 +254495,9 @@ ${line}
|
|
|
254366
254495
|
|
|
254367
254496
|
// src/server/install/installCommand.ts
|
|
254368
254497
|
var crypto8 = __toESM(require("crypto"));
|
|
254369
|
-
var
|
|
254370
|
-
var
|
|
254371
|
-
var
|
|
254498
|
+
var fs19 = __toESM(require("fs"));
|
|
254499
|
+
var os5 = __toESM(require("os"));
|
|
254500
|
+
var path23 = __toESM(require("path"));
|
|
254372
254501
|
init_grainHome();
|
|
254373
254502
|
init_daemonRecordInstance();
|
|
254374
254503
|
init_AppError();
|
|
@@ -255105,8 +255234,8 @@ function unitFor(platform2, launcher, configHome, nodePath) {
|
|
|
255105
255234
|
nodePath,
|
|
255106
255235
|
configHome,
|
|
255107
255236
|
serviceIdentifier: GRAIN_SERVICE_LABEL,
|
|
255108
|
-
stdoutPath:
|
|
255109
|
-
stderrPath:
|
|
255237
|
+
stdoutPath: path23.join(configHome, "grain-daemon.out.log"),
|
|
255238
|
+
stderrPath: path23.join(configHome, "grain-daemon.err.log")
|
|
255110
255239
|
};
|
|
255111
255240
|
if (platform2 === "darwin") {
|
|
255112
255241
|
return generateLaunchAgent(descriptor);
|
|
@@ -255117,7 +255246,7 @@ function unitFor(platform2, launcher, configHome, nodePath) {
|
|
|
255117
255246
|
return generateWindowsStartupTask(descriptor);
|
|
255118
255247
|
}
|
|
255119
255248
|
function platformLauncherPath(platform2, configHome) {
|
|
255120
|
-
return platform2 === "win32" ?
|
|
255249
|
+
return platform2 === "win32" ? path23.join(configHome, "grain-daemon-launcher.cmd") : launcherPath(configHome);
|
|
255121
255250
|
}
|
|
255122
255251
|
function writeLauncher(fileSystem, platform2, configHome, nodePath) {
|
|
255123
255252
|
const scriptPath = launcherPath(configHome);
|
|
@@ -255219,15 +255348,15 @@ function registerCliPayload(fileSystem, configHome, standalonePath, writeOutput)
|
|
|
255219
255348
|
if (!fileSystem.existsSync(standalonePath)) {
|
|
255220
255349
|
return;
|
|
255221
255350
|
}
|
|
255222
|
-
const installedEntry =
|
|
255223
|
-
const manifestPath =
|
|
255351
|
+
const installedEntry = path23.join(configHome, "app", version3, "standalone.js");
|
|
255352
|
+
const manifestPath = path23.join(configHome, "app", version3, "package.json");
|
|
255224
255353
|
const payloadUnchanged = fileSystem.existsSync(installedEntry) && fileSystem.existsSync(manifestPath) && fileSystem.readFileSync(installedEntry).equals(fileSystem.readFileSync(standalonePath));
|
|
255225
255354
|
if (!payloadUnchanged) {
|
|
255226
255355
|
installApplicationPayload({ configHome, version: version3, sourceEntry: standalonePath });
|
|
255227
255356
|
}
|
|
255228
|
-
const versionsDir =
|
|
255357
|
+
const versionsDir = path23.join(configHome, "versions");
|
|
255229
255358
|
fileSystem.mkdirSync(versionsDir, { recursive: true });
|
|
255230
|
-
const pointerPath2 =
|
|
255359
|
+
const pointerPath2 = path23.join(versionsDir, `${version3}.json`);
|
|
255231
255360
|
const pointerContents = JSON.stringify({ path: installedEntry, version: version3 });
|
|
255232
255361
|
if (!fileSystem.existsSync(pointerPath2) || fileSystem.readFileSync(pointerPath2, "utf8") !== pointerContents) {
|
|
255233
255362
|
writeAtomicPointer(fileSystem, pointerPath2, pointerContents);
|
|
@@ -255242,11 +255371,11 @@ async function installCommand(deps = {}) {
|
|
|
255242
255371
|
throw new Error(`Grain does not support supervised installation on ${platform2}.`);
|
|
255243
255372
|
}
|
|
255244
255373
|
const configHome = deps.configHome ?? resolveGrainHome();
|
|
255245
|
-
const homeDirectory = deps.homeDirectory ??
|
|
255374
|
+
const homeDirectory = deps.homeDirectory ?? os5.homedir();
|
|
255246
255375
|
const uid = deps.uid ?? process.getuid?.() ?? 0;
|
|
255247
|
-
const fileSystem = deps.fileSystem ??
|
|
255376
|
+
const fileSystem = deps.fileSystem ?? fs19;
|
|
255248
255377
|
const runner = deps.commandRunner ?? createCommandRunner();
|
|
255249
|
-
const adapter2 = deps.adapter ?? createSupervisorAdapter(platform2, runner, uid);
|
|
255378
|
+
const adapter2 = deps.adapter ?? createSupervisorAdapter(platform2, runner, uid, configHome);
|
|
255250
255379
|
const readStatus = deps.readStatus ?? readGrainServiceStatus;
|
|
255251
255380
|
const wait = deps.wait ?? ((milliseconds) => new Promise((resolve12) => setTimeout(resolve12, milliseconds)));
|
|
255252
255381
|
const writeOutput = deps.writeOutput ?? ((message) => console.log(message));
|
|
@@ -255292,7 +255421,7 @@ async function installCommand(deps = {}) {
|
|
|
255292
255421
|
});
|
|
255293
255422
|
const launcher = platformLauncherPath(platform2, configHome);
|
|
255294
255423
|
const unit = unitFor(platform2, launcher, configHome, nodePath);
|
|
255295
|
-
const unitPath =
|
|
255424
|
+
const unitPath = path23.join(homeDirectory, unit.destination);
|
|
255296
255425
|
const healthy = isRunningGrainService(await readStatus());
|
|
255297
255426
|
const unitExists = fileSystem.existsSync(unitPath);
|
|
255298
255427
|
const unitChanged = unitExists && String(fileSystem.readFileSync(unitPath, { encoding: unit.encoding })) !== unit.content;
|
|
@@ -255304,7 +255433,7 @@ async function installCommand(deps = {}) {
|
|
|
255304
255433
|
return 0;
|
|
255305
255434
|
}
|
|
255306
255435
|
writeLauncher(fileSystem, platform2, configHome, nodePath);
|
|
255307
|
-
fileSystem.mkdirSync(
|
|
255436
|
+
fileSystem.mkdirSync(path23.dirname(unitPath), { recursive: true });
|
|
255308
255437
|
fileSystem.writeFileSync(unitPath, unit.content, { encoding: unit.encoding, mode: 384 });
|
|
255309
255438
|
if (registered && unitChanged) {
|
|
255310
255439
|
await adapter2.unregister(GRAIN_SERVICE_LABEL);
|
|
@@ -255381,7 +255510,7 @@ async function installCommand(deps = {}) {
|
|
|
255381
255510
|
await flushTelemetry(telemetry);
|
|
255382
255511
|
writeOutput(
|
|
255383
255512
|
`Fatal error: Grain service did not become reachable at its published endpoint after installation.
|
|
255384
|
-
Check the supervisor logs at ${
|
|
255513
|
+
Check the supervisor logs at ${path23.join(configHome, "grain-daemon.out.log")} and ${path23.join(configHome, "grain-daemon.err.log")}`
|
|
255385
255514
|
);
|
|
255386
255515
|
return 1;
|
|
255387
255516
|
}
|
|
@@ -255391,9 +255520,9 @@ Check the supervisor logs at ${path22.join(configHome, "grain-daemon.out.log")}
|
|
|
255391
255520
|
}
|
|
255392
255521
|
|
|
255393
255522
|
// src/server/install/uninstallCommand.ts
|
|
255394
|
-
var
|
|
255395
|
-
var
|
|
255396
|
-
var
|
|
255523
|
+
var fs21 = __toESM(require("fs"));
|
|
255524
|
+
var os6 = __toESM(require("os"));
|
|
255525
|
+
var path26 = __toESM(require("path"));
|
|
255397
255526
|
var import_readline = require("readline");
|
|
255398
255527
|
init_grainHome();
|
|
255399
255528
|
init_applicationPayload();
|
|
@@ -255416,7 +255545,7 @@ function reportKept(fileSystem, configHome, writeOutput) {
|
|
|
255416
255545
|
return;
|
|
255417
255546
|
}
|
|
255418
255547
|
lines = fileSystem.readdirSync(configHome).sort().map((name) => {
|
|
255419
|
-
const isDirectory = fileSystem.lstatSync(
|
|
255548
|
+
const isDirectory = fileSystem.lstatSync(path26.join(configHome, name)).isDirectory();
|
|
255420
255549
|
return ` - ${name}${isDirectory ? "/" : ""}`;
|
|
255421
255550
|
});
|
|
255422
255551
|
} catch {
|
|
@@ -255465,16 +255594,16 @@ async function uninstallCommand(deps = {}) {
|
|
|
255465
255594
|
throw new Error(`Grain does not support supervised installation on ${platform2}.`);
|
|
255466
255595
|
}
|
|
255467
255596
|
const configHome = deps.configHome ?? resolveGrainHome();
|
|
255468
|
-
const homeDirectory = deps.homeDirectory ??
|
|
255597
|
+
const homeDirectory = deps.homeDirectory ?? os6.homedir();
|
|
255469
255598
|
const uid = deps.uid ?? process.getuid?.() ?? 0;
|
|
255470
255599
|
const nodePath = deps.nodePath ?? "";
|
|
255471
|
-
const fileSystem = deps.fileSystem ??
|
|
255600
|
+
const fileSystem = deps.fileSystem ?? fs21;
|
|
255472
255601
|
const runner = deps.commandRunner ?? createCommandRunner();
|
|
255473
|
-
const adapter2 = deps.adapter ?? createSupervisorAdapter(platform2, runner, uid);
|
|
255602
|
+
const adapter2 = deps.adapter ?? createSupervisorAdapter(platform2, runner, uid, configHome);
|
|
255474
255603
|
const writeOutput = deps.writeOutput ?? ((message) => console.log(message));
|
|
255475
255604
|
if (deps.purge) {
|
|
255476
|
-
const resolvedHome =
|
|
255477
|
-
if (resolvedHome ===
|
|
255605
|
+
const resolvedHome = path26.resolve(configHome);
|
|
255606
|
+
if (resolvedHome === path26.resolve(homeDirectory) || resolvedHome === path26.parse(resolvedHome).root) {
|
|
255478
255607
|
writeOutput(`Refusing to purge ${configHome}: it is a home directory or filesystem root.`);
|
|
255479
255608
|
return 1;
|
|
255480
255609
|
}
|
|
@@ -255493,13 +255622,13 @@ async function uninstallCommand(deps = {}) {
|
|
|
255493
255622
|
}
|
|
255494
255623
|
const launcher = platformLauncherPath(platform2, configHome);
|
|
255495
255624
|
const unit = unitFor(platform2, launcher, configHome, nodePath);
|
|
255496
|
-
const grainCommand =
|
|
255625
|
+
const grainCommand = path26.join(configHome, "bin", platform2 === "win32" ? "grain.cmd" : "grain");
|
|
255497
255626
|
const ownedPaths = [
|
|
255498
255627
|
grainCommand,
|
|
255499
255628
|
launcherPath(configHome),
|
|
255500
255629
|
launcher,
|
|
255501
255630
|
applicationDir(configHome, ""),
|
|
255502
|
-
|
|
255631
|
+
path26.join(configHome, "runtime", "node")
|
|
255503
255632
|
];
|
|
255504
255633
|
const registered = await adapter2.isRegistered(GRAIN_SERVICE_LABEL);
|
|
255505
255634
|
if (registered) {
|
|
@@ -255532,16 +255661,16 @@ async function uninstallCommand(deps = {}) {
|
|
|
255532
255661
|
return 0;
|
|
255533
255662
|
}
|
|
255534
255663
|
const retainedVersions = retainedApplicationVersions(configHome);
|
|
255535
|
-
removeIfExists(fileSystem,
|
|
255664
|
+
removeIfExists(fileSystem, path26.join(homeDirectory, unit.destination));
|
|
255536
255665
|
removeIfExists(fileSystem, launcherPath(configHome));
|
|
255537
255666
|
removeIfExists(fileSystem, grainCommand);
|
|
255538
255667
|
if (platform2 === "win32") {
|
|
255539
255668
|
removeIfExists(fileSystem, launcher);
|
|
255540
|
-
removeIfExists(fileSystem,
|
|
255669
|
+
removeIfExists(fileSystem, path26.join(configHome, "bin", "grain.ps1"));
|
|
255541
255670
|
}
|
|
255542
255671
|
removeDirectoryIfExists(fileSystem, applicationDir(configHome, ""));
|
|
255543
|
-
removeDirectoryIfExists(fileSystem,
|
|
255544
|
-
removeDirectoryIfExists(fileSystem,
|
|
255672
|
+
removeDirectoryIfExists(fileSystem, path26.join(configHome, "versions"));
|
|
255673
|
+
removeDirectoryIfExists(fileSystem, path26.join(configHome, "runtime", "node"));
|
|
255545
255674
|
const filesToRemove = [
|
|
255546
255675
|
"endpoint.json",
|
|
255547
255676
|
"grain-daemon.out.log",
|
|
@@ -255549,14 +255678,14 @@ async function uninstallCommand(deps = {}) {
|
|
|
255549
255678
|
"service-mode.json"
|
|
255550
255679
|
];
|
|
255551
255680
|
for (const file4 of filesToRemove) {
|
|
255552
|
-
removeIfExists(fileSystem,
|
|
255681
|
+
removeIfExists(fileSystem, path26.join(configHome, file4));
|
|
255553
255682
|
}
|
|
255554
255683
|
try {
|
|
255555
255684
|
if (fileSystem.existsSync(configHome)) {
|
|
255556
255685
|
const allFiles = fileSystem.readdirSync(configHome);
|
|
255557
255686
|
for (const file4 of allFiles) {
|
|
255558
255687
|
if (/^server-\d+\.pid$/.test(file4)) {
|
|
255559
|
-
removeIfExists(fileSystem,
|
|
255688
|
+
removeIfExists(fileSystem, path26.join(configHome, file4));
|
|
255560
255689
|
}
|
|
255561
255690
|
}
|
|
255562
255691
|
}
|
|
@@ -255564,7 +255693,7 @@ async function uninstallCommand(deps = {}) {
|
|
|
255564
255693
|
}
|
|
255565
255694
|
const dirsToRemoveIfEmpty = ["bin", "runtime"];
|
|
255566
255695
|
for (const dirName of dirsToRemoveIfEmpty) {
|
|
255567
|
-
const dirPath =
|
|
255696
|
+
const dirPath = path26.join(configHome, dirName);
|
|
255568
255697
|
try {
|
|
255569
255698
|
if (fileSystem.existsSync(dirPath)) {
|
|
255570
255699
|
const contents = fileSystem.readdirSync(dirPath);
|
|
@@ -255590,7 +255719,7 @@ async function uninstallCommand(deps = {}) {
|
|
|
255590
255719
|
}
|
|
255591
255720
|
|
|
255592
255721
|
// src/server/cli/clientsCommand.ts
|
|
255593
|
-
var
|
|
255722
|
+
var import_crypto5 = __toESM(require("crypto"));
|
|
255594
255723
|
init_grainHome();
|
|
255595
255724
|
init_toolProfiles();
|
|
255596
255725
|
init_ClientRegistry();
|
|
@@ -255659,7 +255788,7 @@ async function clientsCommand(args, configDir = resolveGrainHome()) {
|
|
|
255659
255788
|
return 1;
|
|
255660
255789
|
}
|
|
255661
255790
|
if (!id) {
|
|
255662
|
-
id =
|
|
255791
|
+
id = import_crypto5.default.randomBytes(8).toString("hex");
|
|
255663
255792
|
}
|
|
255664
255793
|
const registration = {
|
|
255665
255794
|
id,
|
|
@@ -255817,7 +255946,7 @@ async function openCommand(configDir = resolveGrainHome(), runner = createComman
|
|
|
255817
255946
|
init_daemonVersion();
|
|
255818
255947
|
|
|
255819
255948
|
// src/server/cli/toolCommands.ts
|
|
255820
|
-
var
|
|
255949
|
+
var fs25 = __toESM(require("fs"));
|
|
255821
255950
|
|
|
255822
255951
|
// node_modules/@modelcontextprotocol/sdk/dist/esm/client/index.js
|
|
255823
255952
|
init_protocol();
|
|
@@ -256777,7 +256906,7 @@ ${dataCommandHelp()}
|
|
|
256777
256906
|
parsed = parseCommand(
|
|
256778
256907
|
command,
|
|
256779
256908
|
words,
|
|
256780
|
-
deps.readFile ?? ((file4) =>
|
|
256909
|
+
deps.readFile ?? ((file4) => fs25.readFileSync(file4 === "-" ? 0 : file4, "utf8"))
|
|
256781
256910
|
);
|
|
256782
256911
|
} catch (error52) {
|
|
256783
256912
|
stderr(`${error52.message}
|