-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Expand file tree
/
Copy pathregistry.ts
More file actions
693 lines (622 loc) · 21.1 KB
/
Copy pathregistry.ts
File metadata and controls
693 lines (622 loc) · 21.1 KB
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
export type ModelCost = 'low' | 'medium' | 'high' | 'very_high';
export type ModelCapability = 'standard' | 'advanced' | 'best_quality';
export interface ModelMetadata {
cost?: ModelCost;
capability?: ModelCapability;
}
export interface AgentModelOption {
id: string;
label: string;
/** Whether the current account/tier can use this model. */
enabled?: boolean;
/** Whether this is the default model for the current account/tier. */
default?: boolean;
/** USD price per 1M input tokens when reported by the provider/catalog. */
inputPriceUsdPerMillion?: number;
/** USD price per 1M output tokens when reported by the provider/catalog. */
outputPriceUsdPerMillion?: number;
/** Provider/catalog-owned model picker metadata. */
metadata?: ModelMetadata;
/** Raw Codex `additional_speed_tiers` values, when the CLI catalog exposes them. */
additionalSpeedTiers?: string[];
/** Service tiers supported by this model, keyed by Codex config id. */
serviceTierOptions?: AgentModelOption[];
}
/**
* A typed "what should the UI do to fix this" intent attached to an
* {@link AgentDiagnostic}. The UI renders a button per intent and owns the
* concrete handler (open a URL, re-run detection, write an env override,
* launch the OAuth terminal flow). Keeping the intent typed — rather than a
* pre-baked button label + URL — means the Settings card, the unavailable
* grid, and (PR-B) the `od agent healthcheck` CLI / health-check panel all
* render the same fix affordances from one source of truth instead of each
* re-deriving copy and wiring.
*/
export type AgentFixIntent =
/** Open the agent's configuration / auth docs (`AgentInfo.docsUrl`). */
| { kind: 'openDocs' }
/** Open the agent's install / download page (`AgentInfo.installUrl`). */
| { kind: 'openInstall' }
/** Re-run agent detection (the Settings "Rescan" affordance). */
| { kind: 'rescan' }
/**
* Prompt the user to point Open Design at an explicit binary by writing
* `envKey` (e.g. `CURSOR_AGENT_BIN`) into `agentCliEnv`. Used when the CLI
* is installed somewhere PATH detection can't reach.
*/
| { kind: 'setEnv'; envKey: string }
/** Clear a previously-set binary override so detection falls back to PATH. */
| { kind: 'clearEnv'; envKey: string }
/**
* Launch the agent's interactive sign-in in a system terminal (today only
* Antigravity's `agy`, via POST /api/agents/:id/oauth-launch).
*/
| { kind: 'launchOAuth'; agentId: string };
/**
* Why a CLI agent is unavailable or only partially usable, in a shape the UI
* can render as "one-line reason + fix button(s)" instead of a silent grey
* card. Emitted by daemon detection (PATH / executable resolution + the auth
* probe) and reused by the connection-test / health-check surfaces so a
* failure is always actionable.
*/
export type AgentDiagnosticReason =
/** The binary (and any fallback names) was not found on PATH. */
| 'not-on-path'
/** A file matched but is not executable (missing +x / wrong PATHEXT). */
| 'not-executable'
/** A wrapper/shim was found but its target is gone (exit 126/127). */
| 'shim-broken'
/** A user-set `*_BIN` override points at a missing/invalid file. */
| 'configured-bin-invalid'
/** Installed and invocable, but the CLI is not authenticated. */
| 'auth-missing'
/** Installed, but auth status could not be verified. */
| 'auth-unknown';
export type AgentDiagnosticSeverity = 'error' | 'warning' | 'info';
export interface AgentDiagnostic {
reason: AgentDiagnosticReason;
severity: AgentDiagnosticSeverity;
/** Short, human-readable, single-sentence explanation. */
message: string;
/** Optional longer context (e.g. the probe's stderr tail). */
detail?: string;
/**
* Directories PATH detection searched, surfaced verbatim for the
* `not-on-path` case so the user can see where we looked before being
* asked to set an explicit binary path. Sourced from the daemon resolver,
* never recomputed in the client.
*/
searchedDirs?: string[];
/** Ordered fix affordances the UI should offer for this diagnostic. */
fixActions?: AgentFixIntent[];
}
export interface AgentInfo {
id: string;
name: string;
bin: string;
available: boolean;
authStatus?: 'ok' | 'missing' | 'unknown';
authMessage?: string;
path?: string;
version?: string | null;
/**
* Actionable reasons this agent is unavailable or only partially usable,
* each carrying typed fix intents. Empty / omitted means "healthy"
* (available and, where probed, authenticated).
*/
diagnostics?: AgentDiagnostic[];
models?: AgentModelOption[];
/** Whether models came from the installed CLI or Open Design's static fallback. */
modelsSource?: 'live' | 'fallback';
reasoningOptions?: AgentModelOption[];
/** HTTPS URL to install or download the CLI (vendor docs, GitHub README, npm). */
installUrl?: string;
/** Optional HTTPS URL for configuration / auth / usage docs. */
docsUrl?: string;
/**
* How the daemon forwards the user's `.od/mcp-config.json` external MCP
* servers to this runtime at spawn time. Mirrors the field on
* `RuntimeAgentDef` in the daemon. Undefined means the runtime has no
* native MCP transport wired yet, in which case the settings UI surfaces
* a "configure MCP in the agent's own config file" hint instead of
* silently dropping the servers (issue #2142).
*/
externalMcpInjection?:
| 'claude-mcp-json'
| 'acp-merge'
| 'opencode-env-content';
/**
* When `false`, the Settings model picker hides the "Custom (fill below)"
* option and the free-text input. Use this for agents whose CLI doesn't
* accept a model id (e.g. Antigravity `agy` has no `--model` flag yet —
* upstream issue #35) or rejects free-form ids (AMR validates against the
* live Vela catalog). Undefined === allow, matching the historical UX.
*/
supportsCustomModel?: boolean;
}
export interface AgentsResponse {
agents: AgentInfo[];
}
export type AmrModelsSource = 'preset' | 'remote';
export interface AmrModelsResponse {
source: AmrModelsSource;
models: AgentModelOption[];
refreshing: boolean;
stale?: boolean;
remoteError?: string;
}
export type SkillSource = 'built-in' | 'user';
export interface SkillSummary {
id: string;
name: string;
displayName?: Record<string, string>;
description: string;
descriptionI18n?: Record<string, string>;
triggers: string[];
mode:
| 'prototype'
| 'deck'
| 'template'
| 'design-system'
| 'image'
| 'video'
| 'audio';
surface?: 'web' | 'image' | 'video' | 'audio';
platform?: 'desktop' | 'mobile' | null;
scenario?: string | null;
// Optional human-readable category (e.g. "image-generation", "video",
// "design-systems"). Surfaced as a filter pill in Settings → Skills so a
// large pre-loaded catalogue stays scannable. Free-form lowercase slug;
// not part of system-prompt composition.
category?: string | null;
// Origin of the skill: 'built-in' lives under the repo's `skills/`
// directory and cannot be deleted from the UI; 'user' lives under
// `<runtimeData>/user-skills/` and is fully owned by the user (delete
// / re-import allowed). New `import` endpoint always tags `user`.
source?: SkillSource;
previewType: string;
designSystemRequired: boolean;
defaultFor: string[];
upstream: string | null;
featured?: number | null;
fidelity?: 'wireframe' | 'high-fidelity' | null;
speakerNotes?: boolean | null;
animations?: boolean | null;
craftRequires?: string[];
hasBody: boolean;
examplePrompt: string;
examplePromptI18n?: Record<string, string>;
// True when this skill exists only to group derived `<parent>:<child>`
// example cards. The Examples gallery hides such cards because their
// preview would duplicate one of the derived cards and add no extra
// information, but the entry stays in the listing so `findSkillById`
// resolves the parent for system-prompt composition and "Use this
// prompt" fast-create on a derived card still composes the parent's
// SKILL.md body.
aggregatesExamples: boolean;
}
// Body shape for POST /api/skills/import. The daemon turns this into a
// SKILL.md under `<runtimeData>/user-skills/<slug>/` and surfaces the
// freshly-listed summary in the response.
export interface SkillImportRequest {
name: string;
description?: string;
body: string;
triggers?: string[];
}
export interface SkillImportResponse {
skill: SkillSummary;
}
// Body for PUT /api/skills/:id — update an existing skill's SKILL.md.
// The route param resolves to the canonical skill id; the daemon refuses
// updates whose body `name` differs from that id (rename = delete +
// re-import).
export interface SkillUpdateRequest {
name?: string;
description?: string;
body: string;
triggers?: string[];
}
export interface SkillUpdateResponse {
skill: SkillSummary;
}
// Returned by GET /api/skills/:id/files — the on-disk file tree under
// the skill's directory, capped to a small number of entries to keep
// the payload bounded. Used by the Settings → Skills detail panel.
export interface SkillFileEntry {
path: string;
kind: 'file' | 'directory';
size: number | null;
}
export interface SkillFilesResponse {
files: SkillFileEntry[];
}
export interface SkillDetail extends SkillSummary {
body: string;
}
export interface SkillsResponse {
skills: SkillSummary[];
}
export interface SkillResponse {
skill: SkillDetail;
}
// Design templates share the SkillSummary/Detail shape (same SKILL.md
// frontmatter, same preview behavior) but live under a separate registry
// root so the EntryView Templates surface and the Settings → Skills surface
// stay decoupled. See specs/current/skills-and-design-templates.md.
export type DesignTemplateSummary = SkillSummary;
export type DesignTemplateDetail = SkillDetail;
export interface DesignTemplatesResponse {
designTemplates: DesignTemplateSummary[];
}
export interface DesignTemplateResponse {
designTemplate: DesignTemplateDetail;
}
export interface DesignSystemSummary {
id: string;
title: string;
category: string;
summary: string;
swatches?: string[];
surface?: 'web' | 'image' | 'video' | 'audio';
source?: 'built-in' | 'installed' | 'user';
status?: 'draft' | 'published';
isEditable?: boolean;
createdAt?: string;
updatedAt?: string;
provenance?: DesignSystemProvenance;
projectId?: string;
}
export interface DesignSystemDetail extends DesignSystemSummary {
body: string;
packageInfo?: DesignSystemPackageInfo;
}
export interface DesignSystemPackageInfo {
manifest?: {
schemaVersion: string;
id: string;
name: string;
category: string;
source?: {
type?: string;
url?: string;
path?: string;
branch?: string;
commit?: string;
importedAt?: string;
// shadcn registry imports (source.type === 'shadcn').
reference?: string;
registryUrl?: string;
item?: string;
homepage?: string;
};
files?: {
design?: string;
tokens?: string;
designTokens?: string;
tailwind?: string;
components?: string;
};
usage?: string;
componentsManifest?: string;
importMode?: string;
craft?: {
applies?: string[];
suggested?: string[];
exemptions?: string[];
};
fonts?: Array<{ family?: string; weight?: string | number; style?: string; file?: string }>;
preview?: {
dir?: string;
pages?: Array<{ path?: string; role?: string; title?: string }>;
};
sourceFiles?: {
scanned?: string;
evidence?: string;
tokens?: string;
report?: string;
snippets?: string;
};
assetsDir?: string;
};
/** Package-relative files the daemon confirmed exist and can be served via /static. */
availableFiles?: string[];
sourceEvidence?: {
scannedFileCount?: number;
tokenCount?: number;
snippetCount?: number;
confidence?: Record<string, string | number>;
evidenceExcerpt?: string;
tokenContract?: {
contract?: string;
grade?: DesignSystemTokenContractGrade;
score?: number;
recommendRebuild?: boolean;
sourceBackedA1?: number;
requiredA1?: number;
fallbackTokens?: number;
selfCheckOk?: boolean;
};
};
}
export interface DesignSystemsResponse {
designSystems: DesignSystemSummary[];
}
export interface DesignSystemResponse {
designSystem: DesignSystemDetail;
}
export interface DesignSystemProvenance {
companyBlurb?: string;
sourceUrls?: string[];
githubUrls?: string[];
localCodeFiles?: string[];
figFiles?: string[];
assetFiles?: string[];
notes?: string;
sourceNotes?: string;
}
export type DesignSystemFileKind =
| 'folder'
| 'page'
| 'stylesheet'
| 'document'
| 'image'
| 'data'
| 'asset';
export interface DesignSystemFileSummary {
path: string;
name: string;
kind: DesignSystemFileKind;
size?: number;
updatedAt?: string;
}
export interface DesignSystemFileDetail extends DesignSystemFileSummary {
content: string;
}
export interface DesignSystemFilesResponse {
files: DesignSystemFileSummary[];
}
export interface DesignSystemFileResponse {
file: DesignSystemFileDetail;
}
export interface DesignSystemWorkspaceResponse {
project: import('./projects.js').Project;
files: import('./files.js').ProjectFile[];
}
export type DesignSystemRevisionStatus = 'pending' | 'accepted' | 'rejected';
export interface DesignSystemRevision {
id: string;
designSystemId: string;
status: DesignSystemRevisionStatus;
feedback: string;
baseBody: string;
proposedBody: string;
createdAt: string;
updatedAt: string;
sectionTitle?: string;
jobId?: string;
fileChanges?: DesignSystemRevisionFileChange[];
}
export interface DesignSystemRevisionFileChange {
path: string;
baseContent: string;
proposedContent: string;
}
export interface DesignSystemRevisionsResponse {
revisions: DesignSystemRevision[];
}
export interface DesignSystemRevisionResponse {
revision: DesignSystemRevision;
}
export type DesignSystemGenerationJobStatus =
| 'queued'
| 'running'
| 'succeeded'
| 'failed';
export type DesignSystemGenerationStepStatus =
| 'pending'
| 'running'
| 'succeeded'
| 'failed';
export interface DesignSystemGenerationStep {
id: string;
title: string;
status: DesignSystemGenerationStepStatus;
message?: string;
startedAt?: string;
completedAt?: string;
}
export interface DesignSystemGenerationJob {
id: string;
kind?: 'generation' | 'revision' | 'token-contract-rebuild';
status: DesignSystemGenerationJobStatus;
progress: number;
steps: DesignSystemGenerationStep[];
createdAt: string;
updatedAt: string;
completedAt?: string;
designSystemId?: string;
revisionId?: string;
error?: string;
message?: string;
}
export interface DesignSystemGenerationJobResponse {
job: DesignSystemGenerationJob;
}
export type DesignSystemPackageAuditSeverity = 'error' | 'warning';
export interface DesignSystemPackageAuditIssue {
severity: DesignSystemPackageAuditSeverity;
code: string;
message: string;
path?: string;
}
export interface DesignSystemPackageAudit {
ok: boolean;
projectPath: string;
filesInspected: number;
errors: DesignSystemPackageAuditIssue[];
warnings: DesignSystemPackageAuditIssue[];
}
export interface DesignSystemPackageAuditResponse {
audit: DesignSystemPackageAudit;
}
export interface DesignSystemRevisionJobRequest {
feedback: string;
sectionTitle?: string;
body?: string;
}
export type DesignSystemTokenContractGrade =
| 'excellent'
| 'usable'
| 'needs-review'
| 'needs-rebuild';
export interface DesignSystemTokenContractRebuildDecision {
designSystemId: string;
available: boolean;
recommended: boolean;
forced: boolean;
reason: string;
triggers: string[];
reportPath?: string;
grade?: DesignSystemTokenContractGrade;
score?: number;
sourceBackedA1?: number;
requiredA1?: number;
fallbackTokens?: number;
selfCheckOk?: boolean;
weakTokens?: Array<{
name: string;
layer?: string;
confidence: string;
reason: string;
sources: string[];
}>;
}
export interface DesignSystemTokenContractRebuildJobRequest {
force?: boolean;
}
export interface DesignSystemTokenContractRebuildJobResponse {
decision: DesignSystemTokenContractRebuildDecision;
job?: DesignSystemGenerationJob;
}
export interface ImportLocalDesignSystemRequest {
/** Absolute local project directory selected by the user. */
baseDir: string;
/** Optional display name override for the generated design-system project. */
name?: string;
/** Import structure mode. Defaults to hybrid for real project imports. */
importMode?: 'normalized' | 'hybrid' | 'verbatim';
/** Craft sections that should actively apply when this system is used. */
craftApplies?: string[];
}
export interface ImportLocalDesignSystemResponse {
designSystem: DesignSystemSummary;
tokenContractRebuild?: DesignSystemTokenContractRebuildJobResponse;
}
export interface ImportGitHubDesignSystemRequest {
/** Public GitHub repository URL, e.g. https://github.com/owner/repo. */
githubUrl: string;
/** Optional branch to clone. Defaults to the repository default branch. */
branch?: string;
/** Optional display name override for the generated design-system project. */
name?: string;
/** Import structure mode. Defaults to hybrid for real project imports. */
importMode?: 'normalized' | 'hybrid' | 'verbatim';
/** Craft sections that should actively apply when this system is used. */
craftApplies?: string[];
}
export interface ImportGitHubDesignSystemResponse {
designSystem: DesignSystemSummary;
tokenContractRebuild?: DesignSystemTokenContractRebuildJobResponse;
}
export interface ImportShadcnDesignSystemRequest {
/**
* shadcn registry item reference. Accepts either the shadcn CLI
* shorthand `<owner>/<repo>/<item>` (optionally suffixed with
* `#<branch|tag|sha>`), which is resolved against the repository's
* root `registry.json` on GitHub, or a direct `https://…/<item>.json`
* URL pointing at a registry-item document. `http://` is accepted only
* for loopback hosts (localhost / 127.0.0.1) so a self-hosted local
* registry can be imported.
*/
reference: string;
/** Optional display name override for the generated design-system project. */
name?: string;
/** Import structure mode. Defaults to hybrid for real project imports. */
importMode?: 'normalized' | 'hybrid' | 'verbatim';
/** Craft sections that should actively apply when this system is used. */
craftApplies?: string[];
}
export interface ImportShadcnDesignSystemResponse {
designSystem: DesignSystemSummary;
tokenContractRebuild?: DesignSystemTokenContractRebuildJobResponse;
}
export interface HealthResponse {
ok: true;
service?: 'daemon';
version?: string;
}
// A pet packaged by the upstream Codex `hatch-pet` skill. Each pet is a
// folder under `${CODEX_HOME:-$HOME/.codex}/pets/<id>/` that contains a
// `pet.json` manifest and a `spritesheet.<png|webp>` atlas. The daemon
// surfaces these so the web pet settings can offer one-click adoption
// of recently-hatched pets without asking the user to re-upload the
// file by hand.
export interface CodexPetSummary {
id: string;
displayName: string;
description: string;
// URL on the daemon that serves the raw spritesheet bytes.
spritesheetUrl: string;
// File extension reported by the on-disk spritesheet (png / webp /
// gif). Useful only as a hint to the client renderer.
spritesheetExt: string;
// Unix milliseconds for the spritesheet file's mtime — lets the
// client sort "most recently hatched" without re-listing.
hatchedAt: number;
// True when the pet ships in the repo under `assets/community-pets/`
// rather than the user's `~/.codex/pets/`. Surfaced so the UI can
// tag the card with a small "Bundled" pill and avoid prompting the
// user to re-sync something that is already on disk.
bundled?: boolean;
}
export interface CodexPetsResponse {
pets: CodexPetSummary[];
// Absolute path of the directory we scanned. Surfaced so the UI can
// tell the user where their pets live (and where to look if a pet
// they expect is missing).
rootDir: string;
}
// Body for `POST /api/codex-pets/sync` — triggers the daemon-side port
// of `scripts/sync-community-pets.ts`. Both fields are optional so the
// default call (`syncCommunityPets({})`) downloads every catalog and
// skips pets that already exist on disk.
export interface SyncCommunityPetsRequest {
// Which catalog(s) to download. Defaults to 'all'.
source?: 'all' | 'petshare' | 'hatchery';
// Re-download pets that already have a folder on disk.
force?: boolean;
}
// Daemon response after a community sync. Matches the script's stdout
// summary so the web UI can show the same "wrote/skipped/failed" line.
export interface SyncCommunityPetsResponse {
wrote: number;
skipped: number;
failed: number;
total: number;
rootDir: string;
// Up to ~10 surfaced error messages (the daemon log keeps the rest).
errors: string[];
}
export type InstallInput =
| { source: 'github'; url: string }
| { source: 'local'; path: string };
export interface InstallSkillResponse {
skill: SkillSummary;
}
export interface InstallDesignSystemResponse {
designSystem: DesignSystemSummary;
}
export interface UninstallResponse {
ok: true;
}