@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 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.25",
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": "./index.js"
14
+ "surph": "./cli.js"
15
15
  },
16
16
  "keywords": [],
17
17
  "author": "SurphAI",
package/scaffold/index.js CHANGED
File without changes
@@ -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
- if (lines[0].toUpperCase() === SKIP_TOKEN) return { skip: true };
63457
- const angles = lines.map((l) => l.replace(/^[-*•]+\s*|^\d+[.)]\s*/, "").replace(/^["'`]+|["'`]+$/g, "").trim()).filter((l) => l && l.toUpperCase() !== SKIP_TOKEN).slice(0, MAX_ANGLES);
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 systemPrompt = buildSystemPrompt(contextMeta);
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 ${SKIP_TOKEN} or 1-${MAX_ANGLES} keyword lines (one per line).`;
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
- }