@tokilake/tokiame 0.0.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 ADDED
@@ -0,0 +1,39 @@
1
+ # `@tokilake/tokiame`
2
+
3
+ `tokiame` 的 npm 包只做一件事:在安装时下载对应平台的预编译二进制,并把它暴露成 `tokiame` 命令。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ npm install -g @tokilake/tokiame
9
+ ```
10
+
11
+ ## 默认配置路径
12
+
13
+ 安装完成后,`tokiame` 默认读取:
14
+
15
+ ```text
16
+ ~/.tokilake/tokiame.json
17
+ ```
18
+
19
+ 安装脚本还会额外写入:
20
+
21
+ ```text
22
+ ~/.tokilake/tokiame.json.example
23
+ ```
24
+
25
+ 你可以直接复制一份开始修改:
26
+
27
+ ```bash
28
+ cp ~/.tokilake/tokiame.json.example ~/.tokilake/tokiame.json
29
+ ```
30
+
31
+ 你也可以通过下面两种方式覆盖:
32
+
33
+ - `TOKIAME_CONFIG=/path/to/tokiame.json`
34
+ - `tokiame --config /path/to/tokiame.json`
35
+
36
+ ## 可选环境变量
37
+
38
+ - `TOKIAME_RELEASE_REPO`: 覆盖 GitHub Releases 仓库,默认 `Tokimorphling/Tokilake`
39
+ - `TOKIAME_RELEASE_TAG`: 覆盖 GitHub Releases tag,默认 `v<package-version>`
package/bin/tokiame.js ADDED
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { spawn } = require("node:child_process");
4
+ const { existsSync } = require("node:fs");
5
+ const path = require("node:path");
6
+
7
+ const binaryName = process.platform === "win32" ? "tokiame.exe" : "tokiame";
8
+ const binaryPath = path.join(__dirname, binaryName);
9
+
10
+ if (!existsSync(binaryPath)) {
11
+ console.error("tokiame binary is missing. Reinstall @tokilake/tokiame.");
12
+ process.exit(1);
13
+ }
14
+
15
+ const child = spawn(binaryPath, process.argv.slice(2), {
16
+ stdio: "inherit",
17
+ });
18
+
19
+ child.on("error", (error) => {
20
+ console.error(error.message);
21
+ process.exit(1);
22
+ });
23
+
24
+ child.on("exit", (code, signal) => {
25
+ if (signal) {
26
+ process.kill(process.pid, signal);
27
+ return;
28
+ }
29
+ process.exit(code ?? 1);
30
+ });
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@tokilake/tokiame",
3
+ "version": "0.0.0",
4
+ "description": "Thin npm installer for the Tokiame worker binary",
5
+ "license": "Apache-2.0",
6
+ "homepage": "https://github.com/Tokimorphling/Tokilake",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/Tokimorphling/Tokilake.git",
10
+ "directory": "packaging/npm/tokiame"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/Tokimorphling/Tokilake/issues"
14
+ },
15
+ "bin": {
16
+ "tokiame": "./bin/tokiame.js"
17
+ },
18
+ "files": [
19
+ "bin",
20
+ "scripts",
21
+ "README.md",
22
+ "tokiame.json.example"
23
+ ],
24
+ "scripts": {
25
+ "postinstall": "node ./scripts/install.js"
26
+ },
27
+ "engines": {
28
+ "node": ">=18"
29
+ },
30
+ "os": [
31
+ "darwin",
32
+ "linux",
33
+ "win32"
34
+ ],
35
+ "cpu": [
36
+ "x64",
37
+ "arm64"
38
+ ]
39
+ }
@@ -0,0 +1,150 @@
1
+ "use strict";
2
+
3
+ const crypto = require("node:crypto");
4
+ const fs = require("node:fs");
5
+ const fsp = require("node:fs/promises");
6
+ const https = require("node:https");
7
+ const os = require("node:os");
8
+ const path = require("node:path");
9
+ const { spawnSync } = require("node:child_process");
10
+
11
+ const packageJSON = require("../package.json");
12
+
13
+ const releaseRepo = process.env.TOKIAME_RELEASE_REPO || "Tokimorphling/Tokilake";
14
+ const releaseTag = process.env.TOKIAME_RELEASE_TAG || `v${packageJSON.version}`;
15
+
16
+ function getTarget() {
17
+ const platform = process.platform;
18
+ const arch = process.arch;
19
+
20
+ if (platform === "darwin" && arch === "x64") {
21
+ return { assetOS: "darwin", assetArch: "amd64", archiveExt: "tar.gz", checksumOS: "darwin", binaryName: "tokiame" };
22
+ }
23
+ if (platform === "darwin" && arch === "arm64") {
24
+ return { assetOS: "darwin", assetArch: "arm64", archiveExt: "tar.gz", checksumOS: "darwin", binaryName: "tokiame" };
25
+ }
26
+ if (platform === "linux" && arch === "x64") {
27
+ return { assetOS: "linux", assetArch: "amd64", archiveExt: "tar.gz", checksumOS: "linux", binaryName: "tokiame" };
28
+ }
29
+ if (platform === "linux" && arch === "arm64") {
30
+ return { assetOS: "linux", assetArch: "arm64", archiveExt: "tar.gz", checksumOS: "linux", binaryName: "tokiame" };
31
+ }
32
+ if (platform === "win32" && arch === "x64") {
33
+ return { assetOS: "windows", assetArch: "amd64", archiveExt: "zip", checksumOS: "windows", binaryName: "tokiame.exe" };
34
+ }
35
+ throw new Error(`unsupported platform: ${platform}/${arch}`);
36
+ }
37
+
38
+ function getAssetURL(fileName) {
39
+ return `https://github.com/${releaseRepo}/releases/download/${releaseTag}/${fileName}`;
40
+ }
41
+
42
+ function download(url) {
43
+ return new Promise((resolve, reject) => {
44
+ const request = https.get(url, { headers: { "User-Agent": "@tokilake/tokiame" } }, (response) => {
45
+ if (response.statusCode && response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
46
+ response.resume();
47
+ download(response.headers.location).then(resolve).catch(reject);
48
+ return;
49
+ }
50
+ if (response.statusCode !== 200) {
51
+ response.resume();
52
+ reject(new Error(`download failed: ${url} (${response.statusCode})`));
53
+ return;
54
+ }
55
+
56
+ const chunks = [];
57
+ response.on("data", (chunk) => chunks.push(chunk));
58
+ response.on("end", () => resolve(Buffer.concat(chunks)));
59
+ });
60
+ request.on("error", reject);
61
+ });
62
+ }
63
+
64
+ function verifyChecksum(buffer, checksumText, assetName) {
65
+ const digest = crypto.createHash("sha256").update(buffer).digest("hex");
66
+ const expectedLine = checksumText
67
+ .split(/\r?\n/)
68
+ .map((line) => line.trim())
69
+ .find((line) => line.endsWith(` ${assetName}`));
70
+
71
+ if (!expectedLine) {
72
+ throw new Error(`checksum entry not found for ${assetName}`);
73
+ }
74
+
75
+ const expected = expectedLine.split(/\s+/)[0];
76
+ if (expected !== digest) {
77
+ throw new Error(`checksum mismatch for ${assetName}`);
78
+ }
79
+ }
80
+
81
+ function extractArchive(archivePath, extractDir, archiveExt) {
82
+ if (archiveExt === "tar.gz") {
83
+ const result = spawnSync("tar", ["-xzf", archivePath, "-C", extractDir], { stdio: "inherit" });
84
+ if (result.status !== 0) {
85
+ throw new Error("failed to extract tar.gz archive");
86
+ }
87
+ return;
88
+ }
89
+
90
+ if (archiveExt === "zip") {
91
+ const command = `Expand-Archive -LiteralPath '${archivePath.replace(/'/g, "''")}' -DestinationPath '${extractDir.replace(/'/g, "''")}' -Force`;
92
+ const result = spawnSync("powershell", ["-NoProfile", "-NonInteractive", "-Command", command], { stdio: "inherit" });
93
+ if (result.status !== 0) {
94
+ throw new Error("failed to extract zip archive");
95
+ }
96
+ return;
97
+ }
98
+
99
+ throw new Error(`unsupported archive format: ${archiveExt}`);
100
+ }
101
+
102
+ async function main() {
103
+ const target = getTarget();
104
+ const assetName = `tokiame_${packageJSON.version}_${target.assetOS}_${target.assetArch}.${target.archiveExt}`;
105
+ const checksumName = `SHA256SUMS-${target.checksumOS}.txt`;
106
+
107
+ const archiveBuffer = await download(getAssetURL(assetName));
108
+ const checksumBuffer = await download(getAssetURL(checksumName));
109
+
110
+ verifyChecksum(archiveBuffer, checksumBuffer.toString("utf8"), assetName);
111
+
112
+ const tempDir = await fsp.mkdtemp(path.join(os.tmpdir(), "tokiame-install-"));
113
+ const archivePath = path.join(tempDir, assetName);
114
+ const extractDir = path.join(tempDir, "extract");
115
+
116
+ await fsp.mkdir(extractDir, { recursive: true });
117
+ await fsp.writeFile(archivePath, archiveBuffer);
118
+ extractArchive(archivePath, extractDir, target.archiveExt);
119
+
120
+ const sourceBinaryPath = path.join(extractDir, target.binaryName);
121
+ const destinationDir = path.join(__dirname, "..", "bin");
122
+ const destinationPath = path.join(destinationDir, target.binaryName);
123
+
124
+ if (!fs.existsSync(sourceBinaryPath)) {
125
+ throw new Error(`extracted binary not found: ${sourceBinaryPath}`);
126
+ }
127
+
128
+ await fsp.mkdir(destinationDir, { recursive: true });
129
+ await fsp.copyFile(sourceBinaryPath, destinationPath);
130
+
131
+ if (process.platform !== "win32") {
132
+ await fsp.chmod(destinationPath, 0o755);
133
+ }
134
+
135
+ const homeDir = os.homedir();
136
+ if (homeDir) {
137
+ const configDir = path.join(homeDir, ".tokilake");
138
+ const exampleSource = path.join(__dirname, "..", "tokiame.json.example");
139
+ const exampleDestination = path.join(configDir, "tokiame.json.example");
140
+ await fsp.mkdir(configDir, { recursive: true });
141
+ if (fs.existsSync(exampleSource) && !fs.existsSync(exampleDestination)) {
142
+ await fsp.copyFile(exampleSource, exampleDestination);
143
+ }
144
+ }
145
+ }
146
+
147
+ main().catch((error) => {
148
+ console.error(`[tokiame] ${error.message}`);
149
+ process.exit(1);
150
+ });
@@ -0,0 +1,16 @@
1
+ {
2
+ "gateway_url": "wss://api.example.com/api/tokilake/connect",
3
+ "token": "sk-your-user-token",
4
+ "namespace": "demo-worker",
5
+ "node_name": "gpu-01",
6
+ "group": "default",
7
+ "backend_type": "openai",
8
+ "heartbeat_interval_seconds": 15,
9
+ "reconnect_delay_seconds": 5,
10
+ "model_targets": {
11
+ "gpt-4o-mini": {
12
+ "url": "http://127.0.0.1:8000/v1",
13
+ "mapped_name": "gpt-4o-mini"
14
+ }
15
+ }
16
+ }