@astrale-os/cli 1.0.0-beta.26 → 1.0.0-beta.28

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 (73) hide show
  1. package/README.md +31 -5
  2. package/dist/astrale.js +1201 -358
  3. package/package.json +3 -2
  4. package/src/commands/__tests__/domain-install-operation.test.ts +23 -0
  5. package/src/commands/__tests__/install-identity-override.test.ts +3 -3
  6. package/src/commands/domain/install.ts +19 -11
  7. package/src/commands/get.ts +1 -1
  8. package/src/commands/identity/register.ts +3 -4
  9. package/src/commands/logs.ts +2 -5
  10. package/src/commands/session/analyze.ts +26 -4
  11. package/src/commands/update.ts +61 -40
  12. package/src/lib/__tests__/skills.test.ts +809 -0
  13. package/src/lib/skills/lock.ts +117 -0
  14. package/src/lib/skills/sync.ts +814 -0
  15. package/src/lib/skills.ts +1 -52
  16. package/src/program/__tests__/program.test.ts +1 -1
  17. package/src/setup/steps/skills.ts +40 -38
  18. package/src/telemetry/__tests__/retention.test.ts +180 -0
  19. package/src/telemetry/__tests__/settings.test.ts +102 -0
  20. package/src/telemetry/__tests__/store-scan.test.ts +128 -0
  21. package/src/telemetry/__tests__/trigger.test.ts +33 -4
  22. package/src/telemetry/recorder.ts +6 -3
  23. package/src/telemetry/retention.ts +129 -0
  24. package/src/telemetry/session.ts +18 -12
  25. package/src/telemetry/settings.ts +64 -11
  26. package/src/telemetry/store.ts +87 -9
  27. package/src/telemetry/trigger.ts +16 -28
  28. package/studio/client/dist/assets/index-BFVFs_8x.js +81 -0
  29. package/studio/client/dist/assets/index-DuB9iUdu.css +2 -0
  30. package/studio/client/dist/assets/schema-studio--a0kX3QU.css +1 -0
  31. package/studio/client/dist/assets/schema-studio-DH6oEWME.js +8 -0
  32. package/studio/client/dist/index.html +3 -3
  33. package/studio/server/agent/prompts/anchors.test.ts +17 -4
  34. package/studio/server/agent/prompts/anchors.ts +3 -2
  35. package/studio/server/agent/prompts/system.test.ts +2 -3
  36. package/studio/server/agent/prompts/system.ts +3 -3
  37. package/studio/server/agent/run/preparation.ts +1 -1
  38. package/studio/server/api/context.ts +1 -1
  39. package/studio/server/api/deployment.ts +1 -1
  40. package/studio/server/api/views.ts +2 -2
  41. package/studio/server/api/workspace.ts +1 -1
  42. package/studio/server/cache.test.ts +3 -8
  43. package/studio/server/cache.ts +4 -45
  44. package/studio/server/cli-consumers.test.ts +31 -2
  45. package/studio/server/handoff/copy.ts +1 -1
  46. package/studio/server/introspect/canonical-schema.test.ts +154 -128
  47. package/studio/server/introspect/canonical-schema.ts +183 -302
  48. package/studio/server/introspect/core.ts +14 -21
  49. package/studio/server/introspect/extractor.ts +11 -15
  50. package/studio/server/introspect/overlay-tsmorph.test.ts +1 -5
  51. package/studio/server/introspect/overlay-tsmorph.ts +0 -1
  52. package/studio/server/introspect/overlay.ts +3 -24
  53. package/studio/server/introspect/revision.test.ts +24 -28
  54. package/studio/server/introspect/revision.ts +10 -21
  55. package/studio/server/introspect/runtime.test.ts +14 -14
  56. package/studio/server/introspect/runtime.ts +1 -46
  57. package/studio/server/lifecycle.ts +4 -1
  58. package/studio/server/state/documents.test.ts +69 -0
  59. package/studio/server/state/documents.ts +57 -10
  60. package/studio/server/workspace/updates.ts +23 -1
  61. package/studio/shared/contracts/runtime.ts +4 -0
  62. package/studio/shared/contracts/schema.ts +10 -26
  63. package/studio/shared/contracts/surface.test.ts +2 -2
  64. package/studio/shared/contracts/workspace.ts +3 -0
  65. package/studio/shared/schema/identity.ts +0 -1
  66. package/tsconfig.json +1 -1
  67. package/studio/client/dist/assets/index-C0FAnwNw.css +0 -2
  68. package/studio/client/dist/assets/index-OeJK1TjG.js +0 -81
  69. package/studio/client/dist/assets/schema-studio-BilUNb6Z.css +0 -1
  70. package/studio/client/dist/assets/schema-studio-DRdfu_cQ.js +0 -8
  71. package/studio/server/introspect/core-extractor.ts +0 -30
  72. package/studio/server/introspect/overlay.test.ts +0 -44
  73. package/studio/server/introspect/source-overlay/annotations.ts +0 -14
@@ -1,108 +1,103 @@
1
+ import * as sdk from '@astrale-os/sdk/schema'
2
+ import {
3
+ core,
4
+ defineSchema,
5
+ edgeClass,
6
+ func,
7
+ method,
8
+ nodeClass,
9
+ output,
10
+ property,
11
+ valueSchema,
12
+ view,
13
+ } from '@astrale-os/sdk/schema'
1
14
  import { describe, expect, test } from 'bun:test'
2
15
 
3
16
  import {
4
- admitCanonicalSchemaFromSdk,
5
- closureFromSdk,
17
+ extractCanonicalSchemaFromSdk,
6
18
  findCanonicalDomainSchemaExport,
7
19
  isCanonicalDomainSchemaV1,
8
20
  projectCanonicalCore,
9
- projectCanonicalSchema,
10
21
  type CanonicalDomainSchemaV1,
11
22
  } from './canonical-schema'
12
23
 
13
- const localRef = { origin: 'docs.example.dev', kind: 'class', name: 'Document' } as const
14
- const baseRef = { origin: 'shared.example.dev', kind: 'class', name: 'Named' } as const
15
- const edgeRef = { origin: 'docs.example.dev', kind: 'class', name: 'links' } as const
16
-
17
- const dependency = {
18
- format: 'astrale.dsl',
19
- version: 'v1',
20
- origin: 'shared.example.dev',
21
- classes: {
22
- Named: {
23
- kind: 'node',
24
- properties: { title: { schema: { type: 'string' }, required: true } },
25
- methods: {},
26
- },
27
- },
28
- } satisfies CanonicalDomainSchemaV1
24
+ const string = valueSchema<string>()({ type: 'string' })
25
+ const boolean = valueSchema<boolean>()({ type: 'boolean' })
26
+ const rename = method({
27
+ input: valueSchema<{ title: string }>()({
28
+ type: 'object',
29
+ properties: { title: { type: 'string' } },
30
+ required: ['title'],
31
+ additionalProperties: false,
32
+ }),
33
+ output: boolean,
34
+ auth: 'authorized',
35
+ })
29
36
 
30
- const root = {
31
- format: 'astrale.dsl',
32
- version: 'v1',
33
- origin: 'docs.example.dev',
34
- dependencies: [{ origin: dependency.origin, revision: `sha256:${'1'.repeat(64)}` }],
35
- classes: {
36
- Document: {
37
- kind: 'node',
38
- extends: [baseRef],
39
- properties: {
40
- slug: { schema: { type: 'string', minLength: 1 }, required: true },
41
- },
42
- methods: {
43
- rename: {
44
- input: {
45
- type: 'object',
46
- properties: { title: { type: 'string' } },
47
- required: ['title'],
48
- },
49
- output: { mode: 'value', schema: { type: 'boolean' } },
50
- auth: 'authorized',
51
- static: false,
52
- inheritance: 'default',
53
- },
54
- },
55
- },
56
- links: {
57
- kind: 'edge',
58
- orientation: 'directed',
59
- endpoints: {
60
- source: { role: 'source', accepts: [localRef], outgoing: '1' },
61
- target: { role: 'target', accepts: [baseRef], incoming: '0..*' },
62
- },
63
- properties: {},
64
- methods: {},
65
- },
66
- },
37
+ const Named = nodeClass({ properties: { title: string } })
38
+ const dependency = defineSchema('shared.example.dev', { classes: { Named } })
39
+ const Document = nodeClass({
40
+ description: 'A document.',
41
+ icon: '<svg />',
42
+ extends: [Named],
43
+ properties: { slug: property(string, { description: 'Stable slug.' }) },
44
+ methods: { rename },
45
+ })
46
+ const links = edgeClass.directed({
47
+ source: { as: 'source', accepts: [Document], outgoing: '1' },
48
+ target: { as: 'target', accepts: [Named], incoming: '0..*' },
49
+ })
50
+ const primary = core.node(Document, { slug: 'primary', title: 'Primary' })
51
+ const primaryLink = core.edge(primary, links, primary, {})
52
+ const schema = defineSchema('docs.example.dev', {
53
+ dependencies: { shared: dependency },
54
+ classes: { Document, links },
67
55
  functions: {
68
- search: {
69
- input: { type: 'object', properties: { text: { type: 'string' } }, required: ['text'] },
70
- output: { mode: 'stream', item: { type: 'string' } },
71
- auth: 'authenticated',
72
- },
73
- },
74
- views: {
75
- documents: {
76
- target: { kind: 'definition', definitions: [localRef] },
77
- },
56
+ search: func({ input: string, output: output.stream(string), auth: 'authenticated' }),
78
57
  },
79
- core: {
80
- nodes: {
81
- primary: { class: localRef, properties: { slug: 'primary' } },
82
- },
83
- edges: [
84
- {
85
- class: edgeRef,
86
- source: { origin: 'docs.example.dev', kind: 'core', name: 'primary' },
87
- target: { kind: 'domain', origin: 'docs.example.dev' },
88
- },
89
- ],
90
- },
91
- } satisfies CanonicalDomainSchemaV1
58
+ views: { documents: view({ target: Document }) },
59
+ core: { nodes: { primary }, edges: [primaryLink] },
60
+ })
61
+ const namedRef = { origin: dependency.origin, kind: 'class', name: 'Named' } as const
62
+ const documentRef = { origin: schema.origin, kind: 'class', name: 'Document' } as const
63
+
64
+ const sdkModule = sdk
92
65
 
93
66
  describe('canonical Schema projection', () => {
94
- test('recognizes and selects only portable V1 schema roots', () => {
95
- expect(isCanonicalDomainSchemaV1(root)).toBe(true)
96
- expect(isCanonicalDomainSchemaV1({ ...root, version: 'v0' })).toBe(false)
97
- expect(findCanonicalDomainSchemaExport({ other: dependency, schema: root })).toBe(root)
67
+ test('recognizes exported V1 roots and preserves an invalid root as a preview', () => {
68
+ expect(isCanonicalDomainSchemaV1(schema)).toBe(true)
69
+ expect(findCanonicalDomainSchemaExport({ other: dependency, schema })).toBe(schema)
70
+
71
+ const invalid = {
72
+ format: 'astrale.dsl',
73
+ version: 'v1',
74
+ origin: 'not an origin',
75
+ dependencies: {},
76
+ classes: {},
77
+ functions: {},
78
+ policies: {},
79
+ views: {},
80
+ core: { nodes: {}, edges: [] },
81
+ } as CanonicalDomainSchemaV1
82
+ expect(extractCanonicalSchemaFromSdk(sdkModule, invalid)).toMatchObject({
83
+ status: 'preview',
84
+ revision: null,
85
+ ir: { domain: 'not an origin' },
86
+ })
98
87
  })
99
88
 
100
- test('projects Classes, exact inheritance/imports, callables, Views, and edges', () => {
101
- const { ir } = projectCanonicalSchema(root, [dependency])
102
- expect(ir.classes.Document).toMatchObject({
103
- extendsRefs: [baseRef],
104
- properties: { slug: { type: 'string', minLength: 1 } },
89
+ test('uses the resolved Domain for Classes, dependency footprint, callables, and Views', () => {
90
+ const extraction = extractCanonicalSchemaFromSdk(sdkModule, schema)
91
+ expect(extraction.status).toBe('admitted')
92
+ expect(extraction.revision).toMatch(/^sha256:[0-9a-f]{64}$/)
93
+ expect(extraction.ir.classes.Document).toMatchObject({
94
+ icon: '<svg />',
95
+ extendsRefs: [namedRef],
96
+ properties: { slug: { type: 'string' } },
105
97
  required: ['slug'],
98
+ propertyMetadata: {
99
+ slug: { required: true, visibility: 'public', description: 'Stable slug.' },
100
+ },
106
101
  methods: {
107
102
  rename: {
108
103
  input: { required: ['title'] },
@@ -111,69 +106,100 @@ describe('canonical Schema projection', () => {
111
106
  },
112
107
  },
113
108
  })
114
- expect(ir.importsByKey['shared.example.dev:class.Named']).toEqual({
115
- origin: baseRef.origin,
116
- ref: baseRef,
109
+ expect(extraction.ir.importsByKey['shared.example.dev:class.Named']).toEqual({
110
+ origin: namedRef.origin,
111
+ ref: namedRef,
117
112
  key: 'shared.example.dev:class.Named',
118
113
  })
119
- expect(ir.importedClassesByKey['shared.example.dev:class.Named']?.properties.title).toEqual({
120
- type: 'string',
114
+ expect(
115
+ extraction.ir.importedClassesByKey['shared.example.dev:class.Named']?.properties.title,
116
+ ).toEqual({ type: 'string' })
117
+ expect(extraction.ir.classes.links.endpoints).toEqual([
118
+ {
119
+ name: 'source',
120
+ types: ['Document'],
121
+ refs: [documentRef],
122
+ cardinality: { min: 1, max: 1 },
123
+ },
124
+ {
125
+ name: 'target',
126
+ types: ['Named'],
127
+ refs: [namedRef],
128
+ cardinality: { min: 0, max: null },
129
+ },
130
+ ])
131
+ expect(extraction.ir.functions.search).toMatchObject({
132
+ auth: 'authenticated',
133
+ output: { mode: 'stream' },
134
+ })
135
+ expect(extraction.ir.views.documents.target).toEqual({
136
+ kind: 'definition',
137
+ definitions: [documentRef],
121
138
  })
122
- expect(ir.classes.links.endpoints).toEqual([
123
- { name: 'source', types: ['Document'], refs: [localRef], cardinality: { min: 1, max: 1 } },
124
- { name: 'target', types: ['Named'], refs: [baseRef], cardinality: { min: 0, max: null } },
139
+ expect(extraction.ir.dependencies).toEqual([
140
+ { origin: dependency.origin, revision: sdk.schema.revision(dependency) },
125
141
  ])
126
- expect(ir.functions.search).toMatchObject({ auth: 'authenticated', output: { mode: 'stream' } })
127
- expect(ir.views?.documents.target).toEqual({ kind: 'definition', definitions: [localRef] })
128
- expect(ir.views?.documents).not.toHaveProperty('auth')
129
142
  })
130
143
 
131
- test('projects canonical Core coordinates without importing Application or Runtime', () => {
132
- expect(projectCanonicalCore(root)).toEqual({
133
- domain: root.origin,
144
+ test('projects Core from the same admitted root without a second Schema import', () => {
145
+ const extraction = extractCanonicalSchemaFromSdk(sdkModule, schema)
146
+ expect(projectCanonicalCore(extraction.root)).toEqual({
147
+ domain: schema.origin,
134
148
  nodes: [
135
149
  {
136
150
  path: '/:docs.example.dev:core.primary',
137
151
  className: 'Document',
138
- data: { slug: 'primary' },
152
+ data: {
153
+ 'docs.example.dev:class.Document.property.slug': 'primary',
154
+ 'shared.example.dev:class.Named.property.title': 'Primary',
155
+ },
139
156
  },
140
157
  ],
141
158
  edges: [
142
159
  {
143
160
  from: '/:docs.example.dev:core.primary',
144
- to: '/:docs.example.dev',
161
+ to: '/:docs.example.dev:core.primary',
145
162
  edgeName: 'links',
163
+ data: {},
146
164
  },
147
165
  ],
148
166
  })
149
167
  })
150
168
 
151
- test('uses the authored SDK for closure, admission, revision, and resolved-source proof', () => {
152
- const accepted = { ...root }
153
- const revision = `sha256:${'a'.repeat(64)}` as const
154
- const sdk = {
155
- bundle: { create: () => ({ root: accepted, closure: [dependency] }) },
169
+ test('delegates admission, resolution, and dependency reachability to the DSL', () => {
170
+ const calls: string[] = []
171
+ const wrapped = {
172
+ ...sdk,
173
+ bundle: {
174
+ ...sdk.bundle,
175
+ create(value: typeof schema) {
176
+ calls.push('bundle.create')
177
+ return sdk.bundle.create(value)
178
+ },
179
+ accept(value: unknown) {
180
+ calls.push('bundle.accept')
181
+ return sdk.bundle.accept(value)
182
+ },
183
+ },
156
184
  schema: {
157
- accept: (candidate: unknown) => candidate,
158
- revision: () => revision,
159
- resolve: (candidate: unknown) => ({ source: candidate, origin: root.origin }),
185
+ ...sdk.schema,
186
+ resolve(value: typeof schema) {
187
+ calls.push('schema.resolve')
188
+ return sdk.schema.resolve(value)
189
+ },
190
+ compareDependencyMeaning(source: typeof schema, target: typeof dependency) {
191
+ calls.push('schema.compareDependencyMeaning')
192
+ return sdk.schema.compareDependencyMeaning(source, target)
193
+ },
160
194
  },
161
- }
162
- expect(closureFromSdk(sdk, accepted)).toEqual([dependency])
163
- expect(admitCanonicalSchemaFromSdk(sdk, accepted)).toEqual({
164
- status: 'admitted',
165
- root: accepted,
166
- closure: [dependency],
167
- revision,
168
- })
169
- })
195
+ } as unknown as typeof sdk
170
196
 
171
- test('fails to preview when the SDK cannot prove admission', () => {
172
- expect(admitCanonicalSchemaFromSdk({}, root)).toEqual({
173
- status: 'preview',
174
- root,
175
- closure: [],
176
- revision: null,
177
- })
197
+ expect(extractCanonicalSchemaFromSdk(wrapped, schema).status).toBe('admitted')
198
+ expect(calls).toEqual([
199
+ 'bundle.create',
200
+ 'bundle.accept',
201
+ 'schema.resolve',
202
+ 'schema.compareDependencyMeaning',
203
+ ])
178
204
  })
179
205
  })