@wwkit/freetoken 1.0.1

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.
Files changed (81) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +96 -0
  3. package/cli/helpers/args.js +48 -0
  4. package/cli/index.js +374 -0
  5. package/cli/pid-manager.js +87 -0
  6. package/client/index.html +25 -0
  7. package/client/public/favicon.svg +1 -0
  8. package/client/public/icons.svg +24 -0
  9. package/client/src/App.vue +136 -0
  10. package/client/src/assets/vite.svg +1 -0
  11. package/client/src/assets/vue.svg +1 -0
  12. package/client/src/components/ChatBox.vue +320 -0
  13. package/client/src/components/CheckList.vue +52 -0
  14. package/client/src/components/CodeBlock.vue +99 -0
  15. package/client/src/components/DetailBlock.vue +47 -0
  16. package/client/src/components/HelloWorld.vue +95 -0
  17. package/client/src/components/LangSwitch.vue +32 -0
  18. package/client/src/components/LinkList.vue +32 -0
  19. package/client/src/components/OsSwitch.vue +42 -0
  20. package/client/src/components/OsToggle.vue +15 -0
  21. package/client/src/components/PageHeader.vue +31 -0
  22. package/client/src/components/StepList.vue +92 -0
  23. package/client/src/composables/useClipboard.js +26 -0
  24. package/client/src/composables/useOs.js +22 -0
  25. package/client/src/data/docs/agent.js +1002 -0
  26. package/client/src/data/docs/auth.js +631 -0
  27. package/client/src/data/docs/builtin-tools.js +322 -0
  28. package/client/src/data/docs/chat.js +181 -0
  29. package/client/src/data/docs/index.js +9 -0
  30. package/client/src/data/docs/skills.js +396 -0
  31. package/client/src/data/docs/spec-conversion.js +1042 -0
  32. package/client/src/data/freeModels/bluesliu.js +97 -0
  33. package/client/src/data/freeModels/index.js +11 -0
  34. package/client/src/data/freeModels/nvidia.js +126 -0
  35. package/client/src/data/freeModels/openrouter.js +116 -0
  36. package/client/src/data/harness/claude.js +89 -0
  37. package/client/src/data/harness/codex.js +66 -0
  38. package/client/src/data/harness/dsh.js +86 -0
  39. package/client/src/data/harness/hermes.js +56 -0
  40. package/client/src/data/harness/index.js +22 -0
  41. package/client/src/data/harness/opencode.js +77 -0
  42. package/client/src/data/proxy/api-relay.js +53 -0
  43. package/client/src/data/proxy/builtin-proxy.js +54 -0
  44. package/client/src/data/proxy/cf-workers.js +67 -0
  45. package/client/src/data/proxy/ecs-forward.js +75 -0
  46. package/client/src/data/proxy/ecs-reverse.js +89 -0
  47. package/client/src/data/proxy/index.js +15 -0
  48. package/client/src/docs/claudecode.md +44 -0
  49. package/client/src/docs/codex.md +90 -0
  50. package/client/src/docs/hermesagent.md +42 -0
  51. package/client/src/docs/opencode.md +103 -0
  52. package/client/src/locales/en.js +436 -0
  53. package/client/src/locales/index.js +31 -0
  54. package/client/src/locales/zh-CN.js +461 -0
  55. package/client/src/main.js +16 -0
  56. package/client/src/router/index.js +76 -0
  57. package/client/src/style.css +15 -0
  58. package/client/src/views/AdminModelsView.vue +214 -0
  59. package/client/src/views/DocsView.vue +1604 -0
  60. package/client/src/views/FreeModelsView.vue +518 -0
  61. package/client/src/views/HarnessView.vue +314 -0
  62. package/client/src/views/HomeView.vue +147 -0
  63. package/client/src/views/ProxyView.vue +112 -0
  64. package/client/src/views/TokenMarketView.vue +26 -0
  65. package/client/vite.config.js +41 -0
  66. package/package.json +85 -0
  67. package/scripts/build-zip.sh +61 -0
  68. package/scripts/postinstall.js +7 -0
  69. package/server/src/config/targets.json +1 -0
  70. package/server/src/index.js +52 -0
  71. package/server/src/lib/coding-test.js +215 -0
  72. package/server/src/lib/database.js +353 -0
  73. package/server/src/lib/run-test.js +15 -0
  74. package/server/src/lib/scheduler.js +21 -0
  75. package/server/src/lib/tester.js +310 -0
  76. package/server/src/routes/admin.js +48 -0
  77. package/server/src/routes/proxy.js +64 -0
  78. package/server/src/routes/speed.js +87 -0
  79. package/src/config.js +45 -0
  80. package/src/config.json5 +27 -0
  81. package/src/index.js +10 -0
@@ -0,0 +1,48 @@
1
+ import express from 'express'
2
+ import { getAllModels, addModel, updateModel, deleteModel } from '../lib/database.js'
3
+
4
+ const router = express.Router()
5
+
6
+ // 获取所有模型
7
+ router.get('/', (req, res) => {
8
+ try {
9
+ const models = getAllModels()
10
+ res.json({ models })
11
+ } catch (error) {
12
+ res.status(500).json({ error: error.message })
13
+ }
14
+ })
15
+
16
+ // 添加模型
17
+ router.post('/', (req, res) => {
18
+ try {
19
+ const model = addModel(req.body)
20
+ res.json({ success: true, model })
21
+ } catch (error) {
22
+ res.status(500).json({ error: error.message })
23
+ }
24
+ })
25
+
26
+ // 更新模型
27
+ router.put('/:id', (req, res) => {
28
+ try {
29
+ const id = parseInt(req.params.id)
30
+ const model = updateModel(id, req.body)
31
+ res.json({ success: true, model })
32
+ } catch (error) {
33
+ res.status(500).json({ error: error.message })
34
+ }
35
+ })
36
+
37
+ // 删除模型
38
+ router.delete('/:id', (req, res) => {
39
+ try {
40
+ const id = parseInt(req.params.id)
41
+ deleteModel(id)
42
+ res.json({ success: true, id })
43
+ } catch (error) {
44
+ res.status(500).json({ error: error.message })
45
+ }
46
+ })
47
+
48
+ export default router
@@ -0,0 +1,64 @@
1
+ import express from 'express'
2
+ import axios from 'axios'
3
+ import { readFileSync } from 'fs'
4
+ import { dirname, join } from 'path'
5
+ import { fileURLToPath } from 'url'
6
+
7
+ const __filename = fileURLToPath(import.meta.url)
8
+ const __dirname = dirname(__filename)
9
+
10
+ const router = express.Router()
11
+
12
+ function loadTargets() {
13
+ const targetsPath = join(__dirname, '../config/targets.json')
14
+ const raw = readFileSync(targetsPath, 'utf-8')
15
+ return JSON.parse(raw)
16
+ }
17
+
18
+ // 动态代理:/api/proxy/:provider/* -> {provider.baseUrl}/*
19
+ router.use('/:provider/*', async (req, res) => {
20
+ const provider = req.params.provider
21
+ const targets = loadTargets()
22
+ const target = targets.find(t => t.provider === provider)
23
+
24
+ if (!target) {
25
+ return res.status(404).json({ error: `Provider ${provider} not found` })
26
+ }
27
+
28
+ // 提取原始路径(去掉 /api/proxy/:provider 前缀)
29
+ const originalUrl = req.originalUrl
30
+ const prefix = `/api/proxy/${provider}`
31
+ const subPath = originalUrl.substring(prefix.length)
32
+
33
+ const targetUrl = `${target.baseUrl}${subPath}`
34
+
35
+ try {
36
+ const response = await axios({
37
+ method: req.method,
38
+ url: targetUrl,
39
+ headers: {
40
+ 'Authorization': req.headers['authorization'] || '',
41
+ 'Content-Type': req.headers['content-type'] || 'application/json',
42
+ },
43
+ data: req.method !== 'GET' ? req.body : undefined,
44
+ timeout: 120000,
45
+ responseType: 'stream',
46
+ })
47
+
48
+ res.status(response.status)
49
+ response.data.pipe(res)
50
+ } catch (error) {
51
+ if (error.response) {
52
+ res.status(error.response.status)
53
+ if (error.response.data) {
54
+ error.response.data.pipe(res)
55
+ } else {
56
+ res.json({ error: error.message })
57
+ }
58
+ } else {
59
+ res.status(500).json({ error: error.message })
60
+ }
61
+ }
62
+ })
63
+
64
+ export default router
@@ -0,0 +1,87 @@
1
+ import express from 'express'
2
+ import { runSpeedTest, getLatestResults, getHistory, getTargets } from '../lib/tester.js'
3
+ import { getAllModels } from '../lib/database.js'
4
+
5
+ const router = express.Router()
6
+
7
+ // 获取 provider 的 API key
8
+ router.get('/apikey/:provider', (req, res) => {
9
+ const provider = req.params.provider
10
+ const envVarMap = {
11
+ 'bluesliu': 'BLUESLIU_API_KEY',
12
+ 'openrouter': 'OPENROUTER_API_KEY',
13
+ 'nvidia': 'NVIDIA_API_KEY',
14
+ }
15
+
16
+ const envVar = envVarMap[provider]
17
+ if (!envVar) {
18
+ return res.json({ apiKey: '' })
19
+ }
20
+
21
+ const apiKey = process.env[envVar] || ''
22
+ res.json({ apiKey })
23
+ })
24
+
25
+ // 获取最新测试结果
26
+ router.get('/latest', async (req, res) => {
27
+ try {
28
+ const results = await getLatestResults()
29
+ res.json({
30
+ lastTested: results.length > 0 ? results[0].testedAt : null,
31
+ results
32
+ })
33
+ } catch (error) {
34
+ res.status(500).json({ error: error.message })
35
+ }
36
+ })
37
+
38
+ // 获取历史记录
39
+ router.get('/history', async (req, res) => {
40
+ try {
41
+ const { model, limit = 20 } = req.query
42
+ if (!model) {
43
+ return res.status(400).json({ error: 'model parameter required' })
44
+ }
45
+ const history = await getHistory(model, parseInt(limit))
46
+ res.json({ model, history })
47
+ } catch (error) {
48
+ res.status(500).json({ error: error.message })
49
+ }
50
+ })
51
+
52
+ // 获取测试目标列表
53
+ router.get('/targets', async (req, res) => {
54
+ try {
55
+ const targets = await getTargets()
56
+ res.json({ targets })
57
+ } catch (error) {
58
+ res.status(500).json({ error: error.message })
59
+ }
60
+ })
61
+
62
+ // 获取所有模型
63
+ router.get('/models', async (req, res) => {
64
+ try {
65
+ const models = await getAllModels()
66
+ res.json({ models })
67
+ } catch (error) {
68
+ res.status(500).json({ error: error.message })
69
+ }
70
+ })
71
+
72
+ // 手动触发测试
73
+ router.post('/run', async (req, res) => {
74
+ try {
75
+ const results = await runSpeedTest()
76
+ res.json({
77
+ success: true,
78
+ testedAt: new Date().toISOString(),
79
+ count: results.length,
80
+ results
81
+ })
82
+ } catch (error) {
83
+ res.status(500).json({ error: error.message })
84
+ }
85
+ })
86
+
87
+ export default router
package/src/config.js ADDED
@@ -0,0 +1,45 @@
1
+ import path from 'node:path'
2
+ import { fileURLToPath } from 'node:url'
3
+ import { createConfigLoader, getXdgConfigDir } from '@wwkit/shared'
4
+
5
+ const __dirname = path.dirname(fileURLToPath(import.meta.url))
6
+ const DEFAULT_CONFIG_PATH = path.join(__dirname, 'config.json5')
7
+
8
+ export function getConfigDir() {
9
+ return getXdgConfigDir('freetoken')
10
+ }
11
+
12
+ export function getConfigPath() {
13
+ return path.join(getConfigDir(), 'config.json5')
14
+ }
15
+
16
+ const _loader = createConfigLoader({
17
+ builtinConfigPath: DEFAULT_CONFIG_PATH,
18
+ userConfigDir: getConfigDir(),
19
+ envContentVar: 'FREETOKEN_CONFIG_CONTENT',
20
+ envDirVar: 'FREETOKEN_CONFIG_DIR',
21
+ })
22
+
23
+ export function loadConfig() {
24
+ return _loader.getConfig()
25
+ }
26
+
27
+ export function mergeBuiltinConfig() {
28
+ return _loader.mergeBuiltinConfig()
29
+ }
30
+
31
+ export function getServerPort() {
32
+ return loadConfig().server?.port ?? 5905
33
+ }
34
+
35
+ export function getClientPort() {
36
+ return loadConfig().client?.port ?? 7905
37
+ }
38
+
39
+ export function getSpeedTestConfig() {
40
+ return loadConfig().speedTest ?? { cron: '0 9 * * *', runOnStart: true }
41
+ }
42
+
43
+ export function shouldOpenBrowser() {
44
+ return loadConfig().web?.openBrowser ?? true
45
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ // freetoken 基础配置
3
+ // 用户配置文件: ~/.config/freetoken/config.json5
4
+ // 环境变量注入: FREETOKEN_CONFIG_CONTENT
5
+ // 配置目录覆盖: FREETOKEN_CONFIG_DIR
6
+
7
+ // 后端 Express 服务
8
+ server: {
9
+ port: 5905,
10
+ },
11
+
12
+ // 前端 Vite 开发服务
13
+ client: {
14
+ port: 7905,
15
+ },
16
+
17
+ // 测速调度
18
+ speedTest: {
19
+ cron: '0 9 * * *',
20
+ runOnStart: true,
21
+ },
22
+
23
+ // Web 行为
24
+ web: {
25
+ openBrowser: true,
26
+ },
27
+ }
package/src/index.js ADDED
@@ -0,0 +1,10 @@
1
+ export {
2
+ loadConfig,
3
+ mergeBuiltinConfig,
4
+ getConfigDir,
5
+ getConfigPath,
6
+ getServerPort,
7
+ getClientPort,
8
+ getSpeedTestConfig,
9
+ shouldOpenBrowser,
10
+ } from './config.js'