@antseed/cli 0.1.0
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/.env.example +15 -0
- package/README.md +169 -0
- package/dist/cli/commands/balance.d.ts +3 -0
- package/dist/cli/commands/balance.d.ts.map +1 -0
- package/dist/cli/commands/balance.js +64 -0
- package/dist/cli/commands/balance.js.map +1 -0
- package/dist/cli/commands/browse.d.ts +7 -0
- package/dist/cli/commands/browse.d.ts.map +1 -0
- package/dist/cli/commands/browse.js +100 -0
- package/dist/cli/commands/browse.js.map +1 -0
- package/dist/cli/commands/config.d.ts +20 -0
- package/dist/cli/commands/config.d.ts.map +1 -0
- package/dist/cli/commands/config.js +239 -0
- package/dist/cli/commands/config.js.map +1 -0
- package/dist/cli/commands/connect.d.ts +14 -0
- package/dist/cli/commands/connect.d.ts.map +1 -0
- package/dist/cli/commands/connect.js +298 -0
- package/dist/cli/commands/connect.js.map +1 -0
- package/dist/cli/commands/connect.test.d.ts +2 -0
- package/dist/cli/commands/connect.test.d.ts.map +1 -0
- package/dist/cli/commands/connect.test.js +54 -0
- package/dist/cli/commands/connect.test.js.map +1 -0
- package/dist/cli/commands/dashboard.d.ts +6 -0
- package/dist/cli/commands/dashboard.d.ts.map +1 -0
- package/dist/cli/commands/dashboard.js +48 -0
- package/dist/cli/commands/dashboard.js.map +1 -0
- package/dist/cli/commands/deposit.d.ts +3 -0
- package/dist/cli/commands/deposit.d.ts.map +1 -0
- package/dist/cli/commands/deposit.js +48 -0
- package/dist/cli/commands/deposit.js.map +1 -0
- package/dist/cli/commands/dev.d.ts +3 -0
- package/dist/cli/commands/dev.d.ts.map +1 -0
- package/dist/cli/commands/dev.js +94 -0
- package/dist/cli/commands/dev.js.map +1 -0
- package/dist/cli/commands/init.d.ts +3 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +91 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/plugin-create.d.ts +11 -0
- package/dist/cli/commands/plugin-create.d.ts.map +1 -0
- package/dist/cli/commands/plugin-create.js +201 -0
- package/dist/cli/commands/plugin-create.js.map +1 -0
- package/dist/cli/commands/plugin-create.test.d.ts +2 -0
- package/dist/cli/commands/plugin-create.test.d.ts.map +1 -0
- package/dist/cli/commands/plugin-create.test.js +53 -0
- package/dist/cli/commands/plugin-create.test.js.map +1 -0
- package/dist/cli/commands/plugin.d.ts +3 -0
- package/dist/cli/commands/plugin.d.ts.map +1 -0
- package/dist/cli/commands/plugin.js +279 -0
- package/dist/cli/commands/plugin.js.map +1 -0
- package/dist/cli/commands/plugin.test.d.ts +2 -0
- package/dist/cli/commands/plugin.test.d.ts.map +1 -0
- package/dist/cli/commands/plugin.test.js +53 -0
- package/dist/cli/commands/plugin.test.js.map +1 -0
- package/dist/cli/commands/profile.d.ts +10 -0
- package/dist/cli/commands/profile.d.ts.map +1 -0
- package/dist/cli/commands/profile.js +89 -0
- package/dist/cli/commands/profile.js.map +1 -0
- package/dist/cli/commands/seed.d.ts +11 -0
- package/dist/cli/commands/seed.d.ts.map +1 -0
- package/dist/cli/commands/seed.js +397 -0
- package/dist/cli/commands/seed.js.map +1 -0
- package/dist/cli/commands/seed.test.d.ts +2 -0
- package/dist/cli/commands/seed.test.d.ts.map +1 -0
- package/dist/cli/commands/seed.test.js +57 -0
- package/dist/cli/commands/seed.test.js.map +1 -0
- package/dist/cli/commands/status.d.ts +8 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/status.js +55 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/commands/types.d.ts +14 -0
- package/dist/cli/commands/types.d.ts.map +1 -0
- package/dist/cli/commands/types.js +41 -0
- package/dist/cli/commands/types.js.map +1 -0
- package/dist/cli/commands/withdraw.d.ts +3 -0
- package/dist/cli/commands/withdraw.d.ts.map +1 -0
- package/dist/cli/commands/withdraw.js +48 -0
- package/dist/cli/commands/withdraw.js.map +1 -0
- package/dist/cli/formatters.d.ts +29 -0
- package/dist/cli/formatters.d.ts.map +1 -0
- package/dist/cli/formatters.js +67 -0
- package/dist/cli/formatters.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +41 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/shutdown.d.ts +11 -0
- package/dist/cli/shutdown.d.ts.map +1 -0
- package/dist/cli/shutdown.js +34 -0
- package/dist/cli/shutdown.js.map +1 -0
- package/dist/config/defaults.d.ts +6 -0
- package/dist/config/defaults.d.ts.map +1 -0
- package/dist/config/defaults.js +48 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/effective.d.ts +26 -0
- package/dist/config/effective.d.ts.map +1 -0
- package/dist/config/effective.js +84 -0
- package/dist/config/effective.js.map +1 -0
- package/dist/config/effective.test.d.ts +2 -0
- package/dist/config/effective.test.d.ts.map +1 -0
- package/dist/config/effective.test.js +65 -0
- package/dist/config/effective.test.js.map +1 -0
- package/dist/config/loader.d.ts +12 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +212 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/loader.test.d.ts +2 -0
- package/dist/config/loader.test.d.ts.map +1 -0
- package/dist/config/loader.test.js +77 -0
- package/dist/config/loader.test.js.map +1 -0
- package/dist/config/types.d.ts +133 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +2 -0
- package/dist/config/types.js.map +1 -0
- package/dist/config/validation.d.ts +10 -0
- package/dist/config/validation.d.ts.map +1 -0
- package/dist/config/validation.js +50 -0
- package/dist/config/validation.js.map +1 -0
- package/dist/env/load-env.d.ts +6 -0
- package/dist/env/load-env.d.ts.map +1 -0
- package/dist/env/load-env.js +18 -0
- package/dist/env/load-env.js.map +1 -0
- package/dist/plugins/loader.d.ts +7 -0
- package/dist/plugins/loader.d.ts.map +1 -0
- package/dist/plugins/loader.js +70 -0
- package/dist/plugins/loader.js.map +1 -0
- package/dist/plugins/manager.d.ts +11 -0
- package/dist/plugins/manager.d.ts.map +1 -0
- package/dist/plugins/manager.js +52 -0
- package/dist/plugins/manager.js.map +1 -0
- package/dist/plugins/registry.d.ts +8 -0
- package/dist/plugins/registry.d.ts.map +1 -0
- package/dist/plugins/registry.js +39 -0
- package/dist/plugins/registry.js.map +1 -0
- package/dist/proxy/buyer-proxy.d.ts +30 -0
- package/dist/proxy/buyer-proxy.d.ts.map +1 -0
- package/dist/proxy/buyer-proxy.js +488 -0
- package/dist/proxy/buyer-proxy.js.map +1 -0
- package/dist/status/node-status.d.ts +22 -0
- package/dist/status/node-status.d.ts.map +1 -0
- package/dist/status/node-status.js +83 -0
- package/dist/status/node-status.js.map +1 -0
- package/package.json +39 -0
- package/src/cli/commands/balance.ts +77 -0
- package/src/cli/commands/browse.ts +113 -0
- package/src/cli/commands/config.ts +271 -0
- package/src/cli/commands/connect.test.ts +69 -0
- package/src/cli/commands/connect.ts +342 -0
- package/src/cli/commands/dashboard.ts +59 -0
- package/src/cli/commands/deposit.ts +61 -0
- package/src/cli/commands/dev.ts +107 -0
- package/src/cli/commands/init.ts +99 -0
- package/src/cli/commands/plugin-create.test.ts +60 -0
- package/src/cli/commands/plugin-create.ts +230 -0
- package/src/cli/commands/plugin.test.ts +55 -0
- package/src/cli/commands/plugin.ts +295 -0
- package/src/cli/commands/profile.ts +95 -0
- package/src/cli/commands/seed.test.ts +70 -0
- package/src/cli/commands/seed.ts +447 -0
- package/src/cli/commands/status.ts +73 -0
- package/src/cli/commands/types.ts +56 -0
- package/src/cli/commands/withdraw.ts +61 -0
- package/src/cli/formatters.ts +64 -0
- package/src/cli/index.ts +46 -0
- package/src/cli/shutdown.ts +38 -0
- package/src/config/defaults.ts +49 -0
- package/src/config/effective.test.ts +80 -0
- package/src/config/effective.ts +119 -0
- package/src/config/loader.test.ts +95 -0
- package/src/config/loader.ts +251 -0
- package/src/config/types.ts +139 -0
- package/src/config/validation.ts +78 -0
- package/src/env/load-env.ts +20 -0
- package/src/plugins/loader.ts +96 -0
- package/src/plugins/manager.ts +66 -0
- package/src/plugins/registry.ts +45 -0
- package/src/proxy/buyer-proxy.ts +604 -0
- package/src/status/node-status.ts +105 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import type { Command } from 'commander'
|
|
2
|
+
import chalk from 'chalk'
|
|
3
|
+
import ora from 'ora'
|
|
4
|
+
import { createInterface } from 'node:readline/promises'
|
|
5
|
+
import { join } from 'node:path'
|
|
6
|
+
import { homedir } from 'node:os'
|
|
7
|
+
import { installPlugin, removePlugin, listInstalledPlugins, getPluginsDir } from '../../plugins/manager.js'
|
|
8
|
+
import { TRUSTED_PLUGINS } from '../../plugins/registry.js'
|
|
9
|
+
import {
|
|
10
|
+
addInstance,
|
|
11
|
+
removeInstance,
|
|
12
|
+
getInstance,
|
|
13
|
+
getInstances,
|
|
14
|
+
updateInstanceConfig,
|
|
15
|
+
loadPluginModule,
|
|
16
|
+
} from '@antseed/node'
|
|
17
|
+
import type { ConfigField, AntseedPlugin } from '@antseed/node'
|
|
18
|
+
import { registerPluginCreateCommand } from './plugin-create.js'
|
|
19
|
+
|
|
20
|
+
const CONFIG_PATH = join(homedir(), '.antseed', 'config.json')
|
|
21
|
+
|
|
22
|
+
function generateInstanceId(pluginName: string): string {
|
|
23
|
+
const suffix = Math.random().toString(36).slice(2, 8)
|
|
24
|
+
return `${pluginName}-${suffix}`
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function getConfigSchema(plugin: AntseedPlugin): ConfigField[] {
|
|
28
|
+
return plugin.configSchema ?? plugin.configKeys ?? []
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function maskSecret(value: string): string {
|
|
32
|
+
if (value.length <= 4) return '****'
|
|
33
|
+
return '****' + value.slice(-4)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function promptForFields(
|
|
37
|
+
rl: ReturnType<typeof createInterface>,
|
|
38
|
+
schema: ConfigField[],
|
|
39
|
+
existingConfig?: Record<string, unknown>,
|
|
40
|
+
): Promise<Record<string, unknown>> {
|
|
41
|
+
const config: Record<string, unknown> = {}
|
|
42
|
+
for (const field of schema) {
|
|
43
|
+
const defaultVal = existingConfig?.[field.key] ?? field.default
|
|
44
|
+
const defaultHint = field.type === 'secret' && typeof defaultVal === 'string' && defaultVal
|
|
45
|
+
? ` [${maskSecret(defaultVal)}]`
|
|
46
|
+
: defaultVal !== undefined
|
|
47
|
+
? ` [${String(defaultVal)}]`
|
|
48
|
+
: ''
|
|
49
|
+
const requiredHint = field.required ? ' (required)' : ''
|
|
50
|
+
const desc = field.description ? ` — ${field.description}` : ''
|
|
51
|
+
const prompt = ` ${field.label}${requiredHint}${desc}${defaultHint}: `
|
|
52
|
+
|
|
53
|
+
let answer: string
|
|
54
|
+
if (field.type === 'secret') {
|
|
55
|
+
process.stdout.write(prompt)
|
|
56
|
+
const stdin = process.stdin
|
|
57
|
+
const wasRaw = stdin.isRaw
|
|
58
|
+
if (stdin.isTTY) stdin.setRawMode(true)
|
|
59
|
+
let secret = ''
|
|
60
|
+
answer = await new Promise<string>((resolve) => {
|
|
61
|
+
const onData = (chunk: Buffer) => {
|
|
62
|
+
const ch = chunk.toString()
|
|
63
|
+
if (ch === '\n' || ch === '\r') {
|
|
64
|
+
stdin.removeListener('data', onData)
|
|
65
|
+
if (stdin.isTTY && wasRaw !== undefined) stdin.setRawMode(wasRaw)
|
|
66
|
+
process.stdout.write('\n')
|
|
67
|
+
resolve(secret)
|
|
68
|
+
} else if (ch === '\u007f' || ch === '\b') {
|
|
69
|
+
secret = secret.slice(0, -1)
|
|
70
|
+
} else if (ch === '\u0003') {
|
|
71
|
+
stdin.removeListener('data', onData)
|
|
72
|
+
if (stdin.isTTY && wasRaw !== undefined) stdin.setRawMode(wasRaw)
|
|
73
|
+
process.exit(0)
|
|
74
|
+
} else {
|
|
75
|
+
secret += ch
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
stdin.on('data', onData)
|
|
79
|
+
})
|
|
80
|
+
} else {
|
|
81
|
+
answer = await rl.question(prompt)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const trimmed = answer.trim()
|
|
85
|
+
if (trimmed) {
|
|
86
|
+
if (field.type === 'number') {
|
|
87
|
+
config[field.key] = Number(trimmed)
|
|
88
|
+
} else if (field.type === 'boolean') {
|
|
89
|
+
config[field.key] = trimmed === 'true' || trimmed === 'yes' || trimmed === '1'
|
|
90
|
+
} else {
|
|
91
|
+
config[field.key] = trimmed
|
|
92
|
+
}
|
|
93
|
+
} else if (defaultVal !== undefined) {
|
|
94
|
+
config[field.key] = defaultVal
|
|
95
|
+
} else if (field.required) {
|
|
96
|
+
console.log(chalk.red(` ${field.label} is required.`))
|
|
97
|
+
// Re-prompt by recursing on just this field
|
|
98
|
+
const retry = await promptForFields(rl, [field], existingConfig)
|
|
99
|
+
config[field.key] = retry[field.key]!
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return config
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function registerPluginCommand(program: Command): void {
|
|
106
|
+
const pluginCmd = program
|
|
107
|
+
.command('plugin')
|
|
108
|
+
.description('Manage antseed plugins')
|
|
109
|
+
|
|
110
|
+
// Task 1: plugin add with interactive config
|
|
111
|
+
pluginCmd
|
|
112
|
+
.command('add <package>')
|
|
113
|
+
.description('Install a plugin from npm and configure an instance')
|
|
114
|
+
.action(async (packageName: string) => {
|
|
115
|
+
const spinner = ora(`Installing ${packageName}...`).start()
|
|
116
|
+
try {
|
|
117
|
+
await installPlugin(packageName)
|
|
118
|
+
spinner.succeed(chalk.green(`Installed ${packageName}`))
|
|
119
|
+
} catch (err) {
|
|
120
|
+
spinner.fail(chalk.red(`Failed to install ${packageName}: ${(err as Error).message}`))
|
|
121
|
+
process.exit(1)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Load plugin module to read configSchema
|
|
125
|
+
let plugin: AntseedPlugin
|
|
126
|
+
try {
|
|
127
|
+
plugin = await loadPluginModule(packageName, getPluginsDir())
|
|
128
|
+
} catch (err) {
|
|
129
|
+
console.log(chalk.yellow(`\nInstalled but could not load plugin module: ${(err as Error).message}`))
|
|
130
|
+
return
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const schema = getConfigSchema(plugin)
|
|
134
|
+
if (schema.length === 0) {
|
|
135
|
+
console.log(chalk.dim('\nPlugin has no configuration fields.'))
|
|
136
|
+
const instanceId = generateInstanceId(plugin.name)
|
|
137
|
+
await addInstance(CONFIG_PATH, {
|
|
138
|
+
id: instanceId,
|
|
139
|
+
package: packageName,
|
|
140
|
+
type: plugin.type,
|
|
141
|
+
config: {},
|
|
142
|
+
enabled: true,
|
|
143
|
+
createdAt: new Date().toISOString(),
|
|
144
|
+
}, schema)
|
|
145
|
+
console.log(chalk.green(`\nCreated instance: ${instanceId}`))
|
|
146
|
+
return
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
console.log(chalk.bold(`\nConfigure ${plugin.displayName}:\n`))
|
|
150
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout })
|
|
151
|
+
try {
|
|
152
|
+
const config = await promptForFields(rl, schema)
|
|
153
|
+
const instanceId = generateInstanceId(plugin.name)
|
|
154
|
+
await addInstance(CONFIG_PATH, {
|
|
155
|
+
id: instanceId,
|
|
156
|
+
package: packageName,
|
|
157
|
+
type: plugin.type,
|
|
158
|
+
config,
|
|
159
|
+
enabled: true,
|
|
160
|
+
createdAt: new Date().toISOString(),
|
|
161
|
+
}, schema)
|
|
162
|
+
console.log(chalk.green(`\nCreated instance: ${instanceId}`))
|
|
163
|
+
} finally {
|
|
164
|
+
rl.close()
|
|
165
|
+
}
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
// Task 2: plugin config command
|
|
169
|
+
pluginCmd
|
|
170
|
+
.command('config <id>')
|
|
171
|
+
.description('Update configuration for a plugin instance')
|
|
172
|
+
.action(async (instanceId: string) => {
|
|
173
|
+
const instance = await getInstance(CONFIG_PATH, instanceId)
|
|
174
|
+
if (!instance) {
|
|
175
|
+
console.log(chalk.red(`Instance "${instanceId}" not found.`))
|
|
176
|
+
process.exit(1)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
let plugin: AntseedPlugin
|
|
180
|
+
try {
|
|
181
|
+
plugin = await loadPluginModule(instance.package, getPluginsDir())
|
|
182
|
+
} catch (err) {
|
|
183
|
+
console.log(chalk.red(`Could not load plugin: ${(err as Error).message}`))
|
|
184
|
+
process.exit(1)
|
|
185
|
+
return // unreachable but satisfies TS
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const schema = getConfigSchema(plugin)
|
|
189
|
+
if (schema.length === 0) {
|
|
190
|
+
console.log(chalk.dim('Plugin has no configuration fields.'))
|
|
191
|
+
return
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
console.log(chalk.bold(`\nConfigure ${plugin.displayName} (${instanceId}):\n`))
|
|
195
|
+
|
|
196
|
+
// Show current values
|
|
197
|
+
for (const field of schema) {
|
|
198
|
+
const current = instance.config[field.key]
|
|
199
|
+
if (current !== undefined) {
|
|
200
|
+
const display = field.type === 'secret' ? maskSecret(String(current)) : String(current)
|
|
201
|
+
console.log(chalk.dim(` Current ${field.label}: ${display}`))
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
console.log('')
|
|
205
|
+
|
|
206
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout })
|
|
207
|
+
try {
|
|
208
|
+
const newConfig = await promptForFields(rl, schema, instance.config)
|
|
209
|
+
await updateInstanceConfig(CONFIG_PATH, instanceId, newConfig, schema)
|
|
210
|
+
console.log(chalk.green(`\nUpdated instance: ${instanceId}`))
|
|
211
|
+
} finally {
|
|
212
|
+
rl.close()
|
|
213
|
+
}
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
// Task 5: plugin remove with instance handling
|
|
217
|
+
pluginCmd
|
|
218
|
+
.command('remove <name>')
|
|
219
|
+
.description('Remove a plugin instance or uninstall a package')
|
|
220
|
+
.action(async (name: string) => {
|
|
221
|
+
// Check if name matches an instance ID
|
|
222
|
+
const instance = await getInstance(CONFIG_PATH, name)
|
|
223
|
+
if (instance) {
|
|
224
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout })
|
|
225
|
+
try {
|
|
226
|
+
const confirm = await rl.question(`Remove instance "${name}"? (y/N): `)
|
|
227
|
+
if (confirm.trim().toLowerCase() !== 'y') {
|
|
228
|
+
console.log(chalk.dim('Cancelled.'))
|
|
229
|
+
return
|
|
230
|
+
}
|
|
231
|
+
await removeInstance(CONFIG_PATH, name)
|
|
232
|
+
console.log(chalk.green(`Removed instance: ${name}`))
|
|
233
|
+
|
|
234
|
+
const uninstall = await rl.question(`Also uninstall npm package "${instance.package}"? (y/N): `)
|
|
235
|
+
if (uninstall.trim().toLowerCase() === 'y') {
|
|
236
|
+
const spinner = ora(`Removing ${instance.package}...`).start()
|
|
237
|
+
try {
|
|
238
|
+
await removePlugin(instance.package)
|
|
239
|
+
spinner.succeed(chalk.green(`Uninstalled ${instance.package}`))
|
|
240
|
+
} catch (err) {
|
|
241
|
+
spinner.fail(chalk.red(`Failed to uninstall: ${(err as Error).message}`))
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
} finally {
|
|
245
|
+
rl.close()
|
|
246
|
+
}
|
|
247
|
+
return
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Fall through: treat as npm package name
|
|
251
|
+
const spinner = ora(`Removing ${name}...`).start()
|
|
252
|
+
try {
|
|
253
|
+
await removePlugin(name)
|
|
254
|
+
spinner.succeed(chalk.green(`Removed ${name}`))
|
|
255
|
+
} catch (err) {
|
|
256
|
+
spinner.fail(chalk.red(`Failed to remove ${name}: ${(err as Error).message}`))
|
|
257
|
+
process.exit(1)
|
|
258
|
+
}
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
// Task 4: plugin list with instances
|
|
262
|
+
pluginCmd
|
|
263
|
+
.command('list')
|
|
264
|
+
.description('List installed plugins and configured instances')
|
|
265
|
+
.action(async () => {
|
|
266
|
+
const installed = await listInstalledPlugins()
|
|
267
|
+
const trusted = new Set(TRUSTED_PLUGINS.map((p) => p.package))
|
|
268
|
+
|
|
269
|
+
if (installed.length === 0) {
|
|
270
|
+
console.log(chalk.dim('No plugins installed. Run: antseed init'))
|
|
271
|
+
} else {
|
|
272
|
+
console.log(chalk.bold('\nInstalled plugins:\n'))
|
|
273
|
+
for (const plugin of installed) {
|
|
274
|
+
const isTrusted = trusted.has(plugin.package)
|
|
275
|
+
const badge = isTrusted ? chalk.green(' [trusted]') : ''
|
|
276
|
+
console.log(` ${chalk.cyan(plugin.package)} ${chalk.dim(plugin.version)}${badge}`)
|
|
277
|
+
}
|
|
278
|
+
console.log('')
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Show configured instances
|
|
282
|
+
const instances = await getInstances(CONFIG_PATH)
|
|
283
|
+
if (instances.length > 0) {
|
|
284
|
+
console.log(chalk.bold('Configured instances:\n'))
|
|
285
|
+
for (const inst of instances) {
|
|
286
|
+
const status = inst.enabled === false ? chalk.red(' [disabled]') : chalk.green(' [enabled]')
|
|
287
|
+
console.log(` ${chalk.cyan(inst.id)} ${chalk.dim(inst.package)}${status}`)
|
|
288
|
+
}
|
|
289
|
+
console.log('')
|
|
290
|
+
}
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
// Register plugin create subcommand (Task 3)
|
|
294
|
+
registerPluginCreateCommand(pluginCmd)
|
|
295
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Register the `antseed profile` command and its subcommands.
|
|
6
|
+
*/
|
|
7
|
+
export function registerProfileCommand(program: Command): void {
|
|
8
|
+
const profile = program
|
|
9
|
+
.command('profile')
|
|
10
|
+
.description('Manage your peer profile');
|
|
11
|
+
|
|
12
|
+
profile
|
|
13
|
+
.command('show')
|
|
14
|
+
.description('Display your current profile')
|
|
15
|
+
.action(async () => {
|
|
16
|
+
const { readFile } = await import('node:fs/promises');
|
|
17
|
+
const { join } = await import('node:path');
|
|
18
|
+
const { homedir } = await import('node:os');
|
|
19
|
+
|
|
20
|
+
const profilePath = join(homedir(), '.antseed', 'profile.json');
|
|
21
|
+
try {
|
|
22
|
+
const data = await readFile(profilePath, 'utf-8');
|
|
23
|
+
const prof = JSON.parse(data);
|
|
24
|
+
console.log(chalk.bold('Peer Profile'));
|
|
25
|
+
console.log(chalk.dim('\u2500'.repeat(40)));
|
|
26
|
+
console.log(` Name: ${chalk.cyan(prof.displayName)}`);
|
|
27
|
+
console.log(` Description: ${prof.description}`);
|
|
28
|
+
console.log(` Tags: ${(prof.tags || []).join(', ')}`);
|
|
29
|
+
console.log(` Capabilities: ${(prof.capabilities || []).join(', ')}`);
|
|
30
|
+
console.log(` Region: ${prof.region}`);
|
|
31
|
+
console.log(` Languages: ${(prof.languages || []).join(', ')}`);
|
|
32
|
+
if (prof.website) console.log(` Website: ${prof.website}`);
|
|
33
|
+
console.log(` Created: ${new Date(prof.createdAt).toISOString()}`);
|
|
34
|
+
console.log(` Updated: ${new Date(prof.updatedAt).toISOString()}`);
|
|
35
|
+
} catch {
|
|
36
|
+
console.log(chalk.yellow('No profile found. Use "antseed profile set" to create one.'));
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
profile
|
|
41
|
+
.command('set')
|
|
42
|
+
.description('Create or update your profile')
|
|
43
|
+
.requiredOption('--name <name>', 'Display name')
|
|
44
|
+
.option('--description <desc>', 'Description of what you offer', '')
|
|
45
|
+
.option('--tags <tags>', 'Comma-separated tags', '')
|
|
46
|
+
.option('--capabilities <caps>', 'Comma-separated capabilities (inference,agent,skill,tool)', 'inference')
|
|
47
|
+
.option('--region <region>', 'Region', 'unknown')
|
|
48
|
+
.option('--languages <langs>', 'Comma-separated languages', 'en')
|
|
49
|
+
.option('--website <url>', 'Website URL')
|
|
50
|
+
.action(async (options) => {
|
|
51
|
+
const { writeFile, mkdir, readFile } = await import('node:fs/promises');
|
|
52
|
+
const { join } = await import('node:path');
|
|
53
|
+
const { homedir } = await import('node:os');
|
|
54
|
+
|
|
55
|
+
const dir = join(homedir(), '.antseed');
|
|
56
|
+
await mkdir(dir, { recursive: true });
|
|
57
|
+
const profilePath = join(dir, 'profile.json');
|
|
58
|
+
|
|
59
|
+
let existing: Record<string, unknown> = {};
|
|
60
|
+
try {
|
|
61
|
+
existing = JSON.parse(await readFile(profilePath, 'utf-8'));
|
|
62
|
+
} catch {}
|
|
63
|
+
|
|
64
|
+
const now = Date.now();
|
|
65
|
+
const prof = {
|
|
66
|
+
...existing,
|
|
67
|
+
displayName: options.name,
|
|
68
|
+
description: options.description || existing.description || '',
|
|
69
|
+
tags: options.tags ? options.tags.split(',').map((t: string) => t.trim()) : existing.tags || [],
|
|
70
|
+
capabilities: options.capabilities.split(',').map((c: string) => c.trim()),
|
|
71
|
+
region: options.region,
|
|
72
|
+
languages: options.languages.split(',').map((l: string) => l.trim()),
|
|
73
|
+
website: options.website || existing.website,
|
|
74
|
+
createdAt: existing.createdAt || now,
|
|
75
|
+
updatedAt: now,
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
await writeFile(profilePath, JSON.stringify(prof, null, 2));
|
|
79
|
+
console.log(chalk.green('Profile saved successfully.'));
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Register the `antseed peer <peerId>` command.
|
|
85
|
+
*/
|
|
86
|
+
export function registerPeerCommand(program: Command): void {
|
|
87
|
+
program
|
|
88
|
+
.command('peer')
|
|
89
|
+
.description('View a peer\'s profile')
|
|
90
|
+
.argument('<peerId>', 'Peer ID (hex)')
|
|
91
|
+
.action(async (peerId: string) => {
|
|
92
|
+
console.log(chalk.bold(`Peer: ${peerId.slice(0, 16)}...`));
|
|
93
|
+
console.log(chalk.yellow('Connect to the network first to view peer details.'));
|
|
94
|
+
});
|
|
95
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
import { createDefaultConfig } from '../../config/defaults.js';
|
|
4
|
+
import { resolveEffectiveSellerConfig } from '../../config/effective.js';
|
|
5
|
+
import {
|
|
6
|
+
buildSellerRuntimeOverridesFromFlags,
|
|
7
|
+
buildSellerPluginRuntimeEnv,
|
|
8
|
+
} from './seed.js';
|
|
9
|
+
|
|
10
|
+
test('seed runtime overrides are runtime-only and win over env/config', () => {
|
|
11
|
+
const config = createDefaultConfig();
|
|
12
|
+
config.seller.reserveFloor = 11;
|
|
13
|
+
config.seller.pricing.defaults.inputUsdPerMillion = 12;
|
|
14
|
+
config.seller.pricing.defaults.outputUsdPerMillion = 18;
|
|
15
|
+
const beforeResolution = JSON.parse(JSON.stringify(config));
|
|
16
|
+
|
|
17
|
+
const env = {
|
|
18
|
+
ANTSEED_SELLER_INPUT_USD_PER_MILLION: '20',
|
|
19
|
+
} as NodeJS.ProcessEnv;
|
|
20
|
+
|
|
21
|
+
const overrides = buildSellerRuntimeOverridesFromFlags({
|
|
22
|
+
reserve: 33,
|
|
23
|
+
inputUsdPerMillion: 44,
|
|
24
|
+
outputUsdPerMillion: 55,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const effective = resolveEffectiveSellerConfig({
|
|
28
|
+
config,
|
|
29
|
+
env,
|
|
30
|
+
sellerOverrides: overrides,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
assert.equal(effective.reserveFloor, 33);
|
|
34
|
+
assert.equal(effective.pricing.defaults.inputUsdPerMillion, 44);
|
|
35
|
+
assert.equal(effective.pricing.defaults.outputUsdPerMillion, 55);
|
|
36
|
+
assert.deepEqual(config, beforeResolution);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('seed maps effective seller pricing into provider runtime keys', () => {
|
|
40
|
+
const config = createDefaultConfig();
|
|
41
|
+
config.seller.maxConcurrentBuyers = 17;
|
|
42
|
+
config.seller.pricing.defaults.inputUsdPerMillion = 10;
|
|
43
|
+
config.seller.pricing.defaults.outputUsdPerMillion = 20;
|
|
44
|
+
config.seller.pricing.providers = {
|
|
45
|
+
anthropic: {
|
|
46
|
+
defaults: {
|
|
47
|
+
inputUsdPerMillion: 15,
|
|
48
|
+
outputUsdPerMillion: 35,
|
|
49
|
+
},
|
|
50
|
+
models: {
|
|
51
|
+
'claude-sonnet-4-5-20250929': {
|
|
52
|
+
inputUsdPerMillion: 18,
|
|
53
|
+
outputUsdPerMillion: 42,
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const runtimeEnv = buildSellerPluginRuntimeEnv(config.seller, 'anthropic');
|
|
60
|
+
assert.equal(runtimeEnv['ANTSEED_INPUT_USD_PER_MILLION'], '15');
|
|
61
|
+
assert.equal(runtimeEnv['ANTSEED_OUTPUT_USD_PER_MILLION'], '35');
|
|
62
|
+
assert.equal(runtimeEnv['ANTSEED_MAX_CONCURRENCY'], '17');
|
|
63
|
+
|
|
64
|
+
const models = JSON.parse(runtimeEnv['ANTSEED_MODEL_PRICING_JSON'] ?? '{}') as Record<string, {
|
|
65
|
+
inputUsdPerMillion: number;
|
|
66
|
+
outputUsdPerMillion: number;
|
|
67
|
+
}>;
|
|
68
|
+
assert.equal(models['claude-sonnet-4-5-20250929']?.inputUsdPerMillion, 18);
|
|
69
|
+
assert.equal(models['claude-sonnet-4-5-20250929']?.outputUsdPerMillion, 42);
|
|
70
|
+
});
|