@ao_zorin/zocket 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -5
- package/dist/zocket.js +338 -24
- package/docs/AI_AUTODEPLOY.md +9 -13
- package/docs/GIT_NPM_RELEASE.md +4 -13
- package/docs/INSTALL.md +33 -119
- package/package.json +1 -1
- package/scripts/install-zocket.ps1 +26 -68
- package/scripts/install-zocket.sh +76 -143
package/README.md
CHANGED
|
@@ -36,18 +36,23 @@ zocket init
|
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
38
|
zocket init
|
|
39
|
-
zocket start --host 127.0.0.1 --web-port 18001 --mcp-port 18002 --mode admin
|
|
39
|
+
zocket start --host 127.0.0.1 --web-port 18001 --mcp-port 18002 --mcp-stream-port 18003 --mode admin
|
|
40
40
|
```
|
|
41
41
|
|
|
42
42
|
Open `http://127.0.0.1:18001`.
|
|
43
43
|
|
|
44
|
-
##
|
|
44
|
+
## CLI / TUI
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
Full CLI management:
|
|
47
|
+
```bash
|
|
48
|
+
zocket projects list
|
|
49
|
+
zocket projects create myproj --description \"demo\"
|
|
50
|
+
zocket secrets set myproj API_KEY abc123 --description \"example\"
|
|
51
|
+
```
|
|
48
52
|
|
|
53
|
+
Interactive TUI:
|
|
49
54
|
```bash
|
|
50
|
-
|
|
55
|
+
zocket tui
|
|
51
56
|
```
|
|
52
57
|
|
|
53
58
|
## Docs
|
package/dist/zocket.js
CHANGED
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
import { createServer } from "http";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { randomBytes as randomBytes6 } from "crypto";
|
|
6
|
+
import { mkdirSync as mkdirSync5 } from "fs";
|
|
7
|
+
import { randomUUID } from "crypto";
|
|
9
8
|
import { serve } from "@hono/node-server";
|
|
10
9
|
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
|
|
10
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
11
|
+
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
11
12
|
|
|
12
13
|
// src/vault.ts
|
|
13
14
|
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
|
|
@@ -58,6 +59,11 @@ var VaultService = class {
|
|
|
58
59
|
mkdirSync(dirname(this.vaultPath), { recursive: true });
|
|
59
60
|
writeFileSync(this.vaultPath, encrypt(Buffer.from(JSON.stringify(data)), this.key));
|
|
60
61
|
}
|
|
62
|
+
ensureExists() {
|
|
63
|
+
if (!existsSync(this.vaultPath)) {
|
|
64
|
+
this.save({ version: 1, projects: {} });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
61
67
|
async withLock(fn) {
|
|
62
68
|
mkdirSync(dirname(this.lockFile), { recursive: true });
|
|
63
69
|
if (!existsSync(this.lockFile)) writeFileSync(this.lockFile, "");
|
|
@@ -302,12 +308,12 @@ function runCommand(command, env, policy, maxChars = 500) {
|
|
|
302
308
|
r.exit_code = proc.status ?? 1;
|
|
303
309
|
return r;
|
|
304
310
|
}
|
|
305
|
-
function runScript(
|
|
306
|
-
const ext =
|
|
311
|
+
function runScript(_lang, code, env, maxChars = 500) {
|
|
312
|
+
const ext = ".mjs";
|
|
307
313
|
const tmpFile = join(tmpdir(), `zkt-${randomBytes3(8).toString("hex")}${ext}`);
|
|
308
314
|
try {
|
|
309
315
|
writeFileSync3(tmpFile, code, "utf8");
|
|
310
|
-
const bin =
|
|
316
|
+
const bin = "node";
|
|
311
317
|
const proc = spawnSync(bin, [tmpFile], {
|
|
312
318
|
env: { ...process.env, ...env },
|
|
313
319
|
encoding: "utf8",
|
|
@@ -483,8 +489,8 @@ function envRefsIn(text) {
|
|
|
483
489
|
const matches = [...text.matchAll(/\$\{?([A-Z_][A-Z0-9_]*)\}?/g)].map((m) => m[1]);
|
|
484
490
|
return [...new Set(matches)];
|
|
485
491
|
}
|
|
486
|
-
function buildCtx(text
|
|
487
|
-
return { text, envRefs: envRefsIn(text), hasNetwork: NETWORK_RE.test(text)
|
|
492
|
+
function buildCtx(text) {
|
|
493
|
+
return { text, envRefs: envRefsIn(text), hasNetwork: NETWORK_RE.test(text) };
|
|
488
494
|
}
|
|
489
495
|
var RULES = [
|
|
490
496
|
// ── Critical ────────────────────────────────────────────────────────────
|
|
@@ -577,13 +583,6 @@ var RULES = [
|
|
|
577
583
|
weight: W.MEDIUM,
|
|
578
584
|
test: (c) => /\b(printenv|env)\b[^|]*>/.test(c.text)
|
|
579
585
|
},
|
|
580
|
-
{
|
|
581
|
-
id: "PYTHON_SUBPROCESS_EXFIL",
|
|
582
|
-
description: "Python subprocess call invoking network tool (curl/wget/nc)",
|
|
583
|
-
severity: "medium",
|
|
584
|
-
weight: W.MEDIUM,
|
|
585
|
-
test: (c) => c.lang === "python" && /subprocess\.(run|Popen|call|check_output)\([^)]*\b(curl|wget|nc\b|netcat)\b/.test(c.text)
|
|
586
|
-
},
|
|
587
586
|
// ── Low ──────────────────────────────────────────────────────────────────
|
|
588
587
|
{
|
|
589
588
|
id: "SINGLE_ENV_NETWORK",
|
|
@@ -648,9 +647,9 @@ var SecurityAnalyzer = class {
|
|
|
648
647
|
const text = isBashC ? command[2] : command.join(" ");
|
|
649
648
|
return this.analyze(buildCtx(text));
|
|
650
649
|
}
|
|
651
|
-
analyzeScript(
|
|
650
|
+
analyzeScript(_lang, code) {
|
|
652
651
|
if (this.cfg.mode === "off") return allow();
|
|
653
|
-
const ctx = buildCtx(code
|
|
652
|
+
const ctx = buildCtx(code);
|
|
654
653
|
return this.analyze(ctx);
|
|
655
654
|
}
|
|
656
655
|
analyze(ctx) {
|
|
@@ -870,18 +869,18 @@ function buildCatalog(services) {
|
|
|
870
869
|
},
|
|
871
870
|
{
|
|
872
871
|
name: "run_script",
|
|
873
|
-
summary: "Run an inline node
|
|
872
|
+
summary: "Run an inline node script with project secrets injected as env vars. Prefer over multiple run_with_project_env calls.",
|
|
874
873
|
register: (server) => {
|
|
875
874
|
server.tool(
|
|
876
875
|
"run_script",
|
|
877
876
|
[
|
|
878
|
-
"Run an inline script with project secrets available as environment variables.",
|
|
877
|
+
"Run an inline node script with project secrets available as environment variables.",
|
|
879
878
|
"Use this instead of multiple run_with_project_env calls \u2014 write the full logic in one script.",
|
|
880
879
|
"Filesystem is NOT shared between calls. Secret values never appear in this conversation."
|
|
881
880
|
].join(" "),
|
|
882
881
|
{
|
|
883
882
|
project: z.string().describe("Project name"),
|
|
884
|
-
lang: z.enum(["node"
|
|
883
|
+
lang: z.enum(["node"]).describe("Script language"),
|
|
885
884
|
code: z.string().min(1).describe("Full script source code"),
|
|
886
885
|
max_chars: z.number().int().min(1).max(32e3).optional().describe("Max output chars (default ~500)"),
|
|
887
886
|
confirm: z.boolean().optional().describe("Set to true to confirm execution of a medium-risk script after reviewing the warning")
|
|
@@ -1922,7 +1921,10 @@ function lockPath(home = zocketHome()) {
|
|
|
1922
1921
|
return join3(home, "vault.lock");
|
|
1923
1922
|
}
|
|
1924
1923
|
|
|
1925
|
-
// src/
|
|
1924
|
+
// src/keys.ts
|
|
1925
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, existsSync as existsSync5 } from "fs";
|
|
1926
|
+
import { dirname as dirname5 } from "path";
|
|
1927
|
+
import { randomBytes as randomBytes6 } from "crypto";
|
|
1926
1928
|
function loadOrCreateKey(keyFile) {
|
|
1927
1929
|
if (existsSync5(keyFile)) {
|
|
1928
1930
|
const raw = readFileSync5(keyFile, "utf8").trim();
|
|
@@ -1933,13 +1935,175 @@ function loadOrCreateKey(keyFile) {
|
|
|
1933
1935
|
writeFileSync5(keyFile, key.toString("hex"), { mode: 384 });
|
|
1934
1936
|
return key;
|
|
1935
1937
|
}
|
|
1936
|
-
|
|
1938
|
+
|
|
1939
|
+
// src/tui.ts
|
|
1940
|
+
import readline from "readline/promises";
|
|
1941
|
+
import { stdin as input, stdout as output } from "process";
|
|
1942
|
+
async function promptMenu(rl, title, options) {
|
|
1943
|
+
output.write(`
|
|
1944
|
+
${title}
|
|
1945
|
+
`);
|
|
1946
|
+
options.forEach((opt, i) => output.write(` ${i + 1}) ${opt}
|
|
1947
|
+
`));
|
|
1948
|
+
const answer = await rl.question("> ");
|
|
1949
|
+
const idx = Number(answer.trim()) - 1;
|
|
1950
|
+
return Number.isFinite(idx) && idx >= 0 && idx < options.length ? idx : -1;
|
|
1951
|
+
}
|
|
1952
|
+
async function promptInput(rl, label, fallback = "") {
|
|
1953
|
+
const ans = await rl.question(`${label}${fallback ? ` (${fallback})` : ""}: `);
|
|
1954
|
+
return ans.trim() || fallback;
|
|
1955
|
+
}
|
|
1956
|
+
async function promptConfirm(rl, label) {
|
|
1957
|
+
const ans = await rl.question(`${label} [y/N]: `);
|
|
1958
|
+
return ans.trim().toLowerCase() === "y";
|
|
1959
|
+
}
|
|
1960
|
+
async function pickProject(rl, vault) {
|
|
1961
|
+
const rows = await vault.listProjects();
|
|
1962
|
+
if (!rows.length) {
|
|
1963
|
+
output.write("No projects\n");
|
|
1964
|
+
return null;
|
|
1965
|
+
}
|
|
1966
|
+
const idx = await promptMenu(rl, "Select project", rows.map((r) => `${r.name} (${r.secret_count})`));
|
|
1967
|
+
if (idx < 0) return null;
|
|
1968
|
+
return rows[idx].name;
|
|
1969
|
+
}
|
|
1970
|
+
async function showProjects(vault) {
|
|
1971
|
+
const rows = await vault.listProjects();
|
|
1972
|
+
if (!rows.length) {
|
|
1973
|
+
output.write("No projects\n");
|
|
1974
|
+
return;
|
|
1975
|
+
}
|
|
1976
|
+
rows.forEach((r) => {
|
|
1977
|
+
output.write(`${r.name} ${r.secret_count} ${r.folder_path ?? ""}
|
|
1978
|
+
`);
|
|
1979
|
+
});
|
|
1980
|
+
}
|
|
1981
|
+
async function showSecrets(vault, project, showValues) {
|
|
1982
|
+
const rows = await vault.listSecrets(project);
|
|
1983
|
+
if (!rows.length) {
|
|
1984
|
+
output.write("No secrets\n");
|
|
1985
|
+
return;
|
|
1986
|
+
}
|
|
1987
|
+
for (const r of rows) {
|
|
1988
|
+
if (showValues) {
|
|
1989
|
+
const v = await vault.getSecretValue(project, r.key);
|
|
1990
|
+
output.write(`${r.key} ${v} ${r.description ?? ""}
|
|
1991
|
+
`);
|
|
1992
|
+
} else {
|
|
1993
|
+
output.write(`${r.key} ${r.description ?? ""}
|
|
1994
|
+
`);
|
|
1995
|
+
}
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
async function runTui(services) {
|
|
1999
|
+
const rl = readline.createInterface({ input, output });
|
|
2000
|
+
const { vault } = services;
|
|
2001
|
+
try {
|
|
2002
|
+
while (true) {
|
|
2003
|
+
const main = await promptMenu(rl, "Zocket TUI", [
|
|
2004
|
+
"Projects",
|
|
2005
|
+
"Secrets",
|
|
2006
|
+
"Exit"
|
|
2007
|
+
]);
|
|
2008
|
+
if (main === 2) break;
|
|
2009
|
+
if (main === 0) {
|
|
2010
|
+
const idx = await promptMenu(rl, "Projects", [
|
|
2011
|
+
"List",
|
|
2012
|
+
"Create",
|
|
2013
|
+
"Delete",
|
|
2014
|
+
"Set folder path",
|
|
2015
|
+
"Set allowed domains",
|
|
2016
|
+
"Back"
|
|
2017
|
+
]);
|
|
2018
|
+
if (idx === 5) continue;
|
|
2019
|
+
if (idx === 0) await showProjects(vault);
|
|
2020
|
+
if (idx === 1) {
|
|
2021
|
+
const name = await promptInput(rl, "Project name");
|
|
2022
|
+
const desc = await promptInput(rl, "Description", "");
|
|
2023
|
+
const folder = await promptInput(rl, "Folder path", "");
|
|
2024
|
+
await vault.createProject(name, desc);
|
|
2025
|
+
if (folder) await vault.setFolder(name, folder);
|
|
2026
|
+
output.write("Project created\n");
|
|
2027
|
+
}
|
|
2028
|
+
if (idx === 2) {
|
|
2029
|
+
const name = await pickProject(rl, vault);
|
|
2030
|
+
if (!name) continue;
|
|
2031
|
+
if (await promptConfirm(rl, `Delete ${name}?`)) {
|
|
2032
|
+
await vault.deleteProject(name);
|
|
2033
|
+
output.write("Project deleted\n");
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
2036
|
+
if (idx === 3) {
|
|
2037
|
+
const name = await pickProject(rl, vault);
|
|
2038
|
+
if (!name) continue;
|
|
2039
|
+
const path = await promptInput(rl, "Folder path (empty to clear)", "");
|
|
2040
|
+
await vault.setFolder(name, path || void 0);
|
|
2041
|
+
output.write("Folder updated\n");
|
|
2042
|
+
}
|
|
2043
|
+
if (idx === 4) {
|
|
2044
|
+
const name = await pickProject(rl, vault);
|
|
2045
|
+
if (!name) continue;
|
|
2046
|
+
const domains = await promptInput(rl, "Allowed domains (comma-separated, empty to clear)", "");
|
|
2047
|
+
const value = domains ? domains.split(",").map((s) => s.trim()).filter(Boolean) : null;
|
|
2048
|
+
await vault.setAllowedDomains(name, value);
|
|
2049
|
+
output.write("Allowed domains updated\n");
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
if (main === 1) {
|
|
2053
|
+
const project = await pickProject(rl, vault);
|
|
2054
|
+
if (!project) continue;
|
|
2055
|
+
const idx = await promptMenu(rl, `Secrets for ${project}`, [
|
|
2056
|
+
"List (no values)",
|
|
2057
|
+
"List (with values)",
|
|
2058
|
+
"Add or update",
|
|
2059
|
+
"Get value",
|
|
2060
|
+
"Delete",
|
|
2061
|
+
"Back"
|
|
2062
|
+
]);
|
|
2063
|
+
if (idx === 5) continue;
|
|
2064
|
+
if (idx === 0) await showSecrets(vault, project, false);
|
|
2065
|
+
if (idx === 1) await showSecrets(vault, project, true);
|
|
2066
|
+
if (idx === 2) {
|
|
2067
|
+
const key = await promptInput(rl, "Key (UPPERCASE)", "");
|
|
2068
|
+
const value = await promptInput(rl, "Value", "");
|
|
2069
|
+
const desc = await promptInput(rl, "Description", "");
|
|
2070
|
+
await vault.setSecret(project, key, value, desc);
|
|
2071
|
+
output.write("Secret saved\n");
|
|
2072
|
+
}
|
|
2073
|
+
if (idx === 3) {
|
|
2074
|
+
const key = await promptInput(rl, "Key", "");
|
|
2075
|
+
const value = await vault.getSecretValue(project, key);
|
|
2076
|
+
output.write(`${value}
|
|
2077
|
+
`);
|
|
2078
|
+
}
|
|
2079
|
+
if (idx === 4) {
|
|
2080
|
+
const key = await promptInput(rl, "Key", "");
|
|
2081
|
+
if (await promptConfirm(rl, `Delete ${key}?`)) {
|
|
2082
|
+
await vault.deleteSecret(project, key);
|
|
2083
|
+
output.write("Secret deleted\n");
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
} finally {
|
|
2089
|
+
rl.close();
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
|
|
2093
|
+
// src/cli.ts
|
|
2094
|
+
function createServices() {
|
|
1937
2095
|
const home = zocketHome();
|
|
1938
|
-
|
|
2096
|
+
mkdirSync5(home, { recursive: true });
|
|
1939
2097
|
const key = loadOrCreateKey(keyPath());
|
|
1940
2098
|
const vault = new VaultService(vaultPath(), lockPath(), key);
|
|
2099
|
+
vault.ensureExists();
|
|
1941
2100
|
const config = new ConfigStore(configPath());
|
|
1942
2101
|
const audit = new AuditLogger(auditPath());
|
|
2102
|
+
config.ensureExists();
|
|
2103
|
+
return { vault, config, audit };
|
|
2104
|
+
}
|
|
2105
|
+
async function cmdStart(opts) {
|
|
2106
|
+
const { vault, config, audit } = createServices();
|
|
1943
2107
|
const cfg = config.ensureExists();
|
|
1944
2108
|
const mode = opts.mode === "admin" ? "admin" : "metadata";
|
|
1945
2109
|
const services = { vault, config, audit, mode };
|
|
@@ -1976,19 +2140,169 @@ async function cmdStart(opts) {
|
|
|
1976
2140
|
});
|
|
1977
2141
|
mcpHttp.listen(opts.mcpPort, opts.host, () => {
|
|
1978
2142
|
console.log(`[zocket] mcp http://${opts.host}:${opts.mcpPort}/sse (mode: ${mode}, loading: ${cfg.mcp_loading})`);
|
|
2143
|
+
});
|
|
2144
|
+
const streamableSessions = /* @__PURE__ */ new Map();
|
|
2145
|
+
const streamableHttp = createServer(async (req, res) => {
|
|
2146
|
+
const url = new URL(req.url ?? "/", `http://${req.headers.host}`);
|
|
2147
|
+
if (url.pathname !== "/mcp") {
|
|
2148
|
+
res.writeHead(404).end("Not found");
|
|
2149
|
+
return;
|
|
2150
|
+
}
|
|
2151
|
+
const method = (req.method ?? "GET").toUpperCase();
|
|
2152
|
+
let parsedBody = void 0;
|
|
2153
|
+
if (method === "POST") {
|
|
2154
|
+
let raw = "";
|
|
2155
|
+
for await (const chunk of req) {
|
|
2156
|
+
raw += chunk;
|
|
2157
|
+
}
|
|
2158
|
+
if (raw.trim().length > 0) {
|
|
2159
|
+
try {
|
|
2160
|
+
parsedBody = JSON.parse(raw);
|
|
2161
|
+
} catch {
|
|
2162
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
2163
|
+
res.end(JSON.stringify({
|
|
2164
|
+
jsonrpc: "2.0",
|
|
2165
|
+
error: { code: -32700, message: "Parse error" },
|
|
2166
|
+
id: null
|
|
2167
|
+
}));
|
|
2168
|
+
return;
|
|
2169
|
+
}
|
|
2170
|
+
}
|
|
2171
|
+
}
|
|
2172
|
+
const header = req.headers["mcp-session-id"];
|
|
2173
|
+
const sessionId = Array.isArray(header) ? header[0] : header;
|
|
2174
|
+
let transport;
|
|
2175
|
+
if (sessionId && streamableSessions.has(sessionId)) {
|
|
2176
|
+
transport = streamableSessions.get(sessionId);
|
|
2177
|
+
} else if (!sessionId && method === "POST" && parsedBody && isInitializeRequest(parsedBody)) {
|
|
2178
|
+
transport = new StreamableHTTPServerTransport({
|
|
2179
|
+
sessionIdGenerator: () => randomUUID(),
|
|
2180
|
+
onsessioninitialized: (sid) => {
|
|
2181
|
+
if (transport) streamableSessions.set(sid, transport);
|
|
2182
|
+
}
|
|
2183
|
+
});
|
|
2184
|
+
transport.onclose = () => {
|
|
2185
|
+
const sid = transport?.sessionId;
|
|
2186
|
+
if (sid && streamableSessions.has(sid)) streamableSessions.delete(sid);
|
|
2187
|
+
};
|
|
2188
|
+
const mcpServer = createMcpServer(services, { loading: cfg.mcp_loading });
|
|
2189
|
+
mcpServer.connect(transport).catch((e) => {
|
|
2190
|
+
console.error("[zocket] MCP streamable connect error:", e);
|
|
2191
|
+
});
|
|
2192
|
+
} else {
|
|
2193
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
2194
|
+
res.end(JSON.stringify({
|
|
2195
|
+
jsonrpc: "2.0",
|
|
2196
|
+
error: { code: -32e3, message: "Bad Request: No valid session ID provided" },
|
|
2197
|
+
id: null
|
|
2198
|
+
}));
|
|
2199
|
+
return;
|
|
2200
|
+
}
|
|
2201
|
+
try {
|
|
2202
|
+
await transport.handleRequest(req, res, parsedBody);
|
|
2203
|
+
} catch (e) {
|
|
2204
|
+
console.error("[zocket] MCP streamable request error:", e);
|
|
2205
|
+
if (!res.headersSent) {
|
|
2206
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
2207
|
+
res.end(JSON.stringify({
|
|
2208
|
+
jsonrpc: "2.0",
|
|
2209
|
+
error: { code: -32603, message: "Internal server error" },
|
|
2210
|
+
id: null
|
|
2211
|
+
}));
|
|
2212
|
+
}
|
|
2213
|
+
}
|
|
2214
|
+
});
|
|
2215
|
+
streamableHttp.listen(opts.mcpStreamPort, opts.host, () => {
|
|
2216
|
+
console.log(`[zocket] mcp http://${opts.host}:${opts.mcpStreamPort}/mcp (streamable-http)`);
|
|
1979
2217
|
console.log(`[zocket] ready \u2014 vault: ${vaultPath()}`);
|
|
1980
2218
|
});
|
|
1981
2219
|
}
|
|
1982
2220
|
function buildCli() {
|
|
1983
2221
|
const program = new Command("zocket").description("Local encrypted vault + MCP server for AI agent workflows").version("1.0.0");
|
|
1984
|
-
program.command("
|
|
2222
|
+
program.command("init").description("Initialize vault and config").action(async () => {
|
|
2223
|
+
createServices();
|
|
2224
|
+
console.log(`[zocket] initialized \u2014 vault: ${vaultPath()}`);
|
|
2225
|
+
});
|
|
2226
|
+
program.command("start").description("Start web panel + MCP SSE + MCP Streamable HTTP servers").option("--host <host>", "Bind host", "127.0.0.1").option("--web-port <port>", "Web panel port", "18001").option("--mcp-port <port>", "MCP SSE port", "18002").option("--mcp-stream-port <port>", "MCP Streamable HTTP port", "18003").option("--mode <mode>", "MCP mode (metadata|admin)", "admin").action(async (opts) => {
|
|
1985
2227
|
await cmdStart({
|
|
1986
2228
|
host: opts.host,
|
|
1987
2229
|
webPort: parseInt(opts.webPort, 10),
|
|
1988
2230
|
mcpPort: parseInt(opts.mcpPort, 10),
|
|
2231
|
+
mcpStreamPort: parseInt(opts.mcpStreamPort, 10),
|
|
1989
2232
|
mode: opts.mode
|
|
1990
2233
|
});
|
|
1991
2234
|
});
|
|
2235
|
+
const projects = program.command("projects").description("Manage projects");
|
|
2236
|
+
projects.command("list").action(async () => {
|
|
2237
|
+
const { vault } = createServices();
|
|
2238
|
+
const rows = await vault.listProjects();
|
|
2239
|
+
if (!rows.length) {
|
|
2240
|
+
console.log("No projects");
|
|
2241
|
+
return;
|
|
2242
|
+
}
|
|
2243
|
+
for (const r of rows) {
|
|
2244
|
+
console.log(`${r.name} ${r.secret_count} ${r.folder_path ?? ""}`);
|
|
2245
|
+
}
|
|
2246
|
+
});
|
|
2247
|
+
projects.command("create <name>").option("--description <text>", "Description", "").option("--folder <path>", "Folder path", "").action(async (name, opts) => {
|
|
2248
|
+
const { vault } = createServices();
|
|
2249
|
+
await vault.createProject(name, opts.description ?? "");
|
|
2250
|
+
if (opts.folder) await vault.setFolder(name, opts.folder);
|
|
2251
|
+
console.log("Project created:", name);
|
|
2252
|
+
});
|
|
2253
|
+
projects.command("delete <name>").action(async (name) => {
|
|
2254
|
+
const { vault } = createServices();
|
|
2255
|
+
await vault.deleteProject(name);
|
|
2256
|
+
console.log("Project deleted:", name);
|
|
2257
|
+
});
|
|
2258
|
+
projects.command("set-folder <name> [path]").description('Set or clear folder path (use "-" to clear)').action(async (name, path) => {
|
|
2259
|
+
const { vault } = createServices();
|
|
2260
|
+
const value = path && path !== "-" ? path : void 0;
|
|
2261
|
+
await vault.setFolder(name, value);
|
|
2262
|
+
console.log("Folder updated:", name);
|
|
2263
|
+
});
|
|
2264
|
+
projects.command("set-domains <name> [domains]").description('Set or clear allowed domains (comma-separated, use "-" to clear)').action(async (name, domains) => {
|
|
2265
|
+
const { vault } = createServices();
|
|
2266
|
+
const value = domains && domains !== "-" ? domains.split(",").map((s) => s.trim()).filter(Boolean) : null;
|
|
2267
|
+
await vault.setAllowedDomains(name, value);
|
|
2268
|
+
console.log("Domains updated:", name);
|
|
2269
|
+
});
|
|
2270
|
+
const secrets = program.command("secrets").description("Manage secrets");
|
|
2271
|
+
secrets.command("list <project>").option("--show-values", "Include secret values", false).action(async (project, opts) => {
|
|
2272
|
+
const { vault } = createServices();
|
|
2273
|
+
const rows = await vault.listSecrets(project);
|
|
2274
|
+
if (!rows.length) {
|
|
2275
|
+
console.log("No secrets");
|
|
2276
|
+
return;
|
|
2277
|
+
}
|
|
2278
|
+
for (const r of rows) {
|
|
2279
|
+
if (opts.showValues) {
|
|
2280
|
+
const v = await vault.getSecretValue(project, r.key);
|
|
2281
|
+
console.log(`${r.key} ${v} ${r.description ?? ""}`);
|
|
2282
|
+
} else {
|
|
2283
|
+
console.log(`${r.key} ${r.description ?? ""}`);
|
|
2284
|
+
}
|
|
2285
|
+
}
|
|
2286
|
+
});
|
|
2287
|
+
secrets.command("get <project> <key>").action(async (project, key) => {
|
|
2288
|
+
const { vault } = createServices();
|
|
2289
|
+
const v = await vault.getSecretValue(project, key);
|
|
2290
|
+
console.log(v);
|
|
2291
|
+
});
|
|
2292
|
+
secrets.command("set <project> <key> <value>").option("--description <text>", "Description", "").action(async (project, key, value, opts) => {
|
|
2293
|
+
const { vault } = createServices();
|
|
2294
|
+
await vault.setSecret(project, key, value, opts.description ?? "");
|
|
2295
|
+
console.log("Secret saved:", key);
|
|
2296
|
+
});
|
|
2297
|
+
secrets.command("delete <project> <key>").action(async (project, key) => {
|
|
2298
|
+
const { vault } = createServices();
|
|
2299
|
+
await vault.deleteSecret(project, key);
|
|
2300
|
+
console.log("Secret deleted:", key);
|
|
2301
|
+
});
|
|
2302
|
+
program.command("tui").description("Interactive terminal UI for full management").action(async () => {
|
|
2303
|
+
const { vault, config, audit } = createServices();
|
|
2304
|
+
await runTui({ vault, config, audit });
|
|
2305
|
+
});
|
|
1992
2306
|
return program;
|
|
1993
2307
|
}
|
|
1994
2308
|
|
package/docs/AI_AUTODEPLOY.md
CHANGED
|
@@ -6,22 +6,22 @@ This is the file you can send to an AI coding agent so it can deploy zocket end-
|
|
|
6
6
|
|
|
7
7
|
If this repo is already present locally:
|
|
8
8
|
```bash
|
|
9
|
-
|
|
9
|
+
bash scripts/install-zocket.sh --source local
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-
If only the file is available, the script will
|
|
12
|
+
If only the file is available, the script will fetch and run the platform installer:
|
|
13
|
+
```bash
|
|
14
|
+
curl -fsSL https://raw.githubusercontent.com/aozorin/zocket/main/scripts/install-zocket.sh | bash
|
|
15
|
+
```
|
|
13
16
|
|
|
14
17
|
## Agent task prompt (copy/paste)
|
|
15
18
|
|
|
16
19
|
```text
|
|
17
20
|
Deploy zocket on this machine with secure defaults:
|
|
18
21
|
1) Detect OS and shell.
|
|
19
|
-
2) Run
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
--mcp-port 18002
|
|
23
|
-
--mcp-mode metadata
|
|
24
|
-
--autostart user (Linux/macOS) or enabled startup task (Windows)
|
|
22
|
+
2) Run installer:
|
|
23
|
+
- Linux/macOS: curl -fsSL https://raw.githubusercontent.com/aozorin/zocket/main/scripts/install-zocket.sh | bash
|
|
24
|
+
- Windows: irm https://raw.githubusercontent.com/aozorin/zocket/main/scripts/install-zocket.ps1 | iex
|
|
25
25
|
3) Verify:
|
|
26
26
|
- web panel on http://127.0.0.1:18001
|
|
27
27
|
- MCP SSE endpoint on http://127.0.0.1:18002/sse (Claude Code)
|
|
@@ -33,11 +33,7 @@ Deploy zocket on this machine with secure defaults:
|
|
|
33
33
|
## Optional production profile (Linux system services)
|
|
34
34
|
|
|
35
35
|
```bash
|
|
36
|
-
|
|
37
|
-
--repo-url https://github.com/your-org/zocket.git \
|
|
38
|
-
--repo-ref main \
|
|
39
|
-
--autostart system \
|
|
40
|
-
--zocket-home /var/lib/zocket
|
|
36
|
+
bash scripts/install-zocket.sh --source git --repo-url https://github.com/aozorin/zocket.git --repo-ref main --autostart system --zocket-home /var/lib/zocket
|
|
41
37
|
```
|
|
42
38
|
|
|
43
39
|
## Post-deploy checklist for agent
|
package/docs/GIT_NPM_RELEASE.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Git + npm
|
|
1
|
+
# Git + npm Release Guide
|
|
2
2
|
|
|
3
3
|
## 1) Initialize git project
|
|
4
4
|
|
|
@@ -17,15 +17,7 @@ git push -u origin main
|
|
|
17
17
|
bash scripts/release-check.sh
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
## 3) Publish
|
|
21
|
-
|
|
22
|
-
```bash
|
|
23
|
-
python3 -m pip install --upgrade build twine
|
|
24
|
-
python3 -m build
|
|
25
|
-
twine upload dist/*
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
## 4) Publish npm package
|
|
20
|
+
## 3) Publish npm package
|
|
29
21
|
|
|
30
22
|
Update metadata in `package.json`:
|
|
31
23
|
- `name`
|
|
@@ -39,15 +31,14 @@ npm login
|
|
|
39
31
|
npm publish --access public
|
|
40
32
|
```
|
|
41
33
|
|
|
42
|
-
##
|
|
34
|
+
## 4) Tag release
|
|
43
35
|
|
|
44
36
|
```bash
|
|
45
37
|
git tag -a v1.0.0 -m "zocket v1.0.0"
|
|
46
38
|
git push origin v1.0.0
|
|
47
39
|
```
|
|
48
40
|
|
|
49
|
-
##
|
|
41
|
+
## 5) Optional GitHub Release artifacts
|
|
50
42
|
|
|
51
43
|
Upload:
|
|
52
|
-
- Python wheels/sdist from `dist/`
|
|
53
44
|
- npm package tarball from `npm pack`
|
package/docs/INSTALL.md
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
# Install Guide (Windows / Linux / macOS)
|
|
2
2
|
|
|
3
|
-
This guide installs **zocket** as:
|
|
3
|
+
This guide installs **zocket** (Node.js) as:
|
|
4
4
|
- local web panel on `127.0.0.1:18001`
|
|
5
5
|
- MCP SSE server on `127.0.0.1:18002/sse` (Claude Code)
|
|
6
6
|
- MCP streamable HTTP server on `127.0.0.1:18003/mcp` (Codex)
|
|
7
|
-
- optional MCP stdio server for local CLI use
|
|
8
7
|
|
|
9
8
|
## 1) Quick Install (recommended)
|
|
10
9
|
|
|
11
10
|
### Linux and macOS
|
|
12
11
|
```bash
|
|
13
|
-
curl -fsSL https://raw.githubusercontent.com/
|
|
12
|
+
curl -fsSL https://raw.githubusercontent.com/aozorin/zocket/main/scripts/install-zocket.sh | bash
|
|
14
13
|
```
|
|
15
14
|
|
|
16
15
|
If you run from a local clone:
|
|
@@ -20,7 +19,7 @@ bash scripts/install-zocket.sh --source local
|
|
|
20
19
|
|
|
21
20
|
### Windows (PowerShell)
|
|
22
21
|
```powershell
|
|
23
|
-
irm https://raw.githubusercontent.com/
|
|
22
|
+
irm https://raw.githubusercontent.com/aozorin/zocket/main/scripts/install-zocket.ps1 | iex
|
|
24
23
|
```
|
|
25
24
|
|
|
26
25
|
If you run from a local clone:
|
|
@@ -32,16 +31,15 @@ powershell -ExecutionPolicy Bypass -File .\scripts\install-zocket.ps1 -Source Lo
|
|
|
32
31
|
|
|
33
32
|
### Debian/Ubuntu and Debian-based
|
|
34
33
|
Installer auto-installs:
|
|
35
|
-
- `
|
|
36
|
-
- `
|
|
37
|
-
- `python3-pip`
|
|
34
|
+
- `nodejs`
|
|
35
|
+
- `npm`
|
|
38
36
|
- `git`
|
|
39
37
|
- `curl`
|
|
40
38
|
|
|
41
39
|
Equivalent manual install:
|
|
42
40
|
```bash
|
|
43
41
|
sudo apt-get update
|
|
44
|
-
sudo apt-get install -y
|
|
42
|
+
sudo apt-get install -y nodejs npm git curl
|
|
45
43
|
```
|
|
46
44
|
|
|
47
45
|
### Other Linux distros
|
|
@@ -52,22 +50,21 @@ Installer supports:
|
|
|
52
50
|
- `apk` (Alpine)
|
|
53
51
|
|
|
54
52
|
If your distro is unsupported, install manually:
|
|
55
|
-
-
|
|
56
|
-
- `
|
|
57
|
-
- `venv`
|
|
53
|
+
- Node.js `>=18`
|
|
54
|
+
- `npm`
|
|
58
55
|
- `git`
|
|
59
56
|
|
|
60
57
|
## 3) macOS details
|
|
61
58
|
|
|
62
59
|
Installer uses Homebrew when dependencies are missing:
|
|
63
60
|
```bash
|
|
64
|
-
brew install
|
|
61
|
+
brew install node git curl
|
|
65
62
|
```
|
|
66
63
|
|
|
67
64
|
## 4) Windows details
|
|
68
65
|
|
|
69
66
|
Requirements:
|
|
70
|
-
-
|
|
67
|
+
- Node.js 18+ (recommended from `nodejs.org` or `winget`)
|
|
71
68
|
- Git for Windows
|
|
72
69
|
|
|
73
70
|
Autostart (enabled by default):
|
|
@@ -75,10 +72,8 @@ Autostart (enabled by default):
|
|
|
75
72
|
powershell -ExecutionPolicy Bypass -File .\scripts\install-zocket.ps1
|
|
76
73
|
```
|
|
77
74
|
|
|
78
|
-
This creates scheduled
|
|
79
|
-
- `
|
|
80
|
-
- `ZocketMcpSse`
|
|
81
|
-
- `ZocketMcpStreamable`
|
|
75
|
+
This creates scheduled task:
|
|
76
|
+
- `Zocket`
|
|
82
77
|
|
|
83
78
|
Disable autostart:
|
|
84
79
|
```powershell
|
|
@@ -87,128 +82,53 @@ powershell -ExecutionPolicy Bypass -File .\scripts\install-zocket.ps1 -EnableAut
|
|
|
87
82
|
|
|
88
83
|
## 5) NPM package usage
|
|
89
84
|
|
|
90
|
-
This repo now includes an npm wrapper package.
|
|
91
|
-
|
|
92
85
|
Global install from npm:
|
|
93
86
|
```bash
|
|
94
|
-
npm i -g @zocket
|
|
95
|
-
zocket setup
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
Or install from your git repo (example):
|
|
99
|
-
```bash
|
|
100
|
-
npm i -g github:your-org/zocket
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
First-run setup:
|
|
104
|
-
```bash
|
|
105
|
-
zocket setup
|
|
87
|
+
npm i -g @ao_zorin/zocket
|
|
106
88
|
```
|
|
107
89
|
|
|
108
90
|
Then use normal CLI:
|
|
109
91
|
```bash
|
|
110
92
|
zocket init
|
|
111
|
-
zocket
|
|
112
|
-
zocket mcp --transport sse --mode metadata --host 127.0.0.1 --port 18002
|
|
113
|
-
zocket mcp --transport streamable-http --mode metadata --host 127.0.0.1 --port 18003
|
|
93
|
+
zocket start --host 127.0.0.1 --web-port 18001 --mcp-port 18002 --mcp-stream-port 18003 --mode admin
|
|
114
94
|
```
|
|
115
95
|
|
|
116
96
|
## 6) Systemd hardening on Linux (production)
|
|
117
97
|
|
|
118
98
|
If you install with `--autostart system`, the installer creates and enables:
|
|
119
|
-
- `zocket
|
|
120
|
-
- `zocket-mcp-sse.service` (Claude Code, 18002)
|
|
121
|
-
- `zocket-mcp-http.service` (Codex, 18003)
|
|
99
|
+
- `zocket.service` (web + SSE + streamable HTTP)
|
|
122
100
|
|
|
123
101
|
Check:
|
|
124
102
|
```bash
|
|
125
|
-
systemctl status zocket
|
|
126
|
-
systemctl status zocket-mcp-sse.service --no-pager
|
|
127
|
-
systemctl status zocket-mcp-http.service --no-pager
|
|
103
|
+
systemctl status zocket.service --no-pager
|
|
128
104
|
```
|
|
129
105
|
|
|
130
106
|
### Linux user-level autostart (no root)
|
|
131
107
|
```bash
|
|
132
|
-
systemctl --user enable --now zocket
|
|
133
|
-
systemctl --user
|
|
134
|
-
systemctl --user enable --now zocket-mcp-http.service
|
|
135
|
-
systemctl --user status zocket-web.service --no-pager
|
|
136
|
-
systemctl --user status zocket-mcp-sse.service --no-pager
|
|
137
|
-
systemctl --user status zocket-mcp-http.service --no-pager
|
|
108
|
+
systemctl --user enable --now zocket.service
|
|
109
|
+
systemctl --user status zocket.service --no-pager
|
|
138
110
|
```
|
|
139
111
|
|
|
140
112
|
### macOS launchd autostart (installed by script)
|
|
141
113
|
Installer creates and loads:
|
|
142
|
-
- `~/Library/LaunchAgents/dev.zocket.
|
|
143
|
-
- `~/Library/LaunchAgents/dev.zocket.mcp-sse.plist`
|
|
144
|
-
- `~/Library/LaunchAgents/dev.zocket.mcp-streamable.plist`
|
|
145
|
-
|
|
146
|
-
If you need to install manually, use:
|
|
147
|
-
|
|
148
|
-
```xml
|
|
149
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
150
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
151
|
-
<plist version="1.0">
|
|
152
|
-
<dict>
|
|
153
|
-
<key>Label</key><string>dev.zocket.web</string>
|
|
154
|
-
<key>ProgramArguments</key>
|
|
155
|
-
<array>
|
|
156
|
-
<string>/Users/YOUR_USER/.local/share/zocket/venv/bin/python3</string>
|
|
157
|
-
<string>-m</string><string>zocket</string>
|
|
158
|
-
<string>web</string><string>--host</string><string>127.0.0.1</string>
|
|
159
|
-
<string>--port</string><string>18001</string>
|
|
160
|
-
</array>
|
|
161
|
-
<key>EnvironmentVariables</key>
|
|
162
|
-
<dict>
|
|
163
|
-
<key>ZOCKET_HOME</key><string>/Users/YOUR_USER/.zocket</string>
|
|
164
|
-
</dict>
|
|
165
|
-
<key>RunAtLoad</key><true/>
|
|
166
|
-
<key>KeepAlive</key><true/>
|
|
167
|
-
</dict>
|
|
168
|
-
</plist>
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
SSE MCP (`dev.zocket.mcp-sse.plist`):
|
|
172
|
-
```xml
|
|
173
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
174
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
175
|
-
<plist version="1.0">
|
|
176
|
-
<dict>
|
|
177
|
-
<key>Label</key><string>dev.zocket.mcp-sse</string>
|
|
178
|
-
<key>ProgramArguments</key>
|
|
179
|
-
<array>
|
|
180
|
-
<string>/Users/YOUR_USER/.local/share/zocket/venv/bin/python3</string>
|
|
181
|
-
<string>-m</string><string>zocket</string>
|
|
182
|
-
<string>mcp</string><string>--transport</string><string>sse</string>
|
|
183
|
-
<string>--mode</string><string>metadata</string>
|
|
184
|
-
<string>--host</string><string>127.0.0.1</string>
|
|
185
|
-
<string>--port</string><string>18002</string>
|
|
186
|
-
</array>
|
|
187
|
-
<key>EnvironmentVariables</key>
|
|
188
|
-
<dict>
|
|
189
|
-
<key>ZOCKET_HOME</key><string>/Users/YOUR_USER/.zocket</string>
|
|
190
|
-
</dict>
|
|
191
|
-
<key>RunAtLoad</key><true/>
|
|
192
|
-
<key>KeepAlive</key><true/>
|
|
193
|
-
</dict>
|
|
194
|
-
</plist>
|
|
195
|
-
```
|
|
114
|
+
- `~/Library/LaunchAgents/dev.zocket.plist`
|
|
196
115
|
|
|
197
|
-
|
|
116
|
+
Manual example:
|
|
198
117
|
```xml
|
|
199
118
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
200
119
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
201
120
|
<plist version="1.0">
|
|
202
121
|
<dict>
|
|
203
|
-
<key>Label</key><string>dev.zocket
|
|
122
|
+
<key>Label</key><string>dev.zocket</string>
|
|
204
123
|
<key>ProgramArguments</key>
|
|
205
124
|
<array>
|
|
206
|
-
<string>/
|
|
207
|
-
<string
|
|
208
|
-
<string>mcp</string><string>--transport</string><string>streamable-http</string>
|
|
209
|
-
<string>--mode</string><string>metadata</string>
|
|
125
|
+
<string>/usr/local/bin/zocket</string>
|
|
126
|
+
<string>start</string>
|
|
210
127
|
<string>--host</string><string>127.0.0.1</string>
|
|
211
|
-
<string>--port</string><string>
|
|
128
|
+
<string>--web-port</string><string>18001</string>
|
|
129
|
+
<string>--mcp-port</string><string>18002</string>
|
|
130
|
+
<string>--mcp-stream-port</string><string>18003</string>
|
|
131
|
+
<string>--mode</string><string>admin</string>
|
|
212
132
|
</array>
|
|
213
133
|
<key>EnvironmentVariables</key>
|
|
214
134
|
<dict>
|
|
@@ -220,11 +140,9 @@ Streamable HTTP MCP (`dev.zocket.mcp-streamable.plist`):
|
|
|
220
140
|
</plist>
|
|
221
141
|
```
|
|
222
142
|
|
|
223
|
-
Load
|
|
143
|
+
Load service:
|
|
224
144
|
```bash
|
|
225
|
-
launchctl load ~/Library/LaunchAgents/dev.zocket.
|
|
226
|
-
launchctl load ~/Library/LaunchAgents/dev.zocket.mcp-sse.plist
|
|
227
|
-
launchctl load ~/Library/LaunchAgents/dev.zocket.mcp-streamable.plist
|
|
145
|
+
launchctl load ~/Library/LaunchAgents/dev.zocket.plist
|
|
228
146
|
```
|
|
229
147
|
|
|
230
148
|
### Windows autostart (Task Scheduler)
|
|
@@ -234,13 +152,9 @@ powershell -ExecutionPolicy Bypass -File .\scripts\install-zocket.ps1 -EnableAut
|
|
|
234
152
|
```
|
|
235
153
|
|
|
236
154
|
Or create manually:
|
|
237
|
-
- task `
|
|
238
|
-
-
|
|
239
|
-
-
|
|
240
|
-
- actions:
|
|
241
|
-
- `python -m zocket web --host 127.0.0.1 --port 18001`
|
|
242
|
-
- `python -m zocket mcp --transport sse --mode metadata --host 127.0.0.1 --port 18002`
|
|
243
|
-
- `python -m zocket mcp --transport streamable-http --mode metadata --host 127.0.0.1 --port 18003`
|
|
155
|
+
- task `Zocket` on logon
|
|
156
|
+
- action:
|
|
157
|
+
- `zocket start --host 127.0.0.1 --web-port 18001 --mcp-port 18002 --mcp-stream-port 18003 --mode admin`
|
|
244
158
|
|
|
245
159
|
## 7) First web open
|
|
246
160
|
|
|
@@ -255,5 +169,5 @@ Open `http://127.0.0.1:18001` and choose one:
|
|
|
255
169
|
curl -I http://127.0.0.1:18001/login
|
|
256
170
|
curl -I http://127.0.0.1:18002/sse
|
|
257
171
|
curl -I http://127.0.0.1:18003/mcp
|
|
258
|
-
zocket mcp --
|
|
172
|
+
zocket start --host 127.0.0.1 --web-port 18001 --mcp-port 18002 --mcp-stream-port 18003 --mode admin
|
|
259
173
|
```
|
package/package.json
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
Param(
|
|
2
|
-
[ValidateSet("Auto", "Local", "Git", "
|
|
2
|
+
[ValidateSet("Auto", "Local", "Git", "Npm")]
|
|
3
3
|
[string]$Source = "Auto",
|
|
4
|
-
[string]$RepoUrl = "https://github.com/
|
|
4
|
+
[string]$RepoUrl = "https://github.com/aozorin/zocket.git",
|
|
5
5
|
[string]$RepoRef = "main",
|
|
6
|
-
[string]$InstallRoot = "$env:LOCALAPPDATA\zocket",
|
|
7
6
|
[string]$ZocketHome = "$env:USERPROFILE\.zocket",
|
|
8
7
|
[ValidateSet("en", "ru")]
|
|
9
8
|
[string]$Lang = "en",
|
|
@@ -11,105 +10,64 @@ Param(
|
|
|
11
10
|
[int]$McpPort = 18002,
|
|
12
11
|
[int]$McpStreamPort = 18003,
|
|
13
12
|
[ValidateSet("metadata", "admin")]
|
|
14
|
-
[string]$McpMode = "
|
|
13
|
+
[string]$McpMode = "admin",
|
|
15
14
|
[bool]$EnableAutostart = $true
|
|
16
15
|
)
|
|
17
16
|
|
|
18
17
|
$ErrorActionPreference = "Stop"
|
|
19
18
|
|
|
20
|
-
function
|
|
21
|
-
if (
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
if (Get-Command python -ErrorAction SilentlyContinue) {
|
|
25
|
-
return @{Cmd = "python"; Prefix = @()}
|
|
19
|
+
function Ensure-Dir([string]$Path) {
|
|
20
|
+
if (-not (Test-Path -LiteralPath $Path)) {
|
|
21
|
+
New-Item -ItemType Directory -Path $Path | Out-Null
|
|
26
22
|
}
|
|
27
|
-
throw "Python 3.10+ not found. Install Python and rerun."
|
|
28
23
|
}
|
|
29
24
|
|
|
30
|
-
function
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
throw "Command failed: $Cmd $($Args -join ' ')"
|
|
25
|
+
function Ensure-Node {
|
|
26
|
+
if (-not (Get-Command node -ErrorAction SilentlyContinue)) {
|
|
27
|
+
throw "Node.js not found. Install Node.js 18+ and rerun."
|
|
34
28
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
function Ensure-Dir([string]$Path) {
|
|
38
|
-
if (-not (Test-Path -LiteralPath $Path)) {
|
|
39
|
-
New-Item -ItemType Directory -Path $Path | Out-Null
|
|
29
|
+
if (-not (Get-Command npm -ErrorAction SilentlyContinue)) {
|
|
30
|
+
throw "npm not found. Install Node.js 18+ (includes npm) and rerun."
|
|
40
31
|
}
|
|
41
32
|
}
|
|
42
33
|
|
|
34
|
+
Ensure-Node
|
|
35
|
+
|
|
43
36
|
$repoRoot = Split-Path -Parent $PSScriptRoot
|
|
44
37
|
if ($Source -eq "Auto") {
|
|
45
|
-
if (Test-Path -LiteralPath (Join-Path $repoRoot "
|
|
38
|
+
if (Test-Path -LiteralPath (Join-Path $repoRoot "package.json")) {
|
|
46
39
|
$Source = "Local"
|
|
47
40
|
} else {
|
|
48
|
-
$Source = "
|
|
41
|
+
$Source = "Npm"
|
|
49
42
|
}
|
|
50
43
|
}
|
|
51
44
|
|
|
52
|
-
Ensure-Dir $InstallRoot
|
|
53
|
-
$srcDir = Join-Path $InstallRoot "src"
|
|
54
|
-
|
|
55
|
-
$pkgSource = $null
|
|
56
45
|
if ($Source -eq "Local") {
|
|
57
|
-
|
|
46
|
+
npm i -g $repoRoot
|
|
58
47
|
} elseif ($Source -eq "Git") {
|
|
59
|
-
|
|
60
|
-
Run-Step "git" @("-C", $srcDir, "fetch", "--all", "--tags")
|
|
61
|
-
Run-Step "git" @("-C", $srcDir, "checkout", $RepoRef)
|
|
62
|
-
Run-Step "git" @("-C", $srcDir, "pull", "--ff-only")
|
|
63
|
-
} else {
|
|
64
|
-
if (Test-Path -LiteralPath $srcDir) {
|
|
65
|
-
Remove-Item -LiteralPath $srcDir -Recurse -Force
|
|
66
|
-
}
|
|
67
|
-
Run-Step "git" @("clone", "--depth", "1", "--branch", $RepoRef, $RepoUrl, $srcDir)
|
|
68
|
-
}
|
|
69
|
-
$pkgSource = $srcDir
|
|
48
|
+
npm i -g "git+$RepoUrl#$RepoRef"
|
|
70
49
|
} else {
|
|
71
|
-
|
|
50
|
+
npm i -g @ao_zorin/zocket
|
|
72
51
|
}
|
|
73
52
|
|
|
74
|
-
$
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
$zocketExe = Join-Path $venvDir "Scripts\zocket.exe"
|
|
78
|
-
|
|
79
|
-
Run-Step $py.Cmd ($py.Prefix + @("-m", "venv", $venvDir))
|
|
80
|
-
Run-Step $venvPy @("-m", "pip", "install", "--upgrade", "pip", "setuptools", "wheel")
|
|
81
|
-
|
|
82
|
-
if ($Source -eq "PyPI") {
|
|
83
|
-
Run-Step $venvPy @("-m", "pip", "install", "--upgrade", $pkgSource)
|
|
84
|
-
} else {
|
|
85
|
-
Run-Step $venvPy @("-m", "pip", "install", "--upgrade", $pkgSource)
|
|
53
|
+
$zocketBin = (Get-Command zocket).Source
|
|
54
|
+
if (-not $zocketBin) {
|
|
55
|
+
throw "zocket binary not found after install"
|
|
86
56
|
}
|
|
87
57
|
|
|
88
58
|
Ensure-Dir $ZocketHome
|
|
89
59
|
$env:ZOCKET_HOME = $ZocketHome
|
|
90
60
|
|
|
91
|
-
|
|
92
|
-
Run-Step $zocketExe @("init")
|
|
93
|
-
}
|
|
94
|
-
Run-Step $zocketExe @("config", "set-language", $Lang)
|
|
61
|
+
& $zocketBin init | Out-Null
|
|
95
62
|
|
|
96
63
|
if ($EnableAutostart) {
|
|
97
|
-
$
|
|
98
|
-
$
|
|
99
|
-
$
|
|
100
|
-
|
|
101
|
-
$webCmd = "`"$venvPy`" -m zocket web --host 127.0.0.1 --port $WebPort"
|
|
102
|
-
$mcpSseCmd = "`"$venvPy`" -m zocket mcp --transport sse --mode $McpMode --host 127.0.0.1 --port $McpPort"
|
|
103
|
-
$mcpStreamCmd = "`"$venvPy`" -m zocket mcp --transport streamable-http --mode $McpMode --host 127.0.0.1 --port $McpStreamPort"
|
|
104
|
-
|
|
105
|
-
schtasks /Create /F /SC ONLOGON /RL LIMITED /TN $webTask /TR $webCmd | Out-Null
|
|
106
|
-
schtasks /Create /F /SC ONLOGON /RL LIMITED /TN $mcpSseTask /TR $mcpSseCmd | Out-Null
|
|
107
|
-
schtasks /Create /F /SC ONLOGON /RL LIMITED /TN $mcpStreamTask /TR $mcpStreamCmd | Out-Null
|
|
64
|
+
$taskName = "Zocket"
|
|
65
|
+
$cmd = "\"$zocketBin\" start --host 127.0.0.1 --web-port $WebPort --mcp-port $McpPort --mcp-stream-port $McpStreamPort --mode $McpMode"
|
|
66
|
+
schtasks /Create /F /SC ONLOGON /RL LIMITED /TN $taskName /TR $cmd | Out-Null
|
|
108
67
|
}
|
|
109
68
|
|
|
110
69
|
Write-Output "zocket installed successfully."
|
|
111
|
-
Write-Output "
|
|
112
|
-
Write-Output "zocket: $zocketExe"
|
|
70
|
+
Write-Output "zocket: $zocketBin"
|
|
113
71
|
Write-Output "ZOCKET_HOME=$ZocketHome"
|
|
114
72
|
Write-Output "web panel: http://127.0.0.1:$WebPort"
|
|
115
73
|
Write-Output "mcp sse: http://127.0.0.1:$McpPort/sse"
|
|
@@ -4,16 +4,15 @@ set -euo pipefail
|
|
|
4
4
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
5
5
|
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
6
6
|
|
|
7
|
-
SOURCE_MODE="${SOURCE_MODE:-auto}" # auto|local|git|
|
|
8
|
-
REPO_URL="${REPO_URL:-https://github.com/
|
|
7
|
+
SOURCE_MODE="${SOURCE_MODE:-auto}" # auto|local|git|npm
|
|
8
|
+
REPO_URL="${REPO_URL:-https://github.com/aozorin/zocket.git}"
|
|
9
9
|
REPO_REF="${REPO_REF:-main}"
|
|
10
|
-
INSTALL_ROOT="${INSTALL_ROOT:-$HOME/.local/share/zocket}"
|
|
11
10
|
ZOCKET_HOME_DIR="${ZOCKET_HOME_DIR:-$HOME/.zocket}"
|
|
12
|
-
LANGUAGE="${LANGUAGE:-en}" # en|ru
|
|
11
|
+
LANGUAGE="${LANGUAGE:-en}" # en|ru (used by UI; config set via web)
|
|
13
12
|
WEB_PORT="${WEB_PORT:-18001}"
|
|
14
13
|
MCP_PORT="${MCP_PORT:-18002}"
|
|
15
14
|
MCP_STREAM_PORT="${MCP_STREAM_PORT:-18003}"
|
|
16
|
-
MCP_MODE="${MCP_MODE:-
|
|
15
|
+
MCP_MODE="${MCP_MODE:-admin}" # metadata|admin
|
|
17
16
|
AUTOSTART="${AUTOSTART:-user}" # user|system|none
|
|
18
17
|
SERVICE_USER="${SERVICE_USER:-zocketd}"
|
|
19
18
|
|
|
@@ -22,10 +21,9 @@ usage() {
|
|
|
22
21
|
Usage: $(basename "$0") [options]
|
|
23
22
|
|
|
24
23
|
Options:
|
|
25
|
-
--source <auto|local|git|
|
|
24
|
+
--source <auto|local|git|npm>
|
|
26
25
|
--repo-url <git-url>
|
|
27
26
|
--repo-ref <branch-or-tag>
|
|
28
|
-
--install-root <path>
|
|
29
27
|
--zocket-home <path>
|
|
30
28
|
--lang <en|ru>
|
|
31
29
|
--web-port <port>
|
|
@@ -45,7 +43,6 @@ while [[ $# -gt 0 ]]; do
|
|
|
45
43
|
--source) SOURCE_MODE="$2"; shift 2 ;;
|
|
46
44
|
--repo-url) REPO_URL="$2"; shift 2 ;;
|
|
47
45
|
--repo-ref) REPO_REF="$2"; shift 2 ;;
|
|
48
|
-
--install-root) INSTALL_ROOT="$2"; shift 2 ;;
|
|
49
46
|
--zocket-home) ZOCKET_HOME_DIR="$2"; shift 2 ;;
|
|
50
47
|
--lang) LANGUAGE="$2"; shift 2 ;;
|
|
51
48
|
--web-port) WEB_PORT="$2"; shift 2 ;;
|
|
@@ -71,54 +68,100 @@ run_sudo() {
|
|
|
71
68
|
fi
|
|
72
69
|
}
|
|
73
70
|
|
|
74
|
-
|
|
71
|
+
install_node_linux() {
|
|
75
72
|
if have_cmd apt-get; then
|
|
76
73
|
run_sudo apt-get update
|
|
77
|
-
run_sudo apt-get install -y
|
|
74
|
+
run_sudo apt-get install -y nodejs npm git curl
|
|
78
75
|
return
|
|
79
76
|
fi
|
|
80
77
|
if have_cmd dnf; then
|
|
81
|
-
run_sudo dnf install -y
|
|
78
|
+
run_sudo dnf install -y nodejs npm git curl
|
|
82
79
|
return
|
|
83
80
|
fi
|
|
84
81
|
if have_cmd yum; then
|
|
85
|
-
run_sudo yum install -y
|
|
82
|
+
run_sudo yum install -y nodejs npm git curl
|
|
86
83
|
return
|
|
87
84
|
fi
|
|
88
85
|
if have_cmd pacman; then
|
|
89
|
-
run_sudo pacman -Sy --noconfirm
|
|
86
|
+
run_sudo pacman -Sy --noconfirm nodejs npm git curl
|
|
90
87
|
return
|
|
91
88
|
fi
|
|
92
89
|
if have_cmd zypper; then
|
|
93
|
-
run_sudo zypper install -y
|
|
90
|
+
run_sudo zypper install -y nodejs npm git curl
|
|
94
91
|
return
|
|
95
92
|
fi
|
|
96
93
|
if have_cmd apk; then
|
|
97
|
-
run_sudo apk add --no-cache
|
|
94
|
+
run_sudo apk add --no-cache nodejs npm git curl
|
|
98
95
|
return
|
|
99
96
|
fi
|
|
100
|
-
echo "Unsupported Linux package manager. Install
|
|
97
|
+
echo "Unsupported Linux package manager. Install Node.js 18+ and npm manually." >&2
|
|
101
98
|
exit 1
|
|
102
99
|
}
|
|
103
100
|
|
|
104
|
-
|
|
101
|
+
install_node_macos() {
|
|
105
102
|
if ! have_cmd brew; then
|
|
106
103
|
echo "Homebrew not found. Install Homebrew first: https://brew.sh" >&2
|
|
107
104
|
exit 1
|
|
108
105
|
fi
|
|
109
|
-
brew install
|
|
106
|
+
brew install node git curl
|
|
110
107
|
}
|
|
111
108
|
|
|
109
|
+
OS="$(uname -s | tr '[:upper:]' '[:lower:]')"
|
|
110
|
+
if ! have_cmd node || ! have_cmd npm; then
|
|
111
|
+
echo "node/npm not found, installing dependencies..."
|
|
112
|
+
case "$OS" in
|
|
113
|
+
linux*) install_node_linux ;;
|
|
114
|
+
darwin*) install_node_macos ;;
|
|
115
|
+
*)
|
|
116
|
+
echo "Unsupported OS for this installer: $OS" >&2
|
|
117
|
+
exit 1
|
|
118
|
+
;;
|
|
119
|
+
esac
|
|
120
|
+
fi
|
|
121
|
+
|
|
122
|
+
if [[ "$SOURCE_MODE" == "auto" ]]; then
|
|
123
|
+
if [[ -f "${REPO_ROOT}/package.json" ]]; then
|
|
124
|
+
SOURCE_MODE="local"
|
|
125
|
+
else
|
|
126
|
+
SOURCE_MODE="npm"
|
|
127
|
+
fi
|
|
128
|
+
fi
|
|
129
|
+
|
|
130
|
+
case "$SOURCE_MODE" in
|
|
131
|
+
local)
|
|
132
|
+
npm i -g "${REPO_ROOT}"
|
|
133
|
+
;;
|
|
134
|
+
git)
|
|
135
|
+
npm i -g "git+${REPO_URL}#${REPO_REF}"
|
|
136
|
+
;;
|
|
137
|
+
npm)
|
|
138
|
+
npm i -g @ao_zorin/zocket
|
|
139
|
+
;;
|
|
140
|
+
*)
|
|
141
|
+
echo "Invalid source mode: $SOURCE_MODE" >&2
|
|
142
|
+
exit 2
|
|
143
|
+
;;
|
|
144
|
+
esac
|
|
145
|
+
|
|
146
|
+
ZOCKET_BIN="$(command -v zocket || true)"
|
|
147
|
+
if [[ -z "${ZOCKET_BIN}" ]]; then
|
|
148
|
+
echo "zocket binary not found after install" >&2
|
|
149
|
+
exit 1
|
|
150
|
+
fi
|
|
151
|
+
|
|
152
|
+
export ZOCKET_HOME="${ZOCKET_HOME_DIR}"
|
|
153
|
+
mkdir -p "${ZOCKET_HOME_DIR}"
|
|
154
|
+
"${ZOCKET_BIN}" init >/dev/null
|
|
155
|
+
|
|
112
156
|
write_systemd_unit() {
|
|
113
157
|
local unit_path="$1"
|
|
114
158
|
local exec_start="$2"
|
|
115
159
|
local svc_user="$3"
|
|
116
160
|
local svc_group="$4"
|
|
117
161
|
local zocket_home="$5"
|
|
118
|
-
local extra_rw="${6:-}"
|
|
119
162
|
run_sudo tee "${unit_path}" >/dev/null <<EOF
|
|
120
163
|
[Unit]
|
|
121
|
-
Description=Zocket
|
|
164
|
+
Description=Zocket service
|
|
122
165
|
After=network-online.target
|
|
123
166
|
Wants=network-online.target
|
|
124
167
|
|
|
@@ -138,7 +181,7 @@ ProtectKernelTunables=true
|
|
|
138
181
|
ProtectControlGroups=true
|
|
139
182
|
LockPersonality=true
|
|
140
183
|
MemoryDenyWriteExecute=true
|
|
141
|
-
ReadWritePaths=${zocket_home}
|
|
184
|
+
ReadWritePaths=${zocket_home}
|
|
142
185
|
|
|
143
186
|
[Install]
|
|
144
187
|
WantedBy=multi-user.target
|
|
@@ -152,7 +195,7 @@ write_systemd_user_unit() {
|
|
|
152
195
|
mkdir -p "$(dirname "${unit_path}")"
|
|
153
196
|
cat > "${unit_path}" <<EOF
|
|
154
197
|
[Unit]
|
|
155
|
-
Description=Zocket
|
|
198
|
+
Description=Zocket service
|
|
156
199
|
After=default.target
|
|
157
200
|
|
|
158
201
|
[Service]
|
|
@@ -198,140 +241,33 @@ EOF
|
|
|
198
241
|
EOF
|
|
199
242
|
}
|
|
200
243
|
|
|
201
|
-
|
|
202
|
-
if ! have_cmd python3; then
|
|
203
|
-
echo "python3 not found, installing dependencies..."
|
|
204
|
-
case "$OS" in
|
|
205
|
-
linux*) install_python_linux ;;
|
|
206
|
-
darwin*) install_python_macos ;;
|
|
207
|
-
*)
|
|
208
|
-
echo "Unsupported OS for this installer: $OS" >&2
|
|
209
|
-
exit 1
|
|
210
|
-
;;
|
|
211
|
-
esac
|
|
212
|
-
fi
|
|
213
|
-
|
|
214
|
-
if ! have_cmd git; then
|
|
215
|
-
echo "git not found, installing..."
|
|
216
|
-
case "$OS" in
|
|
217
|
-
linux*) install_python_linux ;;
|
|
218
|
-
darwin*) install_python_macos ;;
|
|
219
|
-
*) echo "Install git manually and rerun." >&2; exit 1 ;;
|
|
220
|
-
esac
|
|
221
|
-
fi
|
|
222
|
-
|
|
223
|
-
if [[ "$SOURCE_MODE" == "auto" ]]; then
|
|
224
|
-
if [[ -f "${REPO_ROOT}/pyproject.toml" ]]; then
|
|
225
|
-
SOURCE_MODE="local"
|
|
226
|
-
else
|
|
227
|
-
SOURCE_MODE="git"
|
|
228
|
-
fi
|
|
229
|
-
fi
|
|
230
|
-
|
|
231
|
-
PKG_SOURCE=""
|
|
232
|
-
SRC_DIR="${INSTALL_ROOT}/src"
|
|
233
|
-
|
|
234
|
-
mkdir -p "${INSTALL_ROOT}"
|
|
235
|
-
|
|
236
|
-
if [[ "$SOURCE_MODE" == "local" ]]; then
|
|
237
|
-
PKG_SOURCE="${REPO_ROOT}"
|
|
238
|
-
elif [[ "$SOURCE_MODE" == "git" ]]; then
|
|
239
|
-
if [[ -d "${SRC_DIR}/.git" ]]; then
|
|
240
|
-
git -C "${SRC_DIR}" fetch --all --tags
|
|
241
|
-
git -C "${SRC_DIR}" checkout "${REPO_REF}"
|
|
242
|
-
git -C "${SRC_DIR}" pull --ff-only
|
|
243
|
-
else
|
|
244
|
-
rm -rf "${SRC_DIR}"
|
|
245
|
-
git clone --depth 1 --branch "${REPO_REF}" "${REPO_URL}" "${SRC_DIR}"
|
|
246
|
-
fi
|
|
247
|
-
PKG_SOURCE="${SRC_DIR}"
|
|
248
|
-
elif [[ "$SOURCE_MODE" == "pypi" ]]; then
|
|
249
|
-
PKG_SOURCE="zocket"
|
|
250
|
-
else
|
|
251
|
-
echo "Invalid source mode: $SOURCE_MODE" >&2
|
|
252
|
-
exit 2
|
|
253
|
-
fi
|
|
254
|
-
|
|
255
|
-
VENV_DIR="${INSTALL_ROOT}/venv"
|
|
256
|
-
PY_BIN="${VENV_DIR}/bin/python3"
|
|
257
|
-
ZOCKET_BIN="${VENV_DIR}/bin/zocket"
|
|
258
|
-
|
|
259
|
-
python3 -m venv "${VENV_DIR}"
|
|
260
|
-
"${PY_BIN}" -m pip install --upgrade pip setuptools wheel
|
|
261
|
-
|
|
262
|
-
if [[ "$SOURCE_MODE" == "pypi" ]]; then
|
|
263
|
-
"${PY_BIN}" -m pip install --upgrade "${PKG_SOURCE}"
|
|
264
|
-
else
|
|
265
|
-
"${PY_BIN}" -m pip install --upgrade "${PKG_SOURCE}"
|
|
266
|
-
fi
|
|
267
|
-
|
|
268
|
-
mkdir -p "$HOME/.local/bin"
|
|
269
|
-
ln -sf "${ZOCKET_BIN}" "$HOME/.local/bin/zocket"
|
|
270
|
-
|
|
271
|
-
export ZOCKET_HOME="${ZOCKET_HOME_DIR}"
|
|
272
|
-
mkdir -p "${ZOCKET_HOME_DIR}"
|
|
273
|
-
|
|
274
|
-
if [[ ! -f "${ZOCKET_HOME_DIR}/vault.enc" ]]; then
|
|
275
|
-
"${ZOCKET_BIN}" init
|
|
276
|
-
fi
|
|
277
|
-
|
|
278
|
-
"${ZOCKET_BIN}" config set-language "${LANGUAGE}" >/dev/null
|
|
244
|
+
EXEC_START="${ZOCKET_BIN} start --host 127.0.0.1 --web-port ${WEB_PORT} --mcp-port ${MCP_PORT} --mcp-stream-port ${MCP_STREAM_PORT} --mode ${MCP_MODE}"
|
|
279
245
|
|
|
280
246
|
if [[ "$AUTOSTART" == "user" && "$OS" == linux* ]]; then
|
|
281
247
|
USER_UNIT_DIR="$HOME/.config/systemd/user"
|
|
282
|
-
write_systemd_user_unit "${USER_UNIT_DIR}/zocket
|
|
283
|
-
"${PY_BIN} -m zocket web --host 127.0.0.1 --port ${WEB_PORT}" \
|
|
284
|
-
"${ZOCKET_HOME_DIR}"
|
|
285
|
-
write_systemd_user_unit "${USER_UNIT_DIR}/zocket-mcp-sse.service" \
|
|
286
|
-
"${PY_BIN} -m zocket mcp --transport sse --mode ${MCP_MODE} --host 127.0.0.1 --port ${MCP_PORT}" \
|
|
287
|
-
"${ZOCKET_HOME_DIR}"
|
|
288
|
-
write_systemd_user_unit "${USER_UNIT_DIR}/zocket-mcp-http.service" \
|
|
289
|
-
"${PY_BIN} -m zocket mcp --transport streamable-http --mode ${MCP_MODE} --host 127.0.0.1 --port ${MCP_STREAM_PORT}" \
|
|
290
|
-
"${ZOCKET_HOME_DIR}"
|
|
248
|
+
write_systemd_user_unit "${USER_UNIT_DIR}/zocket.service" "${EXEC_START}" "${ZOCKET_HOME_DIR}"
|
|
291
249
|
systemctl --user daemon-reload
|
|
292
|
-
systemctl --user enable --now zocket
|
|
250
|
+
systemctl --user enable --now zocket.service >/dev/null
|
|
293
251
|
fi
|
|
294
252
|
|
|
295
253
|
if [[ "$AUTOSTART" == "system" && "$OS" == linux* ]]; then
|
|
296
|
-
write_systemd_unit "/etc/systemd/system/zocket
|
|
297
|
-
"${PY_BIN} -m zocket web --host 127.0.0.1 --port ${WEB_PORT}" \
|
|
298
|
-
"${SERVICE_USER}" "${SERVICE_USER}" "${ZOCKET_HOME_DIR}" "/tmp"
|
|
299
|
-
write_systemd_unit "/etc/systemd/system/zocket-mcp-sse.service" \
|
|
300
|
-
"${PY_BIN} -m zocket mcp --transport sse --mode ${MCP_MODE} --host 127.0.0.1 --port ${MCP_PORT}" \
|
|
301
|
-
"${SERVICE_USER}" "${SERVICE_USER}" "${ZOCKET_HOME_DIR}"
|
|
302
|
-
write_systemd_unit "/etc/systemd/system/zocket-mcp-http.service" \
|
|
303
|
-
"${PY_BIN} -m zocket mcp --transport streamable-http --mode ${MCP_MODE} --host 127.0.0.1 --port ${MCP_STREAM_PORT}" \
|
|
304
|
-
"${SERVICE_USER}" "${SERVICE_USER}" "${ZOCKET_HOME_DIR}"
|
|
254
|
+
write_systemd_unit "/etc/systemd/system/zocket.service" "${EXEC_START}" "${SERVICE_USER}" "${SERVICE_USER}" "${ZOCKET_HOME_DIR}"
|
|
305
255
|
run_sudo systemctl daemon-reload
|
|
306
|
-
run_sudo systemctl enable --now zocket
|
|
256
|
+
run_sudo systemctl enable --now zocket.service >/dev/null
|
|
307
257
|
fi
|
|
308
258
|
|
|
309
259
|
if [[ "$AUTOSTART" != "none" && "$OS" == darwin* ]]; then
|
|
310
260
|
PLIST_DIR="$HOME/Library/LaunchAgents"
|
|
311
|
-
install_launchd "dev.zocket
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
install_launchd "dev.zocket.mcp-sse" \
|
|
315
|
-
"${PY_BIN} -m zocket mcp --transport sse --mode ${MCP_MODE} --host 127.0.0.1 --port ${MCP_PORT}" \
|
|
316
|
-
"${ZOCKET_HOME_DIR}" "${PLIST_DIR}"
|
|
317
|
-
install_launchd "dev.zocket.mcp-streamable" \
|
|
318
|
-
"${PY_BIN} -m zocket mcp --transport streamable-http --mode ${MCP_MODE} --host 127.0.0.1 --port ${MCP_STREAM_PORT}" \
|
|
319
|
-
"${ZOCKET_HOME_DIR}" "${PLIST_DIR}"
|
|
320
|
-
launchctl unload "${PLIST_DIR}/dev.zocket.web.plist" >/dev/null 2>&1 || true
|
|
321
|
-
launchctl unload "${PLIST_DIR}/dev.zocket.mcp-sse.plist" >/dev/null 2>&1 || true
|
|
322
|
-
launchctl unload "${PLIST_DIR}/dev.zocket.mcp-streamable.plist" >/dev/null 2>&1 || true
|
|
323
|
-
launchctl load "${PLIST_DIR}/dev.zocket.web.plist"
|
|
324
|
-
launchctl load "${PLIST_DIR}/dev.zocket.mcp-sse.plist"
|
|
325
|
-
launchctl load "${PLIST_DIR}/dev.zocket.mcp-streamable.plist"
|
|
261
|
+
install_launchd "dev.zocket" "${EXEC_START}" "${ZOCKET_HOME_DIR}" "${PLIST_DIR}"
|
|
262
|
+
launchctl unload "${PLIST_DIR}/dev.zocket.plist" >/dev/null 2>&1 || true
|
|
263
|
+
launchctl load "${PLIST_DIR}/dev.zocket.plist"
|
|
326
264
|
fi
|
|
327
265
|
|
|
328
266
|
cat <<EOF
|
|
329
267
|
zocket installed successfully.
|
|
330
268
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
zocket: ${ZOCKET_BIN}
|
|
334
|
-
ZOCKET_HOME=${ZOCKET_HOME_DIR}
|
|
269
|
+
zocket: ${ZOCKET_BIN}
|
|
270
|
+
ZOCKET_HOME=${ZOCKET_HOME_DIR}
|
|
335
271
|
|
|
336
272
|
Default ports:
|
|
337
273
|
web panel: http://127.0.0.1:${WEB_PORT}
|
|
@@ -339,8 +275,5 @@ Default ports:
|
|
|
339
275
|
MCP HTTP: http://127.0.0.1:${MCP_STREAM_PORT}/mcp
|
|
340
276
|
|
|
341
277
|
Next steps:
|
|
342
|
-
1)
|
|
343
|
-
2) MCP SSE (Claude Code): ${ZOCKET_BIN} mcp --transport sse --mode ${MCP_MODE} --host 127.0.0.1 --port ${MCP_PORT}
|
|
344
|
-
3) MCP Streamable (Codex): ${ZOCKET_BIN} mcp --transport streamable-http --mode ${MCP_MODE} --host 127.0.0.1 --port ${MCP_STREAM_PORT}
|
|
345
|
-
4) MCP stdio: ${ZOCKET_BIN} mcp --transport stdio --mode ${MCP_MODE}
|
|
278
|
+
1) Start now: ${EXEC_START}
|
|
346
279
|
EOF
|