@surph_ai/cli 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/cli.js ADDED
@@ -0,0 +1,447 @@
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
+ utils.write('manifest.json', JSON.stringify({[slug]: response}, null, 2) + '\n')
216
+ console.log(colors.white('\n\nManifest updated. Check manifest.json for changes.\n'))
217
+ } catch (error) {
218
+ utils.printError(error)
219
+ }
220
+ })
221
+
222
+
223
+ /*
224
+ program.command('connect')
225
+ .description('Connect to Surph project')
226
+ .action(async (cmd, options) => {
227
+ try {
228
+ const user = await auth.currentUser() // must be logged in.
229
+ if (user === false) {
230
+ program.error(`${colors.cyan('\nNot logged in. To log in:')}\n\n${colors.white('$ vly login')}`,
231
+ ERRORS.NOT_LOGGED_IN);
232
+ }
233
+
234
+ const projects = await Realm.restQuery('project', 'get', { 'members.id': user.id })
235
+ if (projects.length === 0) {
236
+ throw new Error(`No Projects not found.`)
237
+ }
238
+
239
+ // console.log('PROJECTS: ' + JSON.stringify(projects))
240
+
241
+ const _projects = projects.map(({ name }, idx) => `${idx + 1}. ${name}`)
242
+
243
+ const schema = {
244
+ properties: {
245
+ framework: {
246
+ message: `\n\n${_projects.join('\n')}\n\nSelect project number then press "enter"\n`,
247
+ required: true
248
+ }
249
+ }
250
+ }
251
+
252
+ prompt.start()
253
+
254
+ prompt.get(schema, (err, result) => {
255
+ if (err) {
256
+ console.log('\n')
257
+ return
258
+ }
259
+
260
+ const selected = result.framework.trim()
261
+ const index = parseInt(selected - 1)
262
+
263
+ if (index < 0) {
264
+ utils.printError(new Error('Invalid entry.'))
265
+ return
266
+ }
267
+
268
+ if (_projects.length < (index + 1)) {
269
+ utils.printError(new Error('Invalid entry.'))
270
+ return
271
+ }
272
+
273
+ const selectedApp = projects[index]
274
+ if (!selectedApp) {
275
+ utils.printError(new Error('Invalid entry.'))
276
+ return
277
+ }
278
+
279
+ // console.log('SELECTED: ' + JSON.stringify(selectedApp))
280
+ const { name, slug } = selectedApp
281
+ const envFile = utils.readFile('.env') || '{}'
282
+ const envJson = dotenv.parse(envFile)
283
+
284
+ const env = {
285
+ ...envJson,
286
+ 'TURBO_PROJECT': name,
287
+ 'TURBO_PROJECT_SLUG': slug
288
+ }
289
+
290
+ let envString = ''
291
+ Object.keys(env).forEach((key, i) => {
292
+ if (env[key] !== null) {
293
+ envString += key+'='+env[key]+'\n'
294
+ }
295
+ })
296
+
297
+ utils.write('.env', envString)
298
+ console.log('\nApp Connected!\nto deploy:\n\n$ vly deploy\n\n')
299
+ })
300
+ } catch (error) {
301
+ utils.printError(error)
302
+ }
303
+ })
304
+ */
305
+
306
+ program.command('publish')
307
+ .description('Publish Surph tool')
308
+ .action(async (cmd, options) => {
309
+ try {
310
+ const manifest = require('../../../manifest.json')
311
+ if (!manifest) {
312
+ throw new Error('manifest.json not found')
313
+ }
314
+
315
+ const keys = Object.keys(manifest)
316
+ if (keys.length === 0) {
317
+ throw new Error('invalid manifest.json')
318
+ }
319
+
320
+ const session = await surphSession()
321
+ if (!session) {
322
+ throw new Error('Not logged in.')
323
+ }
324
+
325
+ // console.log('SESSION: ' + JSON.stringify(session))
326
+ const userId = session?.userId
327
+
328
+ let resp = await fetch(`${SURPH_REST_URL}/rest/user/${userId}`, {
329
+ method: 'GET',
330
+ headers: {
331
+ Accept: 'application/json',
332
+ 'x-surph-client': 'surph-sdk'
333
+ }
334
+ })
335
+
336
+ const { payload: user } = await resp.json()
337
+ // console.log('USER: ' + JSON.stringify(user))
338
+
339
+
340
+ const slug = keys[0]
341
+ resp = await fetch(`${SURPH_REST_URL}/rest/tool?slug=${slug}`, {
342
+ method: 'GET',
343
+ headers: {
344
+ Accept: 'application/json',
345
+ 'x-surph-client': 'surph-sdk'
346
+ }
347
+ })
348
+
349
+ const { payload } = await resp.json()
350
+
351
+ const toolJson = manifest[slug]
352
+
353
+ // tool not registered yet, create:
354
+ if (payload.length === 0) {
355
+ const resp = await fetch(`${SURPH_REST_URL}/rest/tool`, {
356
+ method: 'POST',
357
+ body: JSON.stringify({
358
+ ...toolJson,
359
+ creator: {
360
+ _id: user._id,
361
+ username: user.username,
362
+ avatar: user.avatar
363
+ }
364
+ }),
365
+ headers: {
366
+ Accept: 'application/json',
367
+ 'Content-Type': 'application/json',
368
+ 'x-surph-client': 'surph-sdk'
369
+ }
370
+ })
371
+
372
+ const { payload } = await resp.json()
373
+ console.log(`\nSurph tool registered. To view, visit:\n\nhttps://surph.ai/tool/${slug}\n`)
374
+ return
375
+ }
376
+
377
+ resp = await fetch(`${SURPH_REST_URL}/rest/tool/${payload[0]._id}`, {
378
+ method: 'PUT',
379
+ body: JSON.stringify({
380
+ ...toolJson,
381
+ creator: {
382
+ _id: user._id,
383
+ username: user.username,
384
+ avatar: user.avatar
385
+ }
386
+ }),
387
+ headers: {
388
+ Accept: 'application/json',
389
+ 'Content-Type': 'application/json',
390
+ 'x-surph-client': 'surph-sdk'
391
+ }
392
+ })
393
+
394
+ const data = await resp.json()
395
+ console.log('\nSurph tool updated.')
396
+ } catch (error) {
397
+ console.log(colors.red('\nError: '+error.message+'\n'))
398
+ }
399
+ })
400
+
401
+
402
+ program.command('deploy')
403
+ .description('Deploy to Surph')
404
+ .action(async (cmd, options) => {
405
+ try {
406
+ /*
407
+ const { type } = cmd
408
+ if (type !== 'default' && type !== 'static') {
409
+ const msg = colors.red(`\nError: Invalid deployment type ${type}.\n`) +
410
+ colors.white(`Please select type "static" or omit type flag.\n`);
411
+ program.error(msg, ERRORS.INVALID_PARAMS)
412
+ }
413
+
414
+ const user = await auth.currentUser() // must be logged in.
415
+ if (user === false) {
416
+ program.error(`${colors.cyan('\nNot logged in. To log in:')}\n\n${colors.white('$ vly login')}`,
417
+ ERRORS.NOT_LOGGED_IN);
418
+ }
419
+
420
+ */
421
+
422
+ /*
423
+ const apps = await Realm.restQuery('project', 'get', { slug: TURBO_PROJECT_SLUG })
424
+ if (apps.length === 0) {
425
+ program.error(`App ${TURBO_PROJECT} not found.`, ERRORS.PROJECT_NOT_FOUND)
426
+ }
427
+
428
+ const { members } = apps[0]
429
+ if (!members.find(member => (member.id === user.id))) {
430
+ program.error('Unauthorized', ERRORS.NOT_AUTHORIZED)
431
+ }
432
+ */
433
+
434
+ const { SURPH_TOOL = null } = dotenv.parse(utils.readFile('.env') ?? '')
435
+
436
+ if (SURPH_TOOL === null) {
437
+ program.error('Surph project not connected. To connect, run:\n\n$ surph connect', ERRORS.PROJECT_NOT_CONNECTED)
438
+ }
439
+
440
+ await deploy.tool({ SURPH_TOOL_SLUG: SURPH_TOOL })
441
+ } catch (error) {
442
+ utils.printError(error)
443
+ program.error(`Unexpected error: ${error}`, ERRORS.UNSPECIFIED)
444
+ }
445
+ })
446
+
447
+ program.parse(process.argv)
@@ -0,0 +1,153 @@
1
+ const path = require('path')
2
+ const fs = require('fs')
3
+ const shell = require('shelljs')
4
+ const colors = require('colors/safe') // https://www.npmjs.com/package/colors
5
+ const bestzip = require('bestzip')
6
+
7
+ // const RPC_URL = 'https://e3lunom2hka3u6iiotrlzrcez40uwdnk.lambda-url.us-east-1.on.aws'
8
+ const RPC_URL = 'https://rpc.surph.ai'
9
+ const ZIP_NAME = 'LATEST.zip'
10
+ const STATIC_ARCHIVE_NAME = 'Archive.zip'
11
+ const DEFAULT_HEADERS = {
12
+ 'Content-Type': 'application/json',
13
+ 'Accept': 'application/json'
14
+ }
15
+
16
+ const rpcCall = async (action, args, headers = DEFAULT_HEADERS) => {
17
+ let path = null
18
+ if (action === 'uploadUrl') {
19
+ path = 's3/upload_url'
20
+ }
21
+ if (action === 'deployTool') {
22
+ path = 'tools/deploy'
23
+ }
24
+
25
+ try {
26
+ if (action === null) {
27
+ throw new Error(`Invalid action: ${action}`)
28
+ }
29
+
30
+ const resp = await fetch(`${RPC_URL}/${path}`, {
31
+ method: 'POST',
32
+ body: JSON.stringify(args),
33
+ headers: {
34
+ 'Accept': 'application/json',
35
+ 'Content-Type': 'application/json',
36
+ 'x-surph-client': 'surph-sdk'
37
+ }
38
+ })
39
+
40
+ const { response } = await resp.json()
41
+ if (response.error) {
42
+ throw new Error(response.error)
43
+ }
44
+
45
+ return response
46
+ } catch (error) {
47
+ throw error
48
+ }
49
+ }
50
+
51
+ /*
52
+ // 09/17/2019 - this did not cover all scenarios. Windows worked fine (confirmed
53
+ // by Marianne) but if on a funky linux distro, the "else" block failed.
54
+ var zipDirectory = function(){
55
+ // console.log('PLT == ' + process.platform)
56
+ if (process.platform == 'win32'){ // fuck windows:
57
+ return bestzip.nodeZip({source:'*', destination: '../package.zip'})
58
+ }
59
+ else {
60
+ return new Promise(function(resolve, reject){
61
+ shell.exec('zip -r -q package.zip *')
62
+ resolve(true)
63
+ })
64
+ }
65
+ } */
66
+
67
+ const zipDirectory = (name = 'package.zip') => {
68
+ // the package.zip file has to be created in another directory
69
+ // otherwise it will include "package.zip" itself with nothing
70
+ // inside. This is sloppy so we create the package.zip file in
71
+ // one level up then bring it back into the deploy folder
72
+
73
+ if (process.platform === 'win32'){ // fuck windows:
74
+ return bestzip.nodeZip({
75
+ source: '*',
76
+ destination: `../${name}`
77
+ })
78
+ }
79
+ else {
80
+ return bestzip.nodeZip({
81
+ source: '*',
82
+ destination: `../${name}`
83
+ })
84
+ }
85
+ }
86
+
87
+ const compile = async (deployDir, name = 'package.zip', dir = null) => {
88
+ shell.mkdir('-p', `${deployDir}/`)
89
+ shell.cp('-R', `node_modules/@surph_ai/sdk/deploy/tool-handler.js`, `${deployDir}/main.js`)
90
+
91
+ if (dir === null) {
92
+ shell.cp('-R', '!(node_modules|deploy)', `${deployDir}/`) // copy everything but node_modules and deploy
93
+ } else {
94
+ // compress contents of specified directory:
95
+ shell.cd(dir)
96
+ shell.cp('-R', '!(node_modules|deploy)', `../${deployDir}/`)
97
+ shell.cd('..')
98
+ }
99
+
100
+ shell.cd(deployDir)
101
+ shell.exec('npm install --production --silent')
102
+
103
+ await zipDirectory(name) // package it up!
104
+
105
+ // the zip file has to be created in another directory
106
+ // otherwise it will include "*.zip" itself with nothing
107
+ // inside. This is sloppy so we create the zip file in
108
+ // one level up then bring it back into the deploy folder
109
+ shell.cd('..')
110
+ shell.mv([name], `${deployDir}/`)
111
+ shell.cd(deployDir)
112
+
113
+ shell.mv([name], '..') // place package in root directory
114
+ shell.cd('..')
115
+ shell.rm('-rf', deployDir) // remove hidden deploy dir
116
+ }
117
+
118
+ module.exports = {
119
+ tool: async ({ SURPH_TOOL_SLUG }) => {
120
+ if (!SURPH_TOOL_SLUG) {
121
+ throw new Error(colors.white('Tool undefined. Please check .env file.'))
122
+ }
123
+
124
+ const { uploadUrl } = await rpcCall('uploadUrl', {
125
+ collection: 'tools',
126
+ folder: SURPH_TOOL_SLUG,
127
+ filename: `uploads/${ZIP_NAME}`,
128
+ filetype: 'application/zip'
129
+ })
130
+
131
+ // console.log('UPLOAD URL: ' + uploadUrl)
132
+ console.log(`\n${colors.white('Deploying tool. This usually takes a few minutes...')}\n`)
133
+
134
+ await compile('.deploy', ZIP_NAME)
135
+
136
+ await fetch(uploadUrl, {
137
+ method: 'PUT',
138
+ body: fs.readFileSync(ZIP_NAME),
139
+ headers: {
140
+ 'Accept': 'application/json',
141
+ 'Content-Type': 'application/zip'
142
+ }
143
+ })
144
+
145
+ shell.rm('-rf', ZIP_NAME)
146
+
147
+ // connect container image to function:
148
+ await rpcCall('deployTool', { tool: SURPH_TOOL_SLUG })
149
+
150
+ console.log(colors.white('\nDeploy complete! To view your tool, navigate to\n'))
151
+ console.log(colors.white(`https://surph.ai/tool/${SURPH_TOOL_SLUG}\n\n`))
152
+ }
153
+ }
@@ -0,0 +1,16 @@
1
+ import tool from './dist/tools/index.js';
2
+
3
+ export const handler = async (event) => {
4
+ const { args = {}, context = null, user = null } = event;
5
+ let payload = null;
6
+ try {
7
+ payload = await tool({ args, context, user });
8
+ } catch (error) {
9
+ payload = error instanceof Error ? error.message : String(error);
10
+ }
11
+
12
+ return {
13
+ statusCode: 200,
14
+ body: JSON.stringify(payload),
15
+ };
16
+ };
package/index.js ADDED
@@ -0,0 +1,3 @@
1
+ module.exports = {
2
+
3
+ }
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@surph_ai/cli",
3
+ "version": "0.0.1",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "types": "./types.d.ts",
7
+ "publishConfig": {
8
+ "access": "public"
9
+ },
10
+ "scripts": {
11
+ "test": "echo \"Error: no test specified\" && exit 1"
12
+ },
13
+ "bin": {
14
+ "surph": "./cli.js"
15
+ },
16
+ "keywords": [],
17
+ "author": "SurphAI",
18
+ "license": "ISC",
19
+ "dependencies": {
20
+ "@aws-sdk/client-bedrock-runtime": "^3.1096.0",
21
+ "adm-zip": "^0.5.10",
22
+ "bestzip": "^2.2.1",
23
+ "cheerio": "^1.2.0",
24
+ "colors": "^1.4.0",
25
+ "commander": "^14.0.1",
26
+ "cookie": "^0.5.0",
27
+ "cors": "^2.8.6",
28
+ "dotenv": "^16.0.3",
29
+ "express": "^4.18.1",
30
+ "impit": "^0.14.0",
31
+ "mustache": "^4.2.0",
32
+ "prompt": "^1.3.0",
33
+ "shelljs": "^0.8.5"
34
+ }
35
+ }