@sentio/cli 3.5.1-rc.3 → 3.6.0-rc.2
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/lib/index.js +38418 -11002
- package/package.json +1 -1
- package/src/commands/dashboard.ts +86 -0
- package/src/commands/processor.ts +1 -1
- package/src/commands/stop-processor.ts +63 -0
- package/src/commands/upload.ts +164 -1
- package/src/index.ts +2 -0
- package/src/network.ts +407 -0
package/package.json
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
createApiContext,
|
|
8
8
|
handleCommandError,
|
|
9
9
|
loadJsonInput,
|
|
10
|
+
postApiJson,
|
|
10
11
|
resolveProjectRef,
|
|
11
12
|
unwrapApiResult
|
|
12
13
|
} from '../api.js'
|
|
@@ -30,6 +31,12 @@ interface DashboardImportOptions extends DashboardOptions {
|
|
|
30
31
|
overrideLayouts?: boolean
|
|
31
32
|
}
|
|
32
33
|
|
|
34
|
+
interface DashboardCreateOptions extends DashboardOptions {
|
|
35
|
+
title?: string
|
|
36
|
+
file?: string
|
|
37
|
+
stdin?: boolean
|
|
38
|
+
}
|
|
39
|
+
|
|
33
40
|
interface AddPanelOptions extends DashboardOptions {
|
|
34
41
|
panelName?: string
|
|
35
42
|
type?: string
|
|
@@ -48,6 +55,7 @@ interface AddPanelOptions extends DashboardOptions {
|
|
|
48
55
|
export function createDashboardCommand() {
|
|
49
56
|
const dashboardCommand = new Command('dashboard').description('Manage Sentio dashboards')
|
|
50
57
|
dashboardCommand.addCommand(createDashboardListCommand())
|
|
58
|
+
dashboardCommand.addCommand(createDashboardCreateCommand())
|
|
51
59
|
dashboardCommand.addCommand(createDashboardExportCommand())
|
|
52
60
|
dashboardCommand.addCommand(createDashboardImportCommand())
|
|
53
61
|
dashboardCommand.addCommand(createDashboardAddPanelCommand())
|
|
@@ -68,6 +76,23 @@ function createDashboardListCommand() {
|
|
|
68
76
|
})
|
|
69
77
|
}
|
|
70
78
|
|
|
79
|
+
function createDashboardCreateCommand() {
|
|
80
|
+
return withOutputOptions(
|
|
81
|
+
withSharedProjectOptions(withAuthOptions(new Command('create').description('Create a dashboard for a project')))
|
|
82
|
+
)
|
|
83
|
+
.showHelpAfterError()
|
|
84
|
+
.requiredOption('--title <name>', 'Dashboard title')
|
|
85
|
+
.option('--file <path>', 'Read initial dashboard JSON or YAML from file')
|
|
86
|
+
.option('--stdin', 'Read initial dashboard JSON or YAML from stdin')
|
|
87
|
+
.action(async (options, command) => {
|
|
88
|
+
try {
|
|
89
|
+
await runDashboardCreate(options)
|
|
90
|
+
} catch (error) {
|
|
91
|
+
handleDashboardCommandError(error, command)
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
}
|
|
95
|
+
|
|
71
96
|
function createDashboardExportCommand() {
|
|
72
97
|
return withOutputOptions(
|
|
73
98
|
withSharedProjectOptions(
|
|
@@ -197,6 +222,18 @@ async function runDashboardList(options: DashboardOptions) {
|
|
|
197
222
|
printOutput(options, data)
|
|
198
223
|
}
|
|
199
224
|
|
|
225
|
+
async function runDashboardCreate(options: DashboardCreateOptions) {
|
|
226
|
+
const context = createApiContext(options)
|
|
227
|
+
const project = await resolveProjectRef(options, context, { ownerSlug: true })
|
|
228
|
+
const body = buildDashboardCreateBody(options, project)
|
|
229
|
+
const data = await postApiJson<{ dashboard?: Record<string, unknown> }>('/v1/dashboards', context, body)
|
|
230
|
+
|
|
231
|
+
printOutput(options, {
|
|
232
|
+
message: `Dashboard "${options.title}" created`,
|
|
233
|
+
dashboard: data.dashboard ?? data
|
|
234
|
+
})
|
|
235
|
+
}
|
|
236
|
+
|
|
200
237
|
async function runDashboardExport(dashboardId: string, options: DashboardOptions) {
|
|
201
238
|
const context = createApiContext(options)
|
|
202
239
|
const response = await WebService.exportDashboard({
|
|
@@ -229,6 +266,47 @@ async function runDashboardImport(dashboardId: string, options: DashboardImportO
|
|
|
229
266
|
printOutput(options, { message: `Dashboard imported into ${dashboardId}`, dashboard: data.dashboard })
|
|
230
267
|
}
|
|
231
268
|
|
|
269
|
+
function buildDashboardCreateBody(options: DashboardCreateOptions, project: { owner: string; slug: string }) {
|
|
270
|
+
const input = loadJsonInput(options)
|
|
271
|
+
const initialDashboard = normalizeDashboardInit(input)
|
|
272
|
+
|
|
273
|
+
return {
|
|
274
|
+
name: options.title,
|
|
275
|
+
projectOwner: project.owner,
|
|
276
|
+
projectSlug: project.slug,
|
|
277
|
+
...initialDashboard
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function normalizeDashboardInit(input: unknown) {
|
|
282
|
+
if (input === undefined) {
|
|
283
|
+
return {
|
|
284
|
+
panels: {},
|
|
285
|
+
layouts: {
|
|
286
|
+
responsiveLayouts: {
|
|
287
|
+
lg: { layouts: [] }
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (!input || typeof input !== 'object' || Array.isArray(input)) {
|
|
294
|
+
throw new CliError('Dashboard initialization data must be a JSON or YAML object.')
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const dashboard = input as Record<string, unknown>
|
|
298
|
+
return {
|
|
299
|
+
panels: isRecord(dashboard.panels) ? dashboard.panels : {},
|
|
300
|
+
layouts: isRecord(dashboard.layouts)
|
|
301
|
+
? dashboard.layouts
|
|
302
|
+
: {
|
|
303
|
+
responsiveLayouts: {
|
|
304
|
+
lg: { layouts: [] }
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
232
310
|
async function runDashboardAddPanel(dashboardId: string, options: AddPanelOptions) {
|
|
233
311
|
const context = createApiContext(options)
|
|
234
312
|
|
|
@@ -378,6 +456,10 @@ function collectOption(value: string, previous: string[] = []) {
|
|
|
378
456
|
return previous
|
|
379
457
|
}
|
|
380
458
|
|
|
459
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
460
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value)
|
|
461
|
+
}
|
|
462
|
+
|
|
381
463
|
function withAuthOptions<T extends Command<any, any, any>>(command: T) {
|
|
382
464
|
return command
|
|
383
465
|
.option('--host <host>', 'Override Sentio host')
|
|
@@ -404,6 +486,10 @@ function handleDashboardCommandError(error: unknown, command?: Command) {
|
|
|
404
486
|
error.message.startsWith('Invalid project ') ||
|
|
405
487
|
error.message.startsWith('Dashboard ') ||
|
|
406
488
|
error.message.startsWith('Provide --file or --stdin') ||
|
|
489
|
+
error.message.startsWith('Use either --file or --stdin') ||
|
|
490
|
+
error.message.startsWith('Expected JSON or YAML') ||
|
|
491
|
+
error.message.startsWith('Invalid JSON or YAML') ||
|
|
492
|
+
error.message.startsWith('Dashboard initialization data') ||
|
|
407
493
|
error.message.startsWith('Provide exactly one data source') ||
|
|
408
494
|
error.message.startsWith('Use exactly one of --sql') ||
|
|
409
495
|
error.message.startsWith('Invalid chart type') ||
|
|
@@ -337,7 +337,7 @@ async function runProcessorResume(processorId: string | undefined, options: Proc
|
|
|
337
337
|
})
|
|
338
338
|
}
|
|
339
339
|
|
|
340
|
-
async function runProcessorStop(processorId: string | undefined, options: ProcessorOptions & { yes?: boolean }) {
|
|
340
|
+
export async function runProcessorStop(processorId: string | undefined, options: ProcessorOptions & { yes?: boolean }) {
|
|
341
341
|
const resolvedProcessorId = await resolveAndConfirmProcessor('stop', processorId, options)
|
|
342
342
|
if (!resolvedProcessorId) return
|
|
343
343
|
const context = createApiContext(options)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Command } from '@commander-js/extra-typings'
|
|
2
|
+
import chalk from 'chalk'
|
|
3
|
+
import { handleCommandError } from '../api.js'
|
|
4
|
+
import { confirm } from './upload.js'
|
|
5
|
+
import { runProcessorStop } from './processor.js'
|
|
6
|
+
import {
|
|
7
|
+
getSentioNetworkConfig,
|
|
8
|
+
resolveNetworkAddresses,
|
|
9
|
+
getWalletFromPrivateKey,
|
|
10
|
+
requirePrivateKey,
|
|
11
|
+
stopProcessorOnChain
|
|
12
|
+
} from '../network.js'
|
|
13
|
+
|
|
14
|
+
export function createStopProcessorCommand() {
|
|
15
|
+
return new Command('stop')
|
|
16
|
+
.description('Stop a processor. Uses Sentio Network contract when --sentio-network is specified.')
|
|
17
|
+
.argument('<processorId>', 'ID of the processor to stop')
|
|
18
|
+
.option('--sentio-network <network>', 'Stop via Sentio Network contract (only "testnet" supported)')
|
|
19
|
+
.option('--host <host>', 'Override Sentio host')
|
|
20
|
+
.option('--api-key <key>', 'Use an explicit API key instead of saved credentials')
|
|
21
|
+
.option('--token <token>', 'Use an explicit bearer token instead of saved credentials')
|
|
22
|
+
.option('--project <owner/slug>', 'Sentio project in <owner>/<slug> format')
|
|
23
|
+
.option('--owner <owner>', 'Sentio project owner')
|
|
24
|
+
.option('--name <name>', 'Sentio project name')
|
|
25
|
+
.option('-y, --yes', 'Bypass confirmation')
|
|
26
|
+
.showHelpAfterError()
|
|
27
|
+
.action(async (processorId, options) => {
|
|
28
|
+
try {
|
|
29
|
+
if (options.sentioNetwork) {
|
|
30
|
+
await runStopProcessorOnChain(processorId, options)
|
|
31
|
+
} else {
|
|
32
|
+
await runProcessorStop(processorId, options)
|
|
33
|
+
}
|
|
34
|
+
} catch (error) {
|
|
35
|
+
handleCommandError(error)
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function runStopProcessorOnChain(processorId: string, options: { sentioNetwork?: string; yes?: boolean }) {
|
|
41
|
+
const network = options.sentioNetwork!
|
|
42
|
+
const networkConfig = getSentioNetworkConfig(network)
|
|
43
|
+
|
|
44
|
+
const privateKey = requirePrivateKey()
|
|
45
|
+
const wallet = getWalletFromPrivateKey(privateKey)
|
|
46
|
+
|
|
47
|
+
console.log(chalk.blue(`Wallet address: ${wallet.address}`))
|
|
48
|
+
console.log(chalk.blue('Resolving contract addresses from AddressBook...'))
|
|
49
|
+
const addresses = await resolveNetworkAddresses(networkConfig)
|
|
50
|
+
|
|
51
|
+
if (!options.yes) {
|
|
52
|
+
const confirmed = await confirm(`Stop processor "${processorId}" on Sentio Network ${network}?`)
|
|
53
|
+
if (!confirmed) {
|
|
54
|
+
console.log('Stop cancelled.')
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
await stopProcessorOnChain(networkConfig, addresses, wallet, processorId)
|
|
60
|
+
|
|
61
|
+
console.log()
|
|
62
|
+
console.log(chalk.green(`Processor "${processorId}" stopped on Sentio Network ${network}.`))
|
|
63
|
+
}
|
package/src/commands/upload.ts
CHANGED
|
@@ -13,6 +13,21 @@ import readline from 'readline'
|
|
|
13
13
|
import JSZip from 'jszip'
|
|
14
14
|
import { UserInfo } from '../../../protos/lib/service/common/protos/common.js'
|
|
15
15
|
import { CommandOptionsType } from './types.js'
|
|
16
|
+
import {
|
|
17
|
+
getSentioNetworkConfig,
|
|
18
|
+
resolveNetworkAddresses,
|
|
19
|
+
getWalletFromPrivateKey,
|
|
20
|
+
requirePrivateKey,
|
|
21
|
+
checkSTBalance,
|
|
22
|
+
checkBillingBalance,
|
|
23
|
+
uploadToIPFS,
|
|
24
|
+
createProcessorOnChain,
|
|
25
|
+
startProcessorOnChain,
|
|
26
|
+
stopProcessorOnChain,
|
|
27
|
+
confirmNoPlatformUpload,
|
|
28
|
+
getProcessorOnChain,
|
|
29
|
+
deleteProcessorOnChain
|
|
30
|
+
} from '../network.js'
|
|
16
31
|
import { Auth, DefaultBatchUploader, FileType, IPFSBatchUploader, WalrusBatchUploader } from '../uploader.js'
|
|
17
32
|
export { type Auth } from '../uploader.js'
|
|
18
33
|
|
|
@@ -56,16 +71,164 @@ export function createUploadCommand() {
|
|
|
56
71
|
'--required-chain-id <chain_id...>',
|
|
57
72
|
'(Optional) Specify chain IDs required for the Sentio network. This option is only available when --sentio-network is used. If omitted, all chain IDs from the project configuration (contracts or network overrides) will be used.'
|
|
58
73
|
)
|
|
74
|
+
.option(
|
|
75
|
+
'--no-platform',
|
|
76
|
+
'Upload processor directly to Sentio Network without platform support. Requires $PRIVATE_KEY env var. Only testnet is supported.'
|
|
77
|
+
)
|
|
78
|
+
.option(
|
|
79
|
+
'--ipfs-put-url <url>',
|
|
80
|
+
'IPFS upload endpoint (used with --no-platform)',
|
|
81
|
+
'https://api.sentio.xyz/v1/ipfs/add'
|
|
82
|
+
)
|
|
59
83
|
.action(async (options, command) => {
|
|
60
84
|
const processorConfig = loadProcessorConfig(options.path)
|
|
61
85
|
overrideConfigWithOptions(processorConfig, options)
|
|
62
86
|
if (options.path) {
|
|
63
87
|
process.chdir(options.path)
|
|
64
88
|
}
|
|
65
|
-
|
|
89
|
+
if (options.platform === false) {
|
|
90
|
+
await runNoPlatformUpload(processorConfig, options)
|
|
91
|
+
} else {
|
|
92
|
+
await runUploadInternal(processorConfig, options, command)
|
|
93
|
+
}
|
|
66
94
|
})
|
|
67
95
|
}
|
|
68
96
|
|
|
97
|
+
async function runNoPlatformUpload(
|
|
98
|
+
processorConfig: YamlProjectConfig,
|
|
99
|
+
options: CommandOptionsType<typeof createUploadCommand>
|
|
100
|
+
) {
|
|
101
|
+
// Determine network - default to testnet for --no-platform
|
|
102
|
+
const network = options.sentioNetwork || 'testnet'
|
|
103
|
+
const networkConfig = getSentioNetworkConfig(network)
|
|
104
|
+
|
|
105
|
+
// Step 1: Require PRIVATE_KEY
|
|
106
|
+
const privateKey = requirePrivateKey()
|
|
107
|
+
const wallet = getWalletFromPrivateKey(privateKey)
|
|
108
|
+
const walletAddress = wallet.address
|
|
109
|
+
|
|
110
|
+
// Step 2: Resolve contract addresses via AddressBook
|
|
111
|
+
console.log(chalk.blue('Resolving contract addresses from AddressBook...'))
|
|
112
|
+
const addresses = await resolveNetworkAddresses(networkConfig)
|
|
113
|
+
|
|
114
|
+
// Step 3: Check ST balance and Billing balance, then confirm
|
|
115
|
+
const [stBalance, billingBalance] = await Promise.all([
|
|
116
|
+
checkSTBalance(networkConfig, addresses, walletAddress),
|
|
117
|
+
checkBillingBalance(networkConfig, addresses, walletAddress)
|
|
118
|
+
])
|
|
119
|
+
const confirmed = await confirmNoPlatformUpload(walletAddress, stBalance, billingBalance, addresses, networkConfig)
|
|
120
|
+
if (!confirmed) {
|
|
121
|
+
console.log('Upload cancelled.')
|
|
122
|
+
process.exit(0)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Step 4: Build processor
|
|
126
|
+
if (!options.skipBuild) {
|
|
127
|
+
await buildProcessor(false, options)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const processorFile = path.join(process.cwd(), 'dist/lib.js')
|
|
131
|
+
if (!fs.existsSync(processorFile)) {
|
|
132
|
+
console.error(chalk.red('File not found:', processorFile, "- don't use --skip-build"))
|
|
133
|
+
process.exit(1)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const codeBuffer = fs.readFileSync(processorFile)
|
|
137
|
+
const stat = fs.statSync(processorFile)
|
|
138
|
+
console.log('Packed processor file size', Math.floor(stat.size / 1024) + 'K, last modified', stat.mtime)
|
|
139
|
+
|
|
140
|
+
// Step 5: Upload to IPFS
|
|
141
|
+
const ipfsPutUrl = options.ipfsPutUrl || 'https://api.sentio.xyz/v1/ipfs/add'
|
|
142
|
+
console.log(chalk.blue(`Uploading processor to IPFS (${ipfsPutUrl})...`))
|
|
143
|
+
const cid = await uploadToIPFS(codeBuffer, ipfsPutUrl)
|
|
144
|
+
console.log(chalk.green(`IPFS upload complete.`))
|
|
145
|
+
console.log('\t', chalk.blue('CID:'), cid)
|
|
146
|
+
|
|
147
|
+
// Step 6: Collect required chain IDs
|
|
148
|
+
let requiredChainIds = options.requiredChainId || processorConfig.requiredChainIds || []
|
|
149
|
+
if (requiredChainIds.length === 0) {
|
|
150
|
+
const chainIds = new Set<string>()
|
|
151
|
+
if (processorConfig.contracts) {
|
|
152
|
+
for (const contract of processorConfig.contracts) {
|
|
153
|
+
chainIds.add(String(contract.chain))
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (processorConfig.networkOverrides) {
|
|
157
|
+
for (const override of processorConfig.networkOverrides) {
|
|
158
|
+
chainIds.add(String(override.chain))
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
requiredChainIds = Array.from(chainIds)
|
|
162
|
+
}
|
|
163
|
+
if (requiredChainIds.length === 0) {
|
|
164
|
+
console.error(
|
|
165
|
+
chalk.red(
|
|
166
|
+
'Error: No chain IDs found. Specify --required-chain-id or define contracts/networkOverrides in sentio.yaml.'
|
|
167
|
+
)
|
|
168
|
+
)
|
|
169
|
+
process.exit(1)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Step 7: Derive processor ID from project name
|
|
173
|
+
let processorId = processorConfig.project.replace('/', '_')
|
|
174
|
+
const sdkVersion = getSdkVersion() || 'unknown'
|
|
175
|
+
|
|
176
|
+
// Step 7.5: Check if processor already exists on-chain
|
|
177
|
+
console.log(chalk.blue('Checking if processor already exists on-chain...'))
|
|
178
|
+
const existingProcessor = await getProcessorOnChain(networkConfig, addresses, processorId)
|
|
179
|
+
|
|
180
|
+
if (existingProcessor) {
|
|
181
|
+
const existingOwner = existingProcessor.owner.toLowerCase()
|
|
182
|
+
const currentWallet = wallet.address.toLowerCase()
|
|
183
|
+
|
|
184
|
+
if (existingOwner === currentWallet) {
|
|
185
|
+
// Same owner — prompt to replace
|
|
186
|
+
console.log(chalk.yellow(`Processor "${processorId}" already exists (owned by you).`))
|
|
187
|
+
const shouldReplace = await confirm(`Replace existing processor "${processorId}"?`)
|
|
188
|
+
if (!shouldReplace) {
|
|
189
|
+
console.log('Upload cancelled.')
|
|
190
|
+
process.exit(0)
|
|
191
|
+
}
|
|
192
|
+
// Stop and delete existing processor before re-creating
|
|
193
|
+
await stopProcessorOnChain(networkConfig, addresses, wallet, processorId)
|
|
194
|
+
await deleteProcessorOnChain(networkConfig, addresses, wallet, processorId)
|
|
195
|
+
} else {
|
|
196
|
+
// Different owner — prompt to rename
|
|
197
|
+
console.log(
|
|
198
|
+
chalk.yellow(
|
|
199
|
+
`Processor "${processorId}" already exists and is owned by ${existingProcessor.owner} (not your wallet ${wallet.address}).`
|
|
200
|
+
)
|
|
201
|
+
)
|
|
202
|
+
const randomSuffix = Math.random().toString(36).substring(2, 8)
|
|
203
|
+
const defaultNewId = `${processorId}-${randomSuffix}`
|
|
204
|
+
|
|
205
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout })
|
|
206
|
+
const newId: string = await new Promise((resolve) =>
|
|
207
|
+
rl.question(`Enter a new processor ID [${defaultNewId}]: `, (answer) => {
|
|
208
|
+
rl.close()
|
|
209
|
+
resolve(answer.trim() || defaultNewId)
|
|
210
|
+
})
|
|
211
|
+
)
|
|
212
|
+
processorId = newId
|
|
213
|
+
console.log(chalk.blue(`Using processor ID: ${processorId}`))
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Step 8: Create processor on-chain
|
|
218
|
+
await createProcessorOnChain(networkConfig, addresses, wallet, processorId, cid, requiredChainIds, sdkVersion)
|
|
219
|
+
|
|
220
|
+
// Step 9: Start processor on-chain
|
|
221
|
+
await startProcessorOnChain(networkConfig, addresses, wallet, processorId)
|
|
222
|
+
|
|
223
|
+
console.log()
|
|
224
|
+
console.log(chalk.green('=== Upload Complete ==='))
|
|
225
|
+
console.log('\t', chalk.blue('Processor ID:'), processorId)
|
|
226
|
+
console.log('\t', chalk.blue('IPFS CID:'), cid)
|
|
227
|
+
console.log('\t', chalk.blue('Network:'), network)
|
|
228
|
+
const codeHash = createHash('sha256').update(codeBuffer).digest('hex')
|
|
229
|
+
console.log('\t', chalk.blue('sha256:'), codeHash)
|
|
230
|
+
}
|
|
231
|
+
|
|
69
232
|
function parseCheckpoints(
|
|
70
233
|
checkpoints: string[] | undefined,
|
|
71
234
|
continueFrom: number | undefined,
|
package/src/index.ts
CHANGED
|
@@ -19,6 +19,7 @@ import { createPriceCommand } from './commands/price.js'
|
|
|
19
19
|
import { createSimulationCommand } from './commands/simulation.js'
|
|
20
20
|
import { createEndpointCommand } from './commands/endpoint.js'
|
|
21
21
|
import { createDashboardCommand } from './commands/dashboard.js'
|
|
22
|
+
import { createStopProcessorCommand } from './commands/stop-processor.js'
|
|
22
23
|
import { enableApiDebug } from './api.js'
|
|
23
24
|
import { printVersions } from './utils.js'
|
|
24
25
|
|
|
@@ -52,5 +53,6 @@ program.addCommand(createPriceCommand())
|
|
|
52
53
|
program.addCommand(createSimulationCommand())
|
|
53
54
|
program.addCommand(createEndpointCommand())
|
|
54
55
|
program.addCommand(createDashboardCommand())
|
|
56
|
+
program.addCommand(createStopProcessorCommand())
|
|
55
57
|
|
|
56
58
|
program.parse()
|