@silvana-one/coordination
Version:
Silvana Coordination Client
232 lines • 9.61 kB
JavaScript
import { Transaction } from "@mysten/sui/transactions";
import { SUI_CLOCK_OBJECT_ID } from "@mysten/sui/utils";
import { silvanaRegistryPackage } from "./package.js";
import { fetchSuiDynamicField, fetchSuiObject } from "./fetch.js";
export class AppInstanceManager {
constructor(params) {
this.registry = params.registry;
}
// Note: update_method and remove_method functions are not available in the Move module
// These would need to be implemented in app_instance.move if needed
createAppJob(params) {
const { appInstance, description, method, sequences, data } = params;
// Debug logging
console.log("createAppJob params:", {
appInstance,
description,
method,
sequences,
data: data instanceof Uint8Array ? `Uint8Array(${data.length})` : typeof data,
dataContent: data instanceof Uint8Array ? Array.from(data) : data,
});
const tx = new Transaction();
tx.moveCall({
target: `${silvanaRegistryPackage}::app_instance::create_app_job`,
arguments: [
tx.object(appInstance),
tx.pure.string(method),
tx.pure.option("string", description ?? null),
tx.pure.option("vector<u64>", sequences ?? null),
tx.pure.vector("u8", data),
tx.object(SUI_CLOCK_OBJECT_ID),
],
});
return tx;
}
startAppJob(params) {
const { appInstance, jobId } = params;
const tx = new Transaction();
tx.moveCall({
target: `${silvanaRegistryPackage}::app_instance::start_app_job`,
arguments: [
tx.object(appInstance),
tx.pure.u64(jobId),
tx.object(SUI_CLOCK_OBJECT_ID),
],
});
return tx;
}
completeAppJob(params) {
const { appInstance, jobId } = params;
const tx = new Transaction();
tx.moveCall({
target: `${silvanaRegistryPackage}::app_instance::complete_app_job`,
arguments: [
tx.object(appInstance),
tx.pure.u64(jobId),
tx.object(SUI_CLOCK_OBJECT_ID),
],
});
return tx;
}
failAppJob(params) {
const { appInstance, jobId, error } = params;
const tx = new Transaction();
tx.moveCall({
target: `${silvanaRegistryPackage}::app_instance::fail_app_job`,
arguments: [
tx.object(appInstance),
tx.pure.u64(jobId),
tx.pure.string(error),
tx.object(SUI_CLOCK_OBJECT_ID),
],
});
return tx;
}
async getAppInstance(appInstanceId) {
try {
const appInstance = await fetchSuiObject(appInstanceId);
if (!appInstance)
return undefined;
const fields = appInstance?.data?.content?.fields;
if (!fields)
return undefined;
// Parse methods from VecMap
const methods = {};
const methodsArray = fields?.methods?.fields?.contents;
if (Array.isArray(methodsArray)) {
for (const entry of methodsArray) {
const key = entry?.fields?.key;
const value = entry?.fields?.value;
if (key && value) {
// Value might have a fields property too
const methodFields = value.fields || value;
methods[key] = {
description: methodFields.description ?? undefined,
developer: methodFields.developer,
agent: methodFields.agent,
agentMethod: methodFields.agent_method || methodFields.agentMethod,
};
}
}
}
// Parse metadata from VecMap
const metadata = {};
const metadataArray = fields?.metadata?.fields?.contents;
if (Array.isArray(metadataArray)) {
for (const entry of metadataArray) {
const key = entry?.fields?.key;
const value = entry?.fields?.value;
if (key && value) {
metadata[key] = value;
}
}
}
// Parse kv from VecMap
const kv = {};
const kvArray = fields?.kv?.fields?.contents;
if (Array.isArray(kvArray)) {
for (const entry of kvArray) {
const key = entry?.fields?.key;
const value = entry?.fields?.value;
if (key && value) {
kv[key] = value;
}
}
}
return {
id: fields?.id?.id,
silvanaAppName: fields.silvana_app_name,
description: fields?.description ?? undefined,
metadata,
kv,
methods,
admin: fields.admin,
sequence: Number(fields.sequence),
blockNumber: Number(fields.block_number),
previousBlockTimestamp: Number(fields.previous_block_timestamp),
previousBlockLastSequence: Number(fields.previous_block_last_sequence),
lastProvedBlockNumber: Number(fields.last_proved_block_number),
lastSettledBlockNumber: Number(fields.last_settled_block_number),
lastSettledSequence: Number(fields.last_settled_sequence),
lastPurgedSequence: Number(fields.last_purged_sequence),
isPaused: Boolean(fields.isPaused),
minTimeBetweenBlocks: Number(fields.min_time_between_blocks),
jobsId: String(fields.jobs?.fields?.id?.id ?? ""),
createdAt: Number(fields.created_at),
updatedAt: Number(fields.updated_at),
};
}
catch (error) {
console.error("Error fetching app instance:", error);
return undefined;
}
}
async getAppJob(params) {
try {
const appInstanceObj = await fetchSuiObject(params.appInstance);
if (!appInstanceObj)
return undefined;
// Jobs are embedded in the AppInstance - use correct path
const jobsTableId = appInstanceObj?.data?.content?.fields?.jobs?.fields?.jobs?.fields?.id?.id;
if (!jobsTableId)
return undefined;
const job = await fetchSuiDynamicField({
parentID: jobsTableId,
fieldName: "jobs",
type: "u64",
key: String(params.jobId),
});
if (!job)
return undefined;
const parseStatus = (status) => {
// Check variant field format (used by Sui dynamic fields)
if (status?.variant === "Pending")
return { type: "Pending" };
if (status?.variant === "Running")
return { type: "Running" };
if (status?.variant === "Failed") {
// Get error from fields or from direct property
const error = status?.fields?.error || status?.fields?.[0] || "Unknown error";
return { type: "Failed", error };
}
// Legacy formats
if (status?.Pending !== undefined)
return { type: "Pending" };
if (status?.Running !== undefined)
return { type: "Running" };
if (status?.Failed !== undefined)
return { type: "Failed", error: status.Failed };
return { type: "Pending" };
};
return {
id: job?.id?.id,
jobId: Number(job.job_id),
description: job?.description ?? undefined,
developer: job.developer,
agent: job.agent,
agentMethod: job.agent_method,
app: job.app,
appInstance: job.app_instance,
appInstanceMethod: job.app_instance_method,
sequences: job?.sequences?.map((s) => Number(s)) ?? undefined,
data: new Uint8Array(job.data),
status: parseStatus(job.status),
attempts: Number(job.attempts),
createdAt: Number(job.created_at),
updatedAt: Number(job.updated_at),
};
}
catch (error) {
console.error("Error fetching app job:", error);
return undefined;
}
}
async getAppPendingJobs(appInstance) {
try {
const appInstanceObj = await fetchSuiObject(appInstance);
if (!appInstanceObj)
return [];
// Jobs are embedded in the AppInstance - use correct path
const pendingJobs = appInstanceObj?.data?.content?.fields?.jobs?.fields?.pending_jobs?.fields?.contents;
if (!Array.isArray(pendingJobs))
return [];
return pendingJobs.map((id) => Number(id));
}
catch (error) {
console.error("Error fetching app pending jobs:", error);
return [];
}
}
}
//# sourceMappingURL=app_instance.js.map