@astrale-os/cli 0.4.0-alpha.13

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 (219) hide show
  1. package/.check-workspace.cjs +40 -0
  2. package/README.md +151 -0
  3. package/dist/astrale.js +59749 -0
  4. package/package.json +90 -0
  5. package/src/command.ts +40 -0
  6. package/src/commands/__tests__/admin-instance.test.ts +73 -0
  7. package/src/commands/__tests__/auth-login.test.ts +178 -0
  8. package/src/commands/__tests__/auth-token.test.ts +223 -0
  9. package/src/commands/__tests__/call.test.ts +72 -0
  10. package/src/commands/__tests__/domain-list.test.ts +74 -0
  11. package/src/commands/__tests__/help-contract.test.ts +136 -0
  12. package/src/commands/__tests__/install-identity-override.test.ts +65 -0
  13. package/src/commands/__tests__/instance-bookmark.test.ts +101 -0
  14. package/src/commands/__tests__/instance-create-hosts.test.ts +29 -0
  15. package/src/commands/__tests__/instance-list-rows.test.ts +63 -0
  16. package/src/commands/__tests__/logs.test.ts +117 -0
  17. package/src/commands/__tests__/ls.test.ts +25 -0
  18. package/src/commands/__tests__/setup-plan.test.ts +61 -0
  19. package/src/commands/admin/status.ts +61 -0
  20. package/src/commands/admin/use.ts +77 -0
  21. package/src/commands/auth/login.ts +82 -0
  22. package/src/commands/auth/logout.ts +50 -0
  23. package/src/commands/auth/status.ts +86 -0
  24. package/src/commands/auth/token.ts +162 -0
  25. package/src/commands/browser.ts +207 -0
  26. package/src/commands/call.ts +300 -0
  27. package/src/commands/describe.ts +182 -0
  28. package/src/commands/domain/install.ts +420 -0
  29. package/src/commands/domain/list.ts +154 -0
  30. package/src/commands/domain/publish.ts +155 -0
  31. package/src/commands/get.ts +60 -0
  32. package/src/commands/identity/create.ts +26 -0
  33. package/src/commands/identity/delete.ts +18 -0
  34. package/src/commands/identity/export.ts +66 -0
  35. package/src/commands/identity/import.ts +101 -0
  36. package/src/commands/identity/list.ts +56 -0
  37. package/src/commands/identity/register.ts +170 -0
  38. package/src/commands/identity/sync.ts +32 -0
  39. package/src/commands/identity/unsync.ts +24 -0
  40. package/src/commands/identity/use.ts +18 -0
  41. package/src/commands/identity/whoami.ts +34 -0
  42. package/src/commands/idp/add.ts +150 -0
  43. package/src/commands/idp/list.ts +57 -0
  44. package/src/commands/idp/refresh.ts +38 -0
  45. package/src/commands/idp/remove.ts +36 -0
  46. package/src/commands/idp/show.ts +29 -0
  47. package/src/commands/instance/active.ts +64 -0
  48. package/src/commands/instance/bookmark.ts +72 -0
  49. package/src/commands/instance/create.ts +69 -0
  50. package/src/commands/instance/delete.ts +72 -0
  51. package/src/commands/instance/forget.ts +26 -0
  52. package/src/commands/instance/list.ts +149 -0
  53. package/src/commands/instance/status.ts +42 -0
  54. package/src/commands/instance/use.ts +210 -0
  55. package/src/commands/logs.ts +347 -0
  56. package/src/commands/ls.ts +229 -0
  57. package/src/commands/query.ts +32 -0
  58. package/src/commands/setup.ts +54 -0
  59. package/src/commands/status.ts +60 -0
  60. package/src/commands/studio.ts +401 -0
  61. package/src/commands/token.ts +77 -0
  62. package/src/commands/update.ts +267 -0
  63. package/src/commands/use.ts +87 -0
  64. package/src/errors.ts +65 -0
  65. package/src/kernel/__tests__/auth.test.ts +77 -0
  66. package/src/kernel/__tests__/errors.test.ts +43 -0
  67. package/src/kernel/__tests__/remote-routing.test.ts +70 -0
  68. package/src/kernel/auth.ts +234 -0
  69. package/src/kernel/ca-fetch.ts +119 -0
  70. package/src/kernel/client.ts +191 -0
  71. package/src/kernel/errors.ts +280 -0
  72. package/src/kernel/expand.ts +217 -0
  73. package/src/kernel/index.ts +14 -0
  74. package/src/kernel/options.ts +22 -0
  75. package/src/kernel/remote-routing.ts +88 -0
  76. package/src/kernel/run.ts +63 -0
  77. package/src/kernel/types.ts +14 -0
  78. package/src/lib/__tests__/admin-target.test.ts +112 -0
  79. package/src/lib/__tests__/binary.test.ts +56 -0
  80. package/src/lib/__tests__/command-dx.test.ts +58 -0
  81. package/src/lib/__tests__/concurrency.test.ts +62 -0
  82. package/src/lib/__tests__/config.test.ts +53 -0
  83. package/src/lib/__tests__/design.test.ts +99 -0
  84. package/src/lib/__tests__/domain-identity.test.ts +60 -0
  85. package/src/lib/__tests__/format.test.ts +22 -0
  86. package/src/lib/__tests__/fs-atomic.test.ts +104 -0
  87. package/src/lib/__tests__/identity.test.ts +79 -0
  88. package/src/lib/__tests__/idp-session.driver.ts +53 -0
  89. package/src/lib/__tests__/idp-session.test.ts +357 -0
  90. package/src/lib/__tests__/idp.test.ts +385 -0
  91. package/src/lib/__tests__/instance-candidates.test.ts +73 -0
  92. package/src/lib/__tests__/instance-target.test.ts +183 -0
  93. package/src/lib/__tests__/instance.test.ts +136 -0
  94. package/src/lib/__tests__/keys.test.ts +129 -0
  95. package/src/lib/__tests__/local-status.test.ts +202 -0
  96. package/src/lib/__tests__/output.test.ts +150 -0
  97. package/src/lib/__tests__/panel.test.ts +40 -0
  98. package/src/lib/__tests__/port.test.ts +44 -0
  99. package/src/lib/__tests__/prompt.test.ts +25 -0
  100. package/src/lib/__tests__/sdk-deps.test.ts +68 -0
  101. package/src/lib/__tests__/self.test.ts +272 -0
  102. package/src/lib/__tests__/studio-server-deps.test.ts +74 -0
  103. package/src/lib/__tests__/table.test.ts +53 -0
  104. package/src/lib/__tests__/update.test.ts +246 -0
  105. package/src/lib/__tests__/use-target.test.ts +56 -0
  106. package/src/lib/__tests__/validation.test.ts +34 -0
  107. package/src/lib/admin-domain.ts +25 -0
  108. package/src/lib/admin-instance.ts +26 -0
  109. package/src/lib/admin-target.ts +217 -0
  110. package/src/lib/binary.ts +131 -0
  111. package/src/lib/browser.ts +150 -0
  112. package/src/lib/command-dx.ts +161 -0
  113. package/src/lib/concurrency.ts +31 -0
  114. package/src/lib/config.ts +45 -0
  115. package/src/lib/domain-identity.ts +49 -0
  116. package/src/lib/env.ts +49 -0
  117. package/src/lib/format.ts +4 -0
  118. package/src/lib/fs-atomic.ts +126 -0
  119. package/src/lib/identity.ts +256 -0
  120. package/src/lib/idp-session.ts +134 -0
  121. package/src/lib/idp.ts +876 -0
  122. package/src/lib/instance-candidates.ts +49 -0
  123. package/src/lib/instance-target.ts +182 -0
  124. package/src/lib/instance.ts +395 -0
  125. package/src/lib/keys.ts +294 -0
  126. package/src/lib/local-status.ts +152 -0
  127. package/src/lib/log.ts +116 -0
  128. package/src/lib/login-flow.ts +164 -0
  129. package/src/lib/meta.ts +86 -0
  130. package/src/lib/output.ts +222 -0
  131. package/src/lib/panel.ts +61 -0
  132. package/src/lib/paths.ts +11 -0
  133. package/src/lib/port.ts +41 -0
  134. package/src/lib/proc.ts +82 -0
  135. package/src/lib/prompt.ts +136 -0
  136. package/src/lib/provision-instance.ts +170 -0
  137. package/src/lib/sdk-deps.ts +104 -0
  138. package/src/lib/self.ts +166 -0
  139. package/src/lib/skills.ts +171 -0
  140. package/src/lib/table.ts +62 -0
  141. package/src/lib/update.ts +315 -0
  142. package/src/lib/use-target.ts +24 -0
  143. package/src/lib/validation.ts +59 -0
  144. package/src/program.ts +200 -0
  145. package/src/registry.ts +59 -0
  146. package/src/setup/__tests__/util.test.ts +29 -0
  147. package/src/setup/engine.ts +83 -0
  148. package/src/setup/render.ts +109 -0
  149. package/src/setup/steps/admin.ts +78 -0
  150. package/src/setup/steps/agent-browser.ts +81 -0
  151. package/src/setup/steps/auth.ts +54 -0
  152. package/src/setup/steps/domain.ts +68 -0
  153. package/src/setup/steps/index.ts +18 -0
  154. package/src/setup/steps/instance.ts +119 -0
  155. package/src/setup/steps/skills-bridge.ts +70 -0
  156. package/src/setup/steps/skills.ts +59 -0
  157. package/src/setup/types.ts +61 -0
  158. package/src/setup/util.ts +34 -0
  159. package/src/test-utils.ts +18 -0
  160. package/studio/client/dist/assets/index-DOwzZAEK.css +1 -0
  161. package/studio/client/dist/assets/index-wtU0Zxhy.js +183 -0
  162. package/studio/client/dist/index.html +13 -0
  163. package/studio/package.json +62 -0
  164. package/studio/server/agent/ask.ts +68 -0
  165. package/studio/server/agent/bridge-mcp.ts +182 -0
  166. package/studio/server/agent/bridge.ts +188 -0
  167. package/studio/server/agent/claude.ts +666 -0
  168. package/studio/server/agent/mock.ts +186 -0
  169. package/studio/server/agent/prompt.ts +202 -0
  170. package/studio/server/agent/registry.ts +29 -0
  171. package/studio/server/agent/runner.ts +484 -0
  172. package/studio/server/agent/schema-map.ts +112 -0
  173. package/studio/server/agent/types.ts +120 -0
  174. package/studio/server/api.ts +574 -0
  175. package/studio/server/cache.ts +138 -0
  176. package/studio/server/detect.ts +81 -0
  177. package/studio/server/domain.ts +70 -0
  178. package/studio/server/index.ts +136 -0
  179. package/studio/server/introspect/anatomy-extras.ts +398 -0
  180. package/studio/server/introspect/anatomy.ts +108 -0
  181. package/studio/server/introspect/bundle.ts +57 -0
  182. package/studio/server/introspect/core-extractor.ts +119 -0
  183. package/studio/server/introspect/core.ts +44 -0
  184. package/studio/server/introspect/diff.ts +133 -0
  185. package/studio/server/introspect/extractor.ts +102 -0
  186. package/studio/server/introspect/hash.ts +21 -0
  187. package/studio/server/introspect/overlay-tsmorph.ts +874 -0
  188. package/studio/server/introspect/overlay.ts +57 -0
  189. package/studio/server/introspect/runtime.ts +99 -0
  190. package/studio/server/introspect/schema-refs.ts +46 -0
  191. package/studio/server/lifecycle.ts +38 -0
  192. package/studio/server/sse.ts +57 -0
  193. package/studio/server/state/baseline.ts +211 -0
  194. package/studio/server/state/catalog.ts +117 -0
  195. package/studio/server/state/comments.ts +321 -0
  196. package/studio/server/state/context.ts +167 -0
  197. package/studio/server/state/copy.ts +156 -0
  198. package/studio/server/state/create.ts +156 -0
  199. package/studio/server/state/documents.ts +70 -0
  200. package/studio/server/state/env.ts +161 -0
  201. package/studio/server/state/git.ts +75 -0
  202. package/studio/server/state/handoff.ts +55 -0
  203. package/studio/server/state/harness-gateway.ts +181 -0
  204. package/studio/server/state/harness-token.ts +0 -0
  205. package/studio/server/state/instance.ts +244 -0
  206. package/studio/server/state/integrations.ts +55 -0
  207. package/studio/server/state/layout.ts +55 -0
  208. package/studio/server/state/settings.ts +39 -0
  209. package/studio/server/state/store.ts +97 -0
  210. package/studio/server/state/updates.ts +63 -0
  211. package/studio/server/state/usage.ts +37 -0
  212. package/studio/server/state/views.ts +138 -0
  213. package/studio/server/state/visibility.ts +33 -0
  214. package/studio/server/watch.ts +81 -0
  215. package/studio/server/workspace-state.ts +26 -0
  216. package/studio/server/workspace-watch.ts +101 -0
  217. package/studio/shared/types.ts +873 -0
  218. package/studio/tsconfig.json +23 -0
  219. package/tsconfig.json +14 -0
@@ -0,0 +1,873 @@
1
+ /**
2
+ * shared/types.ts — the ONE contract shared by the Bun server and the React
3
+ * client. Shapes under "DSL Schema IR" mirror EXACTLY what `D.$.ir` /
4
+ * `serialize(schema)` emit (verified empirically against my-domain + evaluation):
5
+ * ir = { version, domain, types, interfaces, classes, imports }
6
+ * class = { type:'node'|'edge', name, implements?, endpoints?(edge), properties, methods }
7
+ * interface = { type:'interface', name, extends?, properties, methods }
8
+ * method = { name, params:{name->JsonSchema}, returns:JsonSchema, static, inheritance }
9
+ * imports = { name -> { origin, definition:'interface'|'class' } }
10
+ * Everything under "Studio overlay/state" is the studio's own augmentation.
11
+ */
12
+
13
+ // ───────────────────────────── DSL Schema IR ─────────────────────────────
14
+
15
+ export interface JsonSchema {
16
+ type?: string | string[]
17
+ enum?: (string | number | boolean | null)[]
18
+ properties?: Record<string, JsonSchema>
19
+ required?: string[]
20
+ items?: JsonSchema
21
+ additionalProperties?: boolean
22
+ minimum?: number
23
+ maximum?: number
24
+ format?: string
25
+ description?: string
26
+ $nodeRef?: unknown
27
+ $dataRef?: unknown
28
+ [k: string]: unknown
29
+ }
30
+
31
+ export type MethodInheritance = 'default' | 'abstract' | 'sealed'
32
+
33
+ export interface IrMethod {
34
+ name: string
35
+ params: Record<string, JsonSchema>
36
+ returns: JsonSchema
37
+ static: boolean
38
+ inheritance: MethodInheritance
39
+ }
40
+
41
+ export interface IrInterface {
42
+ type: 'interface'
43
+ name: string
44
+ extends?: string[]
45
+ properties: Record<string, JsonSchema>
46
+ methods: Record<string, IrMethod>
47
+ }
48
+
49
+ export interface IrEndpoint {
50
+ name: string
51
+ types: string[]
52
+ /** declared multiplicity for this end; max:null = unbounded ("many"). Absent ⇒ unconstrained. */
53
+ cardinality?: { min: number; max: number | null }
54
+ }
55
+
56
+ export interface IrClass {
57
+ type: 'node' | 'edge'
58
+ name: string
59
+ implements?: string[]
60
+ /** edges only: the two (source,target) endpoints with role names + allowed types */
61
+ endpoints?: IrEndpoint[]
62
+ properties: Record<string, JsonSchema>
63
+ methods: Record<string, IrMethod>
64
+ /** class-level icon: raw (lucide-style) SVG markup, `stroke="currentColor"` */
65
+ icon?: string
66
+ }
67
+
68
+ export interface IrImportDescriptor {
69
+ origin: string
70
+ definition: 'interface' | 'class'
71
+ }
72
+
73
+ export interface SchemaIR {
74
+ version: string
75
+ domain: string
76
+ types: Record<string, JsonSchema>
77
+ interfaces: Record<string, IrInterface>
78
+ classes: Record<string, IrClass>
79
+ imports: Record<string, IrImportDescriptor>
80
+ }
81
+
82
+ // ─────────────────────────── Studio schema overlay ───────────────────────────
83
+
84
+ export interface HandlerLink {
85
+ owner: string
86
+ ownerKind: 'class' | 'interface'
87
+ method: string
88
+ static: boolean
89
+ /** the runtime/index.ts wiring site */
90
+ wiringFile?: string
91
+ wiringLine?: number
92
+ /** the resolved handler file (followed from the `execute` import) */
93
+ handlerFile?: string
94
+ handlerLine?: number
95
+ /** kernel ops detected in the handler (::update, ::link, createNode, …) */
96
+ kernelCalls?: string[]
97
+ /** declared auth policy on the runtime config; absent ⇒ defaults to 'required'. */
98
+ auth?: 'public' | 'optional' | 'required'
99
+ /** authorize hook shape: 'absent' (none), 'noop' (allow-all), 'custom' (real check). */
100
+ authorize?: 'absent' | 'noop' | 'custom'
101
+ /** raw source of the authorize hook (for the hover preview). */
102
+ authorizeSnippet?: string
103
+ /** false ⇒ a todo()/NotImplemented stub → "contract-only" badge */
104
+ implemented: boolean
105
+ /** true ⇒ resolver could not link a file → "unlinked" badge */
106
+ unlinked?: boolean
107
+ }
108
+
109
+ export interface SourceSpan {
110
+ file: string
111
+ startLine: number
112
+ endLine: number
113
+ doc?: string
114
+ }
115
+
116
+ export interface SchemaAnnotation {
117
+ /** anchor ref key, e.g. 'class.Monitor.property.status' */
118
+ target: string
119
+ severity: 'warn' | 'info'
120
+ code: 'ENUM_DROPPED_BY_UPDATE' | 'COMPILE_ERROR' | 'EDGE_PROP_TYPE_MISSING'
121
+ message: string
122
+ }
123
+
124
+ export interface CrossDomainImport {
125
+ name: string
126
+ origin: string
127
+ definition: 'interface' | 'class'
128
+ }
129
+
130
+ export interface SchemaOverlay {
131
+ origin: string
132
+ /** defineDomain({ requires }) — runtime domain dependencies */
133
+ requires: string[]
134
+ /** non-kernel ir.imports → true cross-domain imports */
135
+ crossDomainImports: CrossDomainImport[]
136
+ /** kernel-origin ir.imports → mixin interfaces (collapsed chip) */
137
+ mixins: CrossDomainImport[]
138
+ postInstall?: string
139
+ handlerLinks: HandlerLink[]
140
+ /** keyed by anchor ref (class.X / class.X.property.y / class.X.method.m / edge.e / interface.I…) */
141
+ sourceSpans: Record<string, SourceSpan>
142
+ annotations: SchemaAnnotation[]
143
+ }
144
+
145
+ export interface BundleError {
146
+ message: string
147
+ file?: string
148
+ }
149
+
150
+ export interface StudioSchemaBundle {
151
+ domainId: string
152
+ schemaHash: string
153
+ extractedBy: 'runtime-bun' | 'static-tsmorph-fallback'
154
+ depsInstalled: boolean
155
+ ir: SchemaIR | null
156
+ overlay: SchemaOverlay
157
+ /**
158
+ * Member bodies of the interfaces this domain IMPORTS — kernel mixins (Named,
159
+ * Iconable, Timestamped…) and cross-domain interfaces (e.g. Notifiable). The
160
+ * serializer drops these from `ir` (they appear in `ir.imports` as
161
+ * {origin,definition} descriptors only), so the extractor re-serializes each
162
+ * imported schema to recover them. Keyed by interface name; the origin lives in
163
+ * `ir.imports[name]`. Lets the detail pane surface inherited members like
164
+ * Named→name without re-parsing the kernel. Empty when deps aren't installed.
165
+ */
166
+ importedInterfaces?: Record<string, IrInterface>
167
+ /** present when the runtime import failed to compile (render state, not a crash) */
168
+ error?: BundleError | null
169
+ extractedAt: string
170
+ }
171
+
172
+ // ─────────────────────────── Domain anatomy (non-schema) ───────────────────────────
173
+
174
+ export interface DomainOverview {
175
+ origin: string
176
+ adapter: 'astrale' | 'cloudflare' | 'unknown'
177
+ prodTarget?: string
178
+ devSecrets?: string
179
+ postInstall?: string
180
+ requires: string[]
181
+ packageName?: string
182
+ packageVersion?: string
183
+ astraleDeps: Record<string, string>
184
+ schemaDir: string
185
+ client?: string
186
+ }
187
+
188
+ export interface ViewInfo {
189
+ slug: string
190
+ kind: 'inline-html' | 'spa' | 'unknown'
191
+ auth?: string
192
+ mount?: string
193
+ url?: string
194
+ /** the class(es) this view binds to via `viewFor: selfOf(Class)` (DSL allows an array);
195
+ * absent ⇒ unbound/global */
196
+ viewFor?: string | string[]
197
+ file?: string
198
+ description?: string
199
+ }
200
+
201
+ /** Resolved LIVE serving URL of a view on its target instance (ground truth via `astrale get`). */
202
+ export interface ViewUrlResult {
203
+ status: 'installed' | 'not-installed' | 'unknown'
204
+ url?: string | null
205
+ }
206
+
207
+ /** A short-lived delegation token + context so the studio can act as the authenticated
208
+ * SHELL HOST for a live view preview (the parent hands this to the view iframe). */
209
+ export type PreviewToken =
210
+ | { status: 'ok'; token: string; expiresAt: number; kernelUrl: string; functionId: string }
211
+ | { status: 'unavailable'; reason: string }
212
+
213
+ export interface ClientFeature {
214
+ name: string
215
+ files: string[]
216
+ }
217
+
218
+ export interface ClientTree {
219
+ shell: string[]
220
+ features: ClientFeature[]
221
+ routes: Record<string, string>
222
+ present: boolean
223
+ }
224
+
225
+ export interface EnvField {
226
+ name: string
227
+ optional: boolean
228
+ doc?: string
229
+ secret?: boolean
230
+ }
231
+
232
+ export type EnvName = 'dev' | 'prod'
233
+
234
+ /** One env var in the editor: the env.ts contract side (declared/optional/doc)
235
+ * merged with the `.env.<env>` file side (value). A row may be declared-only
236
+ * (in env.ts, not yet in the file), set, or an orphan (in the file, not env.ts). */
237
+ export interface EnvVarRow {
238
+ name: string
239
+ /** current value from `.env.<env>` ('' when unset or empty) */
240
+ value: string
241
+ /** declared as a fillable (non-binding) field in env.ts's Env interface */
242
+ declared: boolean
243
+ /** env.ts marks it optional (`?`). Only required (!optional) + empty raises the signal. */
244
+ optional: boolean
245
+ /** JSDoc on the env.ts field — the inline hint */
246
+ doc?: string
247
+ }
248
+
249
+ /** The editable env for one env name (dev|prod): the `.env.<env>` file reconciled
250
+ * against env.ts. The GUI is the read-only studio's one sanctioned writer of a
251
+ * domain file (the explicit exception). */
252
+ export interface EnvFileModel {
253
+ env: EnvName
254
+ /** the secrets file, relative to the domain root (e.g. '.env.dev') */
255
+ file: string
256
+ /** wired via `astrale.config.ts` (`secrets: '.env.<env>'`)? false ⇒ the file is used
257
+ * by convention but the adapter isn't pointed at it (e.g. managed prod). */
258
+ configured: boolean
259
+ exists: boolean
260
+ adapter: DomainOverview['adapter']
261
+ rows: EnvVarRow[]
262
+ /** declared && required && empty — the count the "needs values" signal shows */
263
+ requiredMissing: number
264
+ }
265
+
266
+ export interface DomainAnatomy {
267
+ overview: DomainOverview
268
+ views: ViewInfo[]
269
+ client: ClientTree
270
+ env: EnvField[]
271
+ depsContainer?: string
272
+ /** shallow readdir of integrations/ top-level dir names — a hint, NOT a parse */
273
+ detectedIntegrations: string[]
274
+ }
275
+
276
+ // ─────────────────────────── Instance / deploy ───────────────────────────
277
+
278
+ /** A kernel instance the CLI knows about (a local bookmark or an admin-managed one). */
279
+ export interface InstanceInfo {
280
+ name: string
281
+ url: string
282
+ active: boolean
283
+ kind: 'bookmark' | 'managed'
284
+ }
285
+
286
+ /** Global (NOT per-domain) — the CLI's instances + which is active. Source of truth: `astrale instance list`. */
287
+ export interface InstancesState {
288
+ active: string | null
289
+ instances: InstanceInfo[]
290
+ }
291
+
292
+ /** What the studio recorded the last time IT ran a deploy for this domain. */
293
+ export interface DeployRecord {
294
+ at: string
295
+ schemaHash: string
296
+ ok: boolean
297
+ url?: string
298
+ }
299
+
300
+ /** Per-domain deploy/install state (the active instance is global — see InstancesState). */
301
+ export interface InstanceStatus {
302
+ /** the domain's configured deploy target (astrale.config.ts prod.instance) */
303
+ deployTarget: string | null
304
+ /** has a `prod` script → deployable via `pnpm prod` */
305
+ deployable: boolean
306
+ /** GROUND TRUTH — queried from the target instance (`astrale get /<origin>`), not local state.
307
+ * 'unknown' = the instance couldn't be reached/queried (no target, not authed, offline). */
308
+ install: 'installed' | 'not-installed' | 'unknown'
309
+ /** local schema vs the schema actually INSTALLED on the instance (ground truth) */
310
+ drift: 'in-sync' | 'drifted' | 'unknown'
311
+ localHash: string | null
312
+ /** hash of the schema installed on the instance (when reachable + installed) */
313
+ installedHash: string | null
314
+ /** the studio's own last deploy — supplementary (service URL / when), not the source of truth */
315
+ lastDeploy: DeployRecord | null
316
+ }
317
+
318
+ export interface DeployResult {
319
+ ok: boolean
320
+ url: string | null
321
+ output: string
322
+ }
323
+
324
+ // ─────────────────────────── Comments / open-questions ───────────────────────────
325
+
326
+ export type ThreadRole = 'user' | 'author'
327
+ export type ThreadEntryType = 'text' | 'choice'
328
+
329
+ export interface ThreadEntry {
330
+ id: string
331
+ role: ThreadRole
332
+ type: ThreadEntryType
333
+ text: string
334
+ options?: string[]
335
+ answer?: string | null
336
+ }
337
+
338
+ export type AnchorKind = 'schema' | 'section' | 'file' | 'free'
339
+
340
+ export interface AnchorRef {
341
+ ref: string
342
+ kind: AnchorKind
343
+ file?: string
344
+ startLine?: number
345
+ endLine?: number
346
+ label?: string
347
+ /** Flow-canvas coordinates for section-level canvas comments. */
348
+ x?: number
349
+ y?: number
350
+ }
351
+
352
+ export interface Comment {
353
+ id: string
354
+ /** annotate-compatible: array of string excerpts */
355
+ anchors: string[]
356
+ /** studio-only, index-aligned to `anchors` */
357
+ anchorRefs: AnchorRef[]
358
+ status: 'open' | 'closed'
359
+ closeNote?: string
360
+ thread: ThreadEntry[]
361
+ createdAt: string
362
+ /** derived: a thread whose first entry is role:'author' is a question */
363
+ kind: 'comment' | 'question'
364
+ /** set when the anchor ref no longer resolves against the current schema */
365
+ orphaned?: boolean
366
+ }
367
+
368
+ export interface CommentStore {
369
+ schemaVersion: string
370
+ comments: Comment[]
371
+ }
372
+
373
+ export interface MergeResult {
374
+ merged: number
375
+ closed: number
376
+ unknownIds: string[]
377
+ schemaMismatch: boolean
378
+ pastedSchemaVersion?: string
379
+ }
380
+
381
+ // ─────────────────────────── Change tracking ───────────────────────────
382
+
383
+ export type SchemaChangeKind =
384
+ | 'class-added'
385
+ | 'class-removed'
386
+ | 'interface-added'
387
+ | 'interface-removed'
388
+ | 'edge-added'
389
+ | 'edge-removed'
390
+ | 'prop-added'
391
+ | 'prop-removed'
392
+ | 'prop-type-changed'
393
+ | 'prop-required-changed'
394
+ | 'method-added'
395
+ | 'method-removed'
396
+ | 'method-signature-changed'
397
+
398
+ export interface SchemaChange {
399
+ kind: SchemaChangeKind
400
+ target: string
401
+ detail?: string
402
+ breaking: boolean
403
+ }
404
+
405
+ export interface FileChange {
406
+ file: string
407
+ status: 'added' | 'modified' | 'removed'
408
+ }
409
+
410
+ export interface ChangeSet {
411
+ source: 'baseline' | 'git'
412
+ hasGit: boolean
413
+ hasBaseline: boolean
414
+ schemaChanges: SchemaChange[]
415
+ fileChanges: FileChange[]
416
+ schemaDiffText?: string
417
+ classification: 'none' | 'additive' | 'breaking'
418
+ baselineCapturedAt?: string
419
+ }
420
+
421
+ // ─────────────────────────── Core (genesis) data ───────────────────────────
422
+ // A domain's `core` is its baked-in genesis graph: the concrete node/edge
423
+ // instances `defineCore(schema, { nodes, edges })` ships in the install bundle.
424
+ // Read-only here — the studio renders it and lets you comment to request changes.
425
+
426
+ /** One materialized node instance in a domain's core graph. */
427
+ export interface StudioCoreNode {
428
+ /** stable slash-path identity, e.g. `/big-shop.example.dev/core/brands/acme` (also the comment anchor key) */
429
+ path: string
430
+ /** the IR class this node instantiates (keys into StudioSchemaBundle.ir.classes) */
431
+ className: string
432
+ /** the node's property values, keyed by (possibly owner-qualified) property name */
433
+ data: Record<string, unknown>
434
+ /** parent node's `path`, when nested under another node (absent for roots) */
435
+ parent?: string
436
+ }
437
+
438
+ /** One materialized edge instance wiring two core nodes. */
439
+ export interface StudioCoreEdge {
440
+ /** source node `path` (or `self(ClassName)` for a meta-node endpoint) */
441
+ from: string
442
+ /** target node `path` (or `self(ClassName)`) */
443
+ to: string
444
+ /** the IR edge class name (keys into StudioSchemaBundle.ir.classes, type:'edge') */
445
+ edgeName: string
446
+ /** edge property values, when the edge class carries data */
447
+ data?: Record<string, unknown>
448
+ }
449
+
450
+ /** A domain's core data: the concrete genesis node/edge instance graph. */
451
+ export interface StudioCore {
452
+ /** the domain origin this core graph belongs to (mirrors SchemaIR.domain) */
453
+ domain: string
454
+ nodes: StudioCoreNode[]
455
+ edges: StudioCoreEdge[]
456
+ /** set when extraction failed; null when extraction succeeded (including "no core defined") */
457
+ error?: { message: string } | null
458
+ extractedAt: string
459
+ }
460
+
461
+ // ─────────────────────────── Context ───────────────────────────
462
+
463
+ export interface ContextItem {
464
+ id: string
465
+ bucket: 'user' | 'auto'
466
+ title: string
467
+ body: string
468
+ source?: string
469
+ updatedAt: string
470
+ /** auto items: included in the copy payload only when true */
471
+ includeInHandoff?: boolean
472
+ freshness?: string
473
+ }
474
+
475
+ export interface ContextStore {
476
+ user: ContextItem[]
477
+ auto: ContextItem[]
478
+ }
479
+
480
+ /** A document the user drops in as context for the AI agent. */
481
+ export interface DocMeta {
482
+ id: string
483
+ name: string
484
+ type: string
485
+ size: number
486
+ addedAt: string
487
+ updatedAt?: string
488
+ /** relative path under the dotted folder (server-side) */
489
+ stored: string
490
+ }
491
+
492
+ // ─────────────────────────── Integrations ───────────────────────────
493
+
494
+ export interface Integration {
495
+ id: string
496
+ name: string
497
+ kind: string
498
+ status: 'planned' | 'active' | 'deprecated' | string
499
+ notes?: string
500
+ }
501
+
502
+ export interface IntegrationsState {
503
+ integrations: Integration[]
504
+ detectedSubfolders: string[]
505
+ }
506
+
507
+ /** A domain in the catalog — local to this workspace, a faked external service, or the kernel. */
508
+ export interface DomainCatalogEntry {
509
+ origin: string
510
+ name: string
511
+ kind: 'kernel' | 'local' | 'external'
512
+ description: string
513
+ /** raw lucide-style SVG icon (stroke="currentColor") */
514
+ icon: string
515
+ /** kernel is always present + required */
516
+ required?: boolean
517
+ }
518
+
519
+ // ─────────────────────────── Domain summary / workspace ───────────────────────────
520
+
521
+ export interface DomainSummary {
522
+ id: string
523
+ origin: string
524
+ path: string
525
+ schemaDir: string
526
+ depsInstalled: boolean
527
+ hasGit: boolean
528
+ configFile: string
529
+ }
530
+
531
+ // ─────────────────────────── Copy payload ───────────────────────────
532
+
533
+ export interface CopyPayload {
534
+ markdown: string
535
+ openComments: number
536
+ }
537
+
538
+ // ─────────────────────────── Agent loop (live, harness-agnostic) ───────────────────────────
539
+
540
+ /** Kinds of activity the studio surfaces while a local agent runs a turn. */
541
+ export type AgentEventKind =
542
+ | 'status' // lifecycle / human note ("session started", "merged 2 replies")
543
+ | 'thinking' // assistant reasoning (summarized)
544
+ | 'message' // assistant prose addressed to the user
545
+ | 'tool' // a tool invocation (edit, bash, read, …)
546
+ | 'reply' // the agent posted a reply to a specific thread (via the bridge)
547
+ | 'error'
548
+
549
+ export interface AgentEvent {
550
+ id: string
551
+ ts: string
552
+ kind: AgentEventKind
553
+ text: string
554
+ /** for kind:'tool' — the tool name (Edit/Write/Bash/Read…) */
555
+ tool?: string
556
+ /** for kind:'tool' — a compact target (file path, command, pattern) */
557
+ target?: string
558
+ /** for kind:'reply' — the comment id the reply landed on */
559
+ commentId?: string
560
+ }
561
+
562
+ export const AGENT_EFFORT_LEVELS = ['low', 'medium', 'high', 'xhigh', 'max', 'ultracode'] as const
563
+ export type AgentEffort = (typeof AGENT_EFFORT_LEVELS)[number]
564
+
565
+ export interface AgentPromptSnapshot {
566
+ createdAt: string
567
+ /** appended to the harness default system prompt */
568
+ systemPrompt: string
569
+ /** piped to the harness on stdin */
570
+ turnPrompt: string
571
+ /** true when this turn started a new harness conversation */
572
+ firstTurn: boolean
573
+ /** true when this turn used a prior harness session id */
574
+ resumed: boolean
575
+ sessionId?: string
576
+ /** Claude Code --effort value used for this turn. */
577
+ effort?: AgentEffort
578
+ /** generated MCP bridge tools exposed to the harness for live write-back */
579
+ mcpTools: string[]
580
+ }
581
+
582
+ export interface AgentSystemPromptInfo {
583
+ bridge: boolean
584
+ systemPrompt: string
585
+ }
586
+
587
+ export type AgentRunStatus =
588
+ | 'queued'
589
+ | 'running'
590
+ | 'succeeded'
591
+ | 'failed'
592
+ | 'canceled'
593
+ /** the studio process died mid-turn (restart/crash) — the spawned agent went with
594
+ * it, but the CONVERSATION is preserved, so the next submit resumes it. */
595
+ | 'interrupted'
596
+
597
+ /** One agent turn for a domain: the live transcript + outcome. */
598
+ export interface AgentRun {
599
+ id: string
600
+ domainId: string
601
+ harness: string
602
+ status: AgentRunStatus
603
+ createdAt: string
604
+ finishedAt?: string
605
+ /** the harness session id (so the next turn resumes the same conversation) */
606
+ sessionId?: string
607
+ /** true ⇒ this turn resumed an existing conversation (vs. started a new one) */
608
+ resumed?: boolean
609
+ /** short human label of what this turn was asked to do */
610
+ summary: string
611
+ /** comment ids this turn was started to answer */
612
+ targetCommentIds: string[]
613
+ events: AgentEvent[]
614
+ costUsd?: number
615
+ numTurns?: number
616
+ /** total tokens processed this turn (input + output + cache) */
617
+ tokens?: number
618
+ error?: string
619
+ /** how many threads the agent answered live via the bridge tools this turn */
620
+ liveReplies?: number
621
+ /** result of merging the agent's final machine-state reply block, if any */
622
+ merge?: MergeResult
623
+ /** exact prompt inputs sent to the harness for this turn */
624
+ prompt?: AgentPromptSnapshot
625
+ }
626
+
627
+ /** The ongoing, resumable conversation for a domain — one continuous Claude Code
628
+ * session threaded across submits. Survives studio restarts (persisted on disk). */
629
+ export interface ConversationInfo {
630
+ /** a resumable session exists → the next submit continues it (vs. starting fresh) */
631
+ active: boolean
632
+ /** successful turns recorded in the current conversation */
633
+ turns: number
634
+ /** which harness the conversation belongs to (resume only applies within it) */
635
+ harness?: string
636
+ }
637
+
638
+ /** The agent's raw resumable session id, surfaced for manual view/edit in Settings. */
639
+ export interface AgentSessionInfo {
640
+ sessionId: string | null
641
+ turns: number
642
+ harness?: string
643
+ }
644
+
645
+ /** Whether the selected agent harness (e.g. Claude Code) is installed & invokable. */
646
+ export interface HarnessStatus {
647
+ id: string
648
+ label: string
649
+ /** the binary/command probed (e.g. 'claude') */
650
+ bin: string
651
+ ok: boolean
652
+ version?: string
653
+ /** human message — install / PATH guidance when not ok */
654
+ message: string
655
+ /** known harnesses for the selector (locked to one for now) */
656
+ options: { id: string; label: string }[]
657
+ }
658
+
659
+ /** One MCP server the harness loaded, with its live connection status. */
660
+ export interface McpServerInfo {
661
+ name: string
662
+ /** harness-reported status: 'connected' | 'needs-auth' | 'failed' | 'pending' | … */
663
+ status: string
664
+ }
665
+
666
+ /** One skill, reconciled between what is installed on disk and what the harness
667
+ * actually LOADED for this domain's cwd. `loaded:false` ⇒ installed but inactive
668
+ * here (e.g. a disabled plugin or a cwd-scoped skill) — the key diagnostic. */
669
+ export interface LoadoutSkill {
670
+ /** the slash-command the harness invokes it by — e.g. 'astrale-domain' or 'vercel:nextjs' */
671
+ command: string
672
+ /** display name from SKILL.md frontmatter (falls back to `command`) */
673
+ name: string
674
+ description?: string
675
+ /** where it lives on disk */
676
+ source: 'project' | 'user' | 'plugin'
677
+ /** the providing plugin, when source==='plugin' */
678
+ plugin?: string
679
+ /** present in the harness's loaded slash-commands for this cwd */
680
+ loaded: boolean
681
+ /** absolute path to the skill's SKILL.md (so the UI can show its content) */
682
+ path?: string
683
+ }
684
+
685
+ /** What the harness ACTUALLY loaded for a domain's cwd — read from the `system/init`
686
+ * event of a headless `claude` probe (the harness-authoritative source), then
687
+ * reconciled against on-disk skills. The studio's read-only window into its agent. */
688
+ export interface HarnessLoadout {
689
+ /** the probe ran and returned an init event */
690
+ ok: boolean
691
+ /** reason when !ok (binary missing / probe timed out / no init event) */
692
+ detail?: string
693
+ model?: string
694
+ permissionMode?: string
695
+ /** how the harness is authed: 'none' | 'ANTHROPIC_API_KEY' | … */
696
+ apiKeySource?: string
697
+ /** the cwd the harness was probed in (the domain root) */
698
+ cwd?: string
699
+ /** built-in tool names loaded (Read, Edit, Bash, …) */
700
+ tools: string[]
701
+ mcpServers: McpServerInfo[]
702
+ skills: LoadoutSkill[]
703
+ /** subagent types available to the harness (incl. plugin-provided) */
704
+ agents: string[]
705
+ /** count of loaded slash-commands that are built-in/harness commands, not skills */
706
+ builtinCommandCount: number
707
+ /** epoch ms when probed */
708
+ probedAt: number
709
+ }
710
+
711
+ /** Domain-attributable agent spend — accumulated from this studio's own runs on
712
+ * this domain (NOT machine-wide). Stored at `.domain-studio/usage.json`. */
713
+ export interface DomainUsage {
714
+ /** runs that reported usage (succeeded or not — a failed turn still costs) */
715
+ runs: number
716
+ /** cumulative tokens (input + output + cache) across those runs */
717
+ tokens: number
718
+ /** cumulative USD across those runs */
719
+ costUsd: number
720
+ lastRunAt?: string
721
+ lastTokens?: number
722
+ lastCostUsd?: number
723
+ }
724
+
725
+ /** Snapshot returned by GET /agent — drives the activity drawer. */
726
+ export interface AgentRunSnapshot {
727
+ harness: string
728
+ available: boolean
729
+ /** the active or most-recent run for this domain (null if none yet) */
730
+ run: AgentRun | null
731
+ /** the resumable conversation behind the runs (turns, whether one is live) */
732
+ conversation: ConversationInfo
733
+ }
734
+
735
+ // ─────────────────────────── SSE events ───────────────────────────
736
+
737
+ export type StudioEvent =
738
+ | { type: 'schema-diff'; domainId: string; schemaHash: string }
739
+ | { type: 'anatomy-diff'; domainId: string }
740
+ | { type: 'comments'; domainId: string }
741
+ | { type: 'compile-error'; domainId: string; message: string }
742
+ | { type: 'resolving'; domainId: string }
743
+ | { type: 'agent-run'; domainId: string; run: AgentRun }
744
+ | { type: 'agent-event'; domainId: string; runId: string; event: AgentEvent }
745
+ | { type: 'hello'; domains: string[] }
746
+ | { type: 'workspace'; domains: string[] }
747
+
748
+ // ─────────────────────────── Render helpers (client) ───────────────────────────
749
+
750
+ // ─────────────────────────── Graph layout ───────────────────────────
751
+
752
+ export interface NodePosition {
753
+ x: number
754
+ y: number
755
+ /** persisted size — only expanded module containers carry one. */
756
+ w?: number
757
+ h?: number
758
+ }
759
+ export interface LayoutState {
760
+ schemaHash?: string
761
+ positions: Record<string, NodePosition>
762
+ }
763
+
764
+ /** Persisted per-domain canvas visibility — the manual hide-set + the inherited-edge
765
+ * category toggle. Sibling of LayoutState: layout owns node POSITIONS, this owns what's SHOWN. */
766
+ export interface VisibilityState {
767
+ /** refs hidden on the canvas: `class.X` | `edge.X` | `domain.<origin>` (interfaces use
768
+ * `materializedInterfaces`, not this set — see below) */
769
+ hidden: Record<string, true>
770
+ showInheritedEdges: boolean
771
+ /** Local interfaces MATERIALIZED as canvas NODES (default = badge). Keyed by BARE interface
772
+ * name (e.g. `Fulfillable`) — a set distinct from `hidden`, so no ref namespace collision. */
773
+ materializedInterfaces: Record<string, true>
774
+ }
775
+
776
+ export type TypeDescriptor =
777
+ | { kind: 'string' | 'number' | 'integer' | 'boolean' | 'null' | 'unknown'; optional: boolean }
778
+ | { kind: 'enum'; values: (string | number | boolean | null)[]; optional: boolean }
779
+ | { kind: 'array'; items: TypeDescriptor; optional: boolean }
780
+ | {
781
+ kind: 'object'
782
+ fields: Record<string, TypeDescriptor>
783
+ required: string[]
784
+ optional: boolean
785
+ }
786
+ | { kind: 'ref'; target: string; refKind: 'node' | 'data'; optional: boolean }
787
+
788
+ // ─────────────────────────── Studio settings (power-user overrides) ───────────────────────────
789
+
790
+ /** Per-domain overrides for values the studio otherwise hard-codes. Stored at
791
+ * `.domain-studio/settings.json`; missing keys fall back to defaults. */
792
+ export interface StudioSettings {
793
+ /** Claude Code reasoning effort passed as --effort (default 'high') */
794
+ agentEffort: AgentEffort
795
+ /** folder under the domain root scanned for integrations (default 'integrations') */
796
+ integrationsDir: string
797
+ /** schema/core extraction subprocess timeout in ms (default 20000) */
798
+ introspectTimeoutMs: number
799
+ /** how often the per-domain instance status refreshes, ms (default 30000) */
800
+ instancePollMs: number
801
+ /** how often the studio re-checks for stale schema / updates, ms (default 600000) */
802
+ updatesPollMs: number
803
+ /** timeout when probing a live view URL from the instance, ms (default 8000) */
804
+ viewProbeTimeoutMs: number
805
+ }
806
+
807
+ // ─────────────────── Harness model gateway (custom LLM endpoint) ───────────────────
808
+
809
+ /**
810
+ * Point the agent harness (Claude Code) at a custom Anthropic-compatible model
811
+ * gateway — e.g. an Astrale `ai-gateway` model node — instead of its built-in
812
+ * auth. The values become env on the SPAWNED harness child only
813
+ * (ANTHROPIC_BASE_URL / ANTHROPIC_AUTH_TOKEN / ANTHROPIC_MODEL): the studio
814
+ * never writes them to your shell, your `~/.claude`, or a `claude` you run
815
+ * yourself outside the studio.
816
+ */
817
+ /**
818
+ * How the harness gets its bearer token for the gateway. Abstracted so the same
819
+ * gateway works whether the studio runs locally or embedded in the Astrale GUI:
820
+ * - `mint` — LOCAL: the studio mints a fresh, short-lived delegation token per
821
+ * run via `astrale token` (audience derived from the gateway URL).
822
+ * No secret is stored on disk; auth follows the active CLI identity.
823
+ * - `token` — MANUAL: a static bearer the user pastes (non-Astrale gateways, or
824
+ * an explicitly long-lived token).
825
+ * - `host` — EMBED: the embedding Astrale GUI supplies the token (via the shell)
826
+ * and owns mint / refresh / scope; the studio just relays it to the
827
+ * harness child. The forward-looking path for the iframe deployment.
828
+ */
829
+ export type HarnessGatewayAuth =
830
+ | { mode: 'mint'; instance?: string } // instance to mint on; omitted ⇒ the active one
831
+ | { mode: 'token'; token: string }
832
+ | { mode: 'host' }
833
+
834
+ export interface HarnessGatewayConfig {
835
+ /** master switch — off ⇒ the harness uses its own default auth */
836
+ enabled: boolean
837
+ /** ANTHROPIC_BASE_URL — Claude Code POSTs to `${baseUrl}/v1/messages`. For an
838
+ * Astrale model node: `https://<gateway-host>/v1/models/<modelNodeId>`. */
839
+ baseUrl: string
840
+ /** ANTHROPIC_MODEL label (cosmetic — the gateway pins the real model by URL). */
841
+ model?: string
842
+ /** how the bearer token is obtained (see HarnessGatewayAuth) */
843
+ auth: HarnessGatewayAuth
844
+ }
845
+
846
+ /** The layered harness-gateway config for a domain: its own per-domain override,
847
+ * the studio-wide global default, and which one actually takes effect. */
848
+ export interface HarnessGatewayState {
849
+ /** per-domain override (`.domain-studio/harness-gateway.json`); null ⇒ inherits global */
850
+ local: HarnessGatewayConfig | null
851
+ /** studio-wide default — applies to every domain that has no local override */
852
+ global: HarnessGatewayConfig | null
853
+ /** the config that actually takes effect (an enabled local ∨ an enabled global), else null */
854
+ effective: HarnessGatewayConfig | null
855
+ /** which layer `effective` came from */
856
+ source: 'domain' | 'global' | 'none'
857
+ }
858
+
859
+ // ─────────────────────────── Update staleness (the header "Update" badge) ───────────────────────────
860
+
861
+ /** The Astrale CLI's own staleness report — `astrale update --check --json`, run
862
+ * in the domain root. The CLI owns the detection; the studio only renders it.
863
+ * Skills are intentionally absent: they ride along with `astrale update`, so a
864
+ * stale CLI or SDK dep is the only thing worth surfacing. */
865
+ export interface StaleReport {
866
+ stale: boolean
867
+ cli: { stale: boolean; managed: boolean; current?: string; latest?: string; channel?: string }
868
+ sdk: {
869
+ stale: boolean
870
+ inProject: boolean
871
+ outdated: { pkg: string; current: string; latest: string }[]
872
+ }
873
+ }