@silvana-one/coordination 1.0.25 → 1.0.26

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 (53) hide show
  1. package/dist/node/agent.d.ts +43 -15
  2. package/dist/node/agent.js +129 -31
  3. package/dist/node/agent.js.map +1 -1
  4. package/dist/node/app_instance.d.ts +58 -0
  5. package/dist/node/app_instance.js +203 -0
  6. package/dist/node/app_instance.js.map +1 -0
  7. package/dist/node/execute.d.ts +1 -0
  8. package/dist/node/execute.js +10 -2
  9. package/dist/node/execute.js.map +1 -1
  10. package/dist/node/index.cjs +750 -27
  11. package/dist/node/index.d.ts +4 -0
  12. package/dist/node/index.js +4 -0
  13. package/dist/node/index.js.map +1 -1
  14. package/dist/node/job.d.ts +57 -0
  15. package/dist/node/job.js +168 -0
  16. package/dist/node/job.js.map +1 -0
  17. package/dist/node/package.d.ts +1 -0
  18. package/dist/node/package.js +6 -0
  19. package/dist/node/package.js.map +1 -0
  20. package/dist/node/test.d.ts +48 -0
  21. package/dist/node/test.js +278 -0
  22. package/dist/node/test.js.map +1 -0
  23. package/dist/tsconfig.tsbuildinfo +1 -1
  24. package/dist/tsconfig.web.tsbuildinfo +1 -1
  25. package/dist/web/agent.d.ts +43 -15
  26. package/dist/web/agent.js +129 -31
  27. package/dist/web/agent.js.map +1 -1
  28. package/dist/web/app_instance.d.ts +58 -0
  29. package/dist/web/app_instance.js +203 -0
  30. package/dist/web/app_instance.js.map +1 -0
  31. package/dist/web/execute.d.ts +1 -0
  32. package/dist/web/execute.js +10 -2
  33. package/dist/web/execute.js.map +1 -1
  34. package/dist/web/index.d.ts +4 -0
  35. package/dist/web/index.js +4 -0
  36. package/dist/web/index.js.map +1 -1
  37. package/dist/web/job.d.ts +57 -0
  38. package/dist/web/job.js +168 -0
  39. package/dist/web/job.js.map +1 -0
  40. package/dist/web/package.d.ts +1 -0
  41. package/dist/web/package.js +6 -0
  42. package/dist/web/package.js.map +1 -0
  43. package/dist/web/test.d.ts +48 -0
  44. package/dist/web/test.js +278 -0
  45. package/dist/web/test.js.map +1 -0
  46. package/package.json +5 -3
  47. package/src/agent.ts +204 -56
  48. package/src/app_instance.ts +264 -0
  49. package/src/execute.ts +11 -2
  50. package/src/index.ts +4 -0
  51. package/src/job.ts +223 -0
  52. package/src/package.ts +6 -0
  53. package/src/test.ts +383 -0
package/src/job.ts ADDED
@@ -0,0 +1,223 @@
1
+ import { Transaction } from "@mysten/sui/transactions";
2
+ import { SUI_CLOCK_OBJECT_ID } from "@mysten/sui/utils";
3
+ import { silvanaRegistryPackage } from "./package.js";
4
+ import { fetchSuiDynamicField, fetchSuiObject } from "./fetch.js";
5
+
6
+ export type JobStatus =
7
+ | { type: "Pending" }
8
+ | { type: "Running" }
9
+ | { type: "Failed"; error: string };
10
+
11
+ export interface Job {
12
+ id: string;
13
+ jobId: number;
14
+ description?: string;
15
+ developer: string;
16
+ agent: string;
17
+ agentMethod: string;
18
+ app: string;
19
+ appInstance: string;
20
+ appInstanceMethod: string;
21
+ sequences?: number[];
22
+ data: Uint8Array;
23
+ status: JobStatus;
24
+ attempts: number;
25
+ createdAt: number;
26
+ updatedAt: number;
27
+ }
28
+
29
+ export interface CreateJobParams {
30
+ jobs: string; // Jobs object ID
31
+ description?: string;
32
+ developer: string;
33
+ agent: string;
34
+ agentMethod: string;
35
+ app: string;
36
+ appInstance: string;
37
+ appInstanceMethod: string;
38
+ sequences?: number[];
39
+ data: Uint8Array;
40
+ }
41
+
42
+ export class JobManager {
43
+ private readonly jobs: string;
44
+
45
+ constructor(params: { jobs: string }) {
46
+ this.jobs = params.jobs;
47
+ }
48
+
49
+ static createJobs(maxAttempts?: number): Transaction {
50
+ const tx = new Transaction();
51
+ tx.moveCall({
52
+ target: `${silvanaRegistryPackage}::jobs::create_jobs`,
53
+ arguments: [
54
+ tx.pure.option("u8", maxAttempts ?? null),
55
+ ],
56
+ });
57
+ return tx;
58
+ }
59
+
60
+ createJob(params: CreateJobParams): Transaction {
61
+ const {
62
+ description,
63
+ developer,
64
+ agent,
65
+ agentMethod,
66
+ app,
67
+ appInstance,
68
+ appInstanceMethod,
69
+ sequences,
70
+ data,
71
+ } = params;
72
+
73
+ const tx = new Transaction();
74
+ tx.moveCall({
75
+ target: `${silvanaRegistryPackage}::jobs::create_job`,
76
+ arguments: [
77
+ tx.object(this.jobs),
78
+ tx.pure.option("string", description ?? null),
79
+ tx.pure.string(developer),
80
+ tx.pure.string(agent),
81
+ tx.pure.string(agentMethod),
82
+ tx.pure.string(app),
83
+ tx.pure.string(appInstance),
84
+ tx.pure.string(appInstanceMethod),
85
+ tx.pure.option("vector<u64>", sequences ?? null),
86
+ tx.pure.vector("u8", Array.from(data)),
87
+ tx.object(SUI_CLOCK_OBJECT_ID),
88
+ ],
89
+ });
90
+ return tx;
91
+ }
92
+
93
+ startJob(jobId: number): Transaction {
94
+ const tx = new Transaction();
95
+ tx.moveCall({
96
+ target: `${silvanaRegistryPackage}::jobs::start_job`,
97
+ arguments: [
98
+ tx.object(this.jobs),
99
+ tx.pure.u64(jobId),
100
+ tx.object(SUI_CLOCK_OBJECT_ID),
101
+ ],
102
+ });
103
+ return tx;
104
+ }
105
+
106
+ completeJob(jobId: number): Transaction {
107
+ const tx = new Transaction();
108
+ tx.moveCall({
109
+ target: `${silvanaRegistryPackage}::jobs::complete_job`,
110
+ arguments: [
111
+ tx.object(this.jobs),
112
+ tx.pure.u64(jobId),
113
+ ],
114
+ });
115
+ return tx;
116
+ }
117
+
118
+ failJob(params: { jobId: number; error: string }): Transaction {
119
+ const { jobId, error } = params;
120
+ const tx = new Transaction();
121
+ tx.moveCall({
122
+ target: `${silvanaRegistryPackage}::jobs::fail_job`,
123
+ arguments: [
124
+ tx.object(this.jobs),
125
+ tx.pure.u64(jobId),
126
+ tx.pure.string(error),
127
+ tx.object(SUI_CLOCK_OBJECT_ID),
128
+ ],
129
+ });
130
+ return tx;
131
+ }
132
+
133
+ updateMaxAttempts(maxAttempts: number): Transaction {
134
+ const tx = new Transaction();
135
+ tx.moveCall({
136
+ target: `${silvanaRegistryPackage}::jobs::update_max_attempts`,
137
+ arguments: [
138
+ tx.object(this.jobs),
139
+ tx.pure.u64(maxAttempts),
140
+ ],
141
+ });
142
+ return tx;
143
+ }
144
+
145
+ async getJob(jobId: number): Promise<Job | undefined> {
146
+ try {
147
+ const jobsObject = await fetchSuiObject(this.jobs);
148
+ if (!jobsObject) return undefined;
149
+
150
+ const jobsTableId = (jobsObject as any)?.jobs?.fields?.id?.id;
151
+ if (!jobsTableId) return undefined;
152
+
153
+ const job = await fetchSuiDynamicField({
154
+ parentID: jobsTableId,
155
+ fieldName: "jobs",
156
+ type: "u64",
157
+ key: String(jobId),
158
+ });
159
+
160
+ if (!job) return undefined;
161
+
162
+ const parseStatus = (status: any): JobStatus => {
163
+ if (status?.Pending !== undefined) return { type: "Pending" };
164
+ if (status?.Running !== undefined) return { type: "Running" };
165
+ if (status?.Failed !== undefined) return { type: "Failed", error: status.Failed };
166
+ return { type: "Pending" };
167
+ };
168
+
169
+ return {
170
+ id: (job as any)?.id?.id,
171
+ jobId: Number((job as any).job_id),
172
+ description: (job as any)?.description ?? undefined,
173
+ developer: (job as any).developer,
174
+ agent: (job as any).agent,
175
+ agentMethod: (job as any).agent_method,
176
+ app: (job as any).app,
177
+ appInstance: (job as any).app_instance,
178
+ appInstanceMethod: (job as any).app_instance_method,
179
+ sequences: (job as any)?.sequences?.map((s: string) => Number(s)) ?? undefined,
180
+ data: new Uint8Array((job as any).data),
181
+ status: parseStatus((job as any).status),
182
+ attempts: Number((job as any).attempts),
183
+ createdAt: Number((job as any).created_at),
184
+ updatedAt: Number((job as any).updated_at),
185
+ };
186
+ } catch (error) {
187
+ console.error("Error fetching job:", error);
188
+ return undefined;
189
+ }
190
+ }
191
+
192
+ async getPendingJobs(): Promise<number[]> {
193
+ try {
194
+ const jobsObject = await fetchSuiObject(this.jobs);
195
+ if (!jobsObject) return [];
196
+
197
+ const pendingJobs = (jobsObject as any)?.pending_jobs?.fields?.contents;
198
+ if (!Array.isArray(pendingJobs)) return [];
199
+
200
+ return pendingJobs.map((id: string) => Number(id));
201
+ } catch (error) {
202
+ console.error("Error fetching pending jobs:", error);
203
+ return [];
204
+ }
205
+ }
206
+
207
+ async getNextPendingJob(): Promise<number | undefined> {
208
+ const pendingJobs = await this.getPendingJobs();
209
+ return pendingJobs.length > 0 ? pendingJobs[0] : undefined;
210
+ }
211
+
212
+ async getMaxAttempts(): Promise<number> {
213
+ try {
214
+ const jobsObject = await fetchSuiObject(this.jobs);
215
+ if (!jobsObject) return 3; // default value
216
+
217
+ return Number((jobsObject as any)?.max_attempts ?? 3);
218
+ } catch (error) {
219
+ console.error("Error fetching max attempts:", error);
220
+ return 3;
221
+ }
222
+ }
223
+ }
package/src/package.ts ADDED
@@ -0,0 +1,6 @@
1
+ // Get Silvana registry package address from environment variables
2
+ // Compatible with both Node.js and Next.js environments
3
+ export const silvanaRegistryPackage =
4
+ process.env.SILVANA_REGISTRY_PACKAGE ??
5
+ process.env.NEXT_PUBLIC_SILVANA_REGISTRY_PACKAGE ??
6
+ "@silvana/agent";
package/src/test.ts ADDED
@@ -0,0 +1,383 @@
1
+ import { AgentRegistry } from "./agent.js";
2
+ import { AppInstanceManager } from "./app_instance.js";
3
+ import { executeTx, waitTx } from "./execute.js";
4
+ import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519";
5
+ import { Transaction } from "@mysten/sui/transactions";
6
+
7
+ export interface TestRegistryConfig {
8
+ registryName?: string;
9
+ developerName?: string;
10
+ developerGithub?: string;
11
+ developerImage?: string;
12
+ developerDescription?: string;
13
+ developerSite?: string;
14
+ appName?: string;
15
+ appDescription?: string;
16
+ appImage?: string;
17
+ appSite?: string;
18
+ testAgentName?: string;
19
+ testAgentImage?: string;
20
+ testAgentChains?: string[];
21
+ }
22
+
23
+ export interface TestRegistryResult {
24
+ registryAddress: string;
25
+ developerName: string;
26
+ appName: string;
27
+ agentName?: string;
28
+ registry: AgentRegistry;
29
+ appInstanceManager: AppInstanceManager;
30
+ keyPair: Ed25519Keypair;
31
+ address: string;
32
+ }
33
+
34
+ export interface TestAppResult {
35
+ testAppAddress: string;
36
+ appInstanceAddress: string;
37
+ appInstanceCapAddress: string;
38
+ registryAddress: string;
39
+ appName: string;
40
+ keyPair: Ed25519Keypair;
41
+ address: string;
42
+ appInstanceManager: AppInstanceManager;
43
+ }
44
+
45
+ /**
46
+ * Creates a test Silvana Registry with a test developer, app, and optionally an agent
47
+ * This is a helper function for testing that sets up a complete test environment
48
+ */
49
+ export async function createTestRegistry(
50
+ config: TestRegistryConfig = {}
51
+ ): Promise<TestRegistryResult> {
52
+ // Default values
53
+ const registryName = config.registryName ?? "Test Silvana Registry";
54
+ const developerName = config.developerName ?? "TestDev";
55
+ const developerGithub = config.developerGithub ?? "testdev";
56
+ const developerImage = config.developerImage;
57
+ const developerDescription = config.developerDescription ?? "Test developer for automated testing";
58
+ const developerSite = config.developerSite ?? "https://test.dev";
59
+
60
+ const appName = config.appName ?? "TestApp";
61
+ const appDescription = config.appDescription ?? "Test application for automated testing";
62
+ const appImage = config.appImage;
63
+ const appSite = config.appSite ?? "https://testapp.dev";
64
+
65
+ const testAgentName = config.testAgentName ?? "TestAgent";
66
+ const testAgentImage = config.testAgentImage;
67
+ const testAgentChains = config.testAgentChains ?? ["sui-testnet", "sui-devnet"];
68
+
69
+ // Get key from environment
70
+ const key = process.env.SUI_KEY;
71
+ if (!key) {
72
+ throw new Error("SUI_KEY is not set in environment");
73
+ }
74
+
75
+ const keyPair = Ed25519Keypair.fromSecretKey(key);
76
+ const address = keyPair.toSuiAddress();
77
+
78
+ console.log("Creating test registry with sender address:", address);
79
+ console.log("Chain:", process.env.SUI_CHAIN);
80
+
81
+ // Step 1: Create the registry
82
+ console.log("Step 1: Creating registry...");
83
+ const createRegistryTx = AgentRegistry.createAgentRegistry({
84
+ name: registryName,
85
+ });
86
+ createRegistryTx.setSender(address);
87
+ createRegistryTx.setGasBudget(100_000_000);
88
+
89
+ const registryResult = await executeTx({
90
+ tx: createRegistryTx,
91
+ keyPair,
92
+ });
93
+
94
+ if (!registryResult) {
95
+ throw new Error("Failed to create registry - no result");
96
+ }
97
+
98
+ if (registryResult.error) {
99
+ throw new Error(`Failed to create registry: ${registryResult.error}`);
100
+ }
101
+
102
+ if (!registryResult.tx?.objectChanges) {
103
+ throw new Error("Failed to create registry - no object changes");
104
+ }
105
+
106
+ // Find the created registry object
107
+ const registryObject = registryResult.tx.objectChanges.find(
108
+ (obj: any) => obj.type === "created" && obj.objectType?.includes("::registry::SilvanaRegistry")
109
+ );
110
+
111
+ if (!registryObject || !('objectId' in registryObject)) {
112
+ throw new Error("Failed to find created registry object");
113
+ }
114
+
115
+ const registryAddress = registryObject.objectId;
116
+ console.log("Registry created with address:", registryAddress);
117
+
118
+ if (registryResult.digest) {
119
+ await waitTx(registryResult.digest);
120
+ }
121
+
122
+ // Create registry instance for further operations
123
+ const registry = new AgentRegistry({ registry: registryAddress });
124
+ const appInstanceManager = new AppInstanceManager({ registry: registryAddress });
125
+
126
+ // Step 2: Create developer
127
+ console.log("Step 2: Creating developer...");
128
+ const createDeveloperTx = registry.createDeveloper({
129
+ name: developerName,
130
+ github: developerGithub,
131
+ image: developerImage,
132
+ description: developerDescription,
133
+ site: developerSite,
134
+ });
135
+ createDeveloperTx.setSender(address);
136
+ createDeveloperTx.setGasBudget(100_000_000);
137
+
138
+ const developerResult = await executeTx({
139
+ tx: createDeveloperTx,
140
+ keyPair,
141
+ });
142
+
143
+ if (!developerResult) {
144
+ throw new Error("Failed to create developer - no result");
145
+ }
146
+
147
+ if (developerResult.error) {
148
+ throw new Error(`Failed to create developer: ${developerResult.error}`);
149
+ }
150
+
151
+ if (developerResult.digest) {
152
+ await waitTx(developerResult.digest);
153
+ }
154
+ console.log("Developer created:", developerName);
155
+
156
+ // Step 3: Create app (using registry::add_app)
157
+ console.log("Step 3: Creating app...");
158
+ const createAppTx = new Transaction();
159
+ createAppTx.moveCall({
160
+ target: `${process.env.SILVANA_REGISTRY_PACKAGE ?? "@silvana/agent"}::registry::add_app`,
161
+ arguments: [
162
+ createAppTx.object(registryAddress),
163
+ createAppTx.pure.string(appName),
164
+ createAppTx.pure.option("string", appDescription),
165
+ createAppTx.object("0x6"), // SUI_CLOCK_OBJECT_ID
166
+ ],
167
+ });
168
+ createAppTx.setSender(address);
169
+ createAppTx.setGasBudget(100_000_000);
170
+
171
+ const appResult = await executeTx({
172
+ tx: createAppTx,
173
+ keyPair,
174
+ });
175
+
176
+ if (!appResult) {
177
+ throw new Error("Failed to create app - no result");
178
+ }
179
+
180
+ if (appResult.error) {
181
+ throw new Error(`Failed to create app: ${appResult.error}`);
182
+ }
183
+
184
+ if (appResult.digest) {
185
+ await waitTx(appResult.digest);
186
+ }
187
+ console.log("App created:", appName);
188
+
189
+ // Step 4: Optionally create agent (if test agent name is provided with chains)
190
+ let agentName: string | undefined;
191
+ if (config.testAgentName && config.testAgentChains) {
192
+ console.log("Step 4: Creating agent...");
193
+ const createAgentTx = registry.createAgent({
194
+ developer: developerName,
195
+ name: testAgentName,
196
+ image: testAgentImage,
197
+ description: "Test agent for automated testing",
198
+ chains: testAgentChains,
199
+ });
200
+ createAgentTx.setSender(address);
201
+ createAgentTx.setGasBudget(100_000_000);
202
+
203
+ const agentResult = await executeTx({
204
+ tx: createAgentTx,
205
+ keyPair,
206
+ });
207
+
208
+ if (!agentResult) {
209
+ throw new Error("Failed to create agent - no result");
210
+ }
211
+
212
+ if (agentResult.error) {
213
+ throw new Error(`Failed to create agent: ${agentResult.error}`);
214
+ }
215
+
216
+ if (agentResult.digest) {
217
+ await waitTx(agentResult.digest);
218
+ }
219
+ agentName = testAgentName;
220
+ console.log("Agent created:", testAgentName);
221
+ }
222
+
223
+ console.log("Test registry setup complete!");
224
+ console.log("Registry address:", registryAddress);
225
+ console.log("Developer:", developerName);
226
+ console.log("App:", appName);
227
+ if (agentName) {
228
+ console.log("Agent:", agentName);
229
+ }
230
+
231
+ return {
232
+ registryAddress,
233
+ developerName,
234
+ appName,
235
+ agentName,
236
+ registry,
237
+ appInstanceManager,
238
+ keyPair,
239
+ address,
240
+ };
241
+ }
242
+
243
+ /**
244
+ * Creates a TestApp with an initialized AppInstance using the new TestApp module
245
+ * This uses the create_test_app function from the Move contract
246
+ */
247
+ export async function createTestApp(
248
+ registryAddress: string,
249
+ appName: string = "test_app"
250
+ ): Promise<TestAppResult> {
251
+ // Get key from environment
252
+ const key = process.env.SUI_KEY;
253
+ if (!key) {
254
+ throw new Error("SUI_KEY is not set in environment");
255
+ }
256
+
257
+ const keyPair = Ed25519Keypair.fromSecretKey(key);
258
+ const address = keyPair.toSuiAddress();
259
+
260
+ console.log("Creating TestApp with AppInstance...");
261
+ console.log("Registry address:", registryAddress);
262
+ console.log("Sender address:", address);
263
+
264
+ // First, check if the app already exists by trying to create it
265
+ // If it fails with "already exists" error, that's fine - we can continue
266
+ const createAppTx = new Transaction();
267
+ createAppTx.moveCall({
268
+ target: `${process.env.SILVANA_REGISTRY_PACKAGE ?? "@silvana/agent"}::registry::add_app`,
269
+ arguments: [
270
+ createAppTx.object(registryAddress),
271
+ createAppTx.pure.string(appName),
272
+ createAppTx.pure.option("string", "Test app for TestApp module"),
273
+ createAppTx.object("0x6"), // SUI_CLOCK_OBJECT_ID
274
+ ],
275
+ });
276
+ createAppTx.setSender(address);
277
+ createAppTx.setGasBudget(100_000_000);
278
+
279
+ const appResult = await executeTx({
280
+ tx: createAppTx,
281
+ keyPair,
282
+ showErrors: false, // Don't throw, we'll handle the error
283
+ });
284
+
285
+ if (appResult?.digest && !appResult.error) {
286
+ await waitTx(appResult.digest);
287
+ console.log("App created:", appName);
288
+ } else if (appResult?.error?.includes("0) in command 0")) {
289
+ // Error code 0 from dynamic_field::add means the field already exists
290
+ console.log("App already exists, continuing:", appName);
291
+ } else if (appResult?.error) {
292
+ throw new Error(`Failed to create app for TestApp: ${appResult.error}`);
293
+ }
294
+
295
+ // Call create_test_app to create a TestApp with an AppInstance
296
+ const tx = new Transaction();
297
+ tx.moveCall({
298
+ target: `${process.env.SILVANA_REGISTRY_PACKAGE ?? "@silvana/agent"}::test_app::create_test_app`,
299
+ arguments: [
300
+ tx.object(registryAddress),
301
+ tx.object("0x6"), // SUI_CLOCK_OBJECT_ID
302
+ ],
303
+ });
304
+
305
+ tx.setSender(address);
306
+ tx.setGasBudget(100_000_000);
307
+
308
+ const result = await executeTx({
309
+ tx,
310
+ keyPair,
311
+ });
312
+
313
+ if (!result) {
314
+ throw new Error("Failed to create TestApp - no result");
315
+ }
316
+
317
+ if (result.error) {
318
+ throw new Error(`Failed to create TestApp: ${result.error}`);
319
+ }
320
+
321
+ if (!result.tx) {
322
+ throw new Error("Failed to create TestApp - no transaction result");
323
+ }
324
+
325
+ // Wait for the transaction to be confirmed
326
+ if (result.digest) {
327
+ const confirmedTx = await waitTx(result.digest);
328
+ result.tx = confirmedTx;
329
+ }
330
+
331
+ if (!result?.tx?.objectChanges) {
332
+ throw new Error("Failed to create TestApp - no object changes");
333
+ }
334
+
335
+ // Find the created TestApp object (it will be shared)
336
+ const testAppObject = result.tx.objectChanges.find(
337
+ (obj: any) =>
338
+ obj.type === "created" &&
339
+ obj.objectType?.includes("::test_app::TestApp")
340
+ );
341
+
342
+ if (!testAppObject || !('objectId' in testAppObject)) {
343
+ throw new Error("Failed to find created TestApp object");
344
+ }
345
+
346
+ // Find the created AppInstance object (it will be shared)
347
+ const appInstanceObject = result.tx.objectChanges.find(
348
+ (obj: any) =>
349
+ obj.type === "created" &&
350
+ obj.objectType?.includes("::app_instance::AppInstance")
351
+ );
352
+
353
+ if (!appInstanceObject || !('objectId' in appInstanceObject)) {
354
+ throw new Error("Failed to find created AppInstance object");
355
+ }
356
+
357
+ const testAppAddress = testAppObject.objectId;
358
+ const appInstanceAddress = appInstanceObject.objectId;
359
+
360
+ // AppInstanceCap is stored inside the TestApp, not as a separate shared object
361
+ // We'll use the TestApp address as a placeholder since the cap is embedded
362
+ const appInstanceCapAddress = testAppAddress;
363
+
364
+ console.log("TestApp created:", testAppAddress);
365
+ console.log("AppInstance created:", appInstanceAddress);
366
+ console.log("AppInstanceCap created:", appInstanceCapAddress);
367
+
368
+ const appInstanceManager = new AppInstanceManager({ registry: registryAddress });
369
+
370
+ return {
371
+ testAppAddress,
372
+ appInstanceAddress,
373
+ appInstanceCapAddress,
374
+ registryAddress,
375
+ appName,
376
+ keyPair,
377
+ address,
378
+ appInstanceManager,
379
+ };
380
+ }
381
+
382
+ // Note: createTestAppInstance has been removed. Use createTestApp instead, which creates
383
+ // a TestApp with an embedded AppInstance using the Move test_app module.