@stream44.studio/t44 0.4.0-rc.30 → 0.4.0-rc.32

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 CHANGED
@@ -3,7 +3,7 @@
3
3
  <td><a href="https://Stream44.Studio"><img src=".o/stream44.studio/assets/Icon-v1.svg" width="42" height="42"></a></td>
4
4
  <td><strong><a href="https://Stream44.Studio">Stream44 Studio</a></strong><br/>Open Development Project</td>
5
5
  <td>Preview release for community feedback.<br/>Get in touch on <a href="https://discord.gg/9eBcQXEJAN">discord</a>.</td>
6
- <td>Hand Designed<br/><b>AI Coded Alpha</a></td>
6
+ <td>Designed by Hand<br/><b>AI assisted Code</a></td>
7
7
  </tr>
8
8
  </table>
9
9
 
@@ -198,7 +198,7 @@ export async function capsule({
198
198
  const { readdir, readFile } = await import('fs/promises')
199
199
  const { join } = await import('path')
200
200
  const rootDir = await this.rootDir
201
- const keyDir = join(rootDir, '@t44.sh~t44~structs~WorkspaceKey')
201
+ const keyDir = join(rootDir, '@stream44.studio~t44~structs~WorkspaceKey')
202
202
  try {
203
203
  const files = await readdir(keyDir)
204
204
  const keys: Array<{ name: string; did: string; createdAt?: string }> = []
@@ -250,7 +250,7 @@ export async function capsule({
250
250
  const { readdir, readFile } = await import('fs/promises')
251
251
  const { join } = await import('path')
252
252
  const rootDir = await this.rootDir
253
- const rackDir = join(rootDir, '@t44.sh~t44~structs~ProjectRack')
253
+ const rackDir = join(rootDir, '@stream44.studio~t44~structs~ProjectRack')
254
254
  try {
255
255
  const files = await readdir(rackDir)
256
256
  const racks: Array<{ name: string; did: string; createdAt?: string }> = []
@@ -65,7 +65,7 @@ export async function capsule({
65
65
  const workspaceConfig = await this.$WorkspaceConfig.config
66
66
  const getProjectionDir = (capsuleName: string) => join(
67
67
  workspaceConfig.rootDir,
68
- '.~o/workspace.foundation/@t44.sh~t44~caps~ProjectDeployment',
68
+ '.~o/workspace.foundation/@stream44.studio~t44~caps~ProjectDeployment',
69
69
  capsuleName.replace(/\//g, '~')
70
70
  )
71
71
 
@@ -184,7 +184,7 @@ export async function capsule({
184
184
  const publishingApi = {
185
185
  getProjectionDir: (capsuleName: string) => join(
186
186
  this.WorkspaceConfig.workspaceRootDir,
187
- '.~o/workspace.foundation/@t44.sh~t44~caps~ProjectPublishing',
187
+ '.~o/workspace.foundation/@stream44.studio~t44~caps~ProjectPublishing',
188
188
  capsuleName.replace(/\//g, '~')
189
189
  ),
190
190
  }
@@ -189,7 +189,7 @@ export async function capsule({
189
189
  const pullingApi = {
190
190
  getProjectionDir: (capsuleName: string) => join(
191
191
  this.WorkspaceConfig.workspaceRootDir,
192
- '.~o/workspace.foundation/@t44.sh~t44~caps~ProjectPulling',
192
+ '.~o/workspace.foundation/@stream44.studio~t44~caps~ProjectPulling',
193
193
  capsuleName.replace(/\//g, '~')
194
194
  ),
195
195
  }
@@ -30,7 +30,7 @@ export async function capsule({
30
30
  const normalizedUri = repoUri.replace(/[\/]/g, '~')
31
31
  return join(
32
32
  this.WorkspaceConfig.workspaceRootDir,
33
- '.~o/workspace.foundation/@t44.sh~t44~caps~ProjectRepository/stage',
33
+ '.~o/workspace.foundation/@stream44.studio~t44~caps~ProjectRepository/stage',
34
34
  normalizedUri
35
35
  )
36
36
  }
@@ -1,4 +1,4 @@
1
- $schema: ../../../../.~o/workspace.foundation/@t44.sh~t44~caps~JsonSchemas/@t44.sh~t44~structs~WorkspaceConfigFile.json
1
+ $schema: ../../../../.~o/workspace.foundation/@stream44.studio~t44~caps~JsonSchemas/@stream44.studio~t44~structs~WorkspaceConfigFile.json
2
2
  extends:
3
3
  - ./WorkspaceShell.yaml
4
4
  "#@stream44.studio/t44/structs/WorkspaceCliConfig":
@@ -696,7 +696,7 @@ async function writeConfigMetadataCache(
696
696
  workspaceRootDir: string
697
697
  ): Promise<void> {
698
698
  const { mkdir, writeFile } = await import('fs/promises')
699
- const metaCacheDir = join(workspaceRootDir, '.~o', 'workspace.foundation', '@t44.sh~t44~caps~WorkspaceEntityFact', '@t44.sh~t44~structs~WorkspaceConfigFileMeta')
699
+ const metaCacheDir = join(workspaceRootDir, '.~o', 'workspace.foundation', '@stream44.studio~t44~caps~WorkspaceEntityFact', '@stream44.studio~t44~structs~WorkspaceConfigFileMeta')
700
700
 
701
701
  await mkdir(metaCacheDir, { recursive: true })
702
702
 
@@ -847,7 +847,7 @@ function resolveSchemaRef(dataFilePath: string, capsuleName: string, workspaceRo
847
847
  workspaceRootDir,
848
848
  '.~o',
849
849
  'workspace.foundation',
850
- '@t44.sh~t44~caps~JsonSchemas'
850
+ '@stream44.studio~t44~caps~JsonSchemas'
851
851
  )
852
852
  const schemaFilename = capsuleName.replace(/\//g, '~') + '.json'
853
853
  const schemaFilePath = join(jsonSchemaDir, schemaFilename)
@@ -77,7 +77,7 @@ export async function capsule({
77
77
  const workspaceConfigStruct = workspaceConfig?.['#@stream44.studio/t44/structs/WorkspaceConfig'] || {}
78
78
  const workspaceName = workspaceConfigStruct.name
79
79
  const connectionType = this.capsuleName.replace(/\//g, '~')
80
- return join(registryDir, '@t44.sh~t44~caps~WorkspaceConnection', workspaceName, `${connectionType}.json`)
80
+ return join(registryDir, '@stream44.studio~t44~caps~WorkspaceConnection', workspaceName, `${connectionType}.json`)
81
81
  }
82
82
  },
83
83
  getRelativeFilepath: {
@@ -47,14 +47,14 @@ export async function capsule({
47
47
  // Build resolver context with all required paths
48
48
  const registryDir = await this.Home.registryDir
49
49
  const foundationDir = join(workspaceRootDir, '.~o', 'workspace.foundation')
50
- const homeRegistryConnectionsDir = join(registryDir, '@t44.sh~t44~caps~WorkspaceConnection', workspaceName)
50
+ const homeRegistryConnectionsDir = join(registryDir, '@stream44.studio~t44~caps~WorkspaceConnection', workspaceName)
51
51
 
52
52
  const resolver = Resolver({
53
53
  workspaceRootDir,
54
54
  workspaceName,
55
- schemasDir: join(foundationDir, '@t44.sh~t44~caps~JsonSchemas'),
56
- factsDir: join(foundationDir, '@t44.sh~t44~caps~WorkspaceEntityFact'),
57
- metaCacheDir: join(foundationDir, '@t44.sh~t44~caps~WorkspaceEntityFact', '@t44.sh~t44~structs~WorkspaceConfigFileMeta'),
55
+ schemasDir: join(foundationDir, '@stream44.studio~t44~caps~JsonSchemas'),
56
+ factsDir: join(foundationDir, '@stream44.studio~t44~caps~WorkspaceEntityFact'),
57
+ metaCacheDir: join(foundationDir, '@stream44.studio~t44~caps~WorkspaceEntityFact', '@stream44.studio~t44~structs~WorkspaceConfigFileMeta'),
58
58
  homeRegistryConnectionsDir
59
59
  })
60
60
 
@@ -100,7 +100,7 @@ export async function capsule({
100
100
 
101
101
  // Build schema lookup: entity name (without #) → schema file path
102
102
  const schemaMap = new Map<string, string>()
103
- const schemasDir = join(workspaceRootDir, '.~o', 'workspace.foundation', '@t44.sh~t44~caps~JsonSchemas')
103
+ const schemasDir = join(workspaceRootDir, '.~o', 'workspace.foundation', '@stream44.studio~t44~caps~JsonSchemas')
104
104
  for (const [schemaId] of schemas) {
105
105
  const entityName = schemaId.replace(/\.v\d+$/, '')
106
106
  // Schema files don't include version in filename, only in $id
@@ -1,4 +1,4 @@
1
- $schema: ../../../../../../../.~o/workspace.foundation/@t44.sh~t44~caps~JsonSchemas/@t44.sh~t44~structs~WorkspaceConfigFile.json
1
+ $schema: ../../../../../../../.~o/workspace.foundation/@stream44.studio~t44~caps~JsonSchemas/@stream44.studio~t44~structs~WorkspaceConfigFile.json
2
2
  "#@stream44.studio/t44/structs/WorkspaceShellConfig":
3
3
  env:
4
4
  default: null
@@ -68,6 +68,19 @@ export async function capsule({
68
68
  modified = true
69
69
  }
70
70
 
71
+ // Also replace tilde-separated variant of scoped names
72
+ // (e.g. @stream44.studio~FramespaceGenesis → @stream44.studio~FramespaceGenesis)
73
+ const pubTilde = publicName.replace(/^(@[^/]+)\//, '$1~')
74
+ if (pubTilde !== publicName) {
75
+ const wsTilde = workspaceName.replace(/^(@[^/]+)\//, '$1~')
76
+ const tildeRegex = new RegExp(pubTilde.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g')
77
+ const replacedTilde = content.replace(tildeRegex, wsTilde)
78
+ if (replacedTilde !== content) {
79
+ content = replacedTilde
80
+ modified = true
81
+ }
82
+ }
83
+
71
84
  // Also replace regex-escaped versions of the public name
72
85
  // (e.g. @stream44\.studio\/dco in test patterns)
73
86
  const pubEscaped = publicName.replace(/[.*+?^${}()|[\]/\\]/g, '\\$&')
@@ -126,6 +139,19 @@ export async function capsule({
126
139
  modified = true
127
140
  }
128
141
 
142
+ // Also replace tilde-separated variant of scoped names
143
+ // (e.g. @stream44.studio~FramespaceGenesis → @stream44.studio~FramespaceGenesis)
144
+ const wsTilde = workspaceName.replace(/^(@[^/]+)\//, '$1~')
145
+ if (wsTilde !== workspaceName) {
146
+ const pubTilde = publicName.replace(/^(@[^/]+)\//, '$1~')
147
+ const tildeRegex = new RegExp(wsTilde.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g')
148
+ const replacedTilde = content.replace(tildeRegex, pubTilde)
149
+ if (replacedTilde !== content) {
150
+ content = replacedTilde
151
+ modified = true
152
+ }
153
+ }
154
+
129
155
  // Also replace regex-escaped versions of the workspace name
130
156
  // (e.g. @stream44\.studio\/encapsulate in test patterns)
131
157
  const wsEscaped = workspaceName.replace(/[.*+?^${}()|[\]/\\]/g, '\\$&')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stream44.studio/t44",
3
- "version": "0.4.0-rc.30",
3
+ "version": "0.4.0-rc.32",
4
4
  "license": "LGPL",
5
5
  "repository": {
6
6
  "type": "git",
@@ -71,15 +71,15 @@
71
71
  "@ucanto/server": "^11.0.3",
72
72
  "json-schema-ref-resolver": "^3.0.0",
73
73
  "json-stable-stringify": "^1.3.0",
74
- "@stream44.studio/encapsulate": "^0.4.0-rc.25",
74
+ "@stream44.studio/encapsulate": "^0.4.0-rc.28",
75
75
  "turbo": "^2.7.5"
76
76
  },
77
77
  "optionalDependencies": {
78
- "@stream44.studio/t44-bunny.net": "^0.1.0-rc.6",
79
- "@stream44.studio/t44-vercel.com": "^0.1.0-rc.6",
80
- "@stream44.studio/t44-github.com": "^0.1.0-rc.6",
81
- "@stream44.studio/t44-dynadot.com": "^0.1.0-rc.6",
82
- "@stream44.studio/t44-npmjs.com": "^0.1.0-rc.6"
78
+ "@stream44.studio/t44-bunny.net": "^0.1.0-rc.8",
79
+ "@stream44.studio/t44-vercel.com": "^0.1.0-rc.8",
80
+ "@stream44.studio/t44-github.com": "^0.1.0-rc.8",
81
+ "@stream44.studio/t44-dynadot.com": "^0.1.0-rc.8",
82
+ "@stream44.studio/t44-npmjs.com": "^0.1.0-rc.8"
83
83
  },
84
84
  "devDependencies": {
85
85
  "@types/bun": "^1.3.4",
@@ -3,6 +3,8 @@
3
3
  import * as bunTest from 'bun:test'
4
4
  import { describe, it, expect } from 'bun:test'
5
5
  import { run } from './standalone-rt'
6
+ import * as fs from 'fs/promises'
7
+ import { join } from 'path'
6
8
 
7
9
  // Top-level run() — shared across all tests (e.g. for workbenchDir)
8
10
  const { test: { workbenchDir } } = await run(async ({ encapsulate, CapsulePropertyTypes, makeImportStack }: any) => {
@@ -147,4 +149,121 @@ describe('standalone-rt multiple run() calls', () => {
147
149
  expect(workbenchDir).toBeDefined();
148
150
  expect(typeof workbenchDir).toBe('string');
149
151
  });
152
+
153
+ it('should write .events.json when captureEvents is true', async () => {
154
+ // Clean up any existing spine-instances directory
155
+ const spineInstancesDir = join(import.meta.dir, '.~o/encapsulate.dev/spine-instances');
156
+ try {
157
+ await fs.rm(spineInstancesDir, { recursive: true });
158
+ } catch { }
159
+
160
+ const result = await run(async ({ encapsulate, CapsulePropertyTypes, makeImportStack }: any) => {
161
+ const spine = await encapsulate({
162
+ '#@stream44.studio/encapsulate/spine-contracts/CapsuleSpineContract.v0': {
163
+ '#@stream44.studio/encapsulate/structs/Capsule': {},
164
+ '#': {
165
+ counter: {
166
+ type: CapsulePropertyTypes.Literal,
167
+ value: 0,
168
+ },
169
+ increment: {
170
+ type: CapsulePropertyTypes.Function,
171
+ value: function (this: any) {
172
+ this.counter++;
173
+ return this.counter;
174
+ }
175
+ }
176
+ }
177
+ }
178
+ }, { importMeta: import.meta, importStack: makeImportStack(), capsuleName: '@stream44.studio/t44/standalone-rt.test.events' })
179
+ return { spine }
180
+ }, async ({ spine, apis }: any) => {
181
+ const api = apis[spine.capsuleSourceLineRef];
182
+ // Trigger some membrane events
183
+ api.increment();
184
+ api.increment();
185
+ const finalCount = api.counter;
186
+ return { finalCount, spineRef: spine.capsuleSourceLineRef };
187
+ }, { importMeta: import.meta, runFromSnapshot: true, captureEvents: true })
188
+
189
+ expect(result.finalCount).toBe(2);
190
+
191
+ // Verify .events.json was written
192
+ const dirs = await fs.readdir(spineInstancesDir);
193
+ expect(dirs.length).toBeGreaterThan(0);
194
+
195
+ const eventsFile = join(spineInstancesDir, dirs[0], 'root-capsule.events.json');
196
+ await fs.access(eventsFile); // Should not throw
197
+
198
+ const eventsContent = await fs.readFile(eventsFile, 'utf-8');
199
+ const events = JSON.parse(eventsContent);
200
+
201
+ expect(Array.isArray(events)).toBe(true);
202
+ expect(events.length).toBeGreaterThan(0);
203
+
204
+ // Verify event structure
205
+ const callEvents = events.filter((e: any) => e.event === 'call');
206
+ expect(callEvents.length).toBe(2); // Two increment() calls
207
+
208
+ const getEvents = events.filter((e: any) => e.event === 'get');
209
+ expect(getEvents.length).toBeGreaterThan(0); // counter property reads
210
+
211
+ // Verify membrane property is captured
212
+ for (const event of events) {
213
+ expect(event.membrane).toBeDefined();
214
+ expect(['external', 'internal']).toContain(event.membrane);
215
+ }
216
+
217
+ // Verify we have both internal and external events
218
+ const externalEvents = events.filter((e: any) => e.membrane === 'external');
219
+ const internalEvents = events.filter((e: any) => e.membrane === 'internal');
220
+ expect(externalEvents.length).toBeGreaterThan(0);
221
+ expect(internalEvents.length).toBeGreaterThan(0);
222
+
223
+ // Clean up
224
+ await fs.rm(spineInstancesDir, { recursive: true });
225
+ });
226
+
227
+ it('should NOT write .events.json when captureEvents is false/undefined', async () => {
228
+ // Clean up any existing spine-instances directory
229
+ const spineInstancesDir = join(import.meta.dir, '.~o/encapsulate.dev/spine-instances');
230
+ try {
231
+ await fs.rm(spineInstancesDir, { recursive: true });
232
+ } catch { }
233
+
234
+ await run(async ({ encapsulate, CapsulePropertyTypes, makeImportStack }: any) => {
235
+ const spine = await encapsulate({
236
+ '#@stream44.studio/encapsulate/spine-contracts/CapsuleSpineContract.v0': {
237
+ '#@stream44.studio/encapsulate/structs/Capsule': {},
238
+ '#': {
239
+ value: {
240
+ type: CapsulePropertyTypes.Literal,
241
+ value: 'test',
242
+ },
243
+ }
244
+ }
245
+ }, { importMeta: import.meta, importStack: makeImportStack(), capsuleName: '@stream44.studio/t44/standalone-rt.test.no-events' })
246
+ return { spine }
247
+ }, async ({ spine, apis }: any) => {
248
+ return apis[spine.capsuleSourceLineRef].value;
249
+ }, { importMeta: import.meta, runFromSnapshot: false }) // No captureEvents
250
+
251
+ // Verify .events.json was NOT written (directory may or may not exist)
252
+ try {
253
+ const dirs = await fs.readdir(spineInstancesDir);
254
+ for (const dir of dirs) {
255
+ const eventsFile = join(spineInstancesDir, dir, 'root-capsule.events.json');
256
+ try {
257
+ await fs.access(eventsFile);
258
+ throw new Error('events.json should not exist');
259
+ } catch (e: any) {
260
+ if (e.message === 'events.json should not exist') throw e;
261
+ // Expected: file does not exist
262
+ }
263
+ }
264
+ } catch (e: any) {
265
+ // Directory doesn't exist - that's fine
266
+ if (e.code !== 'ENOENT') throw e;
267
+ }
268
+ });
150
269
  });
package/standalone-rt.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  const startTime = Date.now()
6
6
 
7
7
  import { resolve, join } from 'path'
8
- import { access } from 'fs/promises'
8
+ import { access, readdir, writeFile } from 'fs/promises'
9
9
  import chalk from 'chalk'
10
10
  import { CapsuleSpineFactory } from "@stream44.studio/encapsulate/spine-factories/CapsuleSpineFactory.v0"
11
11
  import { CapsuleSpineContract } from "@stream44.studio/encapsulate/spine-contracts/CapsuleSpineContract.v0/Membrane.v0"
@@ -24,13 +24,16 @@ async function findPackageRoot(startDir: string): Promise<string> {
24
24
  }
25
25
  }
26
26
 
27
- export async function run(encapsulateHandler: any, runHandler: any, options?: { importMeta?: { dir: string }, runFromSnapshot?: boolean }) {
27
+ export async function run(encapsulateHandler: any, runHandler: any, options?: { importMeta?: { dir: string }, runFromSnapshot?: boolean, captureEvents?: boolean }) {
28
28
 
29
29
  const timing = process.argv.includes('--trace') ? TimingObserver({ startTime }) : undefined
30
+ const shouldCaptureEvents = options?.captureEvents === true || process.argv.includes('--capture-events')
31
+ const saveMembraneEvents = shouldCaptureEvents
30
32
 
31
33
  timing?.recordMajor('INIT SPINE')
32
34
 
33
35
  const eventsByKey = new Map<string, any>()
36
+ const capturedEvents: any[] = []
34
37
 
35
38
  const spineFilesystemRoot = options?.importMeta?.dir
36
39
  ? await findPackageRoot(options.importMeta.dir)
@@ -44,7 +47,7 @@ export async function run(encapsulateHandler: any, runHandler: any, options?: {
44
47
  ['#' + CapsuleSpineContract['#']]: CapsuleSpineContract
45
48
  },
46
49
  timing,
47
- onMembraneEvent: timing ? (event: any) => {
50
+ onMembraneEvent: (timing || shouldCaptureEvents) ? (event: any) => {
48
51
  const instanceId = event.target?.spineContractCapsuleInstanceId
49
52
  const eventIndex = event.eventIndex
50
53
 
@@ -52,31 +55,81 @@ export async function run(encapsulateHandler: any, runHandler: any, options?: {
52
55
  const key = `${eventIndex}`
53
56
  eventsByKey.set(key, event)
54
57
 
55
- let capsuleRef = event.target?.capsuleSourceLineRef
56
- let prop = event.target?.prop
57
- let callerLocation = event.caller ? `${event.caller.filepath}:${event.caller.line}` : 'unknown'
58
- const eventType = event.event
59
-
60
- // For call-result events, look up the original call event to get all info
61
- if (eventType === 'call-result') {
62
- const callKey = `${event.callEventIndex}`
63
- const callEvent = eventsByKey.get(callKey)
64
- if (callEvent) {
65
- capsuleRef = callEvent.target?.capsuleSourceLineRef || capsuleRef
66
- prop = callEvent.target?.prop || prop
67
- if (callEvent.caller) {
68
- callerLocation = `${callEvent.caller.filepath}:${callEvent.caller.line}`
58
+ // Collect event for .events.json persistence
59
+ if (shouldCaptureEvents) {
60
+ // Serialize value/result safely they may contain capsule proxies with cyclic references
61
+ let safeValue: any = undefined
62
+ let safeResult: any = undefined
63
+ try {
64
+ if (event.value !== undefined) {
65
+ JSON.stringify(event.value)
66
+ safeValue = event.value
69
67
  }
68
+ } catch {
69
+ safeValue = typeof event.value === 'object' ? `[${typeof event.value}]` : String(event.value)
70
70
  }
71
+ try {
72
+ if (event.result !== undefined) {
73
+ JSON.stringify(event.result)
74
+ safeResult = event.result
75
+ }
76
+ } catch {
77
+ safeResult = typeof event.result === 'object' ? `[${typeof event.result}]` : String(event.result)
78
+ }
79
+ // Serialize caller safely — strip stack array which may contain non-serializable objects
80
+ let safeCaller: any = undefined
81
+ if (event.caller) {
82
+ const { stack, ...callerRest } = event.caller
83
+ safeCaller = { ...callerRest }
84
+ if (stack) {
85
+ try {
86
+ JSON.stringify(stack)
87
+ safeCaller.stack = stack
88
+ } catch {
89
+ // stack frames may contain non-serializable objects
90
+ }
91
+ }
92
+ }
93
+
94
+ capturedEvents.push({
95
+ eventIndex: event.eventIndex,
96
+ event: event.event,
97
+ membrane: event.membrane,
98
+ target: event.target ? { ...event.target } : undefined,
99
+ value: safeValue,
100
+ result: safeResult,
101
+ caller: safeCaller,
102
+ callEventIndex: event.callEventIndex,
103
+ })
71
104
  }
72
105
 
73
- console.error(
74
- chalk.gray(`[${eventIndex}]`),
75
- chalk.cyan(eventType.padEnd(12)),
76
- chalk.yellow(capsuleRef),
77
- chalk.magenta(`.${prop}`),
78
- chalk.dim(`from ${callerLocation}`)
79
- )
106
+ if (timing) {
107
+ let capsuleRef = event.target?.capsuleSourceLineRef
108
+ let prop = event.target?.prop
109
+ let callerLocation = event.caller ? `${event.caller.filepath}:${event.caller.line}` : 'unknown'
110
+ const eventType = event.event
111
+
112
+ // For call-result events, look up the original call event to get all info
113
+ if (eventType === 'call-result') {
114
+ const callKey = `${event.callEventIndex}`
115
+ const callEvent = eventsByKey.get(callKey)
116
+ if (callEvent) {
117
+ capsuleRef = callEvent.target?.capsuleSourceLineRef || capsuleRef
118
+ prop = callEvent.target?.prop || prop
119
+ if (callEvent.caller) {
120
+ callerLocation = `${callEvent.caller.filepath}:${callEvent.caller.line}`
121
+ }
122
+ }
123
+ }
124
+
125
+ console.error(
126
+ chalk.gray(`[${eventIndex}]`),
127
+ chalk.cyan(eventType.padEnd(12)),
128
+ chalk.yellow(capsuleRef),
129
+ chalk.magenta(`.${prop}`),
130
+ chalk.dim(`from ${callerLocation}`)
131
+ )
132
+ }
80
133
  } : undefined
81
134
  })
82
135
 
@@ -122,16 +175,40 @@ export async function run(encapsulateHandler: any, runHandler: any, options?: {
122
175
  },
123
176
  ['@stream44.studio/t44-ipfs.tech/caps/IpfsWorkbench']: {
124
177
  '#': {
125
- cacheDir: join(spineFilesystemRoot, '.~o/workspace.foundation', '@t44.sh~t44-ipfs.tech~caps~IpfsWorkbench', 'daemons')
178
+ cacheDir: join(spineFilesystemRoot, '.~o/workspace.foundation', '@stream44.studio~t44-ipfs.tech~caps~IpfsWorkbench', 'daemons')
126
179
  }
127
180
  }
128
- }
181
+ },
182
+ saveMembraneEvents,
129
183
  }, async (opts) => {
130
- return runHandler({
184
+ const handlerResult = await runHandler({
131
185
  ...opts,
132
186
  ...(exportedApi || {}),
133
187
  spineFilesystemRoot
134
188
  })
189
+
190
+ // Write .events.json alongside .sit.json files when capture is enabled
191
+ if (saveMembraneEvents && capturedEvents.length > 0 && options?.importMeta?.dir) {
192
+ try {
193
+ const spineInstancesDir = join(options.importMeta.dir, '.~o/encapsulate.dev/spine-instances')
194
+ await access(spineInstancesDir)
195
+ const dirs = await readdir(spineInstancesDir)
196
+ for (const dir of dirs) {
197
+ const dirPath = join(spineInstancesDir, String(dir))
198
+ const eventsFile = join(dirPath, 'root-capsule.events.json')
199
+ try {
200
+ await writeFile(eventsFile, JSON.stringify(capturedEvents, null, 2), 'utf-8')
201
+ } catch (writeErr: any) {
202
+ if (process.env.DEBUG_EVENTS) console.error(`[standalone-rt] write failed for ${eventsFile}: ${writeErr.message}`)
203
+ }
204
+ }
205
+ if (process.env.DEBUG_EVENTS) console.error(`[standalone-rt] Wrote events to ${dirs.length} dirs (${capturedEvents.length} events)`)
206
+ } catch (outerErr: any) {
207
+ if (process.env.DEBUG_EVENTS) console.error(`[standalone-rt] events write skipped: ${outerErr.message}`)
208
+ }
209
+ }
210
+
211
+ return handlerResult
135
212
  })
136
213
 
137
214
  timing?.recordMajor('DONE')
package/workspace-rt.ts CHANGED
@@ -135,7 +135,7 @@ export async function run(encapsulateHandler: any, runHandler: any, options?: {
135
135
  },
136
136
  ['@stream44.studio/t44-ipfs.tech/caps/IpfsWorkbench']: {
137
137
  '#': {
138
- cacheDir: join(workspaceRootDir, '.~o/workspace.foundation', '@t44.sh~t44-ipfs.tech~caps~IpfsWorkbench', 'daemons')
138
+ cacheDir: join(workspaceRootDir, '.~o/workspace.foundation', '@stream44.studio~t44-ipfs.tech~caps~IpfsWorkbench', 'daemons')
139
139
  }
140
140
  }
141
141
  }
package/workspace.yaml CHANGED
@@ -1,3 +1,3 @@
1
- $schema: ../../../.~o/workspace.foundation/@t44.sh~t44~caps~JsonSchemas/@t44.sh~t44~structs~WorkspaceConfigFile.json
1
+ $schema: ../../../.~o/workspace.foundation/@stream44.studio~t44~caps~JsonSchemas/@stream44.studio~t44~structs~WorkspaceConfigFile.json
2
2
  extends:
3
3
  - ./caps/WorkspaceConfig.yaml