@astrale-os/cli 1.0.0-beta.0 → 1.0.0-beta.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.
- package/README.md +23 -12
- package/dist/astrale.js +51494 -56307
- package/dist/public/connect-core.js +2028 -26846
- package/dist/public/keys/index.js +1755 -40694
- package/dist/public/paths/index.js +859 -39518
- package/dist/types/admin/binding.d.ts +19 -0
- package/dist/types/admin/instance/client.d.ts +22 -0
- package/dist/types/admin/instance/index.d.ts +2 -0
- package/dist/types/admin/instance/model.d.ts +5 -12
- package/dist/types/connection/auth.d.ts +3 -0
- package/dist/types/connection/call.d.ts +3 -0
- package/dist/types/connection/command.d.ts +27 -0
- package/dist/types/connection/credential.d.ts +14 -0
- package/dist/types/connection/errors.d.ts +1 -0
- package/dist/types/connection/exchange.d.ts +9 -0
- package/dist/types/connection/failure/classify.d.ts +2 -0
- package/dist/types/connection/failure/index.d.ts +5 -0
- package/dist/types/connection/failure/model.d.ts +25 -0
- package/dist/types/connection/failure/render.d.ts +3 -0
- package/dist/types/connection/index.d.ts +8 -0
- package/dist/types/connection/reasons.d.ts +37 -0
- package/dist/types/connection/self.d.ts +37 -0
- package/dist/types/connection/session.d.ts +31 -0
- package/dist/types/connection/target.d.ts +38 -0
- package/dist/types/errors.d.ts +1 -1
- package/dist/types/lib/admin-instance.d.ts +17 -0
- package/dist/types/lib/admin-target.d.ts +6 -2
- package/dist/types/lib/failure-debug.d.ts +1 -0
- package/dist/types/lib/idp.d.ts +7 -0
- package/dist/types/lib/instance-target.d.ts +1 -1
- package/dist/types/lib/instance.d.ts +10 -2
- package/dist/types/lib/log.d.ts +5 -3
- package/dist/types/lib/meta.d.ts +2 -2
- package/dist/types/lib/proc.d.ts +43 -0
- package/dist/types/lib/self.d.ts +9 -0
- package/dist/types/lib/update.d.ts +131 -0
- package/package.json +8 -10
- package/src/__tests__/fixtures/publication.ts +20 -0
- package/src/admin/__tests__/binding.test.ts +31 -0
- package/src/admin/__tests__/fixture.ts +101 -0
- package/src/admin/binding.ts +98 -0
- package/src/admin/catalog/.spec/api.d.ts +2 -2
- package/src/admin/catalog/__tests__/client.test.ts +20 -49
- package/src/admin/catalog/client.ts +36 -32
- package/src/admin/catalog/model.ts +3 -4
- package/src/admin/graph/.spec/api.d.ts +4 -10
- package/src/admin/graph/nodes.ts +1 -1
- package/src/admin/instance/.spec/api.d.ts +2 -2
- package/src/admin/instance/__tests__/client.test.ts +40 -163
- package/src/admin/instance/client.ts +35 -137
- package/src/admin/instance/index.ts +1 -2
- package/src/admin/instance/model.ts +11 -20
- package/src/commands/__tests__/admin-instance.test.ts +11 -8
- package/src/commands/__tests__/auth-login.test.ts +54 -1
- package/src/commands/__tests__/call-describe.test.ts +46 -33
- package/src/commands/__tests__/call.test.ts +5 -1
- package/src/commands/__tests__/domain-install-operation.test.ts +12 -7
- package/src/commands/__tests__/domain-install-owned.test.ts +6 -2
- package/src/commands/__tests__/domain-list.test.ts +63 -4
- package/src/commands/__tests__/install-identity-override.test.ts +19 -19
- package/src/commands/__tests__/instance-list-rows.test.ts +1 -1
- package/src/commands/__tests__/instance-status.test.ts +139 -0
- package/src/commands/__tests__/read-commands.test.ts +35 -10
- package/src/commands/__tests__/studio-descriptor.test.ts +22 -0
- package/src/commands/__tests__/update.test.ts +95 -0
- package/src/commands/call-describe.ts +60 -68
- package/src/commands/call.ts +43 -56
- package/src/commands/domain/install.ts +13 -17
- package/src/commands/domain/list.ts +18 -13
- package/src/commands/domain/publish.ts +14 -11
- package/src/commands/domain/uninstall.ts +2 -2
- package/src/commands/get.ts +14 -15
- package/src/commands/identity/__tests__/register.test.ts +3 -3
- package/src/commands/identity/register.ts +5 -4
- package/src/commands/instance/create.ts +8 -16
- package/src/commands/instance/delete.ts +12 -9
- package/src/commands/instance/list.ts +2 -2
- package/src/commands/instance/status.ts +66 -20
- package/src/commands/instance/use.ts +16 -12
- package/src/commands/introspect.ts +9 -16
- package/src/commands/logs.ts +24 -25
- package/src/commands/mutate.ts +6 -5
- package/src/commands/query.ts +27 -30
- package/src/commands/studio.ts +20 -0
- package/src/commands/token.ts +8 -7
- package/src/commands/update.ts +68 -22
- package/src/commands/view.ts +7 -6
- package/src/connection/.spec/api.d.ts +7 -8
- package/src/connection/.spec/architecture.md +5 -4
- package/src/connection/.spec/flows/session.ts +2 -2
- package/src/connection/.spec/laws/connection.ts +6 -2
- package/src/connection/.spec/layout.ts +1 -0
- package/src/connection/__tests__/credential.test.ts +20 -1
- package/src/connection/__tests__/errors.test.ts +43 -32
- package/src/connection/__tests__/exchange.test.ts +36 -6
- package/src/connection/__tests__/failure-fixtures.ts +39 -0
- package/src/connection/__tests__/failure-safety.test.ts +75 -0
- package/src/connection/__tests__/self.test.ts +31 -11
- package/src/connection/__tests__/session.test.ts +3 -3
- package/src/connection/__tests__/target.test.ts +1 -0
- package/src/connection/auth.ts +13 -0
- package/src/connection/call.ts +1 -1
- package/src/connection/credential.ts +2 -2
- package/src/connection/errors.ts +1 -355
- package/src/connection/exchange.ts +52 -27
- package/src/connection/failure/classify.ts +110 -0
- package/src/connection/failure/index.ts +18 -0
- package/src/connection/failure/model.ts +28 -0
- package/src/connection/failure/render.ts +116 -0
- package/src/connection/reasons.ts +16 -0
- package/src/connection/self.ts +39 -21
- package/src/connection/session.ts +19 -8
- package/src/errors.ts +3 -2
- package/src/graph/.spec/api.d.ts +12 -5
- package/src/graph/.spec/layout.ts +9 -1
- package/src/graph/__tests__/mutation.test.ts +2 -2
- package/src/graph/__tests__/query.test.ts +25 -6
- package/src/graph/class.ts +30 -0
- package/src/graph/index.ts +1 -0
- package/src/graph/query.ts +21 -32
- package/src/identity/.spec/api.d.ts +2 -2
- package/src/identity/__tests__/registration.test.ts +1 -1
- package/src/identity/registration.ts +3 -3
- package/src/keys/__tests__/keys.test.ts +4 -0
- package/src/keys/credential.ts +1 -0
- package/src/lib/__tests__/admin-target.test.ts +12 -0
- package/src/lib/__tests__/command-dx.test.ts +1 -1
- package/src/lib/__tests__/domain-publication.test.ts +138 -0
- package/src/lib/__tests__/idp-session.test.ts +22 -0
- package/src/lib/__tests__/idp.test.ts +36 -4
- package/src/lib/__tests__/instance-candidates.test.ts +1 -3
- package/src/lib/__tests__/instance-target.test.ts +7 -39
- package/src/lib/__tests__/instance.test.ts +6 -0
- package/src/lib/__tests__/log-errors.test.ts +64 -0
- package/src/lib/__tests__/studio-server-deps.test.ts +53 -16
- package/src/lib/__tests__/update.test.ts +113 -8
- package/src/lib/__tests__/view-open-intent.test.ts +0 -2
- package/src/lib/__tests__/view-server.test.ts +1 -1
- package/src/lib/__tests__/view-session.test.ts +1 -1
- package/src/lib/admin-instance.ts +14 -8
- package/src/lib/admin-target.ts +21 -2
- package/src/lib/command-dx.ts +9 -9
- package/src/lib/domain-publication.ts +104 -0
- package/src/lib/failure-debug.ts +26 -0
- package/src/lib/idp.ts +39 -4
- package/src/lib/instance-candidates.ts +2 -2
- package/src/lib/instance-target.ts +3 -14
- package/src/lib/instance.ts +22 -12
- package/src/lib/log.ts +16 -9
- package/src/lib/login-flow.ts +13 -3
- package/src/lib/meta.ts +2 -2
- package/src/lib/provision-instance.ts +34 -57
- package/src/lib/update.ts +124 -30
- package/src/lib/validation.ts +2 -2
- package/src/lib/view/resolve.ts +1 -1
- package/src/program/__tests__/program.test.ts +20 -4
- package/src/program/build.ts +2 -2
- package/src/setup/__tests__/instance-step.test.ts +27 -10
- package/src/setup/steps/instance.ts +22 -15
- package/studio/client/dist/assets/{elk-api-D2xgMJvi.js → elk-api-ByzoDZWH.js} +1 -1
- package/studio/client/dist/assets/index-B0RCgUOT.js +8 -0
- package/studio/client/dist/assets/index-BiGR8Oo9.css +1 -0
- package/studio/client/dist/assets/index-N8J8EKlB.js +81 -0
- package/studio/client/dist/index.html +2 -2
- package/studio/package.json +5 -10
- package/studio/server/agent/ask.test.ts +1 -1
- package/studio/server/agent/bridge/grant.test.ts +1 -1
- package/studio/server/agent/bridge/routes.test.ts +1 -1
- package/studio/server/agent/bridge/stdio.ts +8 -1
- package/studio/server/agent/conversation.test.ts +10 -5
- package/studio/server/agent/conversation.ts +56 -8
- package/studio/server/agent/harness/claude/adapter.test.ts +15 -5
- package/studio/server/agent/harness/codex/loadout.ts +18 -21
- package/studio/server/agent/harness/gateway/config.ts +20 -18
- package/studio/server/agent/harness/selection.ts +9 -1
- package/studio/server/agent/prompts/anchors.test.ts +32 -36
- package/studio/server/agent/prompts/anchors.ts +20 -49
- package/studio/server/agent/prompts/system.test.ts +3 -2
- package/studio/server/agent/prompts/system.ts +6 -5
- package/studio/server/agent/prompts/turn.ts +13 -4
- package/studio/server/agent/routes.test.ts +1 -1
- package/studio/server/agent/run/completion.ts +2 -2
- package/studio/server/agent/run/coordinator.test.ts +1 -1
- package/studio/server/agent/run/preparation.ts +6 -5
- package/studio/server/agent/run/transcript.test.ts +35 -1
- package/studio/server/agent/run/transcript.ts +169 -3
- package/studio/server/agent/run/usage.test.ts +24 -0
- package/studio/server/agent/run/usage.ts +26 -1
- package/studio/server/api/canvas.ts +43 -0
- package/studio/server/api/comments.ts +85 -0
- package/studio/server/api/context.ts +61 -0
- package/studio/server/api/deployment.ts +39 -0
- package/studio/server/api/documents.ts +55 -0
- package/studio/server/api/domain.ts +59 -0
- package/studio/server/api/http.ts +45 -0
- package/studio/server/api/project.ts +57 -0
- package/studio/server/api/schema.ts +12 -0
- package/studio/server/api/views.ts +57 -0
- package/studio/server/api/workspace.ts +60 -0
- package/studio/server/api-agent-loadout.test.ts +1 -1
- package/studio/server/api.test.ts +147 -0
- package/studio/server/api.ts +20 -422
- package/studio/server/cache.test.ts +122 -0
- package/studio/server/cache.ts +162 -11
- package/studio/server/cli-consumers.test.ts +494 -0
- package/studio/server/cli.test.ts +88 -0
- package/studio/server/cli.ts +240 -0
- package/studio/server/client-package.ts +25 -1
- package/studio/server/domain.test.ts +50 -40
- package/studio/server/domain.ts +83 -52
- package/studio/server/environment/dotenv-preview.test.ts +39 -0
- package/studio/server/environment/dotenv-preview.ts +27 -0
- package/studio/server/environment/files.test.ts +53 -0
- package/studio/server/{state/env.ts → environment/files.ts} +7 -48
- package/studio/server/{state → handoff}/copy.ts +21 -6
- package/studio/server/handoff/service.test.ts +22 -0
- package/studio/server/{state/handoff.ts → handoff/service.ts} +21 -14
- package/studio/server/index.ts +1 -1
- package/studio/server/instances/active.ts +72 -0
- package/studio/server/instances/deploy-record.test.ts +31 -0
- package/studio/server/instances/deploy-record.ts +37 -0
- package/studio/server/instances/deploy.ts +56 -0
- package/studio/server/{state/instance.test.ts → instances/probe.test.ts} +25 -21
- package/studio/server/instances/probe.ts +67 -0
- package/studio/server/instances/status.test.ts +12 -0
- package/studio/server/instances/status.ts +49 -0
- package/studio/server/introspect/anatomy/client-tree.ts +57 -0
- package/studio/server/introspect/anatomy/env-fields.ts +66 -0
- package/studio/server/introspect/anatomy/schema-definition.ts +53 -0
- package/studio/server/introspect/anatomy/source.ts +181 -0
- package/studio/server/introspect/anatomy/views/routes.ts +114 -0
- package/studio/server/introspect/anatomy/views.ts +53 -0
- package/studio/server/introspect/anatomy-extras.test.ts +106 -35
- package/studio/server/introspect/anatomy-extras.ts +7 -726
- package/studio/server/introspect/anatomy.test.ts +22 -14
- package/studio/server/introspect/anatomy.ts +52 -28
- package/studio/server/introspect/bundle.ts +15 -9
- package/studio/server/introspect/canonical-schema.test.ts +93 -309
- package/studio/server/introspect/canonical-schema.ts +257 -425
- package/studio/server/introspect/config-preview.test.ts +32 -0
- package/studio/server/introspect/config-preview.ts +119 -0
- package/studio/server/introspect/core-extractor.ts +18 -127
- package/studio/server/introspect/core.ts +1 -2
- package/studio/server/introspect/diff.test.ts +18 -18
- package/studio/server/introspect/diff.ts +44 -90
- package/studio/server/introspect/extractor.ts +23 -116
- package/studio/server/introspect/hash.ts +5 -2
- package/studio/server/introspect/overlay-tsmorph.test.ts +119 -240
- package/studio/server/introspect/overlay-tsmorph.ts +7 -1305
- package/studio/server/introspect/overlay.test.ts +26 -54
- package/studio/server/introspect/overlay.ts +10 -37
- package/studio/server/introspect/revision.test.ts +54 -0
- package/studio/server/introspect/revision.ts +49 -0
- package/studio/server/introspect/runtime.test.ts +83 -143
- package/studio/server/introspect/runtime.ts +40 -11
- package/studio/server/introspect/schema-ir-json.test.ts +70 -0
- package/studio/server/introspect/schema-ir-json.ts +151 -0
- package/studio/server/introspect/schema-refs.test.ts +43 -88
- package/studio/server/introspect/schema-refs.ts +14 -49
- package/studio/server/introspect/source-overlay/annotations.ts +14 -0
- package/studio/server/introspect/source-overlay/handlers.ts +161 -0
- package/studio/server/introspect/source-overlay/kernel-calls.ts +46 -0
- package/studio/server/introspect/source-overlay/project.ts +248 -0
- package/studio/server/introspect/source-overlay/spans.ts +330 -0
- package/studio/server/json.ts +46 -0
- package/studio/server/lifecycle.ts +5 -1
- package/studio/server/sse.test.ts +26 -0
- package/studio/server/sse.ts +4 -1
- package/studio/server/state/baseline.test.ts +223 -7
- package/studio/server/state/baseline.ts +148 -34
- package/studio/server/state/comments.test.ts +66 -1
- package/studio/server/state/comments.ts +173 -10
- package/studio/server/state/context.ts +34 -2
- package/studio/server/state/documents.ts +42 -1
- package/studio/server/state/integrations.ts +27 -1
- package/studio/server/state/layout.test.ts +34 -0
- package/studio/server/state/layout.ts +44 -17
- package/studio/server/state/settings.ts +29 -22
- package/studio/server/state/store.test.ts +122 -0
- package/studio/server/state/store.ts +66 -17
- package/studio/server/state/visibility.ts +22 -11
- package/studio/server/views/model.test.ts +57 -0
- package/studio/server/views/model.ts +66 -0
- package/studio/server/views/runtime.ts +40 -0
- package/studio/server/views/selection-repository.test.ts +31 -0
- package/studio/server/views/selection-repository.ts +60 -0
- package/studio/server/views/session.test.ts +73 -0
- package/studio/server/views/session.ts +166 -0
- package/studio/server/views/target.test.ts +78 -0
- package/studio/server/views/target.ts +222 -0
- package/studio/server/watch.test.ts +11 -11
- package/studio/server/watch.ts +9 -5
- package/studio/server/workspace/catalog.test.ts +17 -0
- package/studio/server/workspace/catalog.ts +43 -0
- package/studio/server/{state → workspace}/create.test.ts +1 -1
- package/studio/server/{state → workspace}/create.ts +1 -1
- package/studio/server/{state → workspace}/git.ts +1 -1
- package/studio/server/workspace/updates.ts +83 -0
- package/studio/server/workspace-state.ts +1 -1
- package/studio/server/workspace-watch.ts +2 -2
- package/studio/shared/contracts/README.md +16 -0
- package/studio/shared/contracts/agent.ts +270 -0
- package/studio/shared/contracts/runtime.ts +50 -0
- package/studio/shared/contracts/schema.ts +393 -0
- package/studio/shared/contracts/surface.test.ts +317 -0
- package/studio/shared/contracts/workspace.ts +265 -0
- package/studio/shared/layout.test.ts +116 -0
- package/studio/shared/schema/identity.test.ts +34 -0
- package/studio/shared/schema/identity.ts +68 -0
- package/studio/shared/types.ts +11 -1099
- package/studio/tsconfig.json +1 -1
- package/viewer/dist/main.js +40 -61
- package/.check-workspace.cjs +0 -40
- package/src/commands/__tests__/instance-create-hosts.test.ts +0 -29
- package/studio/client/dist/assets/index-BMdnsIJA.css +0 -1
- package/studio/client/dist/assets/index-D-vRV8w7.js +0 -8
- package/studio/client/dist/assets/index-LGSWRrk8.js +0 -81
- package/studio/server/state/catalog.ts +0 -117
- package/studio/server/state/instance.ts +0 -280
- package/studio/server/state/updates.ts +0 -63
- package/studio/server/state/views.test.ts +0 -286
- package/studio/server/state/views.ts +0 -535
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared schema and introspection contracts.
|
|
3
|
+
*
|
|
4
|
+
* Studio's IR remains a lossy render projection. Canonical reference identity
|
|
5
|
+
* is owned by `../schema/identity`; SDK admission owns schema semantics.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { IrClassKey, IrClassRef, IrSchemaRef } from '../schema/identity'
|
|
9
|
+
|
|
10
|
+
export interface JsonSchema {
|
|
11
|
+
type?: string | string[]
|
|
12
|
+
enum?: (string | number | boolean | null)[]
|
|
13
|
+
properties?: Record<string, JsonSchema>
|
|
14
|
+
required?: string[]
|
|
15
|
+
items?: JsonSchema
|
|
16
|
+
additionalProperties?: boolean
|
|
17
|
+
minimum?: number
|
|
18
|
+
maximum?: number
|
|
19
|
+
format?: string
|
|
20
|
+
description?: string
|
|
21
|
+
$nodeRef?: unknown
|
|
22
|
+
$dataRef?: unknown
|
|
23
|
+
[k: string]: unknown
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type MethodInheritance = 'default' | 'abstract' | 'sealed'
|
|
27
|
+
|
|
28
|
+
/** Portable form of the DSL-owned canonical schema revision. */
|
|
29
|
+
export type SchemaRevision = `sha256:${string}`
|
|
30
|
+
|
|
31
|
+
/** Wire-format check only; SDK admission remains the semantic proof. */
|
|
32
|
+
export function isSchemaRevision(value: unknown): value is SchemaRevision {
|
|
33
|
+
return typeof value === 'string' && /^sha256:[0-9a-f]{64}$/.test(value)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Version of Studio's lossy canonical-schema -> render-IR projection. Persisted
|
|
38
|
+
* baselines must match this value before their IR can be compared.
|
|
39
|
+
*/
|
|
40
|
+
export const STUDIO_SCHEMA_PROJECTION_VERSION = 1
|
|
41
|
+
|
|
42
|
+
export type IrCallableAuth = 'anonymous' | 'authenticated' | 'authorized'
|
|
43
|
+
|
|
44
|
+
export type IrCallableOutput =
|
|
45
|
+
| { mode: 'value'; schema: JsonSchema }
|
|
46
|
+
| { mode: 'stream'; item: JsonSchema }
|
|
47
|
+
| { mode: 'binary' }
|
|
48
|
+
|
|
49
|
+
export interface IrCallable {
|
|
50
|
+
name: string
|
|
51
|
+
input: JsonSchema
|
|
52
|
+
output: IrCallableOutput
|
|
53
|
+
description?: string
|
|
54
|
+
auth?: IrCallableAuth
|
|
55
|
+
policy?: unknown
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface IrMethod extends IrCallable {
|
|
59
|
+
static: boolean
|
|
60
|
+
inheritance: MethodInheritance
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Standalone DSL callable projected without inventing a receiver. */
|
|
64
|
+
export interface IrFunction extends IrCallable {}
|
|
65
|
+
|
|
66
|
+
export interface IrEndpoint {
|
|
67
|
+
name: string
|
|
68
|
+
types: string[]
|
|
69
|
+
/** Exact accepted Definition coordinates. `types` is the local display projection. */
|
|
70
|
+
refs?: IrSchemaRef[]
|
|
71
|
+
/** declared multiplicity for this end; max:null = unbounded ("many"). Absent ⇒ unconstrained. */
|
|
72
|
+
cardinality?: { min: number; max: number | null }
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface IrClass {
|
|
76
|
+
type: 'node' | 'edge'
|
|
77
|
+
name: string
|
|
78
|
+
origin: string
|
|
79
|
+
ref: IrClassRef
|
|
80
|
+
extends?: string[]
|
|
81
|
+
extendsRefs?: IrClassRef[]
|
|
82
|
+
/** edges only: the two (source,target) endpoints with role names + allowed types */
|
|
83
|
+
endpoints?: IrEndpoint[]
|
|
84
|
+
orientation?: 'directed' | 'undirected'
|
|
85
|
+
constraints?: { noSelf?: true; acyclic?: true }
|
|
86
|
+
properties: Record<string, JsonSchema>
|
|
87
|
+
/** Canonical `properties.required`; absence, unlike null, encodes optionality. */
|
|
88
|
+
required?: string[]
|
|
89
|
+
methods: Record<string, IrMethod>
|
|
90
|
+
/** class-level icon: raw (lucide-style) SVG markup, `stroke="currentColor"` */
|
|
91
|
+
icon?: string
|
|
92
|
+
description?: string
|
|
93
|
+
propertyMetadata?: Record<string, unknown>
|
|
94
|
+
/** Canonical data declaration; `mediaType` is required and extensions stay lossless. */
|
|
95
|
+
data?: { mediaType: string; [key: string]: unknown }
|
|
96
|
+
policies?: Record<string, IrSchemaRef>
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface IrImportDescriptor {
|
|
100
|
+
origin: string
|
|
101
|
+
ref: IrClassRef
|
|
102
|
+
key: IrClassKey
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export type IrViewTarget = { kind: 'domain' } | { kind: 'definition'; definitions: IrSchemaRef[] }
|
|
106
|
+
|
|
107
|
+
export interface IrView {
|
|
108
|
+
name: string
|
|
109
|
+
target: IrViewTarget
|
|
110
|
+
auth: 'required' | 'optional' | 'public'
|
|
111
|
+
description?: string
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export interface SchemaIR {
|
|
115
|
+
format: 'astrale.dsl'
|
|
116
|
+
version: string
|
|
117
|
+
domain: string
|
|
118
|
+
classes: Record<string, IrClass>
|
|
119
|
+
importsByKey: Record<IrClassKey, IrImportDescriptor>
|
|
120
|
+
importedClassesByKey: Record<IrClassKey, IrClass>
|
|
121
|
+
functions: Record<string, IrFunction>
|
|
122
|
+
views: Record<string, IrView>
|
|
123
|
+
policies: Record<string, unknown>
|
|
124
|
+
dependencies: Array<{ origin: string; revision: string }>
|
|
125
|
+
core: unknown
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export interface HandlerLink {
|
|
129
|
+
owner: string
|
|
130
|
+
ownerKind: 'class' | 'function'
|
|
131
|
+
kind: 'action' | 'workflow'
|
|
132
|
+
method: string
|
|
133
|
+
static: boolean
|
|
134
|
+
/** The Runtime registry or one modular Action/Workflow file. */
|
|
135
|
+
wiringFile?: string
|
|
136
|
+
wiringLine?: number
|
|
137
|
+
/** the resolved handler file (followed from the `execute` import) */
|
|
138
|
+
handlerFile?: string
|
|
139
|
+
handlerLine?: number
|
|
140
|
+
/** kernel ops detected in the handler (::update, ::link, createNode, …) */
|
|
141
|
+
kernelCalls?: string[]
|
|
142
|
+
/** false ⇒ a todo()/NotImplemented stub → "contract-only" badge */
|
|
143
|
+
implemented: boolean
|
|
144
|
+
/** true ⇒ resolver could not link a file → "unlinked" badge */
|
|
145
|
+
unlinked?: boolean
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export interface SourceSpan {
|
|
149
|
+
file: string
|
|
150
|
+
startLine: number
|
|
151
|
+
endLine: number
|
|
152
|
+
doc?: string
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export interface SchemaAnnotation {
|
|
156
|
+
/** anchor ref key, e.g. 'class.Monitor.property.status' */
|
|
157
|
+
target: string
|
|
158
|
+
severity: 'warn' | 'info'
|
|
159
|
+
code: 'COMPILE_ERROR' | 'EDGE_PROP_TYPE_MISSING'
|
|
160
|
+
message: string
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export interface CrossDomainImport {
|
|
164
|
+
name: string
|
|
165
|
+
origin: string
|
|
166
|
+
ref: IrClassRef
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export interface SchemaOverlay {
|
|
170
|
+
origin: string
|
|
171
|
+
/** Exact declared Domain dependencies. */
|
|
172
|
+
requires: string[]
|
|
173
|
+
/** Non-Kernel imported Classes. */
|
|
174
|
+
crossDomainImports: CrossDomainImport[]
|
|
175
|
+
/** Kernel base Classes rendered as inherited platform facts. */
|
|
176
|
+
mixins: CrossDomainImport[]
|
|
177
|
+
handlerLinks: HandlerLink[]
|
|
178
|
+
/** Keyed by Class, Property, Method, Function, Policy, View, or Core anchor. */
|
|
179
|
+
sourceSpans: Record<string, SourceSpan>
|
|
180
|
+
annotations: SchemaAnnotation[]
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export interface BundleError {
|
|
184
|
+
message: string
|
|
185
|
+
file?: string
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export interface StudioSchemaBundle {
|
|
189
|
+
domainId: string
|
|
190
|
+
/** UI/cache identity only. This is not the DSL schema revision. */
|
|
191
|
+
renderFingerprint: string
|
|
192
|
+
schemaMode: 'canonical-admitted' | 'canonical-preview' | 'unavailable'
|
|
193
|
+
/** DSL-owned revision, present only after installed SDK admission. */
|
|
194
|
+
schemaRevision?: SchemaRevision
|
|
195
|
+
extractedBy: 'runtime-bun' | 'static-tsmorph-fallback'
|
|
196
|
+
depsInstalled: boolean
|
|
197
|
+
ir: SchemaIR | null
|
|
198
|
+
/**
|
|
199
|
+
* Portable canonical V1 document emitted by the domain. `schemaMode` records
|
|
200
|
+
* whether the installed SDK admitted it or Studio is rendering it as a structural
|
|
201
|
+
* preview. Kept separate from the lossy render IR.
|
|
202
|
+
*/
|
|
203
|
+
schemaRoot?: unknown
|
|
204
|
+
overlay: SchemaOverlay
|
|
205
|
+
/** present when the runtime import failed to compile (render state, not a crash) */
|
|
206
|
+
error?: BundleError | null
|
|
207
|
+
extractedAt: string
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export interface DomainOverview {
|
|
211
|
+
origin: string
|
|
212
|
+
/** Active SDK Application entry relative to the project root. */
|
|
213
|
+
applicationFile?: string
|
|
214
|
+
adapter: 'astrale' | 'cloudflare' | 'unknown'
|
|
215
|
+
prodTarget?: string
|
|
216
|
+
devSecrets?: string
|
|
217
|
+
requires: string[]
|
|
218
|
+
packageName?: string
|
|
219
|
+
packageVersion?: string
|
|
220
|
+
astraleDeps: Record<string, string>
|
|
221
|
+
schemaDir: string
|
|
222
|
+
client?: string
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export interface ViewInfo {
|
|
226
|
+
slug: string
|
|
227
|
+
kind: 'inline-html' | 'spa' | 'unknown'
|
|
228
|
+
auth?: string
|
|
229
|
+
mount?: string
|
|
230
|
+
url?: string
|
|
231
|
+
/** the class(es) this view binds to via `viewFor: selfOf(Class)` (DSL allows an array);
|
|
232
|
+
* absent ⇒ unbound/global */
|
|
233
|
+
viewFor?: string | string[]
|
|
234
|
+
file?: string
|
|
235
|
+
description?: string
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/** One node that can be supplied as a targeted view's `targetNodeId`. */
|
|
239
|
+
export interface ViewTargetCandidate {
|
|
240
|
+
id: string
|
|
241
|
+
ref: string
|
|
242
|
+
className: string
|
|
243
|
+
classOrigin: string
|
|
244
|
+
label: string
|
|
245
|
+
description?: string
|
|
246
|
+
status?: string
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/** Small durable snapshot retained when a previously selected node disappears. */
|
|
250
|
+
export interface RememberedViewTarget {
|
|
251
|
+
id: string
|
|
252
|
+
className: string
|
|
253
|
+
classOrigin: string
|
|
254
|
+
label: string
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export interface ViewTargetResult {
|
|
258
|
+
status: 'available' | 'unavailable'
|
|
259
|
+
items: ViewTargetCandidate[]
|
|
260
|
+
selected: ViewTargetCandidate | null
|
|
261
|
+
stale: RememberedViewTarget | null
|
|
262
|
+
truncated: boolean
|
|
263
|
+
reason?: string
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/** Full launch context shown before Studio asks `astrale view` to resolve and open the View. */
|
|
267
|
+
export interface ViewRuntime {
|
|
268
|
+
slug: string
|
|
269
|
+
instance: string | null
|
|
270
|
+
targetRequired: boolean
|
|
271
|
+
targets: ViewTargetResult
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export type ViewSessionResult =
|
|
275
|
+
| {
|
|
276
|
+
status: 'ready'
|
|
277
|
+
sessionId: string
|
|
278
|
+
pageUrl: string
|
|
279
|
+
viewUrl: string
|
|
280
|
+
target: ViewTargetCandidate | null
|
|
281
|
+
}
|
|
282
|
+
| { status: 'unavailable'; reason: string }
|
|
283
|
+
|
|
284
|
+
export interface ClientFeature {
|
|
285
|
+
name: string
|
|
286
|
+
files: string[]
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
export interface ClientTree {
|
|
290
|
+
shell: string[]
|
|
291
|
+
features: ClientFeature[]
|
|
292
|
+
routes: Record<string, string>
|
|
293
|
+
present: boolean
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export interface EnvField {
|
|
297
|
+
name: string
|
|
298
|
+
optional: boolean
|
|
299
|
+
doc?: string
|
|
300
|
+
secret?: boolean
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export type EnvName = 'dev' | 'prod'
|
|
304
|
+
|
|
305
|
+
/** One env var in the editor: the env.ts contract side (declared/optional/doc)
|
|
306
|
+
* merged with the `.env.<env>` file side (value). A row may be declared-only
|
|
307
|
+
* (in env.ts, not yet in the file), set, or an orphan (in the file, not env.ts). */
|
|
308
|
+
export interface EnvVarRow {
|
|
309
|
+
name: string
|
|
310
|
+
/** current value from `.env.<env>` ('' when unset or empty) */
|
|
311
|
+
value: string
|
|
312
|
+
/** declared as a fillable (non-binding) field in env.ts's Env interface */
|
|
313
|
+
declared: boolean
|
|
314
|
+
/** env.ts marks it optional (`?`). Only required (!optional) + empty raises the signal. */
|
|
315
|
+
optional: boolean
|
|
316
|
+
/** JSDoc on the env.ts field — the inline hint */
|
|
317
|
+
doc?: string
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/** The editable env for one env name (dev|prod): the `.env.<env>` file reconciled
|
|
321
|
+
* against env.ts. The GUI is the read-only studio's one sanctioned writer of a
|
|
322
|
+
* domain file (the explicit exception). */
|
|
323
|
+
export interface EnvFileModel {
|
|
324
|
+
env: EnvName
|
|
325
|
+
/** the secrets file, relative to the domain root (e.g. '.env.dev') */
|
|
326
|
+
file: string
|
|
327
|
+
/** wired via `astrale.config.ts` (`secrets: '.env.<env>'`)? false ⇒ the file is used
|
|
328
|
+
* by convention but the adapter isn't pointed at it (e.g. managed prod). */
|
|
329
|
+
configured: boolean
|
|
330
|
+
exists: boolean
|
|
331
|
+
adapter: DomainOverview['adapter']
|
|
332
|
+
rows: EnvVarRow[]
|
|
333
|
+
/** declared && required && empty — the count the "needs values" signal shows */
|
|
334
|
+
requiredMissing: number
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
export interface DomainAnatomy {
|
|
338
|
+
overview: DomainOverview
|
|
339
|
+
views: ViewInfo[]
|
|
340
|
+
client: ClientTree
|
|
341
|
+
env: EnvField[]
|
|
342
|
+
depsContainer?: string
|
|
343
|
+
/** shallow readdir of integrations/ top-level dir names — a hint, NOT a parse */
|
|
344
|
+
detectedIntegrations: string[]
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/** One materialized node instance in a domain's core graph. */
|
|
348
|
+
export interface StudioCoreNode {
|
|
349
|
+
/** canonical CoreRef identity, e.g. `/:big-shop.example.dev:core.acme` */
|
|
350
|
+
path: string
|
|
351
|
+
/** the IR class this node instantiates (keys into StudioSchemaBundle.ir.classes) */
|
|
352
|
+
className: string
|
|
353
|
+
/** the node's property values, keyed by (possibly owner-qualified) property name */
|
|
354
|
+
data: Record<string, unknown>
|
|
355
|
+
/** parent node's `path`, when nested under another node (absent for roots) */
|
|
356
|
+
parent?: string
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/** One materialized edge instance wiring two core nodes. */
|
|
360
|
+
export interface StudioCoreEdge {
|
|
361
|
+
/** source Core path or owning Domain path */
|
|
362
|
+
from: string
|
|
363
|
+
/** target Core path or owning Domain path */
|
|
364
|
+
to: string
|
|
365
|
+
/** the IR edge class name (keys into StudioSchemaBundle.ir.classes, type:'edge') */
|
|
366
|
+
edgeName: string
|
|
367
|
+
/** edge property values, when the edge class carries data */
|
|
368
|
+
data?: Record<string, unknown>
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/** A domain's core data: the concrete genesis node/edge instance graph. */
|
|
372
|
+
export interface StudioCore {
|
|
373
|
+
/** the domain origin this core graph belongs to (mirrors SchemaIR.domain) */
|
|
374
|
+
domain: string
|
|
375
|
+
nodes: StudioCoreNode[]
|
|
376
|
+
edges: StudioCoreEdge[]
|
|
377
|
+
/** set when extraction failed; null when extraction succeeded (including "no core defined") */
|
|
378
|
+
error?: { message: string } | null
|
|
379
|
+
extractedAt: string
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/** Client-side rendering description derived from a JSON value schema. */
|
|
383
|
+
export type TypeDescriptor =
|
|
384
|
+
| { kind: 'string' | 'number' | 'integer' | 'boolean' | 'null' | 'unknown'; optional: boolean }
|
|
385
|
+
| { kind: 'enum'; values: (string | number | boolean | null)[]; optional: boolean }
|
|
386
|
+
| { kind: 'array'; items: TypeDescriptor; optional: boolean }
|
|
387
|
+
| {
|
|
388
|
+
kind: 'object'
|
|
389
|
+
fields: Record<string, TypeDescriptor>
|
|
390
|
+
required: string[]
|
|
391
|
+
optional: boolean
|
|
392
|
+
}
|
|
393
|
+
| { kind: 'ref'; target: string; refKind: 'node' | 'data'; optional: boolean }
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { expect, test } from 'bun:test'
|
|
2
|
+
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs'
|
|
3
|
+
import { dirname, join, relative, resolve } from 'node:path'
|
|
4
|
+
import ts from 'typescript'
|
|
5
|
+
|
|
6
|
+
const SHARED_ROOT = resolve(import.meta.dir, '..')
|
|
7
|
+
const STUDIO_ROOT = resolve(SHARED_ROOT, '..')
|
|
8
|
+
|
|
9
|
+
// Closed Studio V1 shared surface. Keeping this manifest independent of Git
|
|
10
|
+
// catches accidental aliases and shadow contracts.
|
|
11
|
+
const TARGET_BARREL_EXPORTS = [
|
|
12
|
+
'AGENT_ACCESS_LEVELS',
|
|
13
|
+
'AGENT_EFFORT_LEVELS',
|
|
14
|
+
'AgentAccess',
|
|
15
|
+
'AgentEffort',
|
|
16
|
+
'AgentEvent',
|
|
17
|
+
'AgentEventKind',
|
|
18
|
+
'AgentPromptSnapshot',
|
|
19
|
+
'AgentRun',
|
|
20
|
+
'AgentRunSnapshot',
|
|
21
|
+
'AgentRunStatus',
|
|
22
|
+
'AgentSessionInfo',
|
|
23
|
+
'AgentSystemPromptInfo',
|
|
24
|
+
'AnchorKind',
|
|
25
|
+
'AnchorRef',
|
|
26
|
+
'BundleError',
|
|
27
|
+
'ChangeSet',
|
|
28
|
+
'ClientFeature',
|
|
29
|
+
'ClientTree',
|
|
30
|
+
'Comment',
|
|
31
|
+
'CommentStore',
|
|
32
|
+
'ContextItem',
|
|
33
|
+
'ContextStore',
|
|
34
|
+
'ConversationInfo',
|
|
35
|
+
'CopyPayload',
|
|
36
|
+
'CrossDomainImport',
|
|
37
|
+
'DeployRecord',
|
|
38
|
+
'DeployResult',
|
|
39
|
+
'DocMeta',
|
|
40
|
+
'DomainAnatomy',
|
|
41
|
+
'DomainCatalogEntry',
|
|
42
|
+
'DomainOverview',
|
|
43
|
+
'DomainSummary',
|
|
44
|
+
'DomainUsage',
|
|
45
|
+
'EnvField',
|
|
46
|
+
'EnvFileModel',
|
|
47
|
+
'EnvName',
|
|
48
|
+
'EnvVarRow',
|
|
49
|
+
'FileChange',
|
|
50
|
+
'HandlerLink',
|
|
51
|
+
'HarnessCapabilities',
|
|
52
|
+
'HarnessGatewayAuth',
|
|
53
|
+
'HarnessGatewayConfig',
|
|
54
|
+
'HarnessGatewayState',
|
|
55
|
+
'HarnessLoadout',
|
|
56
|
+
'HarnessModelOption',
|
|
57
|
+
'HarnessStatus',
|
|
58
|
+
'InstanceInfo',
|
|
59
|
+
'InstanceStatus',
|
|
60
|
+
'InstancesState',
|
|
61
|
+
'Integration',
|
|
62
|
+
'IntegrationsState',
|
|
63
|
+
'IrCallable',
|
|
64
|
+
'IrCallableAuth',
|
|
65
|
+
'IrCallableOutput',
|
|
66
|
+
'IrClass',
|
|
67
|
+
'IrClassKey',
|
|
68
|
+
'IrClassRef',
|
|
69
|
+
'IrEndpoint',
|
|
70
|
+
'IrFunction',
|
|
71
|
+
'IrImportDescriptor',
|
|
72
|
+
'IrMethod',
|
|
73
|
+
'IrSchemaKey',
|
|
74
|
+
'IrSchemaRef',
|
|
75
|
+
'IrView',
|
|
76
|
+
'IrViewTarget',
|
|
77
|
+
'JsonSchema',
|
|
78
|
+
'LayoutState',
|
|
79
|
+
'LoadoutSkill',
|
|
80
|
+
'McpServerInfo',
|
|
81
|
+
'MergeResult',
|
|
82
|
+
'MethodInheritance',
|
|
83
|
+
'NodePosition',
|
|
84
|
+
'RememberedViewTarget',
|
|
85
|
+
'SchemaAnnotation',
|
|
86
|
+
'SchemaChange',
|
|
87
|
+
'SchemaChangeKind',
|
|
88
|
+
'SchemaIR',
|
|
89
|
+
'SchemaOverlay',
|
|
90
|
+
'SourceSpan',
|
|
91
|
+
'StaleReport',
|
|
92
|
+
'StudioCore',
|
|
93
|
+
'StudioCoreEdge',
|
|
94
|
+
'StudioCoreNode',
|
|
95
|
+
'StudioEvent',
|
|
96
|
+
'StudioSchemaBundle',
|
|
97
|
+
'StudioSettings',
|
|
98
|
+
'ThreadEntry',
|
|
99
|
+
'ThreadEntryType',
|
|
100
|
+
'ThreadRole',
|
|
101
|
+
'TypeDescriptor',
|
|
102
|
+
'ViewInfo',
|
|
103
|
+
'ViewRuntime',
|
|
104
|
+
'ViewSessionResult',
|
|
105
|
+
'ViewTargetCandidate',
|
|
106
|
+
'ViewTargetResult',
|
|
107
|
+
'VisibilityState',
|
|
108
|
+
] as const
|
|
109
|
+
|
|
110
|
+
const TARGET_BARREL_HELPERS = [
|
|
111
|
+
'IR_SCHEMA_REF_KINDS',
|
|
112
|
+
'IrSchemaRefKind',
|
|
113
|
+
'STUDIO_SCHEMA_PROJECTION_VERSION',
|
|
114
|
+
'SchemaRevision',
|
|
115
|
+
'classRefKey',
|
|
116
|
+
'isIrClassRef',
|
|
117
|
+
'isIrSchemaRef',
|
|
118
|
+
'isSchemaRevision',
|
|
119
|
+
'parseClassRefKey',
|
|
120
|
+
'parseSchemaRefKey',
|
|
121
|
+
'schemaRefKey',
|
|
122
|
+
] as const
|
|
123
|
+
|
|
124
|
+
function productionFiles(dir: string): string[] {
|
|
125
|
+
return readdirSync(dir).flatMap((entry) => {
|
|
126
|
+
if (entry === 'node_modules') return []
|
|
127
|
+
const path = join(dir, entry)
|
|
128
|
+
if (statSync(path).isDirectory()) return productionFiles(path)
|
|
129
|
+
return /\.(?:ts|tsx)$/.test(path) && !/\.test\.(?:ts|tsx)$/.test(path) ? [path] : []
|
|
130
|
+
})
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function parseTypeScript(file: string): ts.SourceFile {
|
|
134
|
+
return ts.createSourceFile(
|
|
135
|
+
file,
|
|
136
|
+
readFileSync(file, 'utf8'),
|
|
137
|
+
ts.ScriptTarget.Latest,
|
|
138
|
+
false,
|
|
139
|
+
file.endsWith('.tsx') ? ts.ScriptKind.TSX : ts.ScriptKind.TS,
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function hasExportModifier(node: ts.Node): boolean {
|
|
144
|
+
return Boolean(
|
|
145
|
+
ts.canHaveModifiers(node) &&
|
|
146
|
+
ts.getModifiers(node)?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword),
|
|
147
|
+
)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function bindingNames(name: ts.BindingName): string[] {
|
|
151
|
+
if (ts.isIdentifier(name)) return [name.text]
|
|
152
|
+
return name.elements.flatMap((element) =>
|
|
153
|
+
ts.isOmittedExpression(element) ? [] : bindingNames(element.name),
|
|
154
|
+
)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function resolveTypeScriptModule(file: string, specifier: string): string {
|
|
158
|
+
const target = resolve(dirname(file), specifier)
|
|
159
|
+
const candidate = [target, `${target}.ts`, `${target}.tsx`, join(target, 'index.ts')].find(
|
|
160
|
+
existsSync,
|
|
161
|
+
)
|
|
162
|
+
if (!candidate) throw new Error(`Cannot resolve ${specifier} from ${file}`)
|
|
163
|
+
return candidate
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function namedExports(file: string, seen = new Set<string>()): string[] {
|
|
167
|
+
if (seen.has(file)) return []
|
|
168
|
+
seen.add(file)
|
|
169
|
+
const names: string[] = []
|
|
170
|
+
|
|
171
|
+
for (const statement of parseTypeScript(file).statements) {
|
|
172
|
+
if (ts.isExportDeclaration(statement)) {
|
|
173
|
+
if (statement.exportClause && ts.isNamedExports(statement.exportClause)) {
|
|
174
|
+
names.push(...statement.exportClause.elements.map((element) => element.name.text))
|
|
175
|
+
} else if (statement.exportClause && ts.isNamespaceExport(statement.exportClause)) {
|
|
176
|
+
names.push(statement.exportClause.name.text)
|
|
177
|
+
} else if (
|
|
178
|
+
statement.moduleSpecifier &&
|
|
179
|
+
ts.isStringLiteral(statement.moduleSpecifier) &&
|
|
180
|
+
statement.moduleSpecifier.text.startsWith('.')
|
|
181
|
+
) {
|
|
182
|
+
names.push(
|
|
183
|
+
...namedExports(resolveTypeScriptModule(file, statement.moduleSpecifier.text), seen),
|
|
184
|
+
)
|
|
185
|
+
}
|
|
186
|
+
continue
|
|
187
|
+
}
|
|
188
|
+
if (!hasExportModifier(statement)) continue
|
|
189
|
+
if (ts.isVariableStatement(statement)) {
|
|
190
|
+
names.push(
|
|
191
|
+
...statement.declarationList.declarations.flatMap(({ name }) => bindingNames(name)),
|
|
192
|
+
)
|
|
193
|
+
} else if (
|
|
194
|
+
(ts.isClassDeclaration(statement) ||
|
|
195
|
+
ts.isEnumDeclaration(statement) ||
|
|
196
|
+
ts.isFunctionDeclaration(statement) ||
|
|
197
|
+
ts.isInterfaceDeclaration(statement) ||
|
|
198
|
+
ts.isTypeAliasDeclaration(statement)) &&
|
|
199
|
+
statement.name
|
|
200
|
+
) {
|
|
201
|
+
names.push(statement.name.text)
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return names
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function moduleSpecifiers(file: string): string[] {
|
|
208
|
+
const found: string[] = []
|
|
209
|
+
const visit = (node: ts.Node): void => {
|
|
210
|
+
if (
|
|
211
|
+
(ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) &&
|
|
212
|
+
node.moduleSpecifier &&
|
|
213
|
+
ts.isStringLiteralLike(node.moduleSpecifier)
|
|
214
|
+
) {
|
|
215
|
+
found.push(node.moduleSpecifier.text)
|
|
216
|
+
} else if (
|
|
217
|
+
ts.isImportEqualsDeclaration(node) &&
|
|
218
|
+
ts.isExternalModuleReference(node.moduleReference) &&
|
|
219
|
+
node.moduleReference.expression &&
|
|
220
|
+
ts.isStringLiteralLike(node.moduleReference.expression)
|
|
221
|
+
) {
|
|
222
|
+
found.push(node.moduleReference.expression.text)
|
|
223
|
+
} else if (
|
|
224
|
+
ts.isCallExpression(node) &&
|
|
225
|
+
node.arguments.length === 1 &&
|
|
226
|
+
ts.isStringLiteralLike(node.arguments[0]!) &&
|
|
227
|
+
(node.expression.kind === ts.SyntaxKind.ImportKeyword ||
|
|
228
|
+
(ts.isIdentifier(node.expression) && node.expression.text === 'require'))
|
|
229
|
+
) {
|
|
230
|
+
found.push(node.arguments[0]!.text)
|
|
231
|
+
}
|
|
232
|
+
ts.forEachChild(node, visit)
|
|
233
|
+
}
|
|
234
|
+
visit(parseTypeScript(file))
|
|
235
|
+
return found
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function declaredTypes(file: string, candidates: ReadonlySet<string>): string[] {
|
|
239
|
+
const text = readFileSync(file, 'utf8')
|
|
240
|
+
if (![...candidates].some((name) => text.includes(name))) return []
|
|
241
|
+
return parseTypeScript(file).statements.flatMap((statement) => {
|
|
242
|
+
if (
|
|
243
|
+
!ts.isInterfaceDeclaration(statement) &&
|
|
244
|
+
!ts.isTypeAliasDeclaration(statement) &&
|
|
245
|
+
!ts.isClassDeclaration(statement) &&
|
|
246
|
+
!ts.isEnumDeclaration(statement)
|
|
247
|
+
) {
|
|
248
|
+
return []
|
|
249
|
+
}
|
|
250
|
+
const name = statement.name?.text
|
|
251
|
+
return name && candidates.has(name) ? [name] : []
|
|
252
|
+
})
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
test('types.ts remains a re-export-only compatibility facade', () => {
|
|
256
|
+
const source = parseTypeScript(join(SHARED_ROOT, 'types.ts'))
|
|
257
|
+
const exports = source.statements.flatMap((statement) => {
|
|
258
|
+
if (
|
|
259
|
+
!ts.isExportDeclaration(statement) ||
|
|
260
|
+
statement.exportClause ||
|
|
261
|
+
!statement.moduleSpecifier ||
|
|
262
|
+
!ts.isStringLiteral(statement.moduleSpecifier)
|
|
263
|
+
) {
|
|
264
|
+
return []
|
|
265
|
+
}
|
|
266
|
+
return [statement.moduleSpecifier.text]
|
|
267
|
+
})
|
|
268
|
+
expect(exports).toEqual([
|
|
269
|
+
'./schema/identity',
|
|
270
|
+
'./contracts/schema',
|
|
271
|
+
'./contracts/workspace',
|
|
272
|
+
'./contracts/agent',
|
|
273
|
+
'./contracts/runtime',
|
|
274
|
+
])
|
|
275
|
+
expect(source.statements.every(ts.isExportDeclaration)).toBe(true)
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
test('types.ts exposes exactly the Studio V1 shared surface', () => {
|
|
279
|
+
const exports = namedExports(join(SHARED_ROOT, 'types.ts'))
|
|
280
|
+
const duplicates = exports.filter((name, index) => exports.indexOf(name) !== index)
|
|
281
|
+
const expected = [...TARGET_BARREL_EXPORTS, ...TARGET_BARREL_HELPERS].sort()
|
|
282
|
+
|
|
283
|
+
expect(duplicates).toEqual([])
|
|
284
|
+
expect(exports.sort()).toEqual(expected)
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
test('persisted layout and visibility shapes each have one semantic owner', () => {
|
|
288
|
+
const files = productionFiles(STUDIO_ROOT)
|
|
289
|
+
const names = new Set(['LayoutState', 'VisibilityState'])
|
|
290
|
+
const owners = new Map([...names].map((name) => [name, [] as string[]]))
|
|
291
|
+
for (const file of files) {
|
|
292
|
+
for (const name of declaredTypes(file, names)) {
|
|
293
|
+
owners.get(name)!.push(relative(STUDIO_ROOT, file).replaceAll('\\', '/'))
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
expect(Object.fromEntries(owners)).toEqual({
|
|
297
|
+
LayoutState: ['shared/contracts/workspace.ts'],
|
|
298
|
+
VisibilityState: ['shared/contracts/workspace.ts'],
|
|
299
|
+
})
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
test('contract modules follow the documented dependency direction', () => {
|
|
303
|
+
const expectedImports = new Map([
|
|
304
|
+
['schema/identity.ts', []],
|
|
305
|
+
['contracts/schema.ts', ['../schema/identity']],
|
|
306
|
+
['contracts/workspace.ts', ['./schema']],
|
|
307
|
+
['contracts/agent.ts', ['./workspace']],
|
|
308
|
+
['contracts/runtime.ts', ['./agent']],
|
|
309
|
+
])
|
|
310
|
+
|
|
311
|
+
for (const [file, expected] of expectedImports) {
|
|
312
|
+
const imports = moduleSpecifiers(join(SHARED_ROOT, file)).filter((specifier) =>
|
|
313
|
+
specifier.startsWith('.'),
|
|
314
|
+
)
|
|
315
|
+
expect(imports).toEqual(expected)
|
|
316
|
+
}
|
|
317
|
+
})
|