mohdel 0.90.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/LICENSE +21 -0
- package/README.md +377 -0
- package/config/benchmarks.json +39 -0
- package/js/client/call.js +75 -0
- package/js/client/call_image.js +82 -0
- package/js/client/gate-binary.js +72 -0
- package/js/client/index.js +16 -0
- package/js/client/ndjson.js +29 -0
- package/js/client/transport.js +48 -0
- package/js/core/envelope.js +141 -0
- package/js/core/errors.js +75 -0
- package/js/core/events.js +96 -0
- package/js/core/image.js +58 -0
- package/js/core/index.js +10 -0
- package/js/core/status.js +48 -0
- package/js/factory/bridge.js +372 -0
- package/js/session/_cooldown.js +114 -0
- package/js/session/_logger.js +138 -0
- package/js/session/_rate_limiter.js +77 -0
- package/js/session/_tracing.js +58 -0
- package/js/session/adapters/_cancelled.js +44 -0
- package/js/session/adapters/_catalog.js +58 -0
- package/js/session/adapters/_chat_completions.js +439 -0
- package/js/session/adapters/_errors.js +85 -0
- package/js/session/adapters/_images.js +60 -0
- package/js/session/adapters/_lazy_json_cache.js +76 -0
- package/js/session/adapters/_pricing.js +67 -0
- package/js/session/adapters/_providers.js +60 -0
- package/js/session/adapters/_tools.js +185 -0
- package/js/session/adapters/_videos.js +283 -0
- package/js/session/adapters/anthropic.js +397 -0
- package/js/session/adapters/cerebras.js +28 -0
- package/js/session/adapters/deepseek.js +32 -0
- package/js/session/adapters/echo.js +51 -0
- package/js/session/adapters/fake.js +262 -0
- package/js/session/adapters/fireworks.js +46 -0
- package/js/session/adapters/gemini.js +381 -0
- package/js/session/adapters/groq.js +23 -0
- package/js/session/adapters/image/fake.js +55 -0
- package/js/session/adapters/image/index.js +40 -0
- package/js/session/adapters/image/novita.js +135 -0
- package/js/session/adapters/image/openai.js +50 -0
- package/js/session/adapters/index.js +53 -0
- package/js/session/adapters/mistral.js +31 -0
- package/js/session/adapters/novita.js +29 -0
- package/js/session/adapters/openai.js +381 -0
- package/js/session/adapters/openrouter.js +66 -0
- package/js/session/adapters/xai.js +27 -0
- package/js/session/bin.js +54 -0
- package/js/session/driver.js +160 -0
- package/js/session/index.js +18 -0
- package/js/session/run.js +393 -0
- package/js/session/run_image.js +61 -0
- package/package.json +107 -0
- package/src/cli/ask.js +160 -0
- package/src/cli/backup.js +107 -0
- package/src/cli/bench.js +262 -0
- package/src/cli/check.js +123 -0
- package/src/cli/colored-logger.js +67 -0
- package/src/cli/colors.js +13 -0
- package/src/cli/default.js +39 -0
- package/src/cli/index.js +150 -0
- package/src/cli/json-output.js +60 -0
- package/src/cli/model.js +571 -0
- package/src/cli/onboard.js +232 -0
- package/src/cli/rank.js +176 -0
- package/src/cli/ratelimit.js +160 -0
- package/src/cli/tag.js +105 -0
- package/src/lib/assets/alibaba.svg +1 -0
- package/src/lib/assets/anthropic.svg +5 -0
- package/src/lib/assets/deepseek.svg +1 -0
- package/src/lib/assets/gemini.svg +1 -0
- package/src/lib/assets/google.svg +2 -0
- package/src/lib/assets/kwaipilot.svg +1 -0
- package/src/lib/assets/meta.svg +1 -0
- package/src/lib/assets/minimax.svg +9 -0
- package/src/lib/assets/moonshotai.svg +4 -0
- package/src/lib/assets/openai.svg +5 -0
- package/src/lib/assets/xai.svg +1 -0
- package/src/lib/assets/xiaomi.svg +2 -0
- package/src/lib/assets/zai.svg +219 -0
- package/src/lib/benchmark-score.js +215 -0
- package/src/lib/benchmark-truth.js +68 -0
- package/src/lib/cache.js +76 -0
- package/src/lib/common.js +208 -0
- package/src/lib/cooldown.js +63 -0
- package/src/lib/creators.js +71 -0
- package/src/lib/curated-cache.js +146 -0
- package/src/lib/errors.js +126 -0
- package/src/lib/index.js +726 -0
- package/src/lib/logger.js +29 -0
- package/src/lib/providers.js +87 -0
- package/src/lib/rank.js +390 -0
- package/src/lib/rate-limiter.js +50 -0
- package/src/lib/schema.js +150 -0
- package/src/lib/select.js +474 -0
- package/src/lib/tracing.js +62 -0
- package/src/lib/utils.js +85 -0
package/src/cli/check.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { label, err, warn, ok } from './colors.js'
|
|
2
|
+
import providers from '../lib/providers.js'
|
|
3
|
+
import { validate, isValidTag } from '../lib/schema.js'
|
|
4
|
+
import { getCuratedModels, loadDefaultEnv } from '../lib/common.js'
|
|
5
|
+
|
|
6
|
+
// --- Local validation ---
|
|
7
|
+
|
|
8
|
+
const checkLocal = (curated) => {
|
|
9
|
+
const errors = []
|
|
10
|
+
const warnings = []
|
|
11
|
+
const knownProviders = new Set(Object.keys(providers))
|
|
12
|
+
|
|
13
|
+
for (const [key, spec] of Object.entries(curated)) {
|
|
14
|
+
const [keyProvider] = key.split('/')
|
|
15
|
+
|
|
16
|
+
if (spec.deprecated) {
|
|
17
|
+
if (!curated[spec.deprecated]) {
|
|
18
|
+
errors.push(`${key}: deprecated target '${spec.deprecated}' not in curated`)
|
|
19
|
+
}
|
|
20
|
+
continue
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
for (const issue of validate(spec, key)) {
|
|
24
|
+
if (issue.severity === 'error') errors.push(`${key}: ${issue.field} — ${issue.message}`)
|
|
25
|
+
else warnings.push(`${key}: ${issue.field} — ${issue.message}`)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (!knownProviders.has(keyProvider)) {
|
|
29
|
+
errors.push(`${key}: provider '${keyProvider}' not in providers.js`)
|
|
30
|
+
}
|
|
31
|
+
if (spec.provider && spec.provider !== keyProvider) {
|
|
32
|
+
errors.push(`${key}: spec.provider '${spec.provider}' doesn't match key prefix '${keyProvider}'`)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const providerConfig = providers[keyProvider]
|
|
36
|
+
if (providerConfig && spec.sdk && spec.sdk !== providerConfig.sdk) {
|
|
37
|
+
errors.push(`${key}: spec.sdk '${spec.sdk}' doesn't match provider sdk '${providerConfig.sdk}'`)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (!spec.label) warnings.push(`${key}: missing label`)
|
|
41
|
+
|
|
42
|
+
for (const priceField of ['inputPrice', 'outputPrice', 'thinkingPrice']) {
|
|
43
|
+
const val = spec[priceField]
|
|
44
|
+
if (val != null && typeof val === 'object' && val.default == null) {
|
|
45
|
+
errors.push(`${key}: ${priceField} is tiered but missing 'default' key`)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (spec.thinkingEffortLevels && !spec.defaultThinkingEffort) {
|
|
50
|
+
warnings.push(`${key}: has thinkingEffortLevels but no defaultThinkingEffort`)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (Array.isArray(spec.tags)) {
|
|
54
|
+
for (const t of spec.tags) {
|
|
55
|
+
if (!isValidTag(t)) warnings.push(`${key}: invalid tag "${t}" — must match /^[a-zA-Z][a-zA-Z0-9._-]{0,31}$/`)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return { errors, warnings }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// --- CLI ---
|
|
64
|
+
|
|
65
|
+
export async function runCheck (args) {
|
|
66
|
+
if (args.includes('-h') || args.includes('--help')) {
|
|
67
|
+
console.log(`mohdel model check — validate curated catalog
|
|
68
|
+
|
|
69
|
+
Usage:
|
|
70
|
+
model check [options]
|
|
71
|
+
|
|
72
|
+
Options:
|
|
73
|
+
--json Output as JSON
|
|
74
|
+
|
|
75
|
+
Checks:
|
|
76
|
+
Schema types, required fields, deprecated targets, provider/sdk
|
|
77
|
+
consistency, tiered pricing, thinking config.
|
|
78
|
+
|
|
79
|
+
Note: 0.90 drops the upstream-drift check that piggybacked on the
|
|
80
|
+
legacy per-provider SDK factory. If you need upstream drift
|
|
81
|
+
detection, file an issue — it'll be rebuilt on the /session stack.`)
|
|
82
|
+
process.exit(0)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
loadDefaultEnv()
|
|
86
|
+
|
|
87
|
+
const json = args.includes('--json')
|
|
88
|
+
|
|
89
|
+
const curated = await getCuratedModels()
|
|
90
|
+
const active = Object.values(curated).filter(s => !s.deprecated).length
|
|
91
|
+
const deprecated = Object.values(curated).length - active
|
|
92
|
+
|
|
93
|
+
if (!json) {
|
|
94
|
+
console.log(`${label('Catalog:')} ${active} active, ${deprecated} deprecated\n`)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const { errors, warnings: localWarnings } = checkLocal(curated)
|
|
98
|
+
|
|
99
|
+
if (!json) {
|
|
100
|
+
if (errors.length) {
|
|
101
|
+
console.log(err(`${errors.length} error(s):`))
|
|
102
|
+
for (const e of errors) console.log(` ${err('✗')} ${e}`)
|
|
103
|
+
}
|
|
104
|
+
if (localWarnings.length) {
|
|
105
|
+
console.log(warn(`${localWarnings.length} warning(s):`))
|
|
106
|
+
for (const w of localWarnings) console.log(` ${warn('!')} ${w}`)
|
|
107
|
+
}
|
|
108
|
+
if (!errors.length && !localWarnings.length) {
|
|
109
|
+
console.log(ok('Local validation passed'))
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (json) {
|
|
114
|
+
console.log(JSON.stringify({
|
|
115
|
+
active,
|
|
116
|
+
deprecated,
|
|
117
|
+
errors,
|
|
118
|
+
warnings: localWarnings
|
|
119
|
+
}, null, 2))
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (errors.length) process.exit(1)
|
|
123
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI-only colored stderr logger. Extracted from `src/lib/logger.js`
|
|
3
|
+
* so that library consumers (who use `silent` or pass their own pino
|
|
4
|
+
* logger) don't pay for loading chalk. Only CLI code imports this.
|
|
5
|
+
*
|
|
6
|
+
* Matches the logger interface contract
|
|
7
|
+
* `{ trace, debug, info, warn, error, fatal, child }`. Filters by minimum
|
|
8
|
+
* level so noise stays out of the user's view.
|
|
9
|
+
*
|
|
10
|
+
* @module cli/colored-logger
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import chalk from 'chalk'
|
|
14
|
+
|
|
15
|
+
const noop = () => {}
|
|
16
|
+
|
|
17
|
+
const LEVELS = { trace: 10, debug: 20, info: 30, warn: 40, error: 50, fatal: 60 }
|
|
18
|
+
|
|
19
|
+
const COLORS = {
|
|
20
|
+
trace: chalk.dim,
|
|
21
|
+
debug: chalk.cyan,
|
|
22
|
+
info: chalk.white,
|
|
23
|
+
warn: chalk.yellow,
|
|
24
|
+
error: chalk.red,
|
|
25
|
+
fatal: chalk.bold.red
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const LABELS = {
|
|
29
|
+
trace: 'TRC',
|
|
30
|
+
debug: 'DBG',
|
|
31
|
+
info: 'INF',
|
|
32
|
+
warn: 'WRN',
|
|
33
|
+
error: 'ERR',
|
|
34
|
+
fatal: 'FTL'
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Build a colored stderr logger filtered by minimum level.
|
|
39
|
+
*
|
|
40
|
+
* @param {string} [minLevel='warn'] — minimum level to print: trace|debug|info|warn|error|fatal
|
|
41
|
+
* @returns {object} logger object
|
|
42
|
+
*/
|
|
43
|
+
export function cliLogger (minLevel = 'warn') {
|
|
44
|
+
const threshold = LEVELS[minLevel] ?? LEVELS.warn
|
|
45
|
+
const lg = {}
|
|
46
|
+
|
|
47
|
+
for (const level of Object.keys(LEVELS)) {
|
|
48
|
+
if (LEVELS[level] < threshold) {
|
|
49
|
+
lg[level] = noop
|
|
50
|
+
} else {
|
|
51
|
+
const color = COLORS[level]
|
|
52
|
+
const label = color(LABELS[level])
|
|
53
|
+
lg[level] = (...args) => {
|
|
54
|
+
// Pino-style: first arg may be an object with structured fields
|
|
55
|
+
if (args.length && typeof args[0] === 'object' && args[0] !== null) {
|
|
56
|
+
const [, ...rest] = args
|
|
57
|
+
console.error(label, ...rest)
|
|
58
|
+
} else {
|
|
59
|
+
console.error(label, ...args)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
lg.child = () => lg
|
|
66
|
+
return lg
|
|
67
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Semantic color roles for CLI output.
|
|
2
|
+
import chalk from 'chalk'
|
|
3
|
+
|
|
4
|
+
export const id = chalk.cyan // model IDs, provider names
|
|
5
|
+
export const label = chalk.bold // display names, titles
|
|
6
|
+
export const tag = chalk.yellow // tags
|
|
7
|
+
export const price = chalk.green // money values
|
|
8
|
+
export const meta = chalk.dim // counts, field labels, headers, separators
|
|
9
|
+
export const missing = chalk.dim('—') // null/absent values
|
|
10
|
+
export const err = chalk.red // errors
|
|
11
|
+
export const warn = chalk.yellow // warnings
|
|
12
|
+
export const ok = chalk.green // success, ● dots
|
|
13
|
+
export const inactive = chalk.dim // unconfigured, ○ dots
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { intro, outro, select, isCancel, cancel } from '@clack/prompts'
|
|
2
|
+
import { getCuratedModels, CONFIG_PATH, saveConfig } from '../lib/common.js'
|
|
3
|
+
import providers from '../lib/providers.js'
|
|
4
|
+
|
|
5
|
+
export async function runDefault () {
|
|
6
|
+
intro('Mohdel — Set Default Model')
|
|
7
|
+
|
|
8
|
+
const curated = await getCuratedModels()
|
|
9
|
+
const modelOptions = Object.entries(curated).map(([modelId, info]) => ({
|
|
10
|
+
value: modelId,
|
|
11
|
+
label: `${info.label} (${modelId})`
|
|
12
|
+
}))
|
|
13
|
+
modelOptions.sort((a, b) => a.label.localeCompare(b.label))
|
|
14
|
+
|
|
15
|
+
const selectedModelId = await select({
|
|
16
|
+
message: 'Select your default model:',
|
|
17
|
+
options: modelOptions
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
if (isCancel(selectedModelId)) {
|
|
21
|
+
cancel('Cancelled')
|
|
22
|
+
process.exit(0)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const [providerName] = selectedModelId.split('/')
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
const config = { defaultModel: selectedModelId }
|
|
29
|
+
const apiKeyEnv = providers[providerName]?.apiKeyEnv
|
|
30
|
+
if (apiKeyEnv) {
|
|
31
|
+
config.apiKeyInfo = `Set ${apiKeyEnv} environment variable for this provider`
|
|
32
|
+
}
|
|
33
|
+
await saveConfig(config)
|
|
34
|
+
outro(`Default set to ${selectedModelId} — saved to ${CONFIG_PATH}`)
|
|
35
|
+
} catch (err) {
|
|
36
|
+
cancel(`Error: ${err.message}`)
|
|
37
|
+
process.exit(1)
|
|
38
|
+
}
|
|
39
|
+
}
|
package/src/cli/index.js
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* mohdel CLI — model management (noun-verb pattern).
|
|
5
|
+
*
|
|
6
|
+
* Nouns: model, provider, creator, tag, ratelimit
|
|
7
|
+
* Each noun supports: list, show, and noun-specific verbs.
|
|
8
|
+
*
|
|
9
|
+
* Aliases: ls → model list, rl → ratelimit
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const [command, ...args] = process.argv.slice(2)
|
|
13
|
+
|
|
14
|
+
if (!command) {
|
|
15
|
+
const { runOnboard } = await import('./onboard.js')
|
|
16
|
+
await runOnboard()
|
|
17
|
+
process.exit(0)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (command === '-h' || command === '--help') {
|
|
21
|
+
console.log(`mohdel — model catalog management
|
|
22
|
+
|
|
23
|
+
Commands:
|
|
24
|
+
model list [--sort price|context|name] List all curated models
|
|
25
|
+
model search <term> Filter models by name/label
|
|
26
|
+
model stats Catalog summary
|
|
27
|
+
model show <model> Show model details
|
|
28
|
+
model get <model> <key> Get a field value
|
|
29
|
+
model set <model> <key> <value> Set a field
|
|
30
|
+
model rm <model> <key> Remove a field
|
|
31
|
+
model add <provider>/<model-id> Add a model manually
|
|
32
|
+
model check [--local] Validate catalog
|
|
33
|
+
model rank [--use-case <name>] Rank models by benchmarks
|
|
34
|
+
model bench <model> Benchmark with live inference
|
|
35
|
+
model curate [provider] Add upstream models to catalog
|
|
36
|
+
|
|
37
|
+
provider list List all providers
|
|
38
|
+
provider list <provider> List models from a provider
|
|
39
|
+
provider setup <provider> Configure API key (interactive)
|
|
40
|
+
provider rm <provider> Remove API key
|
|
41
|
+
|
|
42
|
+
creator list List all creators
|
|
43
|
+
creator list <creator> List models by a creator
|
|
44
|
+
|
|
45
|
+
tag list List all unique tags
|
|
46
|
+
tag list <model> Show tags on a model
|
|
47
|
+
tag show <tag> List models with a tag
|
|
48
|
+
tag add <model> <tag> Add a tag
|
|
49
|
+
tag rm <model> <tag> Remove a tag
|
|
50
|
+
|
|
51
|
+
ratelimit show <model|provider> Show effective limits
|
|
52
|
+
ratelimit set <model> [rpm] [tpm] Set model-level limits
|
|
53
|
+
ratelimit rm <model> Remove model-level limits
|
|
54
|
+
ratelimit provider set <p> [rpm] [tpm] Set provider-level limits
|
|
55
|
+
ratelimit provider rm <p> Remove provider-level limits
|
|
56
|
+
|
|
57
|
+
ask <provider/model> [prompt] One-shot inference (pipeable)
|
|
58
|
+
|
|
59
|
+
default Set default model (interactive)
|
|
60
|
+
|
|
61
|
+
Aliases:
|
|
62
|
+
models model list
|
|
63
|
+
providers provider list
|
|
64
|
+
creators creator list
|
|
65
|
+
tags tag list
|
|
66
|
+
ls model list
|
|
67
|
+
show <model> model show <model>
|
|
68
|
+
search <term> model search <term>
|
|
69
|
+
stats model stats
|
|
70
|
+
check model check
|
|
71
|
+
setup <provider> provider setup <provider>
|
|
72
|
+
rank model rank
|
|
73
|
+
bench <model> model bench <model>
|
|
74
|
+
curate [provider] model curate [provider]
|
|
75
|
+
rl ratelimit
|
|
76
|
+
|
|
77
|
+
Global flags:
|
|
78
|
+
--json [fields] Output as JSON (omit fields to list available)
|
|
79
|
+
|
|
80
|
+
Environment:
|
|
81
|
+
API keys are loaded from ~/.config/mohdel/environment (KEY=value format).
|
|
82
|
+
Run "mo" with no arguments to configure interactively.
|
|
83
|
+
|
|
84
|
+
ANTHROPIC_API_SK Anthropic API key
|
|
85
|
+
OPENAI_API_SK OpenAI API key
|
|
86
|
+
GEMINI_API_SK Google Gemini API key
|
|
87
|
+
GROQ_API_SK Groq API key
|
|
88
|
+
CEREBRAS_API_SK Cerebras API key
|
|
89
|
+
XAI_API_SK xAI API key
|
|
90
|
+
MISTRAL_API_SK Mistral API key
|
|
91
|
+
DEEPSEEK_API_SK DeepSeek API key
|
|
92
|
+
FIREWORKS_API_SK Fireworks API key
|
|
93
|
+
OPENROUTER_API_SK OpenRouter API key
|
|
94
|
+
NOVITA_API_SK Novita API key
|
|
95
|
+
|
|
96
|
+
Configuration:
|
|
97
|
+
~/.config/mohdel/environment API keys (loaded automatically)
|
|
98
|
+
~/.config/mohdel/curated.json Model catalog
|
|
99
|
+
~/.config/mohdel/providers.json Provider-level rate limits
|
|
100
|
+
~/.config/mohdel/default.json Default model selection`)
|
|
101
|
+
process.exit(0)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Alias resolution: short commands → noun + verb
|
|
105
|
+
const ALIASES = {
|
|
106
|
+
models: { noun: 'model', inject: ['list'] },
|
|
107
|
+
providers: { noun: 'provider', inject: ['list'] },
|
|
108
|
+
creators: { noun: 'creator', inject: ['list'] },
|
|
109
|
+
tags: { noun: 'tag', inject: ['list'] },
|
|
110
|
+
ls: { noun: 'model', inject: ['list'] },
|
|
111
|
+
show: { noun: 'model', inject: ['show'] },
|
|
112
|
+
search: { noun: 'model', inject: ['search'] },
|
|
113
|
+
stats: { noun: 'model', inject: ['stats'] },
|
|
114
|
+
check: { noun: 'model', inject: ['check'] },
|
|
115
|
+
setup: { noun: 'provider', inject: ['setup'] },
|
|
116
|
+
rank: { noun: 'model', inject: ['rank'] },
|
|
117
|
+
bench: { noun: 'model', inject: ['bench'] },
|
|
118
|
+
curate: { noun: 'model', inject: ['curate'] },
|
|
119
|
+
rl: { noun: 'ratelimit', inject: [] }
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const alias = ALIASES[command]
|
|
123
|
+
const resolved = alias ? alias.noun : command
|
|
124
|
+
const resolvedArgs = alias ? [...alias.inject, ...args] : args
|
|
125
|
+
|
|
126
|
+
if (resolved === 'default') {
|
|
127
|
+
const { runDefault } = await import('./default.js')
|
|
128
|
+
await runDefault()
|
|
129
|
+
} else if (resolved === 'ask') {
|
|
130
|
+
const { runAsk } = await import('./ask.js')
|
|
131
|
+
await runAsk(resolvedArgs)
|
|
132
|
+
} else if (resolved === 'model') {
|
|
133
|
+
const { runModel } = await import('./model.js')
|
|
134
|
+
await runModel(resolvedArgs)
|
|
135
|
+
} else if (resolved === 'provider') {
|
|
136
|
+
const { runProvider } = await import('./model.js')
|
|
137
|
+
await runProvider(resolvedArgs)
|
|
138
|
+
} else if (resolved === 'creator') {
|
|
139
|
+
const { runCreator } = await import('./model.js')
|
|
140
|
+
await runCreator(resolvedArgs)
|
|
141
|
+
} else if (resolved === 'tag') {
|
|
142
|
+
const { runTag } = await import('./tag.js')
|
|
143
|
+
await runTag(resolvedArgs)
|
|
144
|
+
} else if (resolved === 'ratelimit' || resolved === 'rl') {
|
|
145
|
+
const { runRateLimit } = await import('./ratelimit.js')
|
|
146
|
+
await runRateLimit(resolvedArgs)
|
|
147
|
+
} else {
|
|
148
|
+
console.error(`Unknown command: ${command}. Run "mo --help" for usage.`)
|
|
149
|
+
process.exit(1)
|
|
150
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// --json flag support (gh-style):
|
|
2
|
+
// --json → list available fields
|
|
3
|
+
// --json f1,f2,f3 → output only those fields
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Extract --json flag and its value from args.
|
|
7
|
+
* Returns { json: false } or { json: true, fields: null } (list fields)
|
|
8
|
+
* or { json: true, fields: ['f1','f2'] } (filter).
|
|
9
|
+
* Mutates args in place to remove consumed --json [value].
|
|
10
|
+
*/
|
|
11
|
+
export function parseJsonFlag (args) {
|
|
12
|
+
const idx = args.indexOf('--json')
|
|
13
|
+
if (idx === -1) return { json: false }
|
|
14
|
+
|
|
15
|
+
args.splice(idx, 1)
|
|
16
|
+
|
|
17
|
+
// Next arg is the field list (if it exists and doesn't look like a flag)
|
|
18
|
+
const next = args[idx]
|
|
19
|
+
if (next && !next.startsWith('-')) {
|
|
20
|
+
args.splice(idx, 1)
|
|
21
|
+
return { json: true, fields: next.split(',').map(f => f.trim()).filter(Boolean) }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return { json: true, fields: null }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Print available fields for a --json call with no field list.
|
|
29
|
+
*/
|
|
30
|
+
export function printAvailableFields (fields) {
|
|
31
|
+
console.log('Available JSON fields:')
|
|
32
|
+
for (const f of fields) console.log(` ${f}`)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Pick selected fields from an object.
|
|
37
|
+
*/
|
|
38
|
+
function pick (obj, fields) {
|
|
39
|
+
const out = {}
|
|
40
|
+
for (const f of fields) {
|
|
41
|
+
if (f in obj) out[f] = obj[f]
|
|
42
|
+
}
|
|
43
|
+
return out
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Output a list of objects as JSON, optionally filtered to specific fields.
|
|
48
|
+
*/
|
|
49
|
+
export function jsonOutput (items, fields) {
|
|
50
|
+
const out = fields ? items.map(item => pick(item, fields)) : items
|
|
51
|
+
console.log(JSON.stringify(out, null, 2))
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Output a single object as JSON, optionally filtered to specific fields.
|
|
56
|
+
*/
|
|
57
|
+
export function jsonOutputOne (item, fields) {
|
|
58
|
+
const out = fields ? pick(item, fields) : item
|
|
59
|
+
console.log(JSON.stringify(out, null, 2))
|
|
60
|
+
}
|