@surph_ai/cli 0.0.1 → 0.0.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.js +7 -2
- package/deploy/index.js +1 -1
- package/package.json +1 -1
- package/toolserver/app.js +1 -1
- package/toolserver/ensure-chat.js +133 -0
- package/types.d.ts +0 -73
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
|
|
|
@@ -151,7 +152,7 @@ program.command('toolserver')
|
|
|
151
152
|
.description('run tool server')
|
|
152
153
|
.action(async (cmd, options) => {
|
|
153
154
|
try {
|
|
154
|
-
shell.exec('nodemon --quiet ./node_modules/@surph_ai/
|
|
155
|
+
shell.exec('nodemon --quiet ./node_modules/@surph_ai/cli/toolserver/app')
|
|
155
156
|
} catch (error) {
|
|
156
157
|
utils.printError(error)
|
|
157
158
|
}
|
|
@@ -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/deploy/index.js
CHANGED
|
@@ -86,7 +86,7 @@ const zipDirectory = (name = 'package.zip') => {
|
|
|
86
86
|
|
|
87
87
|
const compile = async (deployDir, name = 'package.zip', dir = null) => {
|
|
88
88
|
shell.mkdir('-p', `${deployDir}/`)
|
|
89
|
-
shell.cp('-R', `node_modules/@surph_ai/
|
|
89
|
+
shell.cp('-R', `node_modules/@surph_ai/cli/deploy/tool-handler.js`, `${deployDir}/main.js`)
|
|
90
90
|
|
|
91
91
|
if (dir === null) {
|
|
92
92
|
shell.cp('-R', '!(node_modules|deploy)', `${deployDir}/`) // copy everything but node_modules and deploy
|
package/package.json
CHANGED
package/toolserver/app.js
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 }
|
package/types.d.ts
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
// Type definitions for @surph_ai/sdk
|
|
2
|
-
// These describe the runtime contract the tool server hands to each tool's default export.
|
|
3
|
-
|
|
4
|
-
export type OAuthProvider = 'google' | 'github' | (string & {});
|
|
5
|
-
|
|
6
|
-
export interface GoogleOAuth {
|
|
7
|
-
accessToken: string;
|
|
8
|
-
refreshToken?: string;
|
|
9
|
-
/** ISO-8601 timestamp for when accessToken expires. */
|
|
10
|
-
expiresAt: string;
|
|
11
|
-
/** OAuth scopes granted by the user. */
|
|
12
|
-
scopes: string[];
|
|
13
|
-
/** ISO-8601 timestamp for when the user first connected the provider. */
|
|
14
|
-
connectedAt: string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface UserOAuth {
|
|
18
|
-
google?: GoogleOAuth;
|
|
19
|
-
[provider: string]: unknown;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface UserAuth {
|
|
23
|
-
type: OAuthProvider;
|
|
24
|
-
/** Provider-issued stable identifier for the user (e.g. Google 'sub'). */
|
|
25
|
-
id: string;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export interface User {
|
|
29
|
-
_id: string;
|
|
30
|
-
firstName?: string;
|
|
31
|
-
lastName?: string;
|
|
32
|
-
email: string;
|
|
33
|
-
username: string;
|
|
34
|
-
avatar?: string;
|
|
35
|
-
tags?: string[];
|
|
36
|
-
following?: string[];
|
|
37
|
-
notifications?: unknown[];
|
|
38
|
-
type?: 'claimed' | 'anonymous' | (string & {});
|
|
39
|
-
auth?: UserAuth;
|
|
40
|
-
timestamp?: string;
|
|
41
|
-
lastVisit?: string;
|
|
42
|
-
identifiers?: string[];
|
|
43
|
-
tools?: string[];
|
|
44
|
-
oauth?: UserOAuth;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Per-invocation context. Currently opaque — populated by the caller (chat
|
|
49
|
-
* server, deployed harness, etc.). Cast to a more specific shape inside the
|
|
50
|
-
* tool if you know what your caller sends.
|
|
51
|
-
*/
|
|
52
|
-
export type ToolContext = Record<string, unknown> | null;
|
|
53
|
-
|
|
54
|
-
export interface ToolHandlerInput<TArgs = Record<string, unknown>> {
|
|
55
|
-
args: TArgs;
|
|
56
|
-
context: ToolContext;
|
|
57
|
-
user: User;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export type ToolHandler<
|
|
61
|
-
TArgs = Record<string, unknown>,
|
|
62
|
-
TResult = unknown,
|
|
63
|
-
> = (input: ToolHandlerInput<TArgs>) => Promise<TResult> | TResult;
|
|
64
|
-
|
|
65
|
-
export interface Surph {
|
|
66
|
-
fetchUser(username: string): Promise<User>;
|
|
67
|
-
fetchContext(slug: string | ToolContext): Promise<unknown>;
|
|
68
|
-
searchContext(ctxSlug: string, query: string): Promise<unknown>;
|
|
69
|
-
search(query: string, ctxSlug?: string | null): Promise<unknown>;
|
|
70
|
-
toolHandler(tool: string, args?: Record<string, unknown>): Promise<unknown>;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export declare const surph: Surph;
|