shennian 0.2.8 → 0.2.10

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.
@@ -785,10 +785,19 @@ function normalizeCodexStderr(stderr) {
785
785
  .join('\n');
786
786
  }
787
787
  function normalizeTerminalText(text) {
788
+ const escape = String.fromCharCode(27);
789
+ const bell = String.fromCharCode(7);
790
+ const csiPattern = new RegExp(`${escape}\\[[0-?]*[ -/]*[@-~]`, 'g');
791
+ const oscPattern = new RegExp(`${escape}\\][^${bell}]*(?:${bell}|${escape}\\\\)`, 'g');
788
792
  return text
789
- .replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, ' ')
790
- .replace(/\x1b\][^\x07]*(?:\x07|\x1b\\)/g, ' ')
791
- .replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, ' ')
793
+ .replace(csiPattern, ' ')
794
+ .replace(oscPattern, ' ')
795
+ .split('')
796
+ .map((char) => {
797
+ const code = char.charCodeAt(0);
798
+ return (code <= 8 || code === 11 || code === 12 || (code >= 14 && code <= 31) || code === 127) ? ' ' : char;
799
+ })
800
+ .join('')
792
801
  .replace(/\s+/g, ' ')
793
802
  .trim();
794
803
  }
@@ -3,9 +3,12 @@ export declare function stripAnsi(text: string): string;
3
3
  export declare function parseCursorModels(raw: string): ModelInfo[];
4
4
  export declare function parseOpenClawModels(raw: string): ModelInfo[];
5
5
  export declare function parseClaudeModels(raw: string): ModelInfo[];
6
+ type EnvLike = Record<string, string | undefined>;
7
+ export declare function applyClaudeModelEnvOverrides(models: ModelInfo[], env?: EnvLike): ModelInfo[];
6
8
  export declare function parseClaudeBinaryModels(raw: string): ModelInfo[];
7
9
  export declare function fallbackClaudeAliasModels(): ModelInfo[];
8
10
  export declare function parseCodexModels(raw: string): ModelInfo[];
9
11
  export declare function parseCodexAppServerModels(raw: unknown): ModelInfo[];
10
12
  export declare function parseGeminiModels(raw: string): ModelInfo[];
11
13
  export declare function fallbackGeminiModels(): ModelInfo[];
14
+ export {};
@@ -97,7 +97,11 @@ export function parseOpenClawModels(raw) {
97
97
  export function parseClaudeModels(raw) {
98
98
  const rawClean = stripAnsi(raw).replace(/\s+/g, ' ');
99
99
  const patterns = [
100
- [/\d+\.Default\(recommended\).*?(?:Sonnet|currently\s*Sonnet)\s*([\d.]+)/i, 'default', 'Default (recommended)'],
100
+ [
101
+ /\d+\.Default\(recommended\).*?(?:Sonnet|currently\s*Sonnet)\s*([\d.]+)/i,
102
+ 'default',
103
+ 'Default (recommended)',
104
+ ],
101
105
  [/\d+\.Sonnet.*?Sonnet\s*([\d.]+)/i, 'sonnet', 'Sonnet'],
102
106
  [/\d+\.Opus.*?Opus\s*([\d.]+)/i, 'opus', 'Opus'],
103
107
  [/\d+\.Haiku.*?Haiku\s*([\d.]+)/i, 'haiku', 'Haiku'],
@@ -126,9 +130,7 @@ export function parseClaudeModels(raw) {
126
130
  if (!alias)
127
131
  continue;
128
132
  const version = match[2] ?? '';
129
- const isDefault = defaultMatch &&
130
- defaultMatch[1]?.toLowerCase() === family &&
131
- defaultMatch[2] === version;
133
+ const isDefault = defaultMatch && defaultMatch[1]?.toLowerCase() === family && defaultMatch[2] === version;
132
134
  fallback.push({
133
135
  id: alias,
134
136
  name: `${titleCaseSegment(family)} ${version}`,
@@ -166,14 +168,51 @@ function formatClaudeVersion(version) {
166
168
  return undefined;
167
169
  return `v${version.join('.')}`;
168
170
  }
171
+ const CLAUDE_ALIAS_LABELS = {
172
+ default: 'Default (recommended)',
173
+ sonnet: 'Sonnet',
174
+ opus: 'Opus',
175
+ haiku: 'Haiku',
176
+ };
177
+ const CLAUDE_ALIAS_MODEL_ENV = {
178
+ default: 'ANTHROPIC_MODEL',
179
+ sonnet: 'ANTHROPIC_DEFAULT_SONNET_MODEL',
180
+ opus: 'ANTHROPIC_DEFAULT_OPUS_MODEL',
181
+ haiku: 'ANTHROPIC_DEFAULT_HAIKU_MODEL',
182
+ };
183
+ function readEnvValue(env, key) {
184
+ const value = env[key]?.trim();
185
+ return value || null;
186
+ }
187
+ function inferClaudeOverrideProvider(modelName, env) {
188
+ const baseUrl = readEnvValue(env, 'ANTHROPIC_BASE_URL')?.toLowerCase() ?? '';
189
+ if (baseUrl.includes('deepseek.com') || modelName.toLowerCase().startsWith('deepseek-')) {
190
+ return 'deepseek';
191
+ }
192
+ return baseUrl ? 'custom' : 'anthropic';
193
+ }
194
+ export function applyClaudeModelEnvOverrides(models, env = process.env) {
195
+ return models.map((model) => {
196
+ if (!['default', 'sonnet', 'opus', 'haiku'].includes(model.id))
197
+ return model;
198
+ const alias = model.id;
199
+ const configuredModel = readEnvValue(env, CLAUDE_ALIAS_MODEL_ENV[alias]);
200
+ if (!configuredModel)
201
+ return model;
202
+ const aliasLabel = CLAUDE_ALIAS_LABELS[alias];
203
+ const aliasDescription = `Claude Code alias: ${aliasLabel}`;
204
+ return {
205
+ ...model,
206
+ name: configuredModel,
207
+ description: model.description
208
+ ? `${aliasDescription} · ${model.description}`
209
+ : aliasDescription,
210
+ provider: inferClaudeOverrideProvider(configuredModel, env),
211
+ };
212
+ });
213
+ }
169
214
  export function parseClaudeBinaryModels(raw) {
170
215
  const clean = stripAnsi(raw);
171
- const aliasMap = {
172
- default: 'Default (recommended)',
173
- sonnet: 'Sonnet',
174
- opus: 'Opus',
175
- haiku: 'Haiku',
176
- };
177
216
  const familyVersions = new Map();
178
217
  for (const family of ['sonnet', 'opus', 'haiku']) {
179
218
  for (const match of clean.matchAll(new RegExp(`\\bclaude-${family}-[0-9.-]+\\b`, 'gi'))) {
@@ -194,7 +233,7 @@ export function parseClaudeBinaryModels(raw) {
194
233
  const version = (alias === 'default' ? familyVersions.get('sonnet') : familyVersions.get(alias)) ?? null;
195
234
  models.push({
196
235
  id: alias,
197
- name: aliasMap[alias],
236
+ name: CLAUDE_ALIAS_LABELS[alias],
198
237
  description: formatClaudeVersion(version),
199
238
  provider: 'anthropic',
200
239
  isDefault: alias === 'default',
@@ -203,12 +242,12 @@ export function parseClaudeBinaryModels(raw) {
203
242
  return uniqueModels(models);
204
243
  }
205
244
  export function fallbackClaudeAliasModels() {
206
- return [
245
+ return applyClaudeModelEnvOverrides([
207
246
  { id: 'default', name: 'Default (recommended)', provider: 'anthropic', isDefault: true },
208
247
  { id: 'sonnet', name: 'Sonnet', provider: 'anthropic' },
209
248
  { id: 'opus', name: 'Opus', provider: 'anthropic' },
210
249
  { id: 'haiku', name: 'Haiku', provider: 'anthropic' },
211
- ];
250
+ ]);
212
251
  }
213
252
  export function parseCodexModels(raw) {
214
253
  const clean = stripAnsi(raw);
@@ -218,7 +257,10 @@ export function parseCodexModels(raw) {
218
257
  if (!match)
219
258
  continue;
220
259
  const id = match[1].trim();
221
- const description = line.replace(id, '').replace(/^[^\w]+/, '').trim() || undefined;
260
+ const description = line
261
+ .replace(id, '')
262
+ .replace(/^[^\w]+/, '')
263
+ .trim() || undefined;
222
264
  models.push({
223
265
  id,
224
266
  name: prettifyCodexModel(id),
@@ -242,11 +284,7 @@ export function parseCodexAppServerModels(raw) {
242
284
  if (typeof entry !== 'object' || entry === null)
243
285
  continue;
244
286
  const item = entry;
245
- const id = typeof item.id === 'string'
246
- ? item.id
247
- : typeof item.model === 'string'
248
- ? item.model
249
- : '';
287
+ const id = typeof item.id === 'string' ? item.id : typeof item.model === 'string' ? item.model : '';
250
288
  if (!id)
251
289
  continue;
252
290
  const displayName = typeof item.displayName === 'string'
@@ -255,9 +293,7 @@ export function parseCodexAppServerModels(raw) {
255
293
  ? item.name
256
294
  : undefined;
257
295
  const description = typeof item.description === 'string' ? item.description : undefined;
258
- const defaultReasoningEffort = typeof item.defaultReasoningEffort === 'string'
259
- ? item.defaultReasoningEffort
260
- : undefined;
296
+ const defaultReasoningEffort = typeof item.defaultReasoningEffort === 'string' ? item.defaultReasoningEffort : undefined;
261
297
  const supportedReasoningEfforts = Array.isArray(item.supportedReasoningEfforts)
262
298
  ? item.supportedReasoningEfforts
263
299
  .map((effort) => {
@@ -1,7 +1,8 @@
1
- export { getCachedAgentInfos, refreshAgentInfos, resolveAgentInfos } from './model-registry/service.js';
2
- import { parseClaudeBinaryModels, parseClaudeModels, parseCodexModels, parseCodexAppServerModels, parseCursorModels, parseGeminiModels, parseOpenClawModels, stripAnsi } from './model-registry/parsers.js';
1
+ export { getCachedAgentInfos, refreshAgentInfos, resolveAgentInfos, } from './model-registry/service.js';
2
+ import { applyClaudeModelEnvOverrides, parseClaudeBinaryModels, parseClaudeModels, parseCodexModels, parseCodexAppServerModels, parseCursorModels, parseGeminiModels, parseOpenClawModels, stripAnsi } from './model-registry/parsers.js';
3
3
  /** Used by Vitest / `model-switching-e2e.ts` — not a stable public API. */
4
4
  export declare const modelSwitchingTestExports: {
5
+ readonly applyClaudeModelEnvOverrides: typeof applyClaudeModelEnvOverrides;
5
6
  readonly parseClaudeBinaryModels: typeof parseClaudeBinaryModels;
6
7
  readonly parseClaudeModels: typeof parseClaudeModels;
7
8
  readonly parseCodexModels: typeof parseCodexModels;
@@ -1,9 +1,10 @@
1
1
  // @arch docs/architecture/cli/model-discovery.md
2
2
  // @test src/__tests__/model-switching.test.ts
3
- export { getCachedAgentInfos, refreshAgentInfos, resolveAgentInfos } from './model-registry/service.js';
4
- import { parseClaudeBinaryModels, parseClaudeModels, parseCodexModels, parseCodexAppServerModels, parseCursorModels, parseGeminiModels, parseOpenClawModels, stripAnsi, } from './model-registry/parsers.js';
3
+ export { getCachedAgentInfos, refreshAgentInfos, resolveAgentInfos, } from './model-registry/service.js';
4
+ import { applyClaudeModelEnvOverrides, parseClaudeBinaryModels, parseClaudeModels, parseCodexModels, parseCodexAppServerModels, parseCursorModels, parseGeminiModels, parseOpenClawModels, stripAnsi, } from './model-registry/parsers.js';
5
5
  /** Used by Vitest / `model-switching-e2e.ts` — not a stable public API. */
6
6
  export const modelSwitchingTestExports = {
7
+ applyClaudeModelEnvOverrides,
7
8
  parseClaudeBinaryModels,
8
9
  parseClaudeModels,
9
10
  parseCodexModels,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shennian",
3
- "version": "0.2.8",
3
+ "version": "0.2.10",
4
4
  "description": "Shennian — AI Agent Mobile Console CLI",
5
5
  "type": "module",
6
6
  "bin": {