@surph_ai/sdk 0.0.25 → 0.0.26
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 +449 -0
- package/deploy/index.js +0 -0
- package/index.js +0 -449
- package/package.json +2 -2
- package/scaffold/index.js +0 -0
- package/toolserver/chat.js +56 -15
- package/utils/index.js +0 -0
- package/auth/index.js +0 -147
package/cli.js
ADDED
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// REFERENCE:
|
|
4
|
+
// https://developer.atlassian.com/blog/2015/11/scripting-with-node/
|
|
5
|
+
// https://www.npmjs.com/package/shelljs
|
|
6
|
+
// https://github.com/expressjs/generator/blob/master/bin/express-cli.js
|
|
7
|
+
// https://developer.atlassian.com/blog/2015/11/scripting-with-node/
|
|
8
|
+
|
|
9
|
+
const { Command } = require('commander')
|
|
10
|
+
const path = require('path')
|
|
11
|
+
const fs = require('fs')
|
|
12
|
+
const dotenv = require('dotenv')
|
|
13
|
+
const prompt = require('prompt')
|
|
14
|
+
const shell = require('shelljs')
|
|
15
|
+
const colors = require('colors/safe')
|
|
16
|
+
const AdmZip = require('adm-zip')
|
|
17
|
+
// const auth = require('./auth')
|
|
18
|
+
const deploy = require('./deploy')
|
|
19
|
+
const scaffold = require('./scaffold')
|
|
20
|
+
const utils = require('./utils')
|
|
21
|
+
const packageJson = require('./package.json')
|
|
22
|
+
|
|
23
|
+
const VERSION = packageJson.version
|
|
24
|
+
const SURPH_REST_URL = 'https://surph-mongo-http-19075ff8d93f.herokuapp.com'
|
|
25
|
+
// const RPC_URL = 'https://e3lunom2hka3u6iiotrlzrcez40uwdnk.lambda-url.us-east-1.on.aws'
|
|
26
|
+
|
|
27
|
+
// FUTURE: Export as a referenceable type
|
|
28
|
+
const ERRORS = {
|
|
29
|
+
// General errors
|
|
30
|
+
UNSPECIFIED: { exitCode: 1 },
|
|
31
|
+
INVALID_PARAMS: { exitCode: 2 },
|
|
32
|
+
NOT_LOGGED_IN: { exitCode: 3 },
|
|
33
|
+
|
|
34
|
+
// Deploy-specific errors
|
|
35
|
+
PROJECT_NOT_CONNECTED: { exitCode: 100 },
|
|
36
|
+
PROJECT_NOT_FOUND: { exitCode: 101 },
|
|
37
|
+
NOT_AUTHORIZED: { exitCode: 102 },
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const surphSession = async () => {
|
|
41
|
+
try {
|
|
42
|
+
const userSessionPath = path.resolve(__dirname, '../../../.surph/session.json')
|
|
43
|
+
if (!fs.existsSync(userSessionPath)) {
|
|
44
|
+
console.log('Not logged in.')
|
|
45
|
+
return null
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const file = fs.readFileSync(userSessionPath, 'utf-8')
|
|
49
|
+
const session = JSON.parse(file)
|
|
50
|
+
return session
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.log('Not logged in.')
|
|
53
|
+
return null
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const program = new Command()
|
|
58
|
+
|
|
59
|
+
program.version(VERSION, '-v, --version', 'output the current version')
|
|
60
|
+
|
|
61
|
+
program.command('help')
|
|
62
|
+
.description('help')
|
|
63
|
+
.action(() => {
|
|
64
|
+
console.log('HELP!')
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
program.command('version')
|
|
68
|
+
.description('show current version')
|
|
69
|
+
.action((cmd, options) => {
|
|
70
|
+
try {
|
|
71
|
+
const text = `${colors.cyan(`\nSurph Version: `)} ${colors.brightGreen(`${VERSION}`)}\n`
|
|
72
|
+
console.log(text)
|
|
73
|
+
} catch (error) {
|
|
74
|
+
// console.log(colors.red('\nError: '+err.message+'\n'))
|
|
75
|
+
utils.printError(error)
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
program.command('profile')
|
|
80
|
+
.description('Current Surph user')
|
|
81
|
+
.action(async (cmd, options) => {
|
|
82
|
+
// const userSessionPath = path.resolve(__dirname, '../../../.surph/session.json')
|
|
83
|
+
// if (!fs.existsSync(userSessionPath)) {
|
|
84
|
+
// console.log('SESSION NOT FOUND:')
|
|
85
|
+
// return
|
|
86
|
+
// }
|
|
87
|
+
|
|
88
|
+
// const file = fs.readFileSync(userSessionPath, 'utf-8')
|
|
89
|
+
// const data = JSON.parse(file)
|
|
90
|
+
// console.log('SESSION FOUND:' + JSON.stringify(data))
|
|
91
|
+
|
|
92
|
+
// const user = await auth.currentUser()
|
|
93
|
+
// if (user === false) {
|
|
94
|
+
// program.error(`${colors.cyan('\nNot logged in. To log in:')}\n\n${colors.white('$ vly login')}`,
|
|
95
|
+
// ERRORS.NOT_LOGGED_IN);
|
|
96
|
+
// }
|
|
97
|
+
|
|
98
|
+
// const text = `${colors.cyan(`\nLogged in as: `)} ${colors.brightGreen(`${user.username}`)}\n`
|
|
99
|
+
// console.log(text)
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
/*
|
|
103
|
+
program.command('login')
|
|
104
|
+
.description('login to Surph')
|
|
105
|
+
.action(async (cmd, options) => {
|
|
106
|
+
try {
|
|
107
|
+
const credentials = await auth.showPrompt()
|
|
108
|
+
|
|
109
|
+
const resp = await axios({
|
|
110
|
+
url: `${PORTAL_URL}/auth/login`,
|
|
111
|
+
method: 'post',
|
|
112
|
+
data: credentials,
|
|
113
|
+
headers: { Accept: 'application/json'}
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
const { data } = resp.data
|
|
117
|
+
const user = { id: data._id, username: data.username }
|
|
118
|
+
|
|
119
|
+
shell.cd('')
|
|
120
|
+
utils.write('.Surph_user', JSON.stringify(user, null, 2) + '\n')
|
|
121
|
+
|
|
122
|
+
const successText = `${colors.cyan(`\nSuccess! `)} ${colors.brightGreen(`Currently Logged In As ${user.username}\n`)}`
|
|
123
|
+
console.log(successText)
|
|
124
|
+
} catch (error) {
|
|
125
|
+
utils.printError(error)
|
|
126
|
+
}
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
program.command('dev')
|
|
130
|
+
.description('run dev server')
|
|
131
|
+
.action(async (cmd, options) => {
|
|
132
|
+
try {
|
|
133
|
+
// shell.cp('-R', path.join(__dirname, '../default/lambda/js/index.js'), 'vectors/js/index.js')
|
|
134
|
+
shell.cd(path.join(__dirname, 'base/Surph-base'))
|
|
135
|
+
shell.exec('npm run dev')
|
|
136
|
+
// shell.cd('Surph-base')
|
|
137
|
+
|
|
138
|
+
// shell.exec('gulp build --gulpfile node_modules/@turbo360/turbo-sdk/src/gulpfile.js --silent')
|
|
139
|
+
|
|
140
|
+
} catch (error) {
|
|
141
|
+
utils.printError(error)
|
|
142
|
+
}
|
|
143
|
+
})
|
|
144
|
+
*/
|
|
145
|
+
|
|
146
|
+
program.command('new <name>')
|
|
147
|
+
.description('create new Surph app')
|
|
148
|
+
.action(name => scaffold(name, 'project'))
|
|
149
|
+
|
|
150
|
+
program.command('toolserver')
|
|
151
|
+
.description('run tool server')
|
|
152
|
+
.action(async (cmd, options) => {
|
|
153
|
+
try {
|
|
154
|
+
shell.exec('nodemon --quiet ./node_modules/@surph_ai/sdk/toolserver/app')
|
|
155
|
+
} catch (error) {
|
|
156
|
+
utils.printError(error)
|
|
157
|
+
}
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
program.command('chatserver')
|
|
161
|
+
.description('run chat server')
|
|
162
|
+
.action(async (cmd, options) => {
|
|
163
|
+
try {
|
|
164
|
+
shell.exec('nodemon --quiet ./node_modules/@surph_ai/sdk/toolserver/chat')
|
|
165
|
+
} catch (error) {
|
|
166
|
+
utils.printError(error)
|
|
167
|
+
}
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
program.command('tool <name>')
|
|
171
|
+
.description('create new Surph tool')
|
|
172
|
+
.action(name => scaffold(name, 'tool'))
|
|
173
|
+
|
|
174
|
+
program.command('manifest')
|
|
175
|
+
.description('update manifest for Surph tool')
|
|
176
|
+
.action(async (cmd, options) => {
|
|
177
|
+
try {
|
|
178
|
+
const manifestJson = require('../../../manifest.json')
|
|
179
|
+
const slug = Object.keys(manifestJson)[0]
|
|
180
|
+
const manifest = manifestJson[slug]
|
|
181
|
+
if (!manifest) {
|
|
182
|
+
throw new Error('manifest.json not found')
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const schema = {
|
|
186
|
+
properties: {
|
|
187
|
+
description: {
|
|
188
|
+
description: colors.cyan('\nWhat does your tool do?'),
|
|
189
|
+
required: true
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
prompt.message = null
|
|
195
|
+
prompt.start()
|
|
196
|
+
|
|
197
|
+
const { description } = await prompt.get(schema)
|
|
198
|
+
// console.log('ARGS: ' + JSON.stringify(args))
|
|
199
|
+
|
|
200
|
+
const resp = await fetch('https://rpc.surph.ai/tools/manifest', {
|
|
201
|
+
method: "POST",
|
|
202
|
+
body: JSON.stringify({
|
|
203
|
+
description,
|
|
204
|
+
name: manifest.name,
|
|
205
|
+
slug: manifest.slug
|
|
206
|
+
}),
|
|
207
|
+
headers: {
|
|
208
|
+
'Accept': 'application/json',
|
|
209
|
+
'Content-type': 'application/json',
|
|
210
|
+
'x-surph-client': 'surph-sdk'
|
|
211
|
+
}
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
const { response } = await resp.json()
|
|
215
|
+
const updated = { ...manifest, ...response }
|
|
216
|
+
|
|
217
|
+
utils.write('manifest.json', JSON.stringify({[slug]: updated}, null, 2) + '\n')
|
|
218
|
+
console.log(colors.white('\n\nManifest updated. Check manifest.json for changes.\n'))
|
|
219
|
+
} catch (error) {
|
|
220
|
+
utils.printError(error)
|
|
221
|
+
}
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
/*
|
|
226
|
+
program.command('connect')
|
|
227
|
+
.description('Connect to Surph project')
|
|
228
|
+
.action(async (cmd, options) => {
|
|
229
|
+
try {
|
|
230
|
+
const user = await auth.currentUser() // must be logged in.
|
|
231
|
+
if (user === false) {
|
|
232
|
+
program.error(`${colors.cyan('\nNot logged in. To log in:')}\n\n${colors.white('$ vly login')}`,
|
|
233
|
+
ERRORS.NOT_LOGGED_IN);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const projects = await Realm.restQuery('project', 'get', { 'members.id': user.id })
|
|
237
|
+
if (projects.length === 0) {
|
|
238
|
+
throw new Error(`No Projects not found.`)
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// console.log('PROJECTS: ' + JSON.stringify(projects))
|
|
242
|
+
|
|
243
|
+
const _projects = projects.map(({ name }, idx) => `${idx + 1}. ${name}`)
|
|
244
|
+
|
|
245
|
+
const schema = {
|
|
246
|
+
properties: {
|
|
247
|
+
framework: {
|
|
248
|
+
message: `\n\n${_projects.join('\n')}\n\nSelect project number then press "enter"\n`,
|
|
249
|
+
required: true
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
prompt.start()
|
|
255
|
+
|
|
256
|
+
prompt.get(schema, (err, result) => {
|
|
257
|
+
if (err) {
|
|
258
|
+
console.log('\n')
|
|
259
|
+
return
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const selected = result.framework.trim()
|
|
263
|
+
const index = parseInt(selected - 1)
|
|
264
|
+
|
|
265
|
+
if (index < 0) {
|
|
266
|
+
utils.printError(new Error('Invalid entry.'))
|
|
267
|
+
return
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (_projects.length < (index + 1)) {
|
|
271
|
+
utils.printError(new Error('Invalid entry.'))
|
|
272
|
+
return
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const selectedApp = projects[index]
|
|
276
|
+
if (!selectedApp) {
|
|
277
|
+
utils.printError(new Error('Invalid entry.'))
|
|
278
|
+
return
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// console.log('SELECTED: ' + JSON.stringify(selectedApp))
|
|
282
|
+
const { name, slug } = selectedApp
|
|
283
|
+
const envFile = utils.readFile('.env') || '{}'
|
|
284
|
+
const envJson = dotenv.parse(envFile)
|
|
285
|
+
|
|
286
|
+
const env = {
|
|
287
|
+
...envJson,
|
|
288
|
+
'TURBO_PROJECT': name,
|
|
289
|
+
'TURBO_PROJECT_SLUG': slug
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
let envString = ''
|
|
293
|
+
Object.keys(env).forEach((key, i) => {
|
|
294
|
+
if (env[key] !== null) {
|
|
295
|
+
envString += key+'='+env[key]+'\n'
|
|
296
|
+
}
|
|
297
|
+
})
|
|
298
|
+
|
|
299
|
+
utils.write('.env', envString)
|
|
300
|
+
console.log('\nApp Connected!\nto deploy:\n\n$ vly deploy\n\n')
|
|
301
|
+
})
|
|
302
|
+
} catch (error) {
|
|
303
|
+
utils.printError(error)
|
|
304
|
+
}
|
|
305
|
+
})
|
|
306
|
+
*/
|
|
307
|
+
|
|
308
|
+
program.command('publish')
|
|
309
|
+
.description('Publish Surph tool')
|
|
310
|
+
.action(async (cmd, options) => {
|
|
311
|
+
try {
|
|
312
|
+
const manifest = require('../../../manifest.json')
|
|
313
|
+
if (!manifest) {
|
|
314
|
+
throw new Error('manifest.json not found')
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const keys = Object.keys(manifest)
|
|
318
|
+
if (keys.length === 0) {
|
|
319
|
+
throw new Error('invalid manifest.json')
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const session = await surphSession()
|
|
323
|
+
if (!session) {
|
|
324
|
+
throw new Error('Not logged in.')
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// console.log('SESSION: ' + JSON.stringify(session))
|
|
328
|
+
const userId = session?.userId
|
|
329
|
+
|
|
330
|
+
let resp = await fetch(`${SURPH_REST_URL}/rest/user/${userId}`, {
|
|
331
|
+
method: 'GET',
|
|
332
|
+
headers: {
|
|
333
|
+
Accept: 'application/json',
|
|
334
|
+
'x-surph-client': 'surph-sdk'
|
|
335
|
+
}
|
|
336
|
+
})
|
|
337
|
+
|
|
338
|
+
const { payload: user } = await resp.json()
|
|
339
|
+
// console.log('USER: ' + JSON.stringify(user))
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
const slug = keys[0]
|
|
343
|
+
resp = await fetch(`${SURPH_REST_URL}/rest/tool?slug=${slug}`, {
|
|
344
|
+
method: 'GET',
|
|
345
|
+
headers: {
|
|
346
|
+
Accept: 'application/json',
|
|
347
|
+
'x-surph-client': 'surph-sdk'
|
|
348
|
+
}
|
|
349
|
+
})
|
|
350
|
+
|
|
351
|
+
const { payload } = await resp.json()
|
|
352
|
+
|
|
353
|
+
const toolJson = manifest[slug]
|
|
354
|
+
|
|
355
|
+
// tool not registered yet, create:
|
|
356
|
+
if (payload.length === 0) {
|
|
357
|
+
const resp = await fetch(`${SURPH_REST_URL}/rest/tool`, {
|
|
358
|
+
method: 'POST',
|
|
359
|
+
body: JSON.stringify({
|
|
360
|
+
...toolJson,
|
|
361
|
+
creator: {
|
|
362
|
+
_id: user._id,
|
|
363
|
+
username: user.username,
|
|
364
|
+
avatar: user.avatar
|
|
365
|
+
}
|
|
366
|
+
}),
|
|
367
|
+
headers: {
|
|
368
|
+
Accept: 'application/json',
|
|
369
|
+
'Content-Type': 'application/json',
|
|
370
|
+
'x-surph-client': 'surph-sdk'
|
|
371
|
+
}
|
|
372
|
+
})
|
|
373
|
+
|
|
374
|
+
const { payload } = await resp.json()
|
|
375
|
+
console.log(`\nSurph tool registered. To view, visit:\n\nhttps://surph.ai/tool/${slug}\n`)
|
|
376
|
+
return
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
resp = await fetch(`${SURPH_REST_URL}/rest/tool/${payload[0]._id}`, {
|
|
380
|
+
method: 'PUT',
|
|
381
|
+
body: JSON.stringify({
|
|
382
|
+
...toolJson,
|
|
383
|
+
creator: {
|
|
384
|
+
_id: user._id,
|
|
385
|
+
username: user.username,
|
|
386
|
+
avatar: user.avatar
|
|
387
|
+
}
|
|
388
|
+
}),
|
|
389
|
+
headers: {
|
|
390
|
+
Accept: 'application/json',
|
|
391
|
+
'Content-Type': 'application/json',
|
|
392
|
+
'x-surph-client': 'surph-sdk'
|
|
393
|
+
}
|
|
394
|
+
})
|
|
395
|
+
|
|
396
|
+
const data = await resp.json()
|
|
397
|
+
console.log('\nSurph tool updated.')
|
|
398
|
+
} catch (error) {
|
|
399
|
+
console.log(colors.red('\nError: '+error.message+'\n'))
|
|
400
|
+
}
|
|
401
|
+
})
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
program.command('deploy')
|
|
405
|
+
.description('Deploy to Surph')
|
|
406
|
+
.action(async (cmd, options) => {
|
|
407
|
+
try {
|
|
408
|
+
/*
|
|
409
|
+
const { type } = cmd
|
|
410
|
+
if (type !== 'default' && type !== 'static') {
|
|
411
|
+
const msg = colors.red(`\nError: Invalid deployment type ${type}.\n`) +
|
|
412
|
+
colors.white(`Please select type "static" or omit type flag.\n`);
|
|
413
|
+
program.error(msg, ERRORS.INVALID_PARAMS)
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const user = await auth.currentUser() // must be logged in.
|
|
417
|
+
if (user === false) {
|
|
418
|
+
program.error(`${colors.cyan('\nNot logged in. To log in:')}\n\n${colors.white('$ vly login')}`,
|
|
419
|
+
ERRORS.NOT_LOGGED_IN);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
*/
|
|
423
|
+
|
|
424
|
+
/*
|
|
425
|
+
const apps = await Realm.restQuery('project', 'get', { slug: TURBO_PROJECT_SLUG })
|
|
426
|
+
if (apps.length === 0) {
|
|
427
|
+
program.error(`App ${TURBO_PROJECT} not found.`, ERRORS.PROJECT_NOT_FOUND)
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
const { members } = apps[0]
|
|
431
|
+
if (!members.find(member => (member.id === user.id))) {
|
|
432
|
+
program.error('Unauthorized', ERRORS.NOT_AUTHORIZED)
|
|
433
|
+
}
|
|
434
|
+
*/
|
|
435
|
+
|
|
436
|
+
const { SURPH_TOOL = null } = dotenv.parse(utils.readFile('.env') ?? '')
|
|
437
|
+
|
|
438
|
+
if (SURPH_TOOL === null) {
|
|
439
|
+
program.error('Surph project not connected. To connect, run:\n\n$ surph connect', ERRORS.PROJECT_NOT_CONNECTED)
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
await deploy.tool({ SURPH_TOOL_SLUG: SURPH_TOOL })
|
|
443
|
+
} catch (error) {
|
|
444
|
+
utils.printError(error)
|
|
445
|
+
program.error(`Unexpected error: ${error}`, ERRORS.UNSPECIFIED)
|
|
446
|
+
}
|
|
447
|
+
})
|
|
448
|
+
|
|
449
|
+
program.parse(process.argv)
|
package/deploy/index.js
CHANGED
|
File without changes
|
package/index.js
CHANGED
|
@@ -1,449 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// REFERENCE:
|
|
4
|
-
// https://developer.atlassian.com/blog/2015/11/scripting-with-node/
|
|
5
|
-
// https://www.npmjs.com/package/shelljs
|
|
6
|
-
// https://github.com/expressjs/generator/blob/master/bin/express-cli.js
|
|
7
|
-
// https://developer.atlassian.com/blog/2015/11/scripting-with-node/
|
|
8
|
-
|
|
9
|
-
const { Command } = require('commander')
|
|
10
|
-
const path = require('path')
|
|
11
|
-
const fs = require('fs')
|
|
12
|
-
const dotenv = require('dotenv')
|
|
13
|
-
const prompt = require('prompt')
|
|
14
|
-
const shell = require('shelljs')
|
|
15
|
-
const colors = require('colors/safe')
|
|
16
|
-
const AdmZip = require('adm-zip')
|
|
17
|
-
const auth = require('./auth')
|
|
18
|
-
const deploy = require('./deploy')
|
|
19
|
-
const scaffold = require('./scaffold')
|
|
20
|
-
const utils = require('./utils')
|
|
21
|
-
const packageJson = require('./package.json')
|
|
22
|
-
|
|
23
|
-
const VERSION = packageJson.version
|
|
24
|
-
const SURPH_REST_URL = 'https://surph-mongo-http-19075ff8d93f.herokuapp.com'
|
|
25
|
-
// const RPC_URL = 'https://e3lunom2hka3u6iiotrlzrcez40uwdnk.lambda-url.us-east-1.on.aws'
|
|
26
|
-
|
|
27
|
-
// FUTURE: Export as a referenceable type
|
|
28
|
-
const ERRORS = {
|
|
29
|
-
// General errors
|
|
30
|
-
UNSPECIFIED: { exitCode: 1 },
|
|
31
|
-
INVALID_PARAMS: { exitCode: 2 },
|
|
32
|
-
NOT_LOGGED_IN: { exitCode: 3 },
|
|
33
|
-
|
|
34
|
-
// Deploy-specific errors
|
|
35
|
-
PROJECT_NOT_CONNECTED: { exitCode: 100 },
|
|
36
|
-
PROJECT_NOT_FOUND: { exitCode: 101 },
|
|
37
|
-
NOT_AUTHORIZED: { exitCode: 102 },
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
const surphSession = async () => {
|
|
41
|
-
try {
|
|
42
|
-
const userSessionPath = path.resolve(__dirname, '../../../.surph/session.json')
|
|
43
|
-
if (!fs.existsSync(userSessionPath)) {
|
|
44
|
-
console.log('Not logged in.')
|
|
45
|
-
return null
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const file = fs.readFileSync(userSessionPath, 'utf-8')
|
|
49
|
-
const session = JSON.parse(file)
|
|
50
|
-
return session
|
|
51
|
-
} catch (error) {
|
|
52
|
-
console.log('Not logged in.')
|
|
53
|
-
return null
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const program = new Command()
|
|
58
|
-
|
|
59
|
-
program.version(VERSION, '-v, --version', 'output the current version')
|
|
60
|
-
|
|
61
|
-
program.command('help')
|
|
62
|
-
.description('help')
|
|
63
|
-
.action(() => {
|
|
64
|
-
console.log('HELP!')
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
program.command('version')
|
|
68
|
-
.description('show current version')
|
|
69
|
-
.action((cmd, options) => {
|
|
70
|
-
try {
|
|
71
|
-
const text = `${colors.cyan(`\nSurph Version: `)} ${colors.brightGreen(`${VERSION}`)}\n`
|
|
72
|
-
console.log(text)
|
|
73
|
-
} catch (error) {
|
|
74
|
-
// console.log(colors.red('\nError: '+err.message+'\n'))
|
|
75
|
-
utils.printError(error)
|
|
76
|
-
}
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
program.command('profile')
|
|
80
|
-
.description('Current Surph user')
|
|
81
|
-
.action(async (cmd, options) => {
|
|
82
|
-
// const userSessionPath = path.resolve(__dirname, '../../../.surph/session.json')
|
|
83
|
-
// if (!fs.existsSync(userSessionPath)) {
|
|
84
|
-
// console.log('SESSION NOT FOUND:')
|
|
85
|
-
// return
|
|
86
|
-
// }
|
|
87
|
-
|
|
88
|
-
// const file = fs.readFileSync(userSessionPath, 'utf-8')
|
|
89
|
-
// const data = JSON.parse(file)
|
|
90
|
-
// console.log('SESSION FOUND:' + JSON.stringify(data))
|
|
91
|
-
|
|
92
|
-
// const user = await auth.currentUser()
|
|
93
|
-
// if (user === false) {
|
|
94
|
-
// program.error(`${colors.cyan('\nNot logged in. To log in:')}\n\n${colors.white('$ vly login')}`,
|
|
95
|
-
// ERRORS.NOT_LOGGED_IN);
|
|
96
|
-
// }
|
|
97
|
-
|
|
98
|
-
// const text = `${colors.cyan(`\nLogged in as: `)} ${colors.brightGreen(`${user.username}`)}\n`
|
|
99
|
-
// console.log(text)
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
/*
|
|
103
|
-
program.command('login')
|
|
104
|
-
.description('login to Surph')
|
|
105
|
-
.action(async (cmd, options) => {
|
|
106
|
-
try {
|
|
107
|
-
const credentials = await auth.showPrompt()
|
|
108
|
-
|
|
109
|
-
const resp = await axios({
|
|
110
|
-
url: `${PORTAL_URL}/auth/login`,
|
|
111
|
-
method: 'post',
|
|
112
|
-
data: credentials,
|
|
113
|
-
headers: { Accept: 'application/json'}
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
const { data } = resp.data
|
|
117
|
-
const user = { id: data._id, username: data.username }
|
|
118
|
-
|
|
119
|
-
shell.cd('')
|
|
120
|
-
utils.write('.Surph_user', JSON.stringify(user, null, 2) + '\n')
|
|
121
|
-
|
|
122
|
-
const successText = `${colors.cyan(`\nSuccess! `)} ${colors.brightGreen(`Currently Logged In As ${user.username}\n`)}`
|
|
123
|
-
console.log(successText)
|
|
124
|
-
} catch (error) {
|
|
125
|
-
utils.printError(error)
|
|
126
|
-
}
|
|
127
|
-
})
|
|
128
|
-
|
|
129
|
-
program.command('dev')
|
|
130
|
-
.description('run dev server')
|
|
131
|
-
.action(async (cmd, options) => {
|
|
132
|
-
try {
|
|
133
|
-
// shell.cp('-R', path.join(__dirname, '../default/lambda/js/index.js'), 'vectors/js/index.js')
|
|
134
|
-
shell.cd(path.join(__dirname, 'base/Surph-base'))
|
|
135
|
-
shell.exec('npm run dev')
|
|
136
|
-
// shell.cd('Surph-base')
|
|
137
|
-
|
|
138
|
-
// shell.exec('gulp build --gulpfile node_modules/@turbo360/turbo-sdk/src/gulpfile.js --silent')
|
|
139
|
-
|
|
140
|
-
} catch (error) {
|
|
141
|
-
utils.printError(error)
|
|
142
|
-
}
|
|
143
|
-
})
|
|
144
|
-
*/
|
|
145
|
-
|
|
146
|
-
program.command('new <name>')
|
|
147
|
-
.description('create new Surph app')
|
|
148
|
-
.action(name => scaffold(name, 'project'))
|
|
149
|
-
|
|
150
|
-
program.command('toolserver')
|
|
151
|
-
.description('run tool server')
|
|
152
|
-
.action(async (cmd, options) => {
|
|
153
|
-
try {
|
|
154
|
-
shell.exec('nodemon --quiet ./node_modules/@surph_ai/sdk/toolserver/app')
|
|
155
|
-
} catch (error) {
|
|
156
|
-
utils.printError(error)
|
|
157
|
-
}
|
|
158
|
-
})
|
|
159
|
-
|
|
160
|
-
program.command('chatserver')
|
|
161
|
-
.description('run chat server')
|
|
162
|
-
.action(async (cmd, options) => {
|
|
163
|
-
try {
|
|
164
|
-
shell.exec('nodemon --quiet ./node_modules/@surph_ai/sdk/toolserver/chat')
|
|
165
|
-
} catch (error) {
|
|
166
|
-
utils.printError(error)
|
|
167
|
-
}
|
|
168
|
-
})
|
|
169
|
-
|
|
170
|
-
program.command('tool <name>')
|
|
171
|
-
.description('create new Surph tool')
|
|
172
|
-
.action(name => scaffold(name, 'tool'))
|
|
173
|
-
|
|
174
|
-
program.command('manifest')
|
|
175
|
-
.description('update manifest for Surph tool')
|
|
176
|
-
.action(async (cmd, options) => {
|
|
177
|
-
try {
|
|
178
|
-
const manifestJson = require('../../../manifest.json')
|
|
179
|
-
const slug = Object.keys(manifestJson)[0]
|
|
180
|
-
const manifest = manifestJson[slug]
|
|
181
|
-
if (!manifest) {
|
|
182
|
-
throw new Error('manifest.json not found')
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const schema = {
|
|
186
|
-
properties: {
|
|
187
|
-
description: {
|
|
188
|
-
description: colors.cyan('\nWhat does your tool do?'),
|
|
189
|
-
required: true
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
prompt.message = null
|
|
195
|
-
prompt.start()
|
|
196
|
-
|
|
197
|
-
const { description } = await prompt.get(schema)
|
|
198
|
-
// console.log('ARGS: ' + JSON.stringify(args))
|
|
199
|
-
|
|
200
|
-
const resp = await fetch('https://rpc.surph.ai/tools/manifest', {
|
|
201
|
-
method: "POST",
|
|
202
|
-
body: JSON.stringify({
|
|
203
|
-
description,
|
|
204
|
-
name: manifest.name,
|
|
205
|
-
slug: manifest.slug
|
|
206
|
-
}),
|
|
207
|
-
headers: {
|
|
208
|
-
'Accept': 'application/json',
|
|
209
|
-
'Content-type': 'application/json',
|
|
210
|
-
'x-surph-client': 'surph-sdk'
|
|
211
|
-
}
|
|
212
|
-
})
|
|
213
|
-
|
|
214
|
-
const { response } = await resp.json()
|
|
215
|
-
const updated = { ...manifest, ...response }
|
|
216
|
-
|
|
217
|
-
utils.write('manifest.json', JSON.stringify({[slug]: updated}, null, 2) + '\n')
|
|
218
|
-
console.log(colors.white('\n\nManifest updated. Check manifest.json for changes.\n'))
|
|
219
|
-
} catch (error) {
|
|
220
|
-
utils.printError(error)
|
|
221
|
-
}
|
|
222
|
-
})
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
/*
|
|
226
|
-
program.command('connect')
|
|
227
|
-
.description('Connect to Surph project')
|
|
228
|
-
.action(async (cmd, options) => {
|
|
229
|
-
try {
|
|
230
|
-
const user = await auth.currentUser() // must be logged in.
|
|
231
|
-
if (user === false) {
|
|
232
|
-
program.error(`${colors.cyan('\nNot logged in. To log in:')}\n\n${colors.white('$ vly login')}`,
|
|
233
|
-
ERRORS.NOT_LOGGED_IN);
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
const projects = await Realm.restQuery('project', 'get', { 'members.id': user.id })
|
|
237
|
-
if (projects.length === 0) {
|
|
238
|
-
throw new Error(`No Projects not found.`)
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// console.log('PROJECTS: ' + JSON.stringify(projects))
|
|
242
|
-
|
|
243
|
-
const _projects = projects.map(({ name }, idx) => `${idx + 1}. ${name}`)
|
|
244
|
-
|
|
245
|
-
const schema = {
|
|
246
|
-
properties: {
|
|
247
|
-
framework: {
|
|
248
|
-
message: `\n\n${_projects.join('\n')}\n\nSelect project number then press "enter"\n`,
|
|
249
|
-
required: true
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
prompt.start()
|
|
255
|
-
|
|
256
|
-
prompt.get(schema, (err, result) => {
|
|
257
|
-
if (err) {
|
|
258
|
-
console.log('\n')
|
|
259
|
-
return
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
const selected = result.framework.trim()
|
|
263
|
-
const index = parseInt(selected - 1)
|
|
264
|
-
|
|
265
|
-
if (index < 0) {
|
|
266
|
-
utils.printError(new Error('Invalid entry.'))
|
|
267
|
-
return
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
if (_projects.length < (index + 1)) {
|
|
271
|
-
utils.printError(new Error('Invalid entry.'))
|
|
272
|
-
return
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
const selectedApp = projects[index]
|
|
276
|
-
if (!selectedApp) {
|
|
277
|
-
utils.printError(new Error('Invalid entry.'))
|
|
278
|
-
return
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// console.log('SELECTED: ' + JSON.stringify(selectedApp))
|
|
282
|
-
const { name, slug } = selectedApp
|
|
283
|
-
const envFile = utils.readFile('.env') || '{}'
|
|
284
|
-
const envJson = dotenv.parse(envFile)
|
|
285
|
-
|
|
286
|
-
const env = {
|
|
287
|
-
...envJson,
|
|
288
|
-
'TURBO_PROJECT': name,
|
|
289
|
-
'TURBO_PROJECT_SLUG': slug
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
let envString = ''
|
|
293
|
-
Object.keys(env).forEach((key, i) => {
|
|
294
|
-
if (env[key] !== null) {
|
|
295
|
-
envString += key+'='+env[key]+'\n'
|
|
296
|
-
}
|
|
297
|
-
})
|
|
298
|
-
|
|
299
|
-
utils.write('.env', envString)
|
|
300
|
-
console.log('\nApp Connected!\nto deploy:\n\n$ vly deploy\n\n')
|
|
301
|
-
})
|
|
302
|
-
} catch (error) {
|
|
303
|
-
utils.printError(error)
|
|
304
|
-
}
|
|
305
|
-
})
|
|
306
|
-
*/
|
|
307
|
-
|
|
308
|
-
program.command('publish')
|
|
309
|
-
.description('Publish Surph tool')
|
|
310
|
-
.action(async (cmd, options) => {
|
|
311
|
-
try {
|
|
312
|
-
const manifest = require('../../../manifest.json')
|
|
313
|
-
if (!manifest) {
|
|
314
|
-
throw new Error('manifest.json not found')
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
const keys = Object.keys(manifest)
|
|
318
|
-
if (keys.length === 0) {
|
|
319
|
-
throw new Error('invalid manifest.json')
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
const session = await surphSession()
|
|
323
|
-
if (!session) {
|
|
324
|
-
throw new Error('Not logged in.')
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
// console.log('SESSION: ' + JSON.stringify(session))
|
|
328
|
-
const userId = session?.userId
|
|
329
|
-
|
|
330
|
-
let resp = await fetch(`${SURPH_REST_URL}/rest/user/${userId}`, {
|
|
331
|
-
method: 'GET',
|
|
332
|
-
headers: {
|
|
333
|
-
Accept: 'application/json',
|
|
334
|
-
'x-surph-client': 'surph-sdk'
|
|
335
|
-
}
|
|
336
|
-
})
|
|
337
|
-
|
|
338
|
-
const { payload: user } = await resp.json()
|
|
339
|
-
// console.log('USER: ' + JSON.stringify(user))
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
const slug = keys[0]
|
|
343
|
-
resp = await fetch(`${SURPH_REST_URL}/rest/tool?slug=${slug}`, {
|
|
344
|
-
method: 'GET',
|
|
345
|
-
headers: {
|
|
346
|
-
Accept: 'application/json',
|
|
347
|
-
'x-surph-client': 'surph-sdk'
|
|
348
|
-
}
|
|
349
|
-
})
|
|
350
|
-
|
|
351
|
-
const { payload } = await resp.json()
|
|
352
|
-
|
|
353
|
-
const toolJson = manifest[slug]
|
|
354
|
-
|
|
355
|
-
// tool not registered yet, create:
|
|
356
|
-
if (payload.length === 0) {
|
|
357
|
-
const resp = await fetch(`${SURPH_REST_URL}/rest/tool`, {
|
|
358
|
-
method: 'POST',
|
|
359
|
-
body: JSON.stringify({
|
|
360
|
-
...toolJson,
|
|
361
|
-
creator: {
|
|
362
|
-
_id: user._id,
|
|
363
|
-
username: user.username,
|
|
364
|
-
avatar: user.avatar
|
|
365
|
-
}
|
|
366
|
-
}),
|
|
367
|
-
headers: {
|
|
368
|
-
Accept: 'application/json',
|
|
369
|
-
'Content-Type': 'application/json',
|
|
370
|
-
'x-surph-client': 'surph-sdk'
|
|
371
|
-
}
|
|
372
|
-
})
|
|
373
|
-
|
|
374
|
-
const { payload } = await resp.json()
|
|
375
|
-
console.log(`\nSurph tool registered. To view, visit:\n\nhttps://surph.ai/tool/${slug}\n`)
|
|
376
|
-
return
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
resp = await fetch(`${SURPH_REST_URL}/rest/tool/${payload[0]._id}`, {
|
|
380
|
-
method: 'PUT',
|
|
381
|
-
body: JSON.stringify({
|
|
382
|
-
...toolJson,
|
|
383
|
-
creator: {
|
|
384
|
-
_id: user._id,
|
|
385
|
-
username: user.username,
|
|
386
|
-
avatar: user.avatar
|
|
387
|
-
}
|
|
388
|
-
}),
|
|
389
|
-
headers: {
|
|
390
|
-
Accept: 'application/json',
|
|
391
|
-
'Content-Type': 'application/json',
|
|
392
|
-
'x-surph-client': 'surph-sdk'
|
|
393
|
-
}
|
|
394
|
-
})
|
|
395
|
-
|
|
396
|
-
const data = await resp.json()
|
|
397
|
-
console.log('\nSurph tool updated.')
|
|
398
|
-
} catch (error) {
|
|
399
|
-
console.log(colors.red('\nError: '+error.message+'\n'))
|
|
400
|
-
}
|
|
401
|
-
})
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
program.command('deploy')
|
|
405
|
-
.description('Deploy to Surph')
|
|
406
|
-
.action(async (cmd, options) => {
|
|
407
|
-
try {
|
|
408
|
-
/*
|
|
409
|
-
const { type } = cmd
|
|
410
|
-
if (type !== 'default' && type !== 'static') {
|
|
411
|
-
const msg = colors.red(`\nError: Invalid deployment type ${type}.\n`) +
|
|
412
|
-
colors.white(`Please select type "static" or omit type flag.\n`);
|
|
413
|
-
program.error(msg, ERRORS.INVALID_PARAMS)
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
const user = await auth.currentUser() // must be logged in.
|
|
417
|
-
if (user === false) {
|
|
418
|
-
program.error(`${colors.cyan('\nNot logged in. To log in:')}\n\n${colors.white('$ vly login')}`,
|
|
419
|
-
ERRORS.NOT_LOGGED_IN);
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
*/
|
|
423
|
-
|
|
424
|
-
/*
|
|
425
|
-
const apps = await Realm.restQuery('project', 'get', { slug: TURBO_PROJECT_SLUG })
|
|
426
|
-
if (apps.length === 0) {
|
|
427
|
-
program.error(`App ${TURBO_PROJECT} not found.`, ERRORS.PROJECT_NOT_FOUND)
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
const { members } = apps[0]
|
|
431
|
-
if (!members.find(member => (member.id === user.id))) {
|
|
432
|
-
program.error('Unauthorized', ERRORS.NOT_AUTHORIZED)
|
|
433
|
-
}
|
|
434
|
-
*/
|
|
435
|
-
|
|
436
|
-
const { SURPH_TOOL = null } = dotenv.parse(utils.readFile('.env') ?? '')
|
|
437
|
-
|
|
438
|
-
if (SURPH_TOOL === null) {
|
|
439
|
-
program.error('Surph project not connected. To connect, run:\n\n$ surph connect', ERRORS.PROJECT_NOT_CONNECTED)
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
await deploy.tool({ SURPH_TOOL_SLUG: SURPH_TOOL })
|
|
443
|
-
} catch (error) {
|
|
444
|
-
utils.printError(error)
|
|
445
|
-
program.error(`Unexpected error: ${error}`, ERRORS.UNSPECIFIED)
|
|
446
|
-
}
|
|
447
|
-
})
|
|
448
|
-
|
|
449
|
-
program.parse(process.argv)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@surph_ai/sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.26",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "./types.d.ts",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
12
12
|
},
|
|
13
13
|
"bin": {
|
|
14
|
-
"surph": "./
|
|
14
|
+
"surph": "./cli.js"
|
|
15
15
|
},
|
|
16
16
|
"keywords": [],
|
|
17
17
|
"author": "SurphAI",
|
package/scaffold/index.js
CHANGED
|
File without changes
|
package/toolserver/chat.js
CHANGED
|
@@ -44931,7 +44931,7 @@ var require_searchContextItems = __commonJS({
|
|
|
44931
44931
|
const controller = new AbortController();
|
|
44932
44932
|
const timeoutId = setTimeout(() => controller.abort(), 3e4);
|
|
44933
44933
|
const params = { query: query.trim(), context: contextSlug, limit: cappedLimit };
|
|
44934
|
-
console.log("SEARCH CONTEXT: ", JSON.stringify(params, null, 2));
|
|
44934
|
+
console.log("[searchContextItems] SEARCH CONTEXT: ", JSON.stringify(params, null, 2));
|
|
44935
44935
|
try {
|
|
44936
44936
|
const res = await fetch(`${SURPH_RPC_URL}/tools/search`, {
|
|
44937
44937
|
method: "POST",
|
|
@@ -45108,7 +45108,7 @@ var require_searchContext = __commonJS({
|
|
|
45108
45108
|
};
|
|
45109
45109
|
var fetchCollection = async ({ query, contextSlug, collection, limit, signal }) => {
|
|
45110
45110
|
const params = { query, context: contextSlug, collection, limit };
|
|
45111
|
-
console.log("SEARCH CONTEXT: ", JSON.stringify(params, null, 2));
|
|
45111
|
+
console.log("[fetchCollection] SEARCH CONTEXT: ", JSON.stringify(params, null, 2));
|
|
45112
45112
|
const res = await fetch(`${SURPH_RPC_URL}/tools/search`, {
|
|
45113
45113
|
method: "POST",
|
|
45114
45114
|
headers: { "Content-Type": "application/json" },
|
|
@@ -63359,6 +63359,7 @@ var require_queryRewriter = __commonJS({
|
|
|
63359
63359
|
var LABEL = "[queryRewriter]";
|
|
63360
63360
|
var HISTORY_TURNS = 4;
|
|
63361
63361
|
var SKIP_TOKEN = "SKIP";
|
|
63362
|
+
var DELEGATE_TOKEN = "DELEGATE";
|
|
63362
63363
|
var MAX_KEYWORD_TOKENS = 128;
|
|
63363
63364
|
var PREFETCH_LIMIT = 20;
|
|
63364
63365
|
var MAX_ANGLES = 3;
|
|
@@ -63371,20 +63372,30 @@ var require_queryRewriter = __commonJS({
|
|
|
63371
63372
|
return `${speaker}: ${text}`;
|
|
63372
63373
|
}).join("\n\n");
|
|
63373
63374
|
}
|
|
63374
|
-
function buildSystemPrompt(contextMeta) {
|
|
63375
|
+
function buildSystemPrompt(contextMeta, customTools = []) {
|
|
63375
63376
|
const name = contextMeta.name || contextMeta.slug;
|
|
63376
63377
|
const topics = Array.isArray(contextMeta.topics) ? contextMeta.topics : [];
|
|
63377
63378
|
const topicsBlock = topics.length ? topics.map((t) => `- ${t?.name || "Untitled"}${t?.description ? ` \u2014 ${t.description}` : ""}`).join("\n") : "(no topics defined)";
|
|
63379
|
+
const hasCustomTools = Array.isArray(customTools) && customTools.length > 0;
|
|
63380
|
+
const customToolsBlock = hasCustomTools ? `
|
|
63381
|
+
|
|
63382
|
+
The user also has these custom tools installed. Each one operates on this same context but returns its own curated/structured results:
|
|
63383
|
+
|
|
63384
|
+
${customTools.map((t) => `- ${t.name}: ${t.description}`).join("\n")}
|
|
63385
|
+
` : "";
|
|
63386
|
+
const delegateOption = hasCustomTools ? `
|
|
63387
|
+
- ${DELEGATE_TOKEN} \u2014 one of the custom tools above is clearly what the user is asking for (its description covers the turn). Skip prefetch so the main LLM invokes the tool directly. When in doubt, still emit keywords \u2014 the tool remains available for the main LLM to call.` : "";
|
|
63388
|
+
const delegateInOutputRule = hasCustomTools ? `${DELEGATE_TOKEN}, ` : "";
|
|
63378
63389
|
return `You are a query rewriter for a retrieval pipeline. A user is chatting inside a context named "${name}" (slug: ${contextMeta.slug}). The context indexes items across these topics:
|
|
63379
63390
|
|
|
63380
|
-
${topicsBlock}
|
|
63391
|
+
${topicsBlock}${customToolsBlock}
|
|
63381
63392
|
|
|
63382
63393
|
For the latest user turn, decide whether the assistant needs to retrieve items from this context to answer it. Then output ONE of:
|
|
63383
63394
|
|
|
63384
|
-
- ${SKIP_TOKEN} \u2014 the latest turn does NOT need retrieval. Use this ONLY for: greetings, thanks, meta/small-talk, questions about the assistant itself ("who are you", "what can you do"), or pure clarification questions about the wording of the assistant's prior reply ("what did you mean by X", "can you rephrase point 3"). Anything unrelated to the topics above also SKIPs
|
|
63395
|
+
- ${SKIP_TOKEN} \u2014 the latest turn does NOT need retrieval. Use this ONLY for: greetings, thanks, meta/small-talk, questions about the assistant itself ("who are you", "what can you do"), or pure clarification questions about the wording of the assistant's prior reply ("what did you mean by X", "can you rephrase point 3"). Anything unrelated to the topics above also SKIPs.${delegateOption}
|
|
63385
63396
|
- 1 to ${MAX_ANGLES} keyword lines, one per line. Each line is a DISTINCT ANGLE on the same underlying question \u2014 different vocabulary, different specificity, different framings someone else might use for the same concept. The search index is keyword-based and narrow: multiple angles improve recall.
|
|
63386
63397
|
|
|
63387
|
-
Each line: 3 to 10 keywords or short phrases separated by spaces. No punctuation, no numbering, no bullets, no quotes, no explanations.
|
|
63398
|
+
Each keyword line: 3 to 10 keywords or short phrases separated by spaces. No punctuation, no numbering, no bullets, no quotes, no explanations.
|
|
63388
63399
|
|
|
63389
63400
|
Example \u2014 user asks "what orgs help me raise investment?":
|
|
63390
63401
|
startup investors venture capital funding
|
|
@@ -63397,7 +63408,7 @@ Rules:
|
|
|
63397
63408
|
- If the question is narrow enough that one angle covers it (e.g. a specific named entity), one line is fine.
|
|
63398
63409
|
- Short follow-ups that ask for a new facet of a prior subject (who/when/where/how/how much, "any others", "more like this") are CONTENT lookups, not meta chatter. Resolve the pronoun/topic from the conversation history and combine (a) the subject entity from the prior turn with (b) the new facet the user is asking about. Do NOT skip these.
|
|
63399
63410
|
- Focus keywords on nouns, entities, and domain terms. Drop filler words. If the user's turn is a pronoun-only follow-up, the keywords MUST include the subject noun from the prior turn.
|
|
63400
|
-
- Output ONLY ${SKIP_TOKEN} or the keyword lines. Nothing else.`;
|
|
63411
|
+
- Output ONLY ${SKIP_TOKEN}, ${delegateInOutputRule}or the keyword lines. Nothing else.`;
|
|
63401
63412
|
}
|
|
63402
63413
|
async function runCheapModel({ provider, cheapModel, systemPrompt, userMessage }) {
|
|
63403
63414
|
if (provider === "openai") {
|
|
@@ -63453,12 +63464,14 @@ Rules:
|
|
|
63453
63464
|
if (!cleaned) return { skip: true };
|
|
63454
63465
|
const lines = cleaned.split("\n").map((s) => s.trim()).filter(Boolean);
|
|
63455
63466
|
if (lines.length === 0) return { skip: true };
|
|
63456
|
-
|
|
63457
|
-
|
|
63467
|
+
const first = lines[0].toUpperCase();
|
|
63468
|
+
if (first === SKIP_TOKEN) return { skip: true };
|
|
63469
|
+
if (first === DELEGATE_TOKEN) return { delegate: true };
|
|
63470
|
+
const angles = lines.map((l) => l.replace(/^[-*•]+\s*|^\d+[.)]\s*/, "").replace(/^["'`]+|["'`]+$/g, "").trim()).filter((l) => l && l.toUpperCase() !== SKIP_TOKEN && l.toUpperCase() !== DELEGATE_TOKEN).slice(0, MAX_ANGLES);
|
|
63458
63471
|
if (angles.length === 0) return { skip: true };
|
|
63459
63472
|
return { skip: false, angles };
|
|
63460
63473
|
}
|
|
63461
|
-
async function rewriteAndSearch({ messages, contextMeta, model, session, outputStream = null }) {
|
|
63474
|
+
async function rewriteAndSearch({ messages, contextMeta, model, session, outputStream = null, customTools = [] }) {
|
|
63462
63475
|
if (!contextMeta?.slug || !Array.isArray(messages) || messages.length === 0) {
|
|
63463
63476
|
return { skipped: true, reason: "no-context-or-messages" };
|
|
63464
63477
|
}
|
|
@@ -63468,12 +63481,15 @@ Rules:
|
|
|
63468
63481
|
console.warn(`${LABEL} no cheap model for provider ${provider}; skipping rewrite`);
|
|
63469
63482
|
return { skipped: true, reason: "no-cheap-model" };
|
|
63470
63483
|
}
|
|
63471
|
-
const
|
|
63484
|
+
const usableTools = Array.isArray(customTools) ? customTools.filter((t) => t && typeof t.name === "string" && t.name.trim() && typeof t.description === "string" && t.description.trim()) : [];
|
|
63485
|
+
const hasCustomTools = usableTools.length > 0;
|
|
63486
|
+
const systemPrompt = buildSystemPrompt(contextMeta, usableTools);
|
|
63487
|
+
const outputChoices = hasCustomTools ? `${SKIP_TOKEN}, ${DELEGATE_TOKEN}, or 1-${MAX_ANGLES} keyword lines` : `${SKIP_TOKEN} or 1-${MAX_ANGLES} keyword lines`;
|
|
63472
63488
|
const userMessage = `Conversation so far:
|
|
63473
63489
|
|
|
63474
63490
|
${formatConversation(messages)}
|
|
63475
63491
|
|
|
63476
|
-
Output ${
|
|
63492
|
+
Output ${outputChoices} (one per line).`;
|
|
63477
63493
|
let raw = "";
|
|
63478
63494
|
try {
|
|
63479
63495
|
raw = await runCheapModel({ provider, cheapModel, systemPrompt, userMessage });
|
|
@@ -63486,6 +63502,14 @@ Output ${SKIP_TOKEN} or 1-${MAX_ANGLES} keyword lines (one per line).`;
|
|
|
63486
63502
|
console.log(`${LABEL} SKIP raw="${raw.trim().slice(0, 80)}" context=${contextMeta.slug}`);
|
|
63487
63503
|
return { skipped: true, reason: "model-skip" };
|
|
63488
63504
|
}
|
|
63505
|
+
if (parsed.delegate) {
|
|
63506
|
+
if (hasCustomTools) {
|
|
63507
|
+
console.log(`${LABEL} DELEGATE to custom tool context=${contextMeta.slug} tools=${usableTools.map((t) => t.name).join(",")}`);
|
|
63508
|
+
return { skipped: true, reason: "delegate-to-custom-tool" };
|
|
63509
|
+
}
|
|
63510
|
+
console.log(`${LABEL} DELEGATE received but no custom tools installed \u2014 treating as SKIP context=${contextMeta.slug}`);
|
|
63511
|
+
return { skipped: true, reason: "delegate-without-tools" };
|
|
63512
|
+
}
|
|
63489
63513
|
const displayKeywords = parsed.angles.join(" | ");
|
|
63490
63514
|
console.log(`${LABEL} angles=${parsed.angles.length} keywords="${displayKeywords}" context=${contextMeta.slug}`);
|
|
63491
63515
|
const searches = await Promise.all(
|
|
@@ -94448,6 +94472,17 @@ Rules:
|
|
|
94448
94472
|
=== Retrieved items (${items.length}) ===
|
|
94449
94473
|
${lines}
|
|
94450
94474
|
=== End retrieved items ===`;
|
|
94475
|
+
}
|
|
94476
|
+
function frameCustomToolRouting(customTools, contextMeta) {
|
|
94477
|
+
if (!contextMeta?.slug) return null;
|
|
94478
|
+
const usable = Array.isArray(customTools) ? customTools.filter((t) => t && typeof t.name === "string" && t.name.trim() && typeof t.description === "string" && t.description.trim()) : [];
|
|
94479
|
+
if (usable.length === 0) return null;
|
|
94480
|
+
const toolList = usable.map((t) => `- ${t.name}: ${t.description}`).join("\n");
|
|
94481
|
+
return `The user has these custom tools installed. Each is purpose-built for a specific type of query about the active context:
|
|
94482
|
+
|
|
94483
|
+
${toolList}
|
|
94484
|
+
|
|
94485
|
+
Routing rule: when the user's turn matches one of these tools' descriptions, PREFER the custom tool over \`searchContext\`. The custom tool returns curated/structured results tailored to that query type; \`searchContext\` is a generic keyword search. Only fall back to \`searchContext\` for questions the custom tools don't cover.`;
|
|
94451
94486
|
}
|
|
94452
94487
|
function frameDate() {
|
|
94453
94488
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -94616,15 +94651,18 @@ Always use absolute paths for file operations. When the user asks to create a fi
|
|
|
94616
94651
|
workingDirectory,
|
|
94617
94652
|
systemFromHistory,
|
|
94618
94653
|
contextMeta = null,
|
|
94619
|
-
prefetch = null
|
|
94654
|
+
prefetch = null,
|
|
94655
|
+
customTools = []
|
|
94620
94656
|
}) {
|
|
94621
94657
|
const tideBlock = frameTide(params?.tide);
|
|
94622
94658
|
const contextFramed = contextMeta ? frameContext(contextMeta) : null;
|
|
94659
|
+
const customRoutingFramed = frameCustomToolRouting(customTools, contextMeta);
|
|
94623
94660
|
const prefetchFramed = prefetch?.items?.length ? framePrefetchedItems({ contextMeta, items: prefetch.items, keywords: prefetch.keywords }) : null;
|
|
94624
94661
|
const dateBlock = frameDate();
|
|
94625
94662
|
const blocks = [];
|
|
94626
94663
|
if (provider === "anthropic") {
|
|
94627
94664
|
if (contextFramed) blocks.push({ text: contextFramed, cacheable: true });
|
|
94665
|
+
if (customRoutingFramed) blocks.push({ text: customRoutingFramed, cacheable: false });
|
|
94628
94666
|
if (prefetchFramed) blocks.push({ text: prefetchFramed, cacheable: false });
|
|
94629
94667
|
blocks.push({ text: dateBlock, cacheable: false });
|
|
94630
94668
|
if (systemFromHistory) blocks.push({ text: systemFromHistory, cacheable: false });
|
|
@@ -94633,6 +94671,7 @@ Always use absolute paths for file operations. When the user asks to create a fi
|
|
|
94633
94671
|
}
|
|
94634
94672
|
if (tideBlock) blocks.push({ text: tideBlock, cacheable: false });
|
|
94635
94673
|
if (contextFramed) blocks.push({ text: contextFramed, cacheable: true });
|
|
94674
|
+
if (customRoutingFramed) blocks.push({ text: customRoutingFramed, cacheable: false });
|
|
94636
94675
|
if (prefetchFramed) blocks.push({ text: prefetchFramed, cacheable: false });
|
|
94637
94676
|
blocks.push({ text: dateBlock, cacheable: false });
|
|
94638
94677
|
const workingDirInstr = buildWorkingDirectoryInstruction(workingDirectory);
|
|
@@ -94718,9 +94757,10 @@ Always use absolute paths for file operations. When the user asks to create a fi
|
|
|
94718
94757
|
}
|
|
94719
94758
|
const activeContextSlug = params?.context?.slug || params?.currentContextSlug || null;
|
|
94720
94759
|
const contextMeta = activeContextSlug ? await fetchContext({ slug: activeContextSlug }) : null;
|
|
94760
|
+
const customTools = Array.isArray(params?.session?.user?.tools) ? params.session.user.tools : [];
|
|
94721
94761
|
let prefetch = null;
|
|
94722
94762
|
if (params?.context?.slug && contextMeta) {
|
|
94723
|
-
const rw = await rewriteAndSearch({ messages, contextMeta, model, session, outputStream });
|
|
94763
|
+
const rw = await rewriteAndSearch({ messages, contextMeta, model, session, outputStream, customTools });
|
|
94724
94764
|
if (!rw.skipped && Array.isArray(rw.items) && rw.items.length > 0) {
|
|
94725
94765
|
prefetch = { items: rw.items, keywords: rw.keywords };
|
|
94726
94766
|
}
|
|
@@ -94734,7 +94774,8 @@ Always use absolute paths for file operations. When the user asks to create a fi
|
|
|
94734
94774
|
workingDirectory,
|
|
94735
94775
|
systemFromHistory,
|
|
94736
94776
|
contextMeta,
|
|
94737
|
-
prefetch
|
|
94777
|
+
prefetch,
|
|
94778
|
+
customTools
|
|
94738
94779
|
});
|
|
94739
94780
|
console.log(`
|
|
94740
94781
|
|
package/utils/index.js
CHANGED
|
File without changes
|
package/auth/index.js
DELETED
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
const prompt = require('prompt')
|
|
2
|
-
const colors = require('colors/safe')
|
|
3
|
-
const path = require('path')
|
|
4
|
-
const os = require('os')
|
|
5
|
-
const fs = require('fs')
|
|
6
|
-
const utils = require('../utils')
|
|
7
|
-
|
|
8
|
-
module.exports = {
|
|
9
|
-
showPrompt: () => {
|
|
10
|
-
return new Promise((resolve, reject) => {
|
|
11
|
-
const schema = {
|
|
12
|
-
properties: {
|
|
13
|
-
email: {
|
|
14
|
-
description: colors.cyan('\nEmail'),
|
|
15
|
-
required: true
|
|
16
|
-
},
|
|
17
|
-
password: {
|
|
18
|
-
description: colors.cyan('Password'),
|
|
19
|
-
required: true,
|
|
20
|
-
hidden: true
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
prompt.message = null
|
|
26
|
-
prompt.start()
|
|
27
|
-
|
|
28
|
-
// Get two properties from the user: username and email
|
|
29
|
-
prompt.get(schema, (err, result) => {
|
|
30
|
-
if (err) {
|
|
31
|
-
reject(err)
|
|
32
|
-
return
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
resolve(result)
|
|
36
|
-
})
|
|
37
|
-
})
|
|
38
|
-
},
|
|
39
|
-
|
|
40
|
-
isLoggedIn: () => {
|
|
41
|
-
const userProfilePath = path.join(os.homedir(), '.surph_user')
|
|
42
|
-
if (!fs.existsSync(userProfilePath)) {
|
|
43
|
-
return false
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return true
|
|
47
|
-
},
|
|
48
|
-
|
|
49
|
-
connectApp: () => {
|
|
50
|
-
// https://www.npmjs.com/package/prompt
|
|
51
|
-
return new Promise((resolve, reject) => {
|
|
52
|
-
const schema = {
|
|
53
|
-
properties: {
|
|
54
|
-
siteId: {
|
|
55
|
-
description: colors.cyan('\nEnter Site ID'),
|
|
56
|
-
required: true
|
|
57
|
-
},
|
|
58
|
-
apiKey: {
|
|
59
|
-
description: colors.cyan('Enter Site API Key'),
|
|
60
|
-
required: true
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
prompt.message = null
|
|
66
|
-
|
|
67
|
-
prompt.start()
|
|
68
|
-
prompt.get(schema, (err, result) => {
|
|
69
|
-
if (err){
|
|
70
|
-
reject(err)
|
|
71
|
-
return
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
resolve(result)
|
|
75
|
-
})
|
|
76
|
-
})
|
|
77
|
-
},
|
|
78
|
-
|
|
79
|
-
currentUser: () => {
|
|
80
|
-
return new Promise((resolve, reject) => {
|
|
81
|
-
const userProfilePath = path.join(os.homedir(), '.surph_user')
|
|
82
|
-
if (!fs.existsSync(userProfilePath)) {
|
|
83
|
-
// Not logged in
|
|
84
|
-
resolve(false);
|
|
85
|
-
return
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
try {
|
|
89
|
-
const data = utils.readFile(userProfilePath)
|
|
90
|
-
const currentUser = JSON.parse(data)
|
|
91
|
-
resolve(currentUser)
|
|
92
|
-
} catch (error) {
|
|
93
|
-
reject(error)
|
|
94
|
-
}
|
|
95
|
-
})
|
|
96
|
-
},
|
|
97
|
-
|
|
98
|
-
isAuthorized: (app, currentUser) => {
|
|
99
|
-
if (!currentUser)
|
|
100
|
-
return false
|
|
101
|
-
|
|
102
|
-
if (app.profile.id === currentUser.id) // user is admin
|
|
103
|
-
return true
|
|
104
|
-
|
|
105
|
-
// check if currentUser is a collaborator:
|
|
106
|
-
var isCollaborator = false
|
|
107
|
-
for (var i=0; i<app.collaborators.length; i++){
|
|
108
|
-
var collaborator = app.collaborators[i]
|
|
109
|
-
if (collaborator.id == currentUser.id){
|
|
110
|
-
isCollaborator = true
|
|
111
|
-
break
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
return isCollaborator
|
|
116
|
-
},
|
|
117
|
-
|
|
118
|
-
/*
|
|
119
|
-
awsConfig: function(){
|
|
120
|
-
return new Promise(function(resolve, reject){
|
|
121
|
-
var awsConfigPath = path.join(os.homedir(), '.turbo_aws_config')
|
|
122
|
-
if (fs.existsSync(awsConfigPath) == false) {
|
|
123
|
-
reject(new Error('AWS Config not set. To set:\n$ turbo awsConfig'))
|
|
124
|
-
return
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
utils.readFile(awsConfigPath)
|
|
128
|
-
.then(function(data){
|
|
129
|
-
awsSettings = JSON.parse(data)
|
|
130
|
-
resolve(awsSettings)
|
|
131
|
-
return
|
|
132
|
-
})
|
|
133
|
-
.catch(function(err){
|
|
134
|
-
reject(err)
|
|
135
|
-
})
|
|
136
|
-
})
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
awsConfigSet: function(){
|
|
140
|
-
var awsConfigPath = path.join(os.homedir(), '.turbo_aws_config')
|
|
141
|
-
if (fs.existsSync(awsConfigPath) == false) {
|
|
142
|
-
return false
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
return true
|
|
146
|
-
} */
|
|
147
|
-
}
|