@astrale-os/cli 1.0.0-beta.5 → 1.0.0-beta.7

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 (162) hide show
  1. package/dist/astrale.js +66254 -64461
  2. package/dist/public/connect-core.js +14895 -33867
  3. package/dist/public/keys/index.js +1246 -34250
  4. package/dist/public/paths/index.js +866 -33833
  5. package/dist/types/admin/instance/model.d.ts +3 -4
  6. package/dist/types/errors.d.ts +1 -1
  7. package/dist/types/lib/instance-target.d.ts +1 -1
  8. package/dist/types/lib/log.d.ts +2 -2
  9. package/package.json +4 -9
  10. package/src/__tests__/fixtures/publication.ts +20 -0
  11. package/src/admin/__tests__/binding.test.ts +12 -19
  12. package/src/admin/__tests__/fixture.ts +101 -0
  13. package/src/admin/binding.ts +83 -3
  14. package/src/admin/catalog/.spec/api.d.ts +1 -1
  15. package/src/admin/catalog/__tests__/client.test.ts +20 -49
  16. package/src/admin/catalog/client.ts +35 -26
  17. package/src/admin/catalog/model.ts +3 -4
  18. package/src/admin/graph/.spec/api.d.ts +4 -10
  19. package/src/admin/graph/nodes.ts +1 -1
  20. package/src/admin/instance/.spec/api.d.ts +1 -1
  21. package/src/admin/instance/__tests__/client.test.ts +58 -83
  22. package/src/admin/instance/client.ts +49 -39
  23. package/src/admin/instance/model.ts +9 -8
  24. package/src/commands/__tests__/call-describe.test.ts +46 -33
  25. package/src/commands/__tests__/domain-install-operation.test.ts +3 -14
  26. package/src/commands/__tests__/domain-install-owned.test.ts +1 -1
  27. package/src/commands/__tests__/domain-list.test.ts +5 -25
  28. package/src/commands/__tests__/install-identity-override.test.ts +3 -14
  29. package/src/commands/__tests__/read-commands.test.ts +35 -10
  30. package/src/commands/call-describe.ts +59 -67
  31. package/src/commands/call.ts +41 -54
  32. package/src/commands/domain/install.ts +2 -2
  33. package/src/commands/domain/uninstall.ts +2 -2
  34. package/src/commands/get.ts +13 -14
  35. package/src/commands/identity/__tests__/register.test.ts +3 -3
  36. package/src/commands/identity/register.ts +5 -4
  37. package/src/commands/instance/delete.ts +5 -3
  38. package/src/commands/instance/status.ts +1 -9
  39. package/src/commands/introspect.ts +4 -11
  40. package/src/commands/logs.ts +24 -25
  41. package/src/commands/mutate.ts +6 -5
  42. package/src/commands/query.ts +27 -30
  43. package/src/commands/token.ts +8 -7
  44. package/src/commands/view.ts +4 -3
  45. package/src/connection/.spec/api.d.ts +7 -8
  46. package/src/connection/.spec/architecture.md +5 -4
  47. package/src/connection/.spec/flows/session.ts +2 -2
  48. package/src/connection/.spec/laws/connection.ts +6 -2
  49. package/src/connection/.spec/layout.ts +1 -0
  50. package/src/connection/__tests__/credential.test.ts +1 -1
  51. package/src/connection/__tests__/errors.test.ts +27 -20
  52. package/src/connection/__tests__/exchange.test.ts +30 -1
  53. package/src/connection/__tests__/failure-fixtures.ts +39 -0
  54. package/src/connection/__tests__/failure-safety.test.ts +75 -0
  55. package/src/connection/__tests__/self.test.ts +31 -11
  56. package/src/connection/__tests__/session.test.ts +3 -3
  57. package/src/connection/call.ts +1 -1
  58. package/src/connection/credential.ts +2 -2
  59. package/src/connection/errors.ts +1 -355
  60. package/src/connection/exchange.ts +47 -23
  61. package/src/connection/failure/classify.ts +110 -0
  62. package/src/connection/failure/debug.ts +26 -0
  63. package/src/connection/failure/index.ts +18 -0
  64. package/src/connection/failure/model.ts +28 -0
  65. package/src/connection/failure/render.ts +116 -0
  66. package/src/connection/reasons.ts +16 -0
  67. package/src/connection/self.ts +39 -21
  68. package/src/connection/session.ts +8 -8
  69. package/src/errors.ts +3 -2
  70. package/src/graph/.spec/api.d.ts +12 -5
  71. package/src/graph/.spec/layout.ts +9 -1
  72. package/src/graph/__tests__/mutation.test.ts +2 -2
  73. package/src/graph/__tests__/query.test.ts +25 -6
  74. package/src/graph/class.ts +30 -0
  75. package/src/graph/index.ts +1 -0
  76. package/src/graph/query.ts +21 -32
  77. package/src/identity/.spec/api.d.ts +2 -2
  78. package/src/identity/__tests__/registration.test.ts +1 -1
  79. package/src/identity/registration.ts +3 -3
  80. package/src/lib/__tests__/domain-publication.test.ts +4 -15
  81. package/src/lib/__tests__/instance-target.test.ts +6 -39
  82. package/src/lib/__tests__/log-errors.test.ts +53 -0
  83. package/src/lib/__tests__/studio-server-deps.test.ts +1 -1
  84. package/src/lib/command-dx.ts +1 -4
  85. package/src/lib/domain-publication.ts +1 -1
  86. package/src/lib/instance-target.ts +3 -14
  87. package/src/lib/log.ts +11 -6
  88. package/src/lib/validation.ts +2 -2
  89. package/src/lib/view/resolve.ts +1 -1
  90. package/src/program/__tests__/program.test.ts +1 -1
  91. package/studio/client/dist/assets/{elk-api-YEDvaNU-.js → elk-api-Di4OXGNH.js} +1 -1
  92. package/studio/client/dist/assets/index-BLm6J11H.js +81 -0
  93. package/studio/client/dist/assets/index-BiGR8Oo9.css +1 -0
  94. package/studio/client/dist/assets/index-DTjvkOUX.js +8 -0
  95. package/studio/client/dist/index.html +2 -2
  96. package/studio/package.json +2 -7
  97. package/studio/server/agent/ask.test.ts +1 -1
  98. package/studio/server/agent/bridge/grant.test.ts +1 -1
  99. package/studio/server/agent/bridge/routes.test.ts +1 -1
  100. package/studio/server/agent/harness/claude/adapter.test.ts +15 -5
  101. package/studio/server/agent/prompts/anchors.test.ts +32 -36
  102. package/studio/server/agent/prompts/anchors.ts +20 -49
  103. package/studio/server/agent/prompts/system.test.ts +3 -2
  104. package/studio/server/agent/prompts/system.ts +3 -3
  105. package/studio/server/agent/routes.test.ts +1 -1
  106. package/studio/server/agent/run/coordinator.test.ts +1 -1
  107. package/studio/server/api/canvas.ts +0 -1
  108. package/studio/server/api-agent-loadout.test.ts +1 -1
  109. package/studio/server/api.test.ts +1 -1
  110. package/studio/server/cache.test.ts +23 -12
  111. package/studio/server/cache.ts +12 -15
  112. package/studio/server/domain.test.ts +50 -68
  113. package/studio/server/domain.ts +82 -63
  114. package/studio/server/handoff/copy.ts +1 -1
  115. package/studio/server/index.ts +1 -1
  116. package/studio/server/instances/probe.test.ts +1 -1
  117. package/studio/server/introspect/anatomy/views/routes.ts +46 -37
  118. package/studio/server/introspect/anatomy/views.ts +9 -14
  119. package/studio/server/introspect/anatomy-extras.test.ts +26 -42
  120. package/studio/server/introspect/anatomy.test.ts +22 -14
  121. package/studio/server/introspect/anatomy.ts +6 -9
  122. package/studio/server/introspect/bundle.ts +0 -3
  123. package/studio/server/introspect/canonical-schema.test.ts +94 -389
  124. package/studio/server/introspect/canonical-schema.ts +227 -457
  125. package/studio/server/introspect/core-extractor.ts +18 -127
  126. package/studio/server/introspect/core.ts +1 -2
  127. package/studio/server/introspect/diff.test.ts +11 -7
  128. package/studio/server/introspect/diff.ts +18 -55
  129. package/studio/server/introspect/extractor.ts +22 -117
  130. package/studio/server/introspect/overlay-tsmorph.test.ts +119 -240
  131. package/studio/server/introspect/overlay.test.ts +26 -54
  132. package/studio/server/introspect/overlay.ts +10 -37
  133. package/studio/server/introspect/revision.test.ts +1 -1
  134. package/studio/server/introspect/revision.ts +1 -1
  135. package/studio/server/introspect/runtime.test.ts +65 -183
  136. package/studio/server/introspect/runtime.ts +8 -25
  137. package/studio/server/introspect/schema-ir-json.test.ts +70 -0
  138. package/studio/server/introspect/schema-ir-json.ts +38 -68
  139. package/studio/server/introspect/schema-refs.test.ts +43 -88
  140. package/studio/server/introspect/schema-refs.ts +14 -49
  141. package/studio/server/introspect/source-overlay/handlers.ts +116 -636
  142. package/studio/server/introspect/source-overlay/kernel-calls.ts +0 -3
  143. package/studio/server/introspect/source-overlay/spans.ts +15 -45
  144. package/studio/server/state/baseline.test.ts +14 -9
  145. package/studio/server/state/baseline.ts +3 -5
  146. package/studio/server/state/visibility.ts +1 -5
  147. package/studio/server/views/model.ts +3 -3
  148. package/studio/server/views/target.test.ts +17 -74
  149. package/studio/server/views/target.ts +24 -50
  150. package/studio/server/watch.test.ts +11 -11
  151. package/studio/server/watch.ts +8 -4
  152. package/studio/server/workspace-watch.ts +2 -2
  153. package/studio/shared/contracts/schema.ts +38 -107
  154. package/studio/shared/contracts/surface.test.ts +12 -15
  155. package/studio/shared/contracts/workspace.ts +2 -11
  156. package/studio/shared/schema/identity.test.ts +20 -44
  157. package/studio/shared/schema/identity.ts +9 -23
  158. package/viewer/dist/main.js +41 -38
  159. package/studio/client/dist/assets/index-C-PvAXV4.js +0 -81
  160. package/studio/client/dist/assets/index-C4aNQ6dz.css +0 -1
  161. package/studio/client/dist/assets/index-CzpmJD0j.js +0 -8
  162. package/studio/server/introspect/anatomy/views/legacy.ts +0 -232
@@ -1,267 +1,146 @@
1
- import { expect, test } from 'bun:test'
1
+ import { afterEach, describe, expect, test } from 'bun:test'
2
2
  import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
3
3
  import { tmpdir } from 'node:os'
4
4
  import { join } from 'node:path'
5
5
 
6
- import { buildHandlerLinks, buildSchemaAnnotations, buildSourceSpans } from './overlay-tsmorph'
7
-
8
- test('resolves authorization hooks from imported remote method definitions', () => {
9
- const root = mkdtempSync(join(tmpdir(), 'astrale-studio-handler-links-'))
10
- const runtime = join(root, 'runtime')
11
- mkdirSync(runtime)
6
+ import type { SchemaIR } from '../../shared/types'
12
7
 
13
- writeFileSync(
14
- join(runtime, 'index.ts'),
15
- `
16
- import { remoteClassMethods } from '@astrale-os/sdk'
17
- import {
18
- absentMethod,
19
- guardedMethod,
20
- noopMethod,
21
- } from './methods.js'
8
+ import { buildHandlerLinks, buildSchemaAnnotations, buildSourceSpans } from './overlay-tsmorph'
22
9
 
23
- const classMethods = remoteClassMethods()
10
+ const roots: string[] = []
11
+ afterEach(() => {
12
+ while (roots.length) rmSync(roots.pop()!, { recursive: true, force: true })
13
+ })
24
14
 
25
- export const methods = {
26
- class: {
27
- Issue: classMethods(schema, 'Issue', {
28
- absent: absentMethod,
29
- guarded: guardedMethod,
30
- noop: noopMethod,
31
- }),
15
+ const ir: SchemaIR = {
16
+ version: 'v1',
17
+ format: 'astrale.dsl',
18
+ domain: 'issues.example',
19
+ importsByKey: {},
20
+ importedClassesByKey: {},
21
+ classes: {
22
+ Issue: {
23
+ type: 'node',
24
+ name: 'Issue',
25
+ origin: 'issues.example',
26
+ ref: { origin: 'issues.example', kind: 'class', name: 'Issue' },
27
+ properties: {},
28
+ methods: {
29
+ rename: {
30
+ name: 'rename',
31
+ input: {},
32
+ output: { mode: 'value', schema: {} },
33
+ static: false,
34
+ inheritance: 'default',
35
+ auth: 'authorized',
32
36
  },
33
- }
34
- `,
35
- )
36
- writeFileSync(
37
- join(runtime, 'methods.ts'),
38
- `
39
- import { remoteMethod } from '@astrale-os/sdk'
40
-
41
- const method = remoteMethod()
42
- const run = () => 'ok'
43
-
44
- export const guardedMethod = method(schema, 'Issue', 'guarded', {
45
- authorize: ({ auth, kernel, self }) =>
46
- kernel.auth.require({ who: auth.principal, on: self.path, perms: 1 }),
47
- execute: () => run(),
48
- })
49
-
50
- export const noopMethod = method(schema, 'Issue', 'noop', {
51
- authorize: async () => undefined,
52
- execute: () => run(),
53
- })
37
+ },
38
+ },
39
+ },
40
+ functions: {
41
+ createIssue: {
42
+ name: 'createIssue',
43
+ input: {},
44
+ output: { mode: 'value', schema: {} },
45
+ auth: 'anonymous',
46
+ },
47
+ },
48
+ views: {},
49
+ policies: {},
50
+ dependencies: [],
51
+ core: {},
52
+ }
54
53
 
55
- export const absentMethod = method(schema, 'Issue', 'absent', {
56
- execute: () => run(),
57
- })
58
- `,
59
- )
54
+ describe('source overlay', () => {
55
+ test('links modular Action and Workflow declarations to exact callables', () => {
56
+ const root = mkdtempSync(join(tmpdir(), 'studio-runtime-overlay-'))
57
+ roots.push(root)
58
+ mkdirSync(join(root, 'actions'))
59
+ mkdirSync(join(root, 'workflows'))
60
+ writeFileSync(
61
+ join(root, 'actions/rename.ts'),
62
+ `
63
+ import { defineAction } from '@astrale-os/sdk/action'
64
+ export const rename = defineAction()('Issue.rename', async ({ input }) => graph.update(input))
65
+ `,
66
+ )
67
+ writeFileSync(
68
+ join(root, 'workflows/create.ts'),
69
+ `
70
+ import { defineWorkflow } from '@astrale-os/sdk/workflow'
71
+ export const create = defineWorkflow()('createIssue', async ({ input }) => graph.create(input))
72
+ `,
73
+ )
60
74
 
61
- try {
62
- const links = buildHandlerLinks({ ir: null, domainRoot: root })
63
75
  expect(
64
- links.map(({ method, authorize, auth, implemented, unlinked, handlerFile }) => ({
65
- method,
66
- authorize,
67
- auth,
68
- implemented,
69
- unlinked: unlinked ?? false,
70
- handlerFile,
76
+ buildHandlerLinks({ ir, domainRoot: root }).map((link) => ({
77
+ owner: link.owner,
78
+ ownerKind: link.ownerKind,
79
+ kind: link.kind,
80
+ method: link.method,
81
+ handlerFile: link.handlerFile,
82
+ kernelCalls: link.kernelCalls,
71
83
  })),
72
84
  ).toEqual([
73
85
  {
74
- method: 'absent',
75
- authorize: 'absent',
76
- auth: 'required',
77
- implemented: true,
78
- unlinked: false,
79
- handlerFile: 'runtime/methods.ts',
86
+ owner: 'Issue',
87
+ ownerKind: 'class',
88
+ kind: 'action',
89
+ method: 'rename',
90
+ handlerFile: 'actions/rename.ts',
91
+ kernelCalls: ['graph.update'],
80
92
  },
81
93
  {
82
- method: 'guarded',
83
- authorize: 'custom',
84
- auth: 'required',
85
- implemented: true,
86
- unlinked: false,
87
- handlerFile: 'runtime/methods.ts',
88
- },
89
- {
90
- method: 'noop',
91
- authorize: 'noop',
92
- auth: 'required',
93
- implemented: true,
94
- unlinked: false,
95
- handlerFile: 'runtime/methods.ts',
94
+ owner: 'issues.example',
95
+ ownerKind: 'function',
96
+ kind: 'workflow',
97
+ method: 'createIssue',
98
+ handlerFile: 'workflows/create.ts',
99
+ kernelCalls: ['graph.create'],
96
100
  },
97
101
  ])
98
- expect(links.find((link) => link.method === 'guarded')?.authorizeSnippet).toContain(
99
- 'kernel.auth.require',
100
- )
101
- } finally {
102
- rmSync(root, { recursive: true, force: true })
103
- }
104
- })
105
-
106
- test('links current implementation handlers and current edge source spans', () => {
107
- const root = mkdtempSync(join(tmpdir(), 'astrale-studio-current-overlay-'))
108
- const schemaDir = join(root, 'schema')
109
- const actionsDir = join(root, 'actions')
110
- mkdirSync(schemaDir)
111
- mkdirSync(actionsDir)
112
- writeFileSync(
113
- join(root, 'package.json'),
114
- JSON.stringify({ imports: { '#actions/*': './actions/*.ts' } }),
115
- )
102
+ })
116
103
 
117
- writeFileSync(
118
- join(root, 'implementation.ts'),
119
- `
120
- import { rename } from '#actions/rename'
121
- import { createIssue } from '#actions/create-issue'
122
- export const domain = defineDomain.public({
123
- schema,
124
- handlers: {
104
+ test('indexes Class, Property, Method, Edge endpoint, and Function declarations', () => {
105
+ const root = mkdtempSync(join(tmpdir(), 'studio-schema-spans-'))
106
+ roots.push(root)
107
+ const schemaDir = join(root, 'schema')
108
+ mkdirSync(schemaDir)
109
+ writeFileSync(
110
+ join(schemaDir, 'members.ts'),
111
+ `
112
+ export const Issue = nodeClass({
113
+ properties: { title: property(z.string()) },
114
+ methods: { rename },
115
+ })
116
+ export const assigned_to = edgeClass.directed({
117
+ source: { as: 'issue', accepts: [Issue] },
118
+ target: { as: 'owner', accepts: [Issue] },
119
+ properties: { note: property(z.string()) },
120
+ })
121
+ export const createIssue = fn({ input, output })
122
+ `,
123
+ )
124
+ writeFileSync(
125
+ join(schemaDir, 'index.ts'),
126
+ `
127
+ import { Issue, assigned_to, createIssue } from './members.js'
128
+ export const Schema = defineSchema('issues.example', {
129
+ classes: { Issue, assigned_to },
125
130
  functions: { createIssue },
126
- classes: { Issue: { rename } },
127
- interfaces: {},
128
- },
129
- })
130
- `,
131
- )
132
- writeFileSync(
133
- join(actionsDir, 'rename.ts'),
134
- `
135
- export function rename({ input }) {
136
- return graph.update(input)
137
- }
138
- `,
139
- )
140
- writeFileSync(
141
- join(actionsDir, 'create-issue.ts'),
142
- `export const createIssue = ({ input }) => graph.create(input)`,
143
- )
144
- writeFileSync(
145
- join(schemaDir, 'members.ts'),
146
- `
147
- export const iIssue = nodeInterface({
148
- properties: { title: property({ type: 'string' }) },
149
- })
150
- export const Issue = nodeClass({
151
- properties: { title: property({ type: 'string' }) },
152
- methods: { rename },
153
- })
154
- export const assigned_to = edgeClass.directed({
155
- source: { as: 'issue', accepts: [Issue], outgoing: '0..*' },
156
- target: { as: 'owner', accepts: [Issue], incoming: '0..1' },
157
- properties: { note: property({ type: 'string' }) },
158
- })
159
- export const createIssue = fn({ auth: 'anonymous', input, output })
160
- `,
161
- )
162
- writeFileSync(
163
- join(schemaDir, 'index.ts'),
164
- `
165
- import { Issue, assigned_to, createIssue, iIssue as issueInterface } from './members.js'
166
- const schemaInput = {
167
- interfaces: { Issue: issueInterface },
168
- classes: { Issue, assigned_to },
169
- functions: { createIssue },
170
- } as const
171
- export const schema = defineSchema('issues.example', schemaInput)
172
- `,
173
- )
174
-
175
- try {
176
- const links = buildHandlerLinks({
177
- ir: {
178
- version: 'v1',
179
- domain: 'issues.example',
180
- types: {},
181
- interfaces: {},
182
- classes: {
183
- Issue: {
184
- type: 'node',
185
- name: 'Issue',
186
- properties: {},
187
- methods: {
188
- rename: {
189
- name: 'rename',
190
- params: {},
191
- returns: {},
192
- static: false,
193
- inheritance: 'default',
194
- auth: 'authorized',
195
- } as any,
196
- },
197
- },
198
- },
199
- imports: {},
200
- functions: {
201
- createIssue: {
202
- name: 'createIssue',
203
- input: {},
204
- params: {},
205
- output: { mode: 'value', schema: {} },
206
- returns: {},
207
- static: true,
208
- inheritance: 'default',
209
- auth: 'anonymous',
210
- },
211
- },
212
- },
213
- domainRoot: root,
214
- })
215
- expect(links).toHaveLength(2)
216
- expect(links.find((link) => link.ownerKind === 'class')).toMatchObject({
217
- owner: 'Issue',
218
- ownerKind: 'class',
219
- method: 'rename',
220
- wiringFile: 'implementation.ts',
221
- handlerFile: 'actions/rename.ts',
222
- implemented: true,
223
- auth: 'required',
224
- callableAuth: 'authorized',
225
- authorize: 'custom',
226
- kernelCalls: ['graph.update'],
227
- })
228
- expect(links.find((link) => link.ownerKind === 'function')).toMatchObject({
229
- owner: 'issues.example',
230
- method: 'createIssue',
231
- wiringFile: 'implementation.ts',
232
- handlerFile: 'actions/create-issue.ts',
233
- implemented: true,
234
- static: true,
235
- auth: 'public',
236
- callableAuth: 'anonymous',
237
- kernelCalls: ['graph.create'],
238
- })
239
-
240
- const spans = buildSourceSpans({ ir: null, schemaDir })
241
- expect(spans['interface.Issue.property.title']?.file).toBe('schema/members.ts')
131
+ })
132
+ `,
133
+ )
134
+ const spans = buildSourceSpans({ ir, schemaDir })
242
135
  expect(spans['class.Issue.property.title']?.file).toBe('schema/members.ts')
136
+ expect(spans['class.Issue.method.rename']?.file).toBe('schema/members.ts')
243
137
  expect(spans['edge.assigned_to.endpoint.issue']?.file).toBe('schema/members.ts')
244
138
  expect(spans['edge.assigned_to.endpoint.owner']?.file).toBe('schema/members.ts')
245
139
  expect(spans['edge.assigned_to.property.note']?.file).toBe('schema/members.ts')
246
140
  expect(spans['function.createIssue']?.file).toBe('schema/members.ts')
247
- } finally {
248
- rmSync(root, { recursive: true, force: true })
249
- }
250
- })
141
+ })
251
142
 
252
- test('does not emit the obsolete enum update warning', () => {
253
- expect(
254
- buildSchemaAnnotations({
255
- ir: {
256
- classes: {
257
- Issue: {
258
- type: 'node',
259
- name: 'Issue',
260
- properties: { status: { type: 'string', enum: ['open', 'closed'] } },
261
- methods: {},
262
- },
263
- },
264
- } as any,
265
- }),
266
- ).toEqual([])
143
+ test('does not invent warnings for admitted enum values', () => {
144
+ expect(buildSchemaAnnotations({ ir })).toEqual([])
145
+ })
267
146
  })
@@ -1,72 +1,44 @@
1
1
  import { expect, test } from 'bun:test'
2
2
 
3
- import type { SchemaIR } from '../../shared/types'
3
+ import type { IrClassKey, SchemaIR } from '../../shared/types'
4
4
 
5
5
  import { buildOverlay } from './overlay'
6
6
 
7
- test('preserves homonymous and cross-kind imports from the exact index', () => {
7
+ function descriptor(origin: string, name: string) {
8
+ const key = `${origin}:class.${name}` as IrClassKey
9
+ return { origin, ref: { origin, kind: 'class' as const, name }, key }
10
+ }
11
+
12
+ test('preserves homonymous Classes through exact import identity', () => {
13
+ const first = descriptor('a.example', 'Shared')
14
+ const second = descriptor('b.example', 'Shared')
15
+ const kernel = descriptor('kernel.astrale.ai', 'Identity')
8
16
  const ir = {
9
17
  version: 'v1',
18
+ format: 'astrale.dsl',
10
19
  domain: 'app.example',
11
- types: {},
12
- interfaces: {},
13
20
  classes: {},
14
21
  functions: {},
15
- imports: {},
16
22
  importsByKey: {
17
- 'a.example:interface.Shared': {
18
- origin: 'a.example',
19
- definition: 'interface',
20
- ref: { origin: 'a.example', kind: 'interface', name: 'Shared' },
21
- key: 'a.example:interface.Shared',
22
- },
23
- 'b.example:interface.Shared': {
24
- origin: 'b.example',
25
- definition: 'interface',
26
- ref: { origin: 'b.example', kind: 'interface', name: 'Shared' },
27
- key: 'b.example:interface.Shared',
28
- },
29
- 'a.example:class.Shared': {
30
- origin: 'a.example',
31
- definition: 'class',
32
- ref: { origin: 'a.example', kind: 'class', name: 'Shared' },
33
- key: 'a.example:class.Shared',
34
- },
35
- 'kernel.astrale.ai:interface.Identity': {
36
- origin: 'kernel.astrale.ai',
37
- definition: 'interface',
38
- ref: { origin: 'kernel.astrale.ai', kind: 'interface', name: 'Identity' },
39
- key: 'kernel.astrale.ai:interface.Identity',
40
- },
23
+ [first.key]: first,
24
+ [second.key]: second,
25
+ [kernel.key]: kernel,
41
26
  },
27
+ importedClassesByKey: {},
28
+ views: {},
29
+ policies: {},
30
+ dependencies: [
31
+ { origin: first.origin, revision: `sha256:${'a'.repeat(64)}` },
32
+ { origin: second.origin, revision: `sha256:${'b'.repeat(64)}` },
33
+ ],
34
+ core: {},
42
35
  } satisfies SchemaIR
43
36
 
44
37
  const overlay = buildOverlay({ ir, domainRoot: '', schemaDir: '' })
45
-
46
38
  expect(overlay.crossDomainImports).toEqual([
47
- { name: 'Shared', origin: 'a.example', definition: 'interface' },
48
- { name: 'Shared', origin: 'b.example', definition: 'interface' },
49
- { name: 'Shared', origin: 'a.example', definition: 'class' },
50
- ])
51
- expect(overlay.mixins).toEqual([
52
- { name: 'Identity', origin: 'kernel.astrale.ai', definition: 'interface' },
53
- ])
54
- })
55
-
56
- test('retains the legacy short-name import fallback', () => {
57
- const ir = {
58
- version: 'legacy',
59
- domain: 'app.example',
60
- types: {},
61
- interfaces: {},
62
- classes: {},
63
- functions: {},
64
- imports: {
65
- Identity: { origin: 'kernel.astrale.ai', definition: 'interface' },
66
- },
67
- } satisfies SchemaIR
68
-
69
- expect(buildOverlay({ ir, domainRoot: '', schemaDir: '' }).mixins).toEqual([
70
- { name: 'Identity', origin: 'kernel.astrale.ai', definition: 'interface' },
39
+ { name: 'Shared', origin: first.origin, ref: first.ref },
40
+ { name: 'Shared', origin: second.origin, ref: second.ref },
71
41
  ])
42
+ expect(overlay.mixins).toEqual([{ name: 'Identity', origin: kernel.origin, ref: kernel.ref }])
43
+ expect(overlay.requires).toEqual(['a.example', 'b.example'])
72
44
  })
@@ -1,13 +1,9 @@
1
1
  /**
2
2
  * overlay.ts — the Studio overlay over the DSL IR. Computes what the IR cannot
3
- * carry. The imports split + requires/postInstall are done here (pure / light
3
+ * carry. The import split and dependency list are computed here; handlerLinks,
4
4
  * static parse). handlerLinks, sourceSpans (+JSDoc) and annotations are filled
5
- * by a ts-morph pass over the current implementation/schema files, with legacy
6
- * domain.ts and runtime/index.ts support retained.
5
+ * by a ts-morph pass over current Schema, Runtime, Action, and Workflow files.
7
6
  */
8
- import { existsSync, readFileSync } from 'node:fs'
9
- import { join } from 'node:path'
10
-
11
7
  import type { CrossDomainImport, SchemaIR, SchemaOverlay } from '../../shared/types'
12
8
 
13
9
  import { buildHandlerLinks, buildSchemaAnnotations, buildSourceSpans } from './overlay-tsmorph'
@@ -22,46 +18,23 @@ export function buildOverlay({ ir, domainRoot, schemaDir }: OverlayArgs): Schema
22
18
  const origin = ir?.domain ?? ''
23
19
  const mixins: CrossDomainImport[] = []
24
20
  const crossDomainImports: CrossDomainImport[] = []
25
- const imports =
26
- ir?.importsByKey !== undefined
27
- ? Object.values(ir.importsByKey).flatMap((descriptor) =>
28
- descriptor.ref ? [[descriptor.ref.name, descriptor] as const] : [],
29
- )
30
- : Object.entries(ir?.imports ?? {})
31
- for (const [name, d] of imports) {
32
- const entry: CrossDomainImport = { name, origin: d.origin, definition: d.definition }
33
- if (d.origin === 'kernel.astrale.ai') mixins.push(entry)
21
+ for (const descriptor of Object.values(ir?.importsByKey ?? {})) {
22
+ const entry: CrossDomainImport = {
23
+ name: descriptor.ref.name,
24
+ origin: descriptor.origin,
25
+ ref: descriptor.ref,
26
+ }
27
+ if (descriptor.origin === 'kernel.astrale.ai') mixins.push(entry)
34
28
  else crossDomainImports.push(entry)
35
29
  }
36
30
 
37
- const { requires, postInstall } = parseCompositionEntry(domainRoot, origin)
38
-
39
31
  return {
40
32
  origin,
41
- requires,
33
+ requires: (ir?.dependencies ?? []).map(({ origin }) => origin),
42
34
  crossDomainImports,
43
35
  mixins,
44
- postInstall,
45
36
  handlerLinks: buildHandlerLinks({ ir, domainRoot }),
46
37
  sourceSpans: buildSourceSpans({ ir, schemaDir }),
47
38
  annotations: buildSchemaAnnotations({ ir }),
48
39
  }
49
40
  }
50
-
51
- function parseCompositionEntry(
52
- root: string,
53
- origin: string,
54
- ): { requires: string[]; postInstall?: string } {
55
- const f = ['implementation.ts', 'domain.ts'].map((file) => join(root, file)).find(existsSync)
56
- let requires: string[] = []
57
- let postInstall: string | undefined
58
- if (f) {
59
- const src = readFileSync(f, 'utf8')
60
- const rm = src.match(/requires\s*:\s*\[([^\]]*)\]/)
61
- if (rm) requires = [...rm[1].matchAll(/['"`]([^'"`]+)['"`]/g)].map((x) => x[1])
62
- const pm = src.match(/postInstall\s*:\s*[`'"]([^`'"]+)[`'"]/)
63
- if (pm)
64
- postInstall = pm[1].replace(/\$\{schema\.domain\}/g, origin).replace(/\$\{[^}]+\}/g, origin)
65
- }
66
- return { requires, postInstall }
67
- }
@@ -5,7 +5,7 @@ import { admittedBundleRevisionFromSdk } from './revision'
5
5
  const REVISION = `sha256:${'a'.repeat(64)}` as const
6
6
 
7
7
  describe('installed Bundle revision', () => {
8
- test('uses bundle admission before asking the cohort schema owner for its revision', () => {
8
+ test('uses bundle admission before asking the installed schema owner for its revision', () => {
9
9
  const calls: string[] = []
10
10
  const root = { format: 'astrale.dsl', version: 'v1', origin: 'notes.example.dev' }
11
11
  const input = { wire: true }
@@ -5,7 +5,7 @@ import { isSchemaRevision } from '../../shared/types'
5
5
  type AnyRecord = Record<string, unknown>
6
6
 
7
7
  /**
8
- * Cross the installed-bundle JSON boundary through one exact SDK cohort, then
8
+ * Cross the installed-bundle JSON boundary through the Domain's installed SDK, then
9
9
  * ask the DSL owner for the root revision. Invalid/unavailable bundles return
10
10
  * null so Studio reports unknown drift instead of inventing an identity.
11
11
  */