File size: 5,017 Bytes
cd8bd0a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# bin/cli β€” OmniRoute CLI internals

This directory contains the CLI runtime, helpers, and commands for the `omniroute` binary.

## Structure

```
bin/cli/
β”œβ”€β”€ CONVENTIONS.md          ← normative design rules (read this first)
β”œβ”€β”€ README.md               ← this file
β”œβ”€β”€ program.mjs             ← Commander setup β€” global flags, registerCommands()
β”œβ”€β”€ api.mjs                 ← apiFetch() β€” all HTTP calls + retry/backoff
β”œβ”€β”€ runtime.mjs             ← withRuntime() β€” server-first / DB-fallback
β”œβ”€β”€ i18n.mjs                ← t() β€” i18n helper + locale detection
β”œβ”€β”€ output.mjs              ← emit() β€” table/json/jsonl/csv + printSuccess/printError
β”œβ”€β”€ io.mjs                  ← ask() / askSecret() β€” interactive prompts
β”œβ”€β”€ data-dir.mjs            ← resolveDataDir() / resolveStoragePath()
β”œβ”€β”€ sqlite.mjs              ← openOmniRouteDb() β€” DB bootstrap
β”œβ”€β”€ encryption.mjs          ← encrypt/decrypt credentials
β”œβ”€β”€ provider-catalog.mjs    ← static provider catalog
β”œβ”€β”€ provider-store.mjs      ← DB CRUD for provider_connections
β”œβ”€β”€ provider-test.mjs       ← testProviderApiKey()
β”œβ”€β”€ settings-store.mjs      ← DB CRUD for key_value settings
β”œβ”€β”€ locales/
β”‚   β”œβ”€β”€ en.json             ← English strings (source of truth, 42+ locales)
β”‚   β”œβ”€β”€ pt-BR.json          ← Portuguese (Brazil) β€” fully translated
β”‚   └── {locale}.json       ← 40 additional locales (ar, az, de, es, fr, ja, zh-CN, …)
β”œβ”€β”€ scripts/
β”‚   └── generate-locales.mjs ← scaffold new locale files from config/i18n.json
└── commands/
    β”œβ”€β”€ setup.mjs
    β”œβ”€β”€ doctor.mjs
    β”œβ”€β”€ providers.mjs
    β”œβ”€β”€ config.mjs          ← includes `config lang get/set/list`
    β”œβ”€β”€ status.mjs
    β”œβ”€β”€ logs.mjs
    └── update.mjs
```

## Key helpers

### `apiFetch(path, opts)` β€” `api.mjs`

All HTTP calls to the OmniRoute server must go through this wrapper.

```js
import { apiFetch } from "./api.mjs";

const res = await apiFetch("/api/health");
if (!res.ok) await res.assertOk(); // throws ApiError with mapped exit code
const data = await res.json();
```

Options:

- `baseUrl` β€” override base URL (default: `OMNIROUTE_BASE_URL` env or `localhost:20128`)
- `apiKey` β€” override API key (default: `OMNIROUTE_API_KEY`)
- `method`, `body`, `headers` β€” standard fetch options
- `timeout` β€” per-attempt ms (default: `30000`)
- `retry` β€” `false` to disable (default: enabled)
- `retryMax` β€” total attempts (default: `3`)
- `verbose` β€” log retry attempts to stderr

### `withRuntime(fn, opts)` β€” `runtime.mjs`

Provides server-first / DB-fallback transparently.

```js
import { withRuntime } from "./runtime.mjs";

await withRuntime(async (ctx) => {
  if (ctx.kind === "http") {
    const res = await ctx.api("/v1/providers");
    return res.json();
  }
  return ctx.db.prepare("SELECT * FROM provider_connections").all();
});
```

- `opts.requireServer = true` β€” throws `ServerOfflineError` (exit 3) if offline
- `opts.preferDb = true` β€” always use DB (skip server check)

### `t(key, vars)` β€” `i18n.mjs`

Internationalized strings. Catalog loaded from `locales/{locale}.json`.

```js
import { t } from "./i18n.mjs";

console.log(t("common.serverOffline"));
console.log(t("setup.testFailed", { error: err.message }));
```

Locale detection order: `OMNIROUTE_LANG` β†’ `LC_ALL` β†’ `LC_MESSAGES` β†’ `LANG` β†’ `en`.

### `emit(data, opts)` β€” `output.mjs`

Format-aware output. Reads `opts.output` to select table/json/jsonl/csv.

```js
import { emit, printError, EXIT_CODES } from "./output.mjs";

emit(providers, { output: opts.output ?? "table" });
printError("Something went wrong");
process.exit(EXIT_CODES.SERVER_OFFLINE);
```

## Locale selection

The CLI displays text in the user's language. Detection order:

1. `--lang <code>` flag on the command line
2. `OMNIROUTE_LANG` environment variable
3. System env: `LC_ALL` β†’ `LC_MESSAGES` β†’ `LANG`
4. Fallback: `en`

**Set permanently:**

```bash
omniroute config lang set pt-BR       # saves to ~/.omniroute/.env
omniroute config lang list            # show all 42 available locales
omniroute config lang get             # show currently active locale
```

**One-time override:**

```bash
omniroute --lang de providers list    # run in German, not persisted
OMNIROUTE_LANG=ja omniroute status    # same effect via env
```

**Adding a new locale**: add entry to `config/i18n.json`, then run:

```bash
node bin/cli/scripts/generate-locales.mjs
```

## Adding a new command

1. Create `bin/cli/commands/your-command.mjs`
2. Export `registerYourCommand(program)` following the Commander pattern
3. Register in `bin/cli/commands/registry.mjs`
4. Add strings to `locales/en.json` and `locales/pt-BR.json`
5. Write test in `tests/unit/cli-your-command.test.ts`

See `CONVENTIONS.md` for exit codes, flag naming, output format, and destructive-action rules.