gisool 0.0.0-stage → 0.1.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/LICENSE +22 -0
- package/README.md +136 -2
- package/dist/api/routes.js +166 -0
- package/dist/api/security.js +86 -0
- package/dist/api/sse.js +55 -0
- package/dist/api/static.js +32 -0
- package/dist/catalog/fields.js +146 -0
- package/dist/catalog/index.js +133 -0
- package/dist/catalog/scanner.js +185 -0
- package/dist/catalog/uri.js +41 -0
- package/dist/catalog/watcher.js +59 -0
- package/dist/cli/config-writer.js +255 -0
- package/dist/cli/doctor.js +197 -0
- package/dist/cli/eval.js +64 -0
- package/dist/cli/export-import.js +66 -0
- package/dist/cli/init.js +271 -0
- package/dist/cli/main.js +165 -0
- package/dist/cli/open.js +24 -0
- package/dist/cli/serve.js +144 -0
- package/dist/config.js +78 -0
- package/dist/dashboard/assets/app.js +256 -0
- package/dist/dashboard/assets/chunk-4U4GNPH4.js +17 -0
- package/dist/dashboard/assets/chunk-5DFWNJC2.js +1 -0
- package/dist/dashboard/assets/chunk-JPIEF5XO.js +17 -0
- package/dist/dashboard/assets/chunk-ZBUKVJOI.js +1 -0
- package/dist/dashboard/assets/styles.css +487 -0
- package/dist/dashboard/index.html +15 -0
- package/dist/ingest/attribution.js +55 -0
- package/dist/ingest/hooks.js +168 -0
- package/dist/ingest/mcp-recorder.js +41 -0
- package/dist/ingest/otlp.js +160 -0
- package/dist/ingest/pipeline.js +100 -0
- package/dist/ingest/redact.js +36 -0
- package/dist/ingest/runtimes.js +25 -0
- package/dist/mcp/http.js +84 -0
- package/dist/mcp/skills-extension.js +35 -0
- package/dist/mcp/stdio-proxy.js +53 -0
- package/dist/mcp/tools.js +164 -0
- package/dist/packs/claude-code.js +157 -0
- package/dist/packs/codex.js +148 -0
- package/dist/packs/cursor.js +95 -0
- package/dist/packs/gajae.js +7 -0
- package/dist/packs/gemini.js +88 -0
- package/dist/packs/json-config.js +51 -0
- package/dist/packs/omo.js +149 -0
- package/dist/packs/openclaw.js +119 -0
- package/dist/packs/registry.js +13 -0
- package/dist/search/fts.js +61 -0
- package/dist/search/fusion.js +50 -0
- package/dist/search/guidance.js +7 -0
- package/dist/search/index.js +74 -0
- package/dist/search/lexical.js +69 -0
- package/dist/server.js +216 -0
- package/dist/store/db.js +74 -0
- package/dist/store/export.js +199 -0
- package/dist/store/index.js +13 -0
- package/dist/store/queries.js +458 -0
- package/dist/store/schema.sql +96 -0
- package/dist/store/writes.js +172 -0
- package/dist/types.js +1 -0
- package/dist/version.js +19 -0
- package/package.json +46 -4
- package/packs/claude-code/README.md +5 -0
- package/packs/claude-code/settings.template.json +24 -0
- package/packs/codex/README.md +5 -0
- package/packs/codex/config.template.toml +11 -0
- package/packs/cursor/hooks.example.json +8 -0
- package/packs/cursor/mcp.example.json +8 -0
- package/packs/gajae/extension/discovery.md +5 -0
- package/packs/gajae/extension/index.js +44 -0
- package/packs/gemini/settings.example.json +16 -0
- package/packs/hook-post.mjs +29 -0
- package/packs/omo/extension/discovery.md +5 -0
- package/packs/omo/extension/index.js +44 -0
- package/packs/openclaw/.codex-plugin/plugin.json +11 -0
- package/packs/openclaw/.mcp.json +12 -0
- package/packs/openclaw/gisool.json +1 -0
- package/packs/openclaw/hooks/gisool-discovery/HOOK.md +8 -0
- package/packs/openclaw/hooks/gisool-discovery/handler.js +27 -0
- package/packs/openclaw/index.mjs +47 -0
- package/packs/openclaw/openclaw.plugin.json +22 -0
- package/packs/openclaw/package.json +11 -0
- package/src/store/schema.sql +96 -0
- package/src/store/task-types.json +15 -0
package/dist/cli/main.js
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { realpathSync } from "node:fs";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { ensureToken, loadConfig, resolveGisoolHome } from "../config.js";
|
|
5
|
+
import { runDoctor } from "./doctor.js";
|
|
6
|
+
import { runInit, serviceInstall, serviceRemove } from "./init.js";
|
|
7
|
+
import { runOpen } from "./open.js";
|
|
8
|
+
import { makeCliDeps, packageVersion, processIo, runServe, serviceStatus } from "./serve.js";
|
|
9
|
+
const defaultLoaders = {
|
|
10
|
+
exportImport: () => import("./export-import.js"),
|
|
11
|
+
evalApi: () => import("./eval.js"),
|
|
12
|
+
};
|
|
13
|
+
export const HELP = `gisool - local skill router
|
|
14
|
+
|
|
15
|
+
Usage:
|
|
16
|
+
gisool serve [--port <n>] [--foreground] run the daemon in the foreground (used by the service)
|
|
17
|
+
gisool open [--print] print and open the dashboard URL
|
|
18
|
+
gisool init [--dry-run] [--runtime <id>...] [--no-service] [--disable-remote]
|
|
19
|
+
gisool init --rollback [--all] undo the newest (or every) init run
|
|
20
|
+
gisool doctor [--json] check service, daemon, packs, index and backups
|
|
21
|
+
gisool service install|remove|status manage the LaunchAgent / systemd user unit
|
|
22
|
+
gisool mcp-stdio stdio MCP proxy to the local daemon
|
|
23
|
+
gisool export <dir> [--with-history]
|
|
24
|
+
gisool import <dir> [--dry-run]
|
|
25
|
+
gisool eval [--cases <file>] [--json]
|
|
26
|
+
gisool --help | --version
|
|
27
|
+
`;
|
|
28
|
+
class UsageError extends Error {
|
|
29
|
+
code = "usage";
|
|
30
|
+
}
|
|
31
|
+
function parseArgs(args, spec) {
|
|
32
|
+
const parsed = { flags: new Set(), values: new Map(), positional: [] };
|
|
33
|
+
for (let i = 0; i < args.length; i++) {
|
|
34
|
+
const arg = args[i];
|
|
35
|
+
if (spec.flags.includes(arg))
|
|
36
|
+
parsed.flags.add(arg);
|
|
37
|
+
else if (spec.values?.includes(arg) || spec.multi?.includes(arg)) {
|
|
38
|
+
const list = parsed.values.get(arg) ?? [];
|
|
39
|
+
const multi = spec.multi?.includes(arg);
|
|
40
|
+
let took = 0;
|
|
41
|
+
while (i + 1 < args.length && !args[i + 1].startsWith("--") && (multi || took === 0)) {
|
|
42
|
+
list.push(...args[++i].split(",").filter(Boolean));
|
|
43
|
+
took++;
|
|
44
|
+
}
|
|
45
|
+
if (took === 0)
|
|
46
|
+
throw new UsageError(`${arg} needs a value`);
|
|
47
|
+
parsed.values.set(arg, list);
|
|
48
|
+
}
|
|
49
|
+
else if (arg.startsWith("--"))
|
|
50
|
+
throw new UsageError(`unknown option ${arg}`);
|
|
51
|
+
else
|
|
52
|
+
parsed.positional.push(arg);
|
|
53
|
+
}
|
|
54
|
+
return parsed;
|
|
55
|
+
}
|
|
56
|
+
function oneDir(p, command) {
|
|
57
|
+
if (p.positional.length !== 1)
|
|
58
|
+
throw new UsageError(`${command} needs exactly one <dir>`);
|
|
59
|
+
return p.positional[0];
|
|
60
|
+
}
|
|
61
|
+
export async function runMain(argv, opts = {}) {
|
|
62
|
+
const env = opts.env ?? process.env;
|
|
63
|
+
const io = opts.io ?? processIo;
|
|
64
|
+
const loaders = opts.loaders ?? defaultLoaders;
|
|
65
|
+
const [command, ...rest] = argv;
|
|
66
|
+
try {
|
|
67
|
+
switch (command) {
|
|
68
|
+
case undefined:
|
|
69
|
+
case "--help":
|
|
70
|
+
case "-h":
|
|
71
|
+
case "help":
|
|
72
|
+
io.out(HELP);
|
|
73
|
+
return command === undefined ? 1 : 0;
|
|
74
|
+
case "--version":
|
|
75
|
+
case "-v":
|
|
76
|
+
io.out(packageVersion() + "\n");
|
|
77
|
+
return 0;
|
|
78
|
+
case "serve": {
|
|
79
|
+
const p = parseArgs(rest, { flags: ["--foreground"], values: ["--port"] });
|
|
80
|
+
const raw = p.values.get("--port")?.[0];
|
|
81
|
+
const port = raw === undefined ? undefined : Number(raw);
|
|
82
|
+
if (port !== undefined && !(Number.isInteger(port) && port > 0 && port < 65536))
|
|
83
|
+
throw new UsageError(`invalid --port ${raw}`);
|
|
84
|
+
return await runServe({ port }, env, io);
|
|
85
|
+
}
|
|
86
|
+
case "open": {
|
|
87
|
+
const p = parseArgs(rest, { flags: ["--print"] });
|
|
88
|
+
return await runOpen({ print: p.flags.has("--print") }, env, io);
|
|
89
|
+
}
|
|
90
|
+
case "init": {
|
|
91
|
+
const p = parseArgs(rest, { flags: ["--dry-run", "--rollback", "--all", "--no-service", "--disable-remote"], multi: ["--runtime"] });
|
|
92
|
+
if (p.positional.length)
|
|
93
|
+
throw new UsageError(`unexpected argument ${p.positional[0]}`);
|
|
94
|
+
if (p.flags.has("--all") && !p.flags.has("--rollback"))
|
|
95
|
+
throw new UsageError("--all is only valid with --rollback");
|
|
96
|
+
return await runInit({
|
|
97
|
+
dryRun: p.flags.has("--dry-run"), rollback: p.flags.has("--rollback"), all: p.flags.has("--all"),
|
|
98
|
+
noService: p.flags.has("--no-service"), disableRemote: p.flags.has("--disable-remote"), runtimes: p.values.get("--runtime") ?? [],
|
|
99
|
+
}, env, io);
|
|
100
|
+
}
|
|
101
|
+
case "doctor": {
|
|
102
|
+
const p = parseArgs(rest, { flags: ["--json"] });
|
|
103
|
+
return await runDoctor({ json: p.flags.has("--json") }, env, io);
|
|
104
|
+
}
|
|
105
|
+
case "service": {
|
|
106
|
+
const [sub, ...subRest] = rest;
|
|
107
|
+
const p = parseArgs(subRest, { flags: ["--dry-run"] });
|
|
108
|
+
if (sub === "install")
|
|
109
|
+
return await serviceInstall({ dryRun: p.flags.has("--dry-run") }, env, io);
|
|
110
|
+
if (sub === "remove")
|
|
111
|
+
return await serviceRemove(env, io);
|
|
112
|
+
if (sub === "status")
|
|
113
|
+
return serviceStatus(env, io);
|
|
114
|
+
throw new UsageError("service needs install, remove, or status");
|
|
115
|
+
}
|
|
116
|
+
case "mcp-stdio": {
|
|
117
|
+
parseArgs(rest, { flags: [] });
|
|
118
|
+
const config = loadConfig(env);
|
|
119
|
+
const { runStdioProxy } = await import("../mcp/stdio-proxy.js");
|
|
120
|
+
await runStdioProxy({ port: config.port, token: ensureToken(resolveGisoolHome(env)) });
|
|
121
|
+
return 0;
|
|
122
|
+
}
|
|
123
|
+
case "export": {
|
|
124
|
+
const p = parseArgs(rest, { flags: ["--with-history"] });
|
|
125
|
+
const dir = oneDir(p, "export");
|
|
126
|
+
return await (await loaders.exportImport()).runExport(dir, { withHistory: p.flags.has("--with-history") }, makeCliDeps(env));
|
|
127
|
+
}
|
|
128
|
+
case "import": {
|
|
129
|
+
const p = parseArgs(rest, { flags: ["--dry-run"] });
|
|
130
|
+
const dir = oneDir(p, "import");
|
|
131
|
+
return await (await loaders.exportImport()).runImport(dir, { dryRun: p.flags.has("--dry-run") }, makeCliDeps(env));
|
|
132
|
+
}
|
|
133
|
+
case "eval": {
|
|
134
|
+
const p = parseArgs(rest, { flags: ["--json"], values: ["--cases"] });
|
|
135
|
+
const cases = p.values.get("--cases")?.[0];
|
|
136
|
+
return await (await loaders.evalApi()).runEval({ ...(cases ? { cases } : {}), json: p.flags.has("--json") }, makeCliDeps(env));
|
|
137
|
+
}
|
|
138
|
+
default:
|
|
139
|
+
throw new UsageError(`unknown command ${command}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
if (error instanceof UsageError) {
|
|
144
|
+
io.err(`usage: ${error.message}\n\n${HELP}`);
|
|
145
|
+
return 2;
|
|
146
|
+
}
|
|
147
|
+
const code = error.code;
|
|
148
|
+
const message = error.message ?? String(error);
|
|
149
|
+
io.err(`error: ${typeof code === "string" && !message.startsWith(code) ? `${code}: ` : ""}${message}\n`);
|
|
150
|
+
return 1;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
function invokedDirectly() {
|
|
154
|
+
if (!process.argv[1])
|
|
155
|
+
return false;
|
|
156
|
+
try {
|
|
157
|
+
return realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url));
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (invokedDirectly()) {
|
|
164
|
+
process.exitCode = await runMain(process.argv.slice(2));
|
|
165
|
+
}
|
package/dist/cli/open.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { loadConfig, ensureToken, resolveGisoolHome } from "../config.js";
|
|
3
|
+
export function dashboardUrl(port, token) {
|
|
4
|
+
return `http://127.0.0.1:${port}/?token=${token}`;
|
|
5
|
+
}
|
|
6
|
+
export async function runOpen(opts, env, io) {
|
|
7
|
+
const config = loadConfig(env);
|
|
8
|
+
const url = dashboardUrl(config.port, ensureToken(resolveGisoolHome(env)));
|
|
9
|
+
io.out(url + "\n");
|
|
10
|
+
if (opts.print || env.GISOOL_NO_CLI)
|
|
11
|
+
return 0;
|
|
12
|
+
const opener = process.platform === "darwin" ? "open" : "xdg-open";
|
|
13
|
+
return new Promise((resolve) => {
|
|
14
|
+
const child = spawn(opener, [url], { env, stdio: "ignore", detached: true });
|
|
15
|
+
child.once("error", (error) => {
|
|
16
|
+
io.err(`could not run ${opener}: ${error.message}; open the URL above manually\n`);
|
|
17
|
+
resolve(1);
|
|
18
|
+
});
|
|
19
|
+
child.once("spawn", () => {
|
|
20
|
+
child.unref();
|
|
21
|
+
resolve(0);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { existsSync, readFileSync, realpathSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { loadConfig, resolveGisoolHome, resolveHome } from "../config.js";
|
|
6
|
+
import { startServer } from "../server.js";
|
|
7
|
+
import { openDb } from "../store/db.js";
|
|
8
|
+
import { createStore } from "../store/index.js";
|
|
9
|
+
import { readTarget } from "./config-writer.js";
|
|
10
|
+
export const processIo = { out: (t) => process.stdout.write(t), err: (t) => process.stderr.write(t) };
|
|
11
|
+
export const SERVICE_LABEL = "dev.gisool.daemon";
|
|
12
|
+
export function packageRoot() {
|
|
13
|
+
return realpathSync(fileURLToPath(new URL("../../", import.meta.url)));
|
|
14
|
+
}
|
|
15
|
+
export function nodePath() {
|
|
16
|
+
return realpathSync(process.execPath);
|
|
17
|
+
}
|
|
18
|
+
export function packageVersion() {
|
|
19
|
+
const pkg = JSON.parse(readFileSync(join(packageRoot(), "package.json"), "utf8"));
|
|
20
|
+
return pkg.version;
|
|
21
|
+
}
|
|
22
|
+
export function dbPath(gisoolHome) {
|
|
23
|
+
return join(gisoolHome, "gisool.db");
|
|
24
|
+
}
|
|
25
|
+
export function makeCliDeps(env) {
|
|
26
|
+
const config = loadConfig(env);
|
|
27
|
+
const home = resolveHome(env);
|
|
28
|
+
const gisoolHome = resolveGisoolHome(env);
|
|
29
|
+
return { config, home, gisoolHome, env, openStore: () => createStore(openDb(dbPath(gisoolHome)), { config, home, gisoolHome }) };
|
|
30
|
+
}
|
|
31
|
+
export async function runServe(opts, env, io) {
|
|
32
|
+
const deps = makeCliDeps(env);
|
|
33
|
+
const config = opts.port === undefined ? deps.config : { ...deps.config, port: opts.port };
|
|
34
|
+
const server = await startServer(config, { gisoolHome: deps.gisoolHome, openStore: deps.openStore });
|
|
35
|
+
io.err(`gisool listening on http://127.0.0.1:${server.port}\n`);
|
|
36
|
+
return 0;
|
|
37
|
+
}
|
|
38
|
+
const xml = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
39
|
+
const unitQuote = (s) => `"${s.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
40
|
+
export function servicePath(home, platform) {
|
|
41
|
+
return platform === "darwin"
|
|
42
|
+
? join(home, "Library", "LaunchAgents", `${SERVICE_LABEL}.plist`)
|
|
43
|
+
: join(home, ".config", "systemd", "user", "gisool.service");
|
|
44
|
+
}
|
|
45
|
+
export function daemonLogPath(gisoolHome) {
|
|
46
|
+
return join(gisoolHome, "logs", "daemon.log");
|
|
47
|
+
}
|
|
48
|
+
function plist(ctx) {
|
|
49
|
+
const log = xml(daemonLogPath(ctx.gisoolHome));
|
|
50
|
+
const args = [ctx.nodePath, join(ctx.packageRoot, "dist", "cli", "main.js"), "serve"].map((a) => ` <string>${xml(a)}</string>`).join("\n");
|
|
51
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
52
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
53
|
+
<plist version="1.0">
|
|
54
|
+
<dict>
|
|
55
|
+
<key>Label</key>
|
|
56
|
+
<string>${SERVICE_LABEL}</string>
|
|
57
|
+
<key>ProgramArguments</key>
|
|
58
|
+
<array>
|
|
59
|
+
${args}
|
|
60
|
+
</array>
|
|
61
|
+
<key>EnvironmentVariables</key>
|
|
62
|
+
<dict>
|
|
63
|
+
<key>GISOOL_HOME</key>
|
|
64
|
+
<string>${xml(ctx.gisoolHome)}</string>
|
|
65
|
+
</dict>
|
|
66
|
+
<key>RunAtLoad</key>
|
|
67
|
+
<true/>
|
|
68
|
+
<key>KeepAlive</key>
|
|
69
|
+
<true/>
|
|
70
|
+
<key>StandardOutPath</key>
|
|
71
|
+
<string>${log}</string>
|
|
72
|
+
<key>StandardErrorPath</key>
|
|
73
|
+
<string>${log}</string>
|
|
74
|
+
</dict>
|
|
75
|
+
</plist>
|
|
76
|
+
`;
|
|
77
|
+
}
|
|
78
|
+
function unit(ctx) {
|
|
79
|
+
const log = daemonLogPath(ctx.gisoolHome);
|
|
80
|
+
return `[Unit]
|
|
81
|
+
Description=gisool local skill router
|
|
82
|
+
|
|
83
|
+
[Service]
|
|
84
|
+
ExecStart=${unitQuote(ctx.nodePath)} ${unitQuote(join(ctx.packageRoot, "dist", "cli", "main.js"))} serve
|
|
85
|
+
Environment=${unitQuote(`GISOOL_HOME=${ctx.gisoolHome}`)}
|
|
86
|
+
Restart=always
|
|
87
|
+
StandardOutput=append:${log}
|
|
88
|
+
StandardError=append:${log}
|
|
89
|
+
|
|
90
|
+
[Install]
|
|
91
|
+
WantedBy=default.target
|
|
92
|
+
`;
|
|
93
|
+
}
|
|
94
|
+
export function planService(ctx) {
|
|
95
|
+
const path = servicePath(ctx.home, ctx.platform);
|
|
96
|
+
const before = readTarget(path);
|
|
97
|
+
const after = ctx.platform === "darwin" ? plist(ctx) : unit(ctx);
|
|
98
|
+
const file = { path, before, after, mode: 0o644 };
|
|
99
|
+
const commands = [];
|
|
100
|
+
const notes = [];
|
|
101
|
+
if (before === after)
|
|
102
|
+
return { file, commands, notes: ["service definition already up to date"] };
|
|
103
|
+
if (ctx.platform === "darwin") {
|
|
104
|
+
const domain = `gui/${ctx.uid}`;
|
|
105
|
+
if (before !== null)
|
|
106
|
+
commands.push({ argv: ["launchctl", "bootout", `${domain}/${SERVICE_LABEL}`], description: "Unload the previous gisool LaunchAgent", allowFailure: true });
|
|
107
|
+
commands.push({ argv: ["launchctl", "bootstrap", domain, path], description: "Load the gisool LaunchAgent",
|
|
108
|
+
undo: { argv: ["launchctl", "bootout", `${domain}/${SERVICE_LABEL}`], description: "Unload the gisool LaunchAgent" } });
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
commands.push({ argv: ["systemctl", "--user", "daemon-reload"], description: "Reload systemd user units" });
|
|
112
|
+
commands.push({ argv: ["systemctl", "--user", "enable", "--now", "gisool.service"], description: "Enable and start the gisool user service",
|
|
113
|
+
undo: { argv: ["systemctl", "--user", "disable", "--now", "gisool.service"], description: "Stop and disable the gisool user service" } });
|
|
114
|
+
if (before !== null)
|
|
115
|
+
commands.push({ argv: ["systemctl", "--user", "restart", "gisool.service"], description: "Restart gisool with the new unit" });
|
|
116
|
+
}
|
|
117
|
+
if (ctx.env.GISOOL_NO_CLI) {
|
|
118
|
+
notes.push(`GISOOL_NO_CLI set: service file written but not loaded; run manually: ${commands.map((c) => c.argv.join(" ")).join(" && ")}`);
|
|
119
|
+
return { file, commands: [], notes };
|
|
120
|
+
}
|
|
121
|
+
return { file, commands, notes };
|
|
122
|
+
}
|
|
123
|
+
export function serviceLoaded(platform, uid, env) {
|
|
124
|
+
const argv = platform === "darwin" ? ["launchctl", "print", `gui/${uid}/${SERVICE_LABEL}`] : ["systemctl", "--user", "is-active", "gisool"];
|
|
125
|
+
const r = spawnSync(argv[0], argv.slice(1), { env, stdio: "ignore" });
|
|
126
|
+
return r.status === 0;
|
|
127
|
+
}
|
|
128
|
+
export function currentServicePlatform() {
|
|
129
|
+
return process.platform === "darwin" ? "darwin" : "linux";
|
|
130
|
+
}
|
|
131
|
+
export function serviceStatus(env, io) {
|
|
132
|
+
const home = resolveHome(env);
|
|
133
|
+
const platform = currentServicePlatform();
|
|
134
|
+
const path = servicePath(home, platform);
|
|
135
|
+
const installed = existsSync(path);
|
|
136
|
+
io.out(`service file: ${installed ? path : "not installed"}\n`);
|
|
137
|
+
if (env.GISOOL_NO_CLI) {
|
|
138
|
+
io.out("service loaded: skipped (GISOOL_NO_CLI)\n");
|
|
139
|
+
return installed ? 0 : 1;
|
|
140
|
+
}
|
|
141
|
+
const loaded = serviceLoaded(platform, process.getuid?.() ?? 0, env);
|
|
142
|
+
io.out(`service loaded: ${loaded ? "yes" : "no"}\n`);
|
|
143
|
+
return installed && loaded ? 0 : 1;
|
|
144
|
+
}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { isAbsolute, join } from "node:path";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
export const DEFAULT_PORT = 8799;
|
|
7
|
+
export const DEFAULT_ROOTS = [
|
|
8
|
+
{ id: "agents", path: "~/.agents/skills" },
|
|
9
|
+
{ id: "claude", path: "~/.claude/skills" },
|
|
10
|
+
{ id: "codex", path: "~/.codex/skills" },
|
|
11
|
+
{ id: "gemini", path: "~/.gemini/skills" },
|
|
12
|
+
{ id: "omo", path: "~/.omo/agent/skills" },
|
|
13
|
+
{ id: "gajae", path: "~/.gjc/agent/skills" },
|
|
14
|
+
{ id: "openclaw", path: "~/.openclaw/skills" },
|
|
15
|
+
{ id: "gisul", path: "~/gisul/skills" },
|
|
16
|
+
];
|
|
17
|
+
const runtimeIds = ["claude-code", "codex", "gemini", "cursor", "omo", "gajae", "openclaw", "unknown"];
|
|
18
|
+
const fileSchema = z.object({
|
|
19
|
+
port: z.number().int().min(1).max(65535).optional(),
|
|
20
|
+
captureLevel: z.enum(["metadata", "redacted", "full"]).optional(),
|
|
21
|
+
retentionDays: z.number().int().positive().nullable().optional(),
|
|
22
|
+
roots: z.array(z.object({ id: z.string().min(1), path: z.string().min(1) })).optional(),
|
|
23
|
+
runtimes: z.partialRecord(z.enum(runtimeIds), z.object({ enabled: z.boolean() })).optional(),
|
|
24
|
+
});
|
|
25
|
+
export function resolveHome(env) {
|
|
26
|
+
return env.HOME && env.HOME.length > 0 ? env.HOME : homedir();
|
|
27
|
+
}
|
|
28
|
+
export function resolveGisoolHome(env) {
|
|
29
|
+
return env.GISOOL_HOME && env.GISOOL_HOME.length > 0 ? env.GISOOL_HOME : join(resolveHome(env), ".gisool");
|
|
30
|
+
}
|
|
31
|
+
export function expandHome(path, home) {
|
|
32
|
+
if (path === "~")
|
|
33
|
+
return home;
|
|
34
|
+
if (path.startsWith("~/"))
|
|
35
|
+
return join(home, path.slice(2));
|
|
36
|
+
return isAbsolute(path) ? path : join(home, path);
|
|
37
|
+
}
|
|
38
|
+
export function defaultConfigFile() {
|
|
39
|
+
return { port: DEFAULT_PORT, captureLevel: "full", retentionDays: null, roots: DEFAULT_ROOTS.map((r) => ({ ...r })), runtimes: {} };
|
|
40
|
+
}
|
|
41
|
+
export function ensureGisoolHome(env) {
|
|
42
|
+
const dir = resolveGisoolHome(env);
|
|
43
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
44
|
+
chmodSync(dir, 0o700);
|
|
45
|
+
return dir;
|
|
46
|
+
}
|
|
47
|
+
/** Returns the daemon bearer token, creating a 32-byte hex token (mode 0600) on first use. */
|
|
48
|
+
export function ensureToken(gisoolHome) {
|
|
49
|
+
const path = join(gisoolHome, "token");
|
|
50
|
+
if (existsSync(path)) {
|
|
51
|
+
const token = readFileSync(path, "utf8").trim();
|
|
52
|
+
if (/^[0-9a-f]{64}$/.test(token))
|
|
53
|
+
return token;
|
|
54
|
+
}
|
|
55
|
+
const token = randomBytes(32).toString("hex");
|
|
56
|
+
writeFileSync(path, token + "\n", { mode: 0o600 });
|
|
57
|
+
chmodSync(path, 0o600);
|
|
58
|
+
return token;
|
|
59
|
+
}
|
|
60
|
+
/** Loads (and on first run creates) GISOOL_HOME, config.json and the token. No work happens at import time. */
|
|
61
|
+
export function loadConfig(env) {
|
|
62
|
+
const home = resolveHome(env);
|
|
63
|
+
const gisoolHome = ensureGisoolHome(env);
|
|
64
|
+
const configPath = join(gisoolHome, "config.json");
|
|
65
|
+
if (!existsSync(configPath)) {
|
|
66
|
+
writeFileSync(configPath, JSON.stringify(defaultConfigFile(), null, 2) + "\n", { mode: 0o600 });
|
|
67
|
+
}
|
|
68
|
+
const parsed = fileSchema.parse(JSON.parse(readFileSync(configPath, "utf8")));
|
|
69
|
+
ensureToken(gisoolHome);
|
|
70
|
+
const defaults = defaultConfigFile();
|
|
71
|
+
return {
|
|
72
|
+
port: parsed.port ?? defaults.port,
|
|
73
|
+
captureLevel: parsed.captureLevel ?? defaults.captureLevel,
|
|
74
|
+
retentionDays: parsed.retentionDays ?? null,
|
|
75
|
+
roots: (parsed.roots ?? defaults.roots).map((r) => ({ id: r.id, path: expandHome(r.path, home) })),
|
|
76
|
+
runtimes: parsed.runtimes ?? {},
|
|
77
|
+
};
|
|
78
|
+
}
|