@sprucelabs/spruce-cli
Version:
Command line interface for building Spruce skills.
128 lines (114 loc) • 4.69 kB
text/typescript
import { buildSchema, SchemaValues } from '@sprucelabs/schema'
import AbstractAction from '../../AbstractAction'
import { FeatureActionResponse } from '../../features.types'
const optionsSchema = buildSchema({
id: 'testConversationOptions',
description: 'Test your conversation topics.',
fields: {
shouldReturnImmediately: {
type: 'boolean',
isPrivate: true,
},
shouldRunSilently: {
type: 'boolean',
isPrivate: true,
},
},
})
type OptionsSchema = typeof optionsSchema
type Options = SchemaValues<OptionsSchema>
export default class TestAction extends AbstractAction<OptionsSchema> {
public optionsSchema = optionsSchema
public commandAliases = ['test.conversation', 'chat']
public invocationMessage = "Let's test talking about topics... 🎙"
private killHandler?: () => void
public async execute(options: Options): Promise<FeatureActionResponse> {
const { shouldReturnImmediately, shouldRunSilently } =
this.validateAndNormalizeOptions(options)
this.ui.startLoading('Booting skill...')
try {
const command = this.Service('command')
let isWriting = false
const promise = new Promise((resolve, reject) => {
command
.execute('yarn boot.local', {
spawnOptions: shouldRunSilently
? undefined
: {
stdio: [process.stdin, 'pipe', 'pipe'],
},
onData: shouldRunSilently
? undefined
: async (data) => {
if (!isWriting) {
isWriting =
data.search(':: Skill booted') > -1
if (isWriting) {
this.ui.clear()
this.ui.stopLoading()
process.stdout?.write(
'? Send your first message to kick-off the conversation: '
)
}
} else if (isWriting) {
process.stdout?.write(
data.replace(
'Skill :: Skill booted',
''
)
)
}
},
onError: (data) => {
if (!data.includes('warning package.json')) {
// const err = new SpruceError({
// friendlyMessage:
// `Testing conversations failed because of the following error:\n\n` +
// data,
// code: 'EXECUTING_COMMAND_FAILED',
// cmd: 'ACTION=test.conversation yarn.boot.local',
// stderr: data,
// })
// reject(err)
}
},
env: {
ACTION: 'test.conversation',
},
})
.then(resolve)
.catch(reject)
})
this.killHandler = command.kill.bind(command)
if (shouldReturnImmediately) {
return {
meta: {
kill: this.killHandler,
pid: command.pid() as number,
promise,
},
}
} else {
await promise
}
} catch (err: any) {
if (
err.options?.stderr?.includes('SIGINT') ||
err.options?.code === 'CONVERSATION_ABORTED'
) {
return {
summaryLines: ['Conversation terminated. ✌️'],
}
}
return {
errors: [err],
}
}
return {
summaryLines: ['Talk soon! 👋'],
}
}
public async kill() {
this.killHandler?.()
}
}