kentutai-app 1.1.2 → 1.1.4
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/cli/scripts/build-cli.js +265 -0
- package/cli/scripts/buildMitm.js +70 -0
- package/package.json +11 -3
- package/.dockerignore +0 -32
- package/Dockerfile +0 -52
- package/captain-definition +0 -4
- package/check-connections.js +0 -20
- package/check-db.js +0 -41
- package/check-key.js +0 -20
- package/check-key2.js +0 -21
- package/check-mimo-conn.js +0 -29
- package/check-mimo.js +0 -22
- package/check-nodes.js +0 -26
- package/check-nodes2.js +0 -29
- package/check-providers.js +0 -48
- package/dev.log +0 -15
- package/next-env.d.ts +0 -6
- package/opencode.json +0 -22
- package/server-error.log +0 -2
- package/server.log +0 -245
- package/start.sh +0 -4
- package/tsconfig.tsbuildinfo +0 -1
- package/vitest.config.js +0 -21
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const { execSync } = require("child_process");
|
|
6
|
+
|
|
7
|
+
const cliDir = path.resolve(__dirname, "..");
|
|
8
|
+
const appDir = path.resolve(cliDir, "..");
|
|
9
|
+
const rootDir = path.resolve(appDir, "..");
|
|
10
|
+
const cliAppDir = path.join(cliDir, "app");
|
|
11
|
+
const buildHomeDir = path.join(cliDir, ".build-home");
|
|
12
|
+
const buildDistDirName = ".next-cli-build";
|
|
13
|
+
const buildDistDir = path.join(appDir, buildDistDirName);
|
|
14
|
+
|
|
15
|
+
// Exclude patterns for files/folders we don't want to copy
|
|
16
|
+
const EXCLUDE_PATTERNS = [
|
|
17
|
+
"@img", // Sharp image processing (not needed with unoptimized images)
|
|
18
|
+
"sharp", // Sharp core lib (not needed with unoptimized images)
|
|
19
|
+
"detect-libc", // Sharp dependency
|
|
20
|
+
"logs", // Runtime logs
|
|
21
|
+
".env", // Environment files
|
|
22
|
+
".env.local",
|
|
23
|
+
".env.*.local",
|
|
24
|
+
"*.log", // Log files
|
|
25
|
+
"tmp", // Temp files
|
|
26
|
+
".DS_Store", // macOS files
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
function shouldExclude(name) {
|
|
30
|
+
return EXCLUDE_PATTERNS.some(pattern => {
|
|
31
|
+
if (pattern.includes("*")) {
|
|
32
|
+
const regex = new RegExp("^" + pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*") + "$");
|
|
33
|
+
return regex.test(name);
|
|
34
|
+
}
|
|
35
|
+
return name === pattern;
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function copyRecursive(src, dest) {
|
|
40
|
+
if (!fs.existsSync(src)) {
|
|
41
|
+
console.warn(`Warning: Source ${src} does not exist`);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!fs.existsSync(dest)) {
|
|
46
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
50
|
+
for (const entry of entries) {
|
|
51
|
+
if (shouldExclude(entry.name)) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const srcPath = path.join(src, entry.name);
|
|
56
|
+
const destPath = path.join(dest, entry.name);
|
|
57
|
+
|
|
58
|
+
// Skip broken symlinks (common in workspace setups)
|
|
59
|
+
try {
|
|
60
|
+
fs.accessSync(srcPath);
|
|
61
|
+
} catch {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (entry.isDirectory()) {
|
|
66
|
+
copyRecursive(srcPath, destPath);
|
|
67
|
+
} else if (entry.isSymbolicLink()) {
|
|
68
|
+
// Resolve and copy target (avoid linking outside bundle)
|
|
69
|
+
try {
|
|
70
|
+
const real = fs.realpathSync(srcPath);
|
|
71
|
+
if (fs.statSync(real).isDirectory()) {
|
|
72
|
+
copyRecursive(real, destPath);
|
|
73
|
+
} else {
|
|
74
|
+
fs.copyFileSync(real, destPath);
|
|
75
|
+
}
|
|
76
|
+
} catch {}
|
|
77
|
+
} else {
|
|
78
|
+
try {
|
|
79
|
+
fs.copyFileSync(srcPath, destPath);
|
|
80
|
+
} catch {}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
console.log("📦 Building KentutAI CLI package with Next.js...\n");
|
|
86
|
+
|
|
87
|
+
fs.mkdirSync(buildHomeDir, { recursive: true });
|
|
88
|
+
fs.mkdirSync(path.join(buildHomeDir, "AppData", "Roaming"), { recursive: true });
|
|
89
|
+
fs.mkdirSync(path.join(buildHomeDir, "AppData", "Local"), { recursive: true });
|
|
90
|
+
|
|
91
|
+
// Step 0: Sync version from app/cli/package.json to app/package.json
|
|
92
|
+
console.log("0️⃣ Syncing version to app/package.json...");
|
|
93
|
+
const cliPkg = JSON.parse(fs.readFileSync(path.join(cliDir, "package.json"), "utf8"));
|
|
94
|
+
const appPkgPath = path.join(appDir, "package.json");
|
|
95
|
+
const appPkg = JSON.parse(fs.readFileSync(appPkgPath, "utf8"));
|
|
96
|
+
if (appPkg.version !== cliPkg.version) {
|
|
97
|
+
appPkg.version = cliPkg.version;
|
|
98
|
+
fs.writeFileSync(appPkgPath, JSON.stringify(appPkg, null, 2) + "\n");
|
|
99
|
+
console.log(`✅ Version synced: ${cliPkg.version}\n`);
|
|
100
|
+
} else {
|
|
101
|
+
console.log(`✅ Version already synced: ${cliPkg.version}\n`);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Step 1: Build app with Next.js (workspace tracing root → traced node_modules in standalone).
|
|
105
|
+
console.log("1️⃣ Building Next.js app...");
|
|
106
|
+
try {
|
|
107
|
+
execSync("npm run build", {
|
|
108
|
+
stdio: "inherit",
|
|
109
|
+
cwd: appDir,
|
|
110
|
+
env: {
|
|
111
|
+
...process.env,
|
|
112
|
+
HOME: buildHomeDir,
|
|
113
|
+
USERPROFILE: buildHomeDir,
|
|
114
|
+
APPDATA: path.join(buildHomeDir, "AppData", "Roaming"),
|
|
115
|
+
LOCALAPPDATA: path.join(buildHomeDir, "AppData", "Local"),
|
|
116
|
+
NEXT_DIST_DIR: buildDistDirName,
|
|
117
|
+
NEXT_TRACING_ROOT_MODE: "workspace",
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
console.log("✅ Next.js build completed\n");
|
|
121
|
+
} catch (error) {
|
|
122
|
+
console.error("❌ Next.js build failed");
|
|
123
|
+
process.exit(1);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Step 2: Clean old app/cli/app if exists
|
|
127
|
+
console.log("2️⃣ Cleaning old app/cli/app...");
|
|
128
|
+
if (fs.existsSync(cliAppDir)) {
|
|
129
|
+
fs.rmSync(cliAppDir, { recursive: true, force: true });
|
|
130
|
+
}
|
|
131
|
+
console.log("✅ Cleaned\n");
|
|
132
|
+
|
|
133
|
+
// Step 3: Copy Next.js standalone build to app/cli/app.
|
|
134
|
+
// Newer Next.js standalone output writes server.js/package.json plus .next/, src/, and
|
|
135
|
+
// node_modules/ directly under .next/standalone. Older builds may still use a nested app/.
|
|
136
|
+
console.log("3️⃣ Copying Next.js standalone build to app/cli/app...");
|
|
137
|
+
const standaloneRoot = path.join(appDir, ".next", "standalone");
|
|
138
|
+
const standaloneRootResolved = path.join(buildDistDir, "standalone");
|
|
139
|
+
const standaloneRootToUse = fs.existsSync(standaloneRootResolved) ? standaloneRootResolved : standaloneRoot;
|
|
140
|
+
const standaloneApp = fs.existsSync(path.join(standaloneRootToUse, "server.js"))
|
|
141
|
+
? standaloneRootToUse
|
|
142
|
+
: path.join(standaloneRootToUse, "app");
|
|
143
|
+
if (!fs.existsSync(standaloneApp)) {
|
|
144
|
+
console.error("❌ Next.js standalone build not found under .next/standalone");
|
|
145
|
+
console.error("Expected either .next/standalone/server.js or .next/standalone/app/");
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
148
|
+
copyRecursive(standaloneApp, cliAppDir);
|
|
149
|
+
|
|
150
|
+
// Older nested-app layout stores traced node_modules at standalone root.
|
|
151
|
+
const standaloneNodeModules = path.join(standaloneRootToUse, "node_modules");
|
|
152
|
+
if (standaloneApp !== standaloneRootToUse && fs.existsSync(standaloneNodeModules)) {
|
|
153
|
+
copyRecursive(standaloneNodeModules, path.join(cliAppDir, "node_modules"));
|
|
154
|
+
}
|
|
155
|
+
console.log("✅ Copied standalone build\n");
|
|
156
|
+
|
|
157
|
+
// Step 3b: Ensure sql.js (pure JS fallback) bundled in app/cli/app/node_modules.
|
|
158
|
+
// Strip better-sqlite3 (native) — it lives in ~/.kentutai/runtime to avoid
|
|
159
|
+
// Windows EBUSY during global CLI updates. node:sqlite (Node ≥22.5) is also
|
|
160
|
+
// available as a no-install middle tier.
|
|
161
|
+
console.log("3️⃣ b Configuring SQLite drivers...");
|
|
162
|
+
function ensureModuleInBundle(pkg) {
|
|
163
|
+
const dest = path.join(cliAppDir, "node_modules", pkg);
|
|
164
|
+
if (fs.existsSync(dest)) {
|
|
165
|
+
console.log(`✅ ${pkg} already bundled`);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
const candidates = [
|
|
169
|
+
path.join(appDir, "node_modules", pkg),
|
|
170
|
+
path.join(rootDir, "node_modules", pkg),
|
|
171
|
+
];
|
|
172
|
+
const src = candidates.find((p) => fs.existsSync(p));
|
|
173
|
+
if (!src) {
|
|
174
|
+
console.warn(`⚠️ ${pkg} not found locally — bundle will rely on node:sqlite or runtime install`);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
178
|
+
copyRecursive(src, dest);
|
|
179
|
+
console.log(`✅ Bundled ${pkg}`);
|
|
180
|
+
}
|
|
181
|
+
ensureModuleInBundle("sql.js");
|
|
182
|
+
const betterDir = path.join(cliAppDir, "node_modules", "better-sqlite3");
|
|
183
|
+
if (fs.existsSync(betterDir)) {
|
|
184
|
+
fs.rmSync(betterDir, { recursive: true, force: true });
|
|
185
|
+
console.log("✅ Stripped better-sqlite3 (lives in ~/.kentutai/runtime)");
|
|
186
|
+
}
|
|
187
|
+
console.log("");
|
|
188
|
+
|
|
189
|
+
// Step 4: Copy static files
|
|
190
|
+
console.log("4️⃣ Copying static files...");
|
|
191
|
+
const staticSrc = path.join(appDir, ".next", "static");
|
|
192
|
+
const staticSrcResolved = path.join(buildDistDir, "static");
|
|
193
|
+
const staticDest = path.join(cliAppDir, buildDistDirName, "static");
|
|
194
|
+
if (fs.existsSync(staticSrcResolved) || fs.existsSync(staticSrc)) {
|
|
195
|
+
copyRecursive(fs.existsSync(staticSrcResolved) ? staticSrcResolved : staticSrc, staticDest);
|
|
196
|
+
console.log("✅ Copied static files\n");
|
|
197
|
+
} else {
|
|
198
|
+
console.log("⏭️ No static files found\n");
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Step 5: Copy public folder if exists
|
|
202
|
+
console.log("5️⃣ Copying public folder...");
|
|
203
|
+
const publicSrc = path.join(appDir, "public");
|
|
204
|
+
const publicDest = path.join(cliAppDir, "public");
|
|
205
|
+
if (fs.existsSync(publicSrc)) {
|
|
206
|
+
copyRecursive(publicSrc, publicDest);
|
|
207
|
+
console.log("✅ Copied public folder\n");
|
|
208
|
+
} else {
|
|
209
|
+
console.log("⏭️ No public folder found\n");
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Step 6: Copy vendor-chunks (required for production)
|
|
213
|
+
console.log("6️⃣ Copying vendor-chunks...");
|
|
214
|
+
const vendorChunksSrc = path.join(appDir, ".next", "server", "vendor-chunks");
|
|
215
|
+
const vendorChunksSrcResolved = path.join(buildDistDir, "server", "vendor-chunks");
|
|
216
|
+
const vendorChunksDest = path.join(cliAppDir, buildDistDirName, "server", "vendor-chunks");
|
|
217
|
+
if (fs.existsSync(vendorChunksSrcResolved) || fs.existsSync(vendorChunksSrc)) {
|
|
218
|
+
copyRecursive(fs.existsSync(vendorChunksSrcResolved) ? vendorChunksSrcResolved : vendorChunksSrc, vendorChunksDest);
|
|
219
|
+
console.log("✅ Copied vendor-chunks\n");
|
|
220
|
+
} else {
|
|
221
|
+
console.log("⏭️ No vendor-chunks found\n");
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Step 7: Copy MITM server files (not bundled by Next.js standalone)
|
|
225
|
+
console.log("7️⃣ Copying MITM server files...");
|
|
226
|
+
const mitmSrc = path.join(appDir, "src", "mitm");
|
|
227
|
+
const mitmDest = path.join(cliAppDir, "src", "mitm");
|
|
228
|
+
if (fs.existsSync(mitmSrc)) {
|
|
229
|
+
copyRecursive(mitmSrc, mitmDest);
|
|
230
|
+
console.log("✅ Copied MITM files\n");
|
|
231
|
+
} else {
|
|
232
|
+
console.log("⏭️ No MITM files found\n");
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Step 7b: Copy standalone updater (headless Node process for install progress)
|
|
236
|
+
console.log("7️⃣ b Copying updater files...");
|
|
237
|
+
const updaterSrc = path.join(appDir, "src", "lib", "updater");
|
|
238
|
+
const updaterDest = path.join(cliAppDir, "src", "lib", "updater");
|
|
239
|
+
if (fs.existsSync(updaterSrc)) {
|
|
240
|
+
copyRecursive(updaterSrc, updaterDest);
|
|
241
|
+
console.log("✅ Copied updater files\n");
|
|
242
|
+
} else {
|
|
243
|
+
console.log("⏭️ No updater files found\n");
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Step 8: Build MITM server (config driven - see app/cli/scripts/buildMitm.js)
|
|
247
|
+
console.log("8️⃣ Building MITM server...");
|
|
248
|
+
try {
|
|
249
|
+
execSync("node scripts/buildMitm.js", { stdio: "inherit", cwd: cliDir });
|
|
250
|
+
console.log("✅ MITM server build completed\n");
|
|
251
|
+
} catch (error) {
|
|
252
|
+
console.error("❌ MITM build failed");
|
|
253
|
+
process.exit(1);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
console.log("✨ CLI package build completed!");
|
|
257
|
+
console.log(`📁 Output: ${cliAppDir}`);
|
|
258
|
+
|
|
259
|
+
try {
|
|
260
|
+
const { execSync: exec } = require("child_process");
|
|
261
|
+
const size = exec(`du -sh "${cliAppDir}"`, { encoding: "utf8" }).trim();
|
|
262
|
+
console.log(`📊 Package size: ${size.split("\t")[0]}`);
|
|
263
|
+
} catch (e) {
|
|
264
|
+
// Silent fail on size check
|
|
265
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
const esbuild = require("esbuild");
|
|
2
|
+
const fs = require("fs");
|
|
3
|
+
const path = require("path");
|
|
4
|
+
|
|
5
|
+
// ── Build config ─────────────────────────────────────────
|
|
6
|
+
const BUILD_CONFIG = {
|
|
7
|
+
bundle: true,
|
|
8
|
+
minify: true,
|
|
9
|
+
cleanPlainFiles: true,
|
|
10
|
+
};
|
|
11
|
+
// ─────────────────────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
const cliDir = path.resolve(__dirname, "..");
|
|
14
|
+
const appDir = path.resolve(cliDir, "..");
|
|
15
|
+
const cliMitmDir = path.join(cliDir, "app", "src", "mitm");
|
|
16
|
+
// Bundle everything — no externals. This keeps MITM runtime self-contained so
|
|
17
|
+
// it can be copied to DATA_DIR/runtime/ and spawned from there (escapes
|
|
18
|
+
// node_modules file locks that block `npm i -g kentutai@latest` on Windows).
|
|
19
|
+
const EXTERNALS = [];
|
|
20
|
+
const ENTRIES = ["server.js"];
|
|
21
|
+
|
|
22
|
+
async function buildEntry(entry) {
|
|
23
|
+
const mitmSrc = path.join(appDir, "src", "mitm");
|
|
24
|
+
const output = path.join(cliMitmDir, entry);
|
|
25
|
+
|
|
26
|
+
const buildPlugin = {
|
|
27
|
+
name: "build-plugin",
|
|
28
|
+
setup(build) {
|
|
29
|
+
// Stub .git file scanned by esbuild
|
|
30
|
+
build.onResolve({ filter: /\.git/ }, args => ({ path: args.path, namespace: "git-stub" }));
|
|
31
|
+
build.onLoad({ filter: /.*/, namespace: "git-stub" }, () => ({ contents: "module.exports={}", loader: "js" }));
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const steps = [];
|
|
36
|
+
|
|
37
|
+
if (BUILD_CONFIG.bundle) {
|
|
38
|
+
await esbuild.build({
|
|
39
|
+
entryPoints: [path.join(mitmSrc, entry)],
|
|
40
|
+
bundle: true,
|
|
41
|
+
minify: BUILD_CONFIG.minify,
|
|
42
|
+
platform: "node",
|
|
43
|
+
target: "node18",
|
|
44
|
+
external: EXTERNALS,
|
|
45
|
+
plugins: [buildPlugin],
|
|
46
|
+
outfile: output,
|
|
47
|
+
});
|
|
48
|
+
steps.push("bundled");
|
|
49
|
+
if (BUILD_CONFIG.minify) steps.push("minified");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
console.log(`✅ ${steps.join(" + ")} → ${output}`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function run() {
|
|
56
|
+
const flags = Object.entries(BUILD_CONFIG).filter(([, v]) => v).map(([k]) => k).join(", ");
|
|
57
|
+
console.log(`⚙️ Config: ${flags}`);
|
|
58
|
+
|
|
59
|
+
for (const entry of ENTRIES) await buildEntry(entry);
|
|
60
|
+
|
|
61
|
+
if (BUILD_CONFIG.cleanPlainFiles) {
|
|
62
|
+
const keep = new Set(ENTRIES);
|
|
63
|
+
for (const name of fs.readdirSync(cliMitmDir)) {
|
|
64
|
+
if (!keep.has(name)) fs.rmSync(path.join(cliMitmDir, name), { recursive: true, force: true });
|
|
65
|
+
}
|
|
66
|
+
console.log("✅ Removed plain MITM files from CLI bundle");
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
run().catch((e) => { console.error(e); process.exit(1); });
|
package/package.json
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kentutai-app",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.4",
|
|
4
4
|
"description": "KentutAI web dashboard",
|
|
5
5
|
"private": false,
|
|
6
6
|
"bin": {
|
|
7
|
-
"kentutai": "bin.js"
|
|
7
|
+
"kentutai": "./bin.js"
|
|
8
8
|
},
|
|
9
|
-
"
|
|
9
|
+
"files": [
|
|
10
|
+
"bin.js",
|
|
11
|
+
"cli/",
|
|
12
|
+
"public/",
|
|
13
|
+
"README.md",
|
|
14
|
+
"LICENSE",
|
|
15
|
+
"package.json"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
10
18
|
"dev": "next dev --webpack --port 20123",
|
|
11
19
|
"build": "next build",
|
|
12
20
|
"build:cli": "powershell -ExecutionPolicy Bypass -File scripts/build-cli.ps1",
|
package/.dockerignore
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
# VCS
|
|
2
|
-
.git
|
|
3
|
-
**/.git
|
|
4
|
-
|
|
5
|
-
# Editor
|
|
6
|
-
.vscode
|
|
7
|
-
**/.vscode
|
|
8
|
-
|
|
9
|
-
# Dependencies and build output
|
|
10
|
-
node_modules
|
|
11
|
-
.next
|
|
12
|
-
out
|
|
13
|
-
build
|
|
14
|
-
dist
|
|
15
|
-
coverage
|
|
16
|
-
|
|
17
|
-
# Runtime data and logs
|
|
18
|
-
data
|
|
19
|
-
logs
|
|
20
|
-
|
|
21
|
-
# Local env files (inject at runtime via --env-file or -e)
|
|
22
|
-
.env
|
|
23
|
-
.env.local
|
|
24
|
-
.env.development.local
|
|
25
|
-
.env.test.local
|
|
26
|
-
.env.production.local
|
|
27
|
-
|
|
28
|
-
# Debug logs
|
|
29
|
-
npm-debug.log*
|
|
30
|
-
yarn-debug.log*
|
|
31
|
-
yarn-error.log*
|
|
32
|
-
.pnpm-debug.log*
|
package/Dockerfile
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
# syntax=docker/dockerfile:1.7
|
|
2
|
-
ARG NODE_IMAGE=node:22-alpine
|
|
3
|
-
FROM ${NODE_IMAGE} AS base
|
|
4
|
-
WORKDIR /app
|
|
5
|
-
|
|
6
|
-
FROM base AS builder
|
|
7
|
-
|
|
8
|
-
RUN apk --no-cache upgrade && apk --no-cache add python3 make g++ linux-headers
|
|
9
|
-
|
|
10
|
-
COPY package.json ./
|
|
11
|
-
RUN --mount=type=cache,target=/root/.npm \
|
|
12
|
-
npm install
|
|
13
|
-
|
|
14
|
-
COPY . ./
|
|
15
|
-
ENV NEXT_TELEMETRY_DISABLED=1
|
|
16
|
-
RUN npm run build
|
|
17
|
-
|
|
18
|
-
FROM ${NODE_IMAGE} AS runner
|
|
19
|
-
WORKDIR /app
|
|
20
|
-
|
|
21
|
-
LABEL org.opencontainers.image.title="kentutai"
|
|
22
|
-
|
|
23
|
-
ENV NODE_ENV=production
|
|
24
|
-
ENV PORT=20123
|
|
25
|
-
ENV HOSTNAME=0.0.0.0
|
|
26
|
-
ENV NEXT_TELEMETRY_DISABLED=1
|
|
27
|
-
ENV DATA_DIR=/app/data
|
|
28
|
-
|
|
29
|
-
COPY --from=builder /app/public ./public
|
|
30
|
-
COPY --from=builder /app/.next/static ./.next/static
|
|
31
|
-
COPY --from=builder /app/.next/standalone ./
|
|
32
|
-
COPY --from=builder /app/open-sse ./open-sse
|
|
33
|
-
# Next file tracing can omit sibling files; MITM runs server.js as a separate process.
|
|
34
|
-
COPY --from=builder /app/src/mitm ./src/mitm
|
|
35
|
-
# Standalone node_modules may omit deps only required by the MITM child process.
|
|
36
|
-
COPY --from=builder /app/node_modules/node-forge ./node_modules/node-forge
|
|
37
|
-
# Ensure `next` is available at runtime in case tracing did not include it.
|
|
38
|
-
COPY --from=builder /app/node_modules/next ./node_modules/next
|
|
39
|
-
|
|
40
|
-
RUN mkdir -p /app/data && chown -R node:node /app && \
|
|
41
|
-
mkdir -p /app/data-home && chown node:node /app/data-home && \
|
|
42
|
-
ln -sf /app/data-home /root/.kentutai 2>/dev/null || true
|
|
43
|
-
|
|
44
|
-
# Fix permissions at runtime (handles mounted volumes)
|
|
45
|
-
RUN apk --no-cache upgrade && apk --no-cache add su-exec && \
|
|
46
|
-
printf '#!/bin/sh\nchown -R node:node /app/data /app/data-home 2>/dev/null\nexec su-exec node "$@"\n' > /entrypoint.sh && \
|
|
47
|
-
chmod +x /entrypoint.sh
|
|
48
|
-
|
|
49
|
-
EXPOSE 20123
|
|
50
|
-
|
|
51
|
-
ENTRYPOINT ["/entrypoint.sh"]
|
|
52
|
-
CMD ["node", "server.js"]
|
package/captain-definition
DELETED
package/check-connections.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
const initSqlJs = require('sql.js');
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
|
|
4
|
-
initSqlJs().then(SQL => {
|
|
5
|
-
const buffer = fs.readFileSync('E:/var/lib/9router/db/data.sqlite');
|
|
6
|
-
const db = new SQL.Database(buffer);
|
|
7
|
-
|
|
8
|
-
// Get all provider connections with full data
|
|
9
|
-
const connections = db.exec("SELECT id, data FROM providerConnections");
|
|
10
|
-
if (connections[0]?.values) {
|
|
11
|
-
connections[0].values.forEach(row => {
|
|
12
|
-
const data = JSON.parse(row[1]);
|
|
13
|
-
console.log(`\n--- Connection: ${row[0]} ---`);
|
|
14
|
-
console.log(JSON.stringify(data, null, 2));
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
db.close();
|
|
19
|
-
process.exit(0);
|
|
20
|
-
});
|
package/check-db.js
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
const initSqlJs = require('sql.js');
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
|
|
4
|
-
initSqlJs().then(SQL => {
|
|
5
|
-
const buffer = fs.readFileSync('E:/var/lib/9router/db/data.sqlite');
|
|
6
|
-
const db = new SQL.Database(buffer);
|
|
7
|
-
|
|
8
|
-
// List all tables
|
|
9
|
-
const tables = db.exec("SELECT name FROM sqlite_master WHERE type='table'");
|
|
10
|
-
console.log('Tables:', tables[0]?.values.map(v => v[0]).join(', '));
|
|
11
|
-
|
|
12
|
-
// Get providers
|
|
13
|
-
try {
|
|
14
|
-
const providers = db.exec("SELECT id, data FROM providers");
|
|
15
|
-
console.log('\nProviders:', providers[0]?.columns);
|
|
16
|
-
if (providers[0]?.values) {
|
|
17
|
-
providers[0].values.forEach(row => {
|
|
18
|
-
const data = JSON.parse(row[1]);
|
|
19
|
-
console.log(`- ${row[0]}: ${data.name || 'unknown'}, enabled: ${data.enabled}`);
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
} catch (e) {
|
|
23
|
-
console.log('Providers error:', e.message);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Get API keys
|
|
27
|
-
try {
|
|
28
|
-
const keys = db.exec("SELECT id, key, name FROM apiKeys");
|
|
29
|
-
console.log('\nAPI Keys:', keys[0]?.columns);
|
|
30
|
-
if (keys[0]?.values) {
|
|
31
|
-
keys[0].values.forEach(row => {
|
|
32
|
-
console.log(`- ${row[2]}: ${row[1]?.substring(0, 10)}...`);
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
} catch (e) {
|
|
36
|
-
console.log('API Keys error:', e.message);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
db.close();
|
|
40
|
-
process.exit(0);
|
|
41
|
-
});
|
package/check-key.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
const initSqlJs = require('sql.js');
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
|
|
4
|
-
initSqlJs().then(SQL => {
|
|
5
|
-
const buffer = fs.readFileSync('E:/var/lib/9router/db/data.sqlite');
|
|
6
|
-
const db = new SQL.Database(buffer);
|
|
7
|
-
|
|
8
|
-
// Get full key data for the first key
|
|
9
|
-
const result = db.exec("SELECT id, key, name, data FROM apiKeys LIMIT 1");
|
|
10
|
-
if (result[0]?.values) {
|
|
11
|
-
console.log('First API Key:');
|
|
12
|
-
console.log('ID:', result[0].values[0][0]);
|
|
13
|
-
console.log('Key:', result[0].values[0][1]);
|
|
14
|
-
console.log('Name:', result[0].values[0][2]);
|
|
15
|
-
console.log('Data:', result[0].values[0][3]);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
db.close();
|
|
19
|
-
process.exit(0);
|
|
20
|
-
});
|
package/check-key2.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
const initSqlJs = require('sql.js');
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
|
|
4
|
-
initSqlJs().then(SQL => {
|
|
5
|
-
const buffer = fs.readFileSync('E:/var/lib/9router/db/data.sqlite');
|
|
6
|
-
const db = new SQL.Database(buffer);
|
|
7
|
-
|
|
8
|
-
// Get schema
|
|
9
|
-
const schema = db.exec("SELECT sql FROM sqlite_master WHERE name='apiKeys'");
|
|
10
|
-
console.log('apiKeys schema:', schema[0]?.values[0][0]);
|
|
11
|
-
|
|
12
|
-
// Get first key
|
|
13
|
-
const result = db.exec("SELECT * FROM apiKeys LIMIT 1");
|
|
14
|
-
if (result[0]) {
|
|
15
|
-
console.log('\nColumns:', result[0].columns);
|
|
16
|
-
console.log('First row:', result[0].values[0]);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
db.close();
|
|
20
|
-
process.exit(0);
|
|
21
|
-
});
|
package/check-mimo-conn.js
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
const initSqlJs = require('sql.js');
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
|
|
4
|
-
initSqlJs().then(SQL => {
|
|
5
|
-
const buffer = fs.readFileSync('E:/var/lib/9router/db/data.sqlite');
|
|
6
|
-
const db = new SQL.Database(buffer);
|
|
7
|
-
|
|
8
|
-
// Get all connections with provider column
|
|
9
|
-
const result = db.exec("SELECT id, provider, isActive, data FROM providerConnections");
|
|
10
|
-
if (result[0]?.values) {
|
|
11
|
-
console.log('All Provider Connections (with provider column):\n');
|
|
12
|
-
result[0].values.forEach(row => {
|
|
13
|
-
const data = JSON.parse(row[3] || '{}');
|
|
14
|
-
const isMimo = data.baseUrl?.includes('mimo') || data.providerSpecificData?.baseUrl?.includes('mimo');
|
|
15
|
-
if (isMimo || row[1]?.includes('mimo')) {
|
|
16
|
-
console.log(`--- ${row[0]} ---`);
|
|
17
|
-
console.log(`Provider: ${row[1]}`);
|
|
18
|
-
console.log(`isActive: ${row[2]}`);
|
|
19
|
-
console.log(`API Key: ${data.apiKey || data.providerSpecificData?.apiKey || 'NOT FOUND'}`);
|
|
20
|
-
console.log(`Prefix: ${data.prefix || data.providerSpecificData?.prefix}`);
|
|
21
|
-
console.log(`Base URL: ${data.baseUrl || data.providerSpecificData?.baseUrl}`);
|
|
22
|
-
console.log('');
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
db.close();
|
|
28
|
-
process.exit(0);
|
|
29
|
-
});
|
package/check-mimo.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
const initSqlJs = require('sql.js');
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
|
|
4
|
-
initSqlJs().then(SQL => {
|
|
5
|
-
const buffer = fs.readFileSync('E:/var/lib/9router/db/data.sqlite');
|
|
6
|
-
const db = new SQL.Database(buffer);
|
|
7
|
-
|
|
8
|
-
// Get MiMo provider full data
|
|
9
|
-
const nodes = db.exec("SELECT id, data FROM providerNodes");
|
|
10
|
-
if (nodes[0]?.values) {
|
|
11
|
-
nodes[0].values.forEach(row => {
|
|
12
|
-
const data = JSON.parse(row[1]);
|
|
13
|
-
if (data.baseUrl?.includes('mimo')) {
|
|
14
|
-
console.log('MiMo Provider Full Data:');
|
|
15
|
-
console.log(JSON.stringify(data, null, 2));
|
|
16
|
-
}
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
db.close();
|
|
21
|
-
process.exit(0);
|
|
22
|
-
});
|
package/check-nodes.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
const initSqlJs = require('sql.js');
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
|
|
4
|
-
initSqlJs().then(SQL => {
|
|
5
|
-
const buffer = fs.readFileSync('E:/var/lib/9router/db/data.sqlite');
|
|
6
|
-
const db = new SQL.Database(buffer);
|
|
7
|
-
|
|
8
|
-
// Get all provider nodes
|
|
9
|
-
const result = db.exec("SELECT id, provider, name, data FROM providerNodes");
|
|
10
|
-
if (result[0]?.values) {
|
|
11
|
-
console.log('All Provider Nodes:\n');
|
|
12
|
-
result[0].values.forEach(row => {
|
|
13
|
-
const data = JSON.parse(row[3] || '{}');
|
|
14
|
-
console.log(`ID: ${row[0]}`);
|
|
15
|
-
console.log(`Provider: ${row[1]}`);
|
|
16
|
-
console.log(`Name: ${row[2]}`);
|
|
17
|
-
console.log(`Prefix: ${data.prefix}`);
|
|
18
|
-
console.log(`Base URL: ${data.baseUrl}`);
|
|
19
|
-
console.log(`Has API Key: ${!!data.apiKey}`);
|
|
20
|
-
console.log('---');
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
db.close();
|
|
25
|
-
process.exit(0);
|
|
26
|
-
});
|
package/check-nodes2.js
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
const initSqlJs = require('sql.js');
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
|
|
4
|
-
initSqlJs().then(SQL => {
|
|
5
|
-
const buffer = fs.readFileSync('E:/var/lib/9router/db/data.sqlite');
|
|
6
|
-
const db = new SQL.Database(buffer);
|
|
7
|
-
|
|
8
|
-
// Get schema
|
|
9
|
-
const schema = db.exec("SELECT sql FROM sqlite_master WHERE name='providerNodes'");
|
|
10
|
-
console.log('providerNodes schema:');
|
|
11
|
-
console.log(schema[0]?.values[0][0]);
|
|
12
|
-
|
|
13
|
-
// Get all nodes without provider column
|
|
14
|
-
const result = db.exec("SELECT id, data FROM providerNodes");
|
|
15
|
-
if (result[0]?.values) {
|
|
16
|
-
console.log('\nAll Provider Nodes:\n');
|
|
17
|
-
result[0].values.forEach(row => {
|
|
18
|
-
const data = JSON.parse(row[1] || '{}');
|
|
19
|
-
console.log(`ID: ${row[0]}`);
|
|
20
|
-
console.log(`Prefix: ${data.prefix}`);
|
|
21
|
-
console.log(`Base URL: ${data.baseUrl}`);
|
|
22
|
-
console.log(`Name: ${data.name}`);
|
|
23
|
-
console.log('---');
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
db.close();
|
|
28
|
-
process.exit(0);
|
|
29
|
-
});
|