@surph_ai/cli 0.0.2 → 0.0.5
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.js +6 -1
- package/package.json +1 -1
- package/toolserver/ensure-chat.js +133 -0
- package/toolserver/chat.js +0 -95222
package/cli.js
CHANGED
|
@@ -17,6 +17,7 @@ const AdmZip = require('adm-zip')
|
|
|
17
17
|
// const auth = require('./auth')
|
|
18
18
|
const deploy = require('./deploy')
|
|
19
19
|
const scaffold = require('./scaffold')
|
|
20
|
+
const { ensureChat } = require('./toolserver/ensure-chat')
|
|
20
21
|
const utils = require('./utils')
|
|
21
22
|
const packageJson = require('./package.json')
|
|
22
23
|
|
|
@@ -161,7 +162,11 @@ program.command('chatserver')
|
|
|
161
162
|
.description('run chat server')
|
|
162
163
|
.action(async (cmd, options) => {
|
|
163
164
|
try {
|
|
164
|
-
|
|
165
|
+
const chatPath = await ensureChat({
|
|
166
|
+
cliVersion: VERSION,
|
|
167
|
+
log: (msg) => console.log(colors.cyan(`\n${msg}\n`))
|
|
168
|
+
})
|
|
169
|
+
shell.exec(`nodemon --quiet "${chatPath}"`)
|
|
165
170
|
} catch (error) {
|
|
166
171
|
utils.printError(error)
|
|
167
172
|
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
const crypto = require('crypto')
|
|
2
|
+
const fs = require('fs')
|
|
3
|
+
const os = require('os')
|
|
4
|
+
const path = require('path')
|
|
5
|
+
|
|
6
|
+
const MANIFEST_URL = 'https://surph.s3.us-east-1.amazonaws.com/tools/cli/manifest.json'
|
|
7
|
+
const CHAT_URL = 'https://surph.s3.us-east-1.amazonaws.com/tools/cli/chat.js'
|
|
8
|
+
const CACHE_DIR = path.join(os.homedir(), '.surph', 'cache')
|
|
9
|
+
const CACHE_FILE = path.join(CACHE_DIR, 'chat.js')
|
|
10
|
+
const CACHE_META = path.join(CACHE_DIR, 'chat.meta.json')
|
|
11
|
+
const BUNDLED_FALLBACK = path.join(__dirname, 'chat.js')
|
|
12
|
+
|
|
13
|
+
const parseVersion = (value) => String(value || '0').split('.').map((part) => {
|
|
14
|
+
const n = parseInt(part, 10)
|
|
15
|
+
return Number.isFinite(n) ? n : 0
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const isCliTooOld = (cliVersion, minCliVersion) => {
|
|
19
|
+
const current = parseVersion(cliVersion)
|
|
20
|
+
const required = parseVersion(minCliVersion)
|
|
21
|
+
const length = Math.max(current.length, required.length)
|
|
22
|
+
for (let i = 0; i < length; i++) {
|
|
23
|
+
const a = current[i] || 0
|
|
24
|
+
const b = required[i] || 0
|
|
25
|
+
if (a < b) return true
|
|
26
|
+
if (a > b) return false
|
|
27
|
+
}
|
|
28
|
+
return false
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const sha256 = (buf) => crypto.createHash('sha256').update(buf).digest('hex')
|
|
32
|
+
|
|
33
|
+
const readJsonFile = (filepath) => {
|
|
34
|
+
try {
|
|
35
|
+
return JSON.parse(fs.readFileSync(filepath, 'utf8'))
|
|
36
|
+
} catch {
|
|
37
|
+
return null
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const cacheMatches = (manifest) => {
|
|
42
|
+
const meta = readJsonFile(CACHE_META)
|
|
43
|
+
if (!meta || meta.sha256 !== manifest.sha256) return false
|
|
44
|
+
if (!fs.existsSync(CACHE_FILE)) return false
|
|
45
|
+
if (manifest.size) {
|
|
46
|
+
const { size } = fs.statSync(CACHE_FILE)
|
|
47
|
+
if (size !== manifest.size) return false
|
|
48
|
+
}
|
|
49
|
+
return true
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const fetchManifest = async () => {
|
|
53
|
+
const resp = await fetch(MANIFEST_URL, {
|
|
54
|
+
cache: 'no-store',
|
|
55
|
+
headers: { Accept: 'application/json' }
|
|
56
|
+
})
|
|
57
|
+
if (!resp.ok) {
|
|
58
|
+
throw new Error(`Failed to fetch chat server manifest (${resp.status})`)
|
|
59
|
+
}
|
|
60
|
+
const manifest = await resp.json()
|
|
61
|
+
if (!manifest || typeof manifest.sha256 !== 'string' || manifest.sha256.length !== 64) {
|
|
62
|
+
throw new Error('Invalid chat server manifest')
|
|
63
|
+
}
|
|
64
|
+
return manifest
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const downloadChat = async (manifest) => {
|
|
68
|
+
const resp = await fetch(CHAT_URL, { cache: 'no-store' })
|
|
69
|
+
if (!resp.ok) {
|
|
70
|
+
throw new Error(`Failed to download chat server (${resp.status})`)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const buf = Buffer.from(await resp.arrayBuffer())
|
|
74
|
+
if (manifest.size && buf.length !== manifest.size) {
|
|
75
|
+
throw new Error(`Chat server size mismatch (expected ${manifest.size}, got ${buf.length})`)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const hash = sha256(buf)
|
|
79
|
+
if (hash !== manifest.sha256) {
|
|
80
|
+
throw new Error('Chat server checksum mismatch')
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
fs.mkdirSync(CACHE_DIR, { recursive: true, mode: 0o700 })
|
|
84
|
+
const tmp = `${CACHE_FILE}.tmp`
|
|
85
|
+
fs.writeFileSync(tmp, buf, { mode: 0o600 })
|
|
86
|
+
fs.renameSync(tmp, CACHE_FILE)
|
|
87
|
+
fs.writeFileSync(CACHE_META, JSON.stringify(manifest, null, 2) + '\n', { mode: 0o600 })
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const fallbackPath = () => {
|
|
91
|
+
if (fs.existsSync(CACHE_FILE)) return CACHE_FILE
|
|
92
|
+
if (fs.existsSync(BUNDLED_FALLBACK)) return BUNDLED_FALLBACK
|
|
93
|
+
return null
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const ensureChat = async ({ cliVersion, log } = {}) => {
|
|
97
|
+
let manifest
|
|
98
|
+
try {
|
|
99
|
+
manifest = await fetchManifest()
|
|
100
|
+
} catch (error) {
|
|
101
|
+
const fallback = fallbackPath()
|
|
102
|
+
if (fallback) {
|
|
103
|
+
if (log) log('Using cached chat server (offline).')
|
|
104
|
+
return fallback
|
|
105
|
+
}
|
|
106
|
+
throw error
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (manifest.minCliVersion && isCliTooOld(cliVersion, manifest.minCliVersion)) {
|
|
110
|
+
throw new Error(
|
|
111
|
+
`Chat server requires CLI ${manifest.minCliVersion} or newer (current: ${cliVersion}). Update with: npm i -D @surph_ai/cli@latest`
|
|
112
|
+
)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (cacheMatches(manifest)) {
|
|
116
|
+
return CACHE_FILE
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (log) log('Downloading latest chat server...')
|
|
120
|
+
try {
|
|
121
|
+
await downloadChat(manifest)
|
|
122
|
+
return CACHE_FILE
|
|
123
|
+
} catch (error) {
|
|
124
|
+
const fallback = fallbackPath()
|
|
125
|
+
if (fallback) {
|
|
126
|
+
if (log) log('Download failed, using cached chat server.')
|
|
127
|
+
return fallback
|
|
128
|
+
}
|
|
129
|
+
throw error
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
module.exports = { ensureChat }
|