dsh-code 0.9.1 → 1.0.1

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.
Files changed (67) hide show
  1. package/README.en.md +278 -249
  2. package/README.md +131 -102
  3. package/bin/deepseek.mjs +100 -6
  4. package/cordis.patch.yml +36 -1
  5. package/lib/index.mjs +3055 -819
  6. package/lib/startup.mjs +21 -11
  7. package/lib/{theme-BEi4i_aN.mjs → theme-DCT8Y2xf.mjs} +13 -9
  8. package/lib/types/app.d.ts +84 -16
  9. package/lib/types/attachments.d.ts +20 -0
  10. package/lib/types/authorization-panel.d.ts +22 -0
  11. package/lib/types/authorization.d.ts +36 -0
  12. package/lib/types/editor.d.ts +6 -0
  13. package/lib/types/fork.d.ts +8 -0
  14. package/lib/types/git-workflow.d.ts +23 -0
  15. package/lib/types/index.d.ts +6 -0
  16. package/lib/types/kernel-panels.d.ts +39 -0
  17. package/lib/types/keyboard.d.ts +41 -0
  18. package/lib/types/mentions.d.ts +30 -38
  19. package/lib/types/models.d.ts +3 -1
  20. package/lib/types/permissions.d.ts +4 -14
  21. package/lib/types/presets.d.ts +5 -20
  22. package/lib/types/provider-settings.d.ts +16 -0
  23. package/lib/types/render/animations.d.ts +10 -39
  24. package/lib/types/render/editor.d.ts +137 -0
  25. package/lib/types/render/export.d.ts +1 -1
  26. package/lib/types/render/lines.d.ts +6 -2
  27. package/lib/types/render/markdown.d.ts +3 -1
  28. package/lib/types/render/projection.d.ts +29 -3
  29. package/lib/types/render/status.d.ts +6 -13
  30. package/lib/types/session-directory.d.ts +1 -3
  31. package/lib/types/startup.d.ts +14 -11
  32. package/lib/types/store.d.ts +11 -9
  33. package/lib/types/subagents.d.ts +3 -3
  34. package/lib/types/theme.d.ts +14 -1
  35. package/lib/types/version.d.ts +15 -2
  36. package/package.json +159 -141
  37. package/src/app.ts +1490 -663
  38. package/src/attachments.ts +128 -0
  39. package/src/authorization-panel.ts +285 -0
  40. package/src/authorization.ts +147 -0
  41. package/src/editor.ts +51 -0
  42. package/src/fork.ts +31 -0
  43. package/src/git-workflow.ts +87 -0
  44. package/src/index.ts +1523 -1374
  45. package/src/internals.ts +14 -1
  46. package/src/kernel-panels.ts +914 -798
  47. package/src/keyboard.ts +126 -0
  48. package/src/mentions.ts +78 -117
  49. package/src/models.ts +20 -14
  50. package/src/permissions.ts +5 -13
  51. package/src/presets.ts +6 -22
  52. package/src/provider-settings.ts +95 -1
  53. package/src/render/animations.ts +420 -450
  54. package/src/render/editor.ts +398 -0
  55. package/src/render/export.ts +79 -79
  56. package/src/render/lines.ts +342 -236
  57. package/src/render/markdown.ts +99 -26
  58. package/src/render/projection.ts +106 -19
  59. package/src/render/status.ts +713 -650
  60. package/src/render/text.ts +150 -150
  61. package/src/render/tool-detail.ts +3 -1
  62. package/src/session-directory.ts +4 -4
  63. package/src/startup.ts +136 -119
  64. package/src/store.ts +23 -11
  65. package/src/subagents.ts +13 -5
  66. package/src/theme.ts +214 -206
  67. package/src/version.ts +58 -1
@@ -129,6 +129,28 @@ function profileRefOf(profile: unknown): string | undefined {
129
129
  return typeof ref === 'string' && ref.length > 0 ? ref : undefined
130
130
  }
131
131
 
132
+ /** Extract only fields the terminal can round-trip without touching provider-specific extras. */
133
+ function configurationOf(profile: unknown): ProviderConfiguration {
134
+ if (typeof profile !== 'object' || profile === null) return { models: [] }
135
+ const record = profile as Record<string, unknown>
136
+ const source = Array.isArray(record.models) ? record.models : []
137
+ const models = source.flatMap((value): ProviderModelSettings[] => {
138
+ if (typeof value !== 'object' || value === null) return []
139
+ const entry = value as Record<string, unknown>
140
+ if (typeof entry.id !== 'string' || entry.id.trim() === '') return []
141
+ return [{
142
+ id: entry.id,
143
+ ...(typeof entry.name === 'string' && entry.name.trim() !== '' ? { name: entry.name } : {}),
144
+ ...(typeof entry.contextWindow === 'number' && Number.isFinite(entry.contextWindow) ? { contextWindow: entry.contextWindow } : {}),
145
+ ...(typeof entry.maxTokens === 'number' && Number.isFinite(entry.maxTokens) ? { maxTokens: entry.maxTokens } : {}),
146
+ }]
147
+ })
148
+ return {
149
+ ...(typeof record.baseURL === 'string' && record.baseURL.trim() !== '' ? { baseURL: record.baseURL } : {}),
150
+ models,
151
+ }
152
+ }
153
+
132
154
  /* ------------------------------------------------------------------ *
133
155
  * Exported contracts.
134
156
  * ------------------------------------------------------------------ */
@@ -166,6 +188,20 @@ export type ProviderCredentialView =
166
188
  | ({ readonly kind: 'facts' } & ProviderCredentialFacts)
167
189
  | { readonly kind: 'error'; readonly message: string }
168
190
 
191
+ /** One explicit model enabled for a provider profile. */
192
+ export interface ProviderModelSettings {
193
+ readonly id: string
194
+ readonly name?: string
195
+ readonly contextWindow?: number
196
+ readonly maxTokens?: number
197
+ }
198
+
199
+ /** The small, portable subset of a provider profile the terminal edits. */
200
+ export interface ProviderConfiguration {
201
+ readonly baseURL?: string
202
+ readonly models: readonly ProviderModelSettings[]
203
+ }
204
+
169
205
  /**
170
206
  * One provider row in the TUI provider-management panel: the configurable
171
207
  * directory entry joined with its settings profile and credential facts.
@@ -195,6 +231,8 @@ export interface ProviderTargetView {
195
231
  readonly suggestedRef: string
196
232
  /** Credential facts, a bounded describe error, or undefined when there is no ref to describe. */
197
233
  readonly credential: ProviderCredentialView | undefined
234
+ /** Endpoint and explicit model overrides visible to the provider editor. */
235
+ readonly configuration: ProviderConfiguration
198
236
  /** The owning adapter reports this route as hand-declared (absent when it draws no distinction). */
199
237
  readonly declared?: boolean
200
238
  }
@@ -211,7 +249,7 @@ export interface ProviderSettingsDirectory {
211
249
 
212
250
  /** Events that invalidate the official Models provider/settings/credential join. */
213
251
  const PROVIDER_SETTINGS_EVENTS = [
214
- 'credentials/updated',
252
+ 'credentials/reference-updated',
215
253
  'settings/document-updated',
216
254
  'llm/adapters-updated',
217
255
  ] as const
@@ -339,6 +377,7 @@ export async function loadProviderSettings(ctx: Context): Promise<ProviderSettin
339
377
  settingsRevision: namespace?.revision ?? 0,
340
378
  configured,
341
379
  removable,
380
+ configuration: configurationOf(profile),
342
381
  ...credentialRef === undefined ? {} : { credentialRef },
343
382
  suggestedRef: deriveCredentialRef(base.provider),
344
383
  ...base.declared === undefined ? {} : { declared: base.declared },
@@ -441,6 +480,61 @@ export async function saveProviderCredential(ctx: Context, target: ProviderTarge
441
480
  }
442
481
  }
443
482
 
483
+ /** Save the endpoint and an explicit model allow-list without rebuilding the profile. */
484
+ export async function saveProviderConfiguration(
485
+ ctx: Context,
486
+ target: ProviderTargetView,
487
+ configuration: ProviderConfiguration,
488
+ ): Promise<void> {
489
+ if (target.settingsNs.length === 0) {
490
+ throw new ProviderSettingsError(`provider "${target.provider}" has no managed settings namespace; configure it in settings.yaml`)
491
+ }
492
+ const settings = ctx.get('settings') as SettingsFace | undefined
493
+ if (settings === undefined || settings.writable !== true) {
494
+ throw new ProviderSettingsError('settings are read-only; provider configuration cannot be changed here')
495
+ }
496
+ const baseURL = configuration.baseURL?.trim()
497
+ if (baseURL !== undefined && baseURL !== '') {
498
+ try {
499
+ const parsed = new URL(baseURL)
500
+ if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') throw new Error('unsupported protocol')
501
+ } catch {
502
+ throw new ProviderSettingsError('base URL must be an absolute http or https URL')
503
+ }
504
+ }
505
+ const seen = new Set<string>()
506
+ const models = configuration.models.map((model) => {
507
+ const id = model.id.trim()
508
+ if (id === '' || seen.has(id)) throw new ProviderSettingsError('each selected model must have a unique non-empty id')
509
+ seen.add(id)
510
+ for (const [label, value] of [['context window', model.contextWindow], ['output window', model.maxTokens]] as const) {
511
+ if (value !== undefined && (!Number.isSafeInteger(value) || value <= 0)) {
512
+ throw new ProviderSettingsError(`${label} must be a positive integer`)
513
+ }
514
+ }
515
+ return {
516
+ id,
517
+ ...(model.name === undefined || model.name.trim() === '' ? {} : { name: model.name.trim() }),
518
+ ...(model.contextWindow === undefined ? {} : { contextWindow: model.contextWindow }),
519
+ ...(model.maxTokens === undefined ? {} : { maxTokens: model.maxTokens }),
520
+ }
521
+ })
522
+ const root = target.settingsPath
523
+ const ops: SettingsPathOpFace[] = [
524
+ baseURL === undefined || baseURL === ''
525
+ ? { op: 'unset', path: [...root, 'baseURL'] }
526
+ : { op: 'set', path: [...root, 'baseURL'], value: baseURL },
527
+ // An explicit empty list is deliberate: it prevents a provider's shipped
528
+ // catalog from silently becoming the active selection.
529
+ { op: 'set', path: [...root, 'models'], value: models },
530
+ ]
531
+ try {
532
+ await settings.mutate(target.settingsNs, ops, target.settingsRevision)
533
+ } catch (error) {
534
+ throw new ProviderSettingsError(singleLine(messageOf(error)))
535
+ }
536
+ }
537
+
444
538
  /**
445
539
  * Remove the currently named credential without touching the provider
446
540
  * profile. Only the resolved profile's own reference is unset; a dormant or