@nosana/node 1.1.14-rc → 1.1.15-rc

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 (77) hide show
  1. package/dist/.env.dev +1 -2
  2. package/dist/.env.prd +1 -2
  3. package/dist/package.json +4 -2
  4. package/dist/src/NodeManager/checkForMaintenance.d.ts +1 -0
  5. package/dist/src/NodeManager/checkForMaintenance.js +40 -0
  6. package/dist/src/NodeManager/clients/hostManager/index.d.ts +4 -0
  7. package/dist/src/NodeManager/clients/hostManager/index.js +25 -0
  8. package/dist/src/NodeManager/clients/maintenanceMiddleware.d.ts +2 -0
  9. package/dist/src/NodeManager/clients/maintenanceMiddleware.js +11 -0
  10. package/dist/src/NodeManager/clients/maintenanceState.d.ts +10 -0
  11. package/dist/src/NodeManager/clients/maintenanceState.js +66 -0
  12. package/dist/src/NodeManager/clients/types.utils.d.ts +45 -0
  13. package/dist/src/NodeManager/configs/configs.d.ts +1 -3
  14. package/dist/src/NodeManager/configs/configs.js +1 -3
  15. package/dist/src/NodeManager/errors/NodeBannedError.d.ts +3 -0
  16. package/dist/src/NodeManager/errors/NodeBannedError.js +6 -0
  17. package/dist/src/NodeManager/errors/NodeNotQualifiedError.d.ts +3 -0
  18. package/dist/src/NodeManager/errors/NodeNotQualifiedError.js +6 -0
  19. package/dist/src/NodeManager/errors/NodeNotRegisteredError.d.ts +3 -0
  20. package/dist/src/NodeManager/errors/NodeNotRegisteredError.js +6 -0
  21. package/dist/src/NodeManager/index.js +25 -57
  22. package/dist/src/NodeManager/monitoring/log/NodeLog.d.ts +0 -1
  23. package/dist/src/NodeManager/monitoring/log/NodeLog.js +11 -38
  24. package/dist/src/NodeManager/monitoring/log/console/ConsoleLogger.d.ts +3 -0
  25. package/dist/src/NodeManager/monitoring/log/console/ConsoleLogger.js +33 -5
  26. package/dist/src/NodeManager/monitoring/ping/PingHandler.d.ts +2 -4
  27. package/dist/src/NodeManager/monitoring/ping/PingHandler.js +31 -26
  28. package/dist/src/NodeManager/monitoring/proxy/loggingProxy.d.ts +2 -1
  29. package/dist/src/NodeManager/monitoring/proxy/loggingProxy.js +2 -3
  30. package/dist/src/NodeManager/node/Node.d.ts +4 -6
  31. package/dist/src/NodeManager/node/Node.js +16 -29
  32. package/dist/src/NodeManager/node/api/ApiHandler.js +2 -3
  33. package/dist/src/NodeManager/node/api/middlewares/index.d.ts +0 -1
  34. package/dist/src/NodeManager/node/api/middlewares/index.js +0 -1
  35. package/dist/src/NodeManager/node/api/routes/post/index.d.ts +0 -1
  36. package/dist/src/NodeManager/node/api/routes/post/index.js +0 -1
  37. package/dist/src/NodeManager/node/grid/gridHandler.d.ts +2 -8
  38. package/dist/src/NodeManager/node/grid/gridHandler.js +10 -143
  39. package/dist/src/NodeManager/node/health/healthHandler.d.ts +1 -1
  40. package/dist/src/NodeManager/node/health/healthHandler.js +13 -11
  41. package/dist/src/NodeManager/node/job/JobRegistry.js +5 -3
  42. package/dist/src/NodeManager/node/job/defination/strategy/IpfsJobDefinitionStrategy.d.ts +1 -3
  43. package/dist/src/NodeManager/node/job/defination/strategy/IpfsJobDefinitionStrategy.js +2 -4
  44. package/dist/src/NodeManager/node/job/jobExternalUtil.d.ts +2 -3
  45. package/dist/src/NodeManager/node/job/jobExternalUtil.js +4 -4
  46. package/dist/src/NodeManager/node/job/jobHandler.js +5 -3
  47. package/dist/src/NodeManager/node/key/keyHandler.d.ts +0 -1
  48. package/dist/src/NodeManager/node/key/keyHandler.js +1 -38
  49. package/dist/src/NodeManager/node/market/benchmark.d.ts +15 -0
  50. package/dist/src/NodeManager/node/market/benchmark.js +84 -0
  51. package/dist/src/NodeManager/node/market/hostManager.d.ts +114 -0
  52. package/dist/src/NodeManager/node/market/hostManager.js +112 -0
  53. package/dist/src/NodeManager/node/market/marketAccess.d.ts +8 -0
  54. package/dist/src/NodeManager/node/market/marketAccess.js +53 -0
  55. package/dist/src/NodeManager/node/market/marketHandler.d.ts +10 -3
  56. package/dist/src/NodeManager/node/market/marketHandler.js +88 -6
  57. package/dist/src/NodeManager/node/register/index.d.ts +2 -12
  58. package/dist/src/NodeManager/node/register/index.js +12 -56
  59. package/dist/src/NodeManager/node/resource/resourceManager.d.ts +2 -1
  60. package/dist/src/NodeManager/node/resource/resourceManager.js +4 -4
  61. package/dist/src/NodeManager/sdk/index.js +4 -14
  62. package/dist/src/NodeManager/utils/wslCheck.js +1 -1
  63. package/dist/src/cli/createNosanaCli.js +2 -0
  64. package/dist/src/ipfs/IpfsClient.d.ts +7 -0
  65. package/dist/src/ipfs/IpfsClient.js +17 -0
  66. package/npm-shrinkwrap.json +30 -2
  67. package/package.json +5 -3
  68. package/dist/src/NodeManager/client/index.d.ts +0 -4
  69. package/dist/src/NodeManager/client/index.js +0 -14
  70. package/dist/src/NodeManager/node/api/middlewares/rest/verifyBackendSignatureMiddleware.d.ts +0 -3
  71. package/dist/src/NodeManager/node/api/middlewares/rest/verifyBackendSignatureMiddleware.js +0 -20
  72. package/dist/src/NodeManager/node/api/routes/post/node-validation.d.ts +0 -4
  73. package/dist/src/NodeManager/node/api/routes/post/node-validation.js +0 -1787
  74. package/dist/src/NodeManager/node/specs/specsHandler.d.ts +0 -17
  75. package/dist/src/NodeManager/node/specs/specsHandler.js +0 -135
  76. package/dist/src/NodeManager/node/specs/type.d.ts +0 -15
  77. /package/dist/src/NodeManager/{node/specs/type.js → clients/types.utils.js} +0 -0
@@ -1,4 +1,6 @@
1
+ import { ipfsHashToSolBytesArray } from '@nosana/ipfs';
1
2
  import { JobExternalUtil } from './jobExternalUtil.js';
3
+ import { IpfsClientSingleton } from '../../../ipfs/IpfsClient.js';
2
4
  export class JobRegistry {
3
5
  constructor() {
4
6
  this.registry = new Map();
@@ -28,10 +30,10 @@ export class JobRegistry {
28
30
  const stopPromises = [];
29
31
  for (const [jobId, job] of this.registry.entries()) {
30
32
  const promise = (async () => {
31
- const jobExternalUtil = new JobExternalUtil(sdk, repository);
33
+ const jobExternalUtil = new JobExternalUtil(repository);
32
34
  const result = await jobExternalUtil.resolveResult(jobId, job);
33
- const ipfsResult = await sdk.ipfs.pin(result);
34
- const bytesArray = sdk.ipfs.IpfsHashToByteArray(ipfsResult);
35
+ const ipfsResult = await IpfsClientSingleton.pin(result);
36
+ const bytesArray = ipfsHashToSolBytesArray(ipfsResult);
35
37
  await sdk.jobs.submitResult(bytesArray, this.runs.get(jobId), job.market);
36
38
  this.runs.delete(jobId);
37
39
  this.registry.delete(jobId);
@@ -1,7 +1,5 @@
1
- import { Client as SDK, JobDefinition } from '@nosana/sdk';
1
+ import { JobDefinition } from '@nosana/sdk';
2
2
  import { JobDefinitionStrategy } from '../JobDefinitionStrategy.js';
3
3
  export declare class IpfsJobDefinitionStrategy implements JobDefinitionStrategy {
4
- private sdk;
5
- constructor(sdk: SDK);
6
4
  load(id: string): Promise<JobDefinition>;
7
5
  }
@@ -1,10 +1,8 @@
1
+ import { IpfsClientSingleton } from '../../../../../ipfs/IpfsClient.js';
1
2
  export class IpfsJobDefinitionStrategy {
2
- constructor(sdk) {
3
- this.sdk = sdk;
4
- }
5
3
  async load(id) {
6
4
  try {
7
- return await this.sdk.ipfs.retrieve(id);
5
+ return await IpfsClientSingleton.retrieve(id);
8
6
  }
9
7
  catch (e) {
10
8
  throw new Error(`Failed to load job from IPFS: ${e.message}`);
@@ -1,9 +1,8 @@
1
- import { Job, Client as SDK, JobDefinition, FlowState } from '@nosana/sdk';
1
+ import { Job, JobDefinition, FlowState } from '@nosana/sdk';
2
2
  import { NodeRepository } from '../../repository/NodeRepository.js';
3
3
  export declare class JobExternalUtil {
4
- private sdk;
5
4
  private repository;
6
- constructor(sdk: SDK, repository: NodeRepository);
5
+ constructor(repository: NodeRepository);
7
6
  resolveJobDefinition(id: string, job: Job): Promise<JobDefinition | null>;
8
7
  resolveResult(id: string, job: Job): Promise<FlowState>;
9
8
  validate(id: string, jobDefinition: JobDefinition): Promise<boolean>;
@@ -1,14 +1,14 @@
1
1
  import { validateJobDefinition } from '@nosana/sdk';
2
+ import { IpfsClientSingleton } from '../../../ipfs/IpfsClient.js';
2
3
  import { JobDefinitionStrategySelector } from './defination/JobDefinitionStrategy.js';
3
4
  import { ResultReturnStrategySelector } from './result/ResultReturnStrategy.js';
4
5
  import { createInitialFlow } from '../task/helpers/createInitialFlow.js';
5
6
  export class JobExternalUtil {
6
- constructor(sdk, repository) {
7
- this.sdk = sdk;
7
+ constructor(repository) {
8
8
  this.repository = repository;
9
9
  }
10
10
  async resolveJobDefinition(id, job) {
11
- let jobDefinition = await this.sdk.ipfs.retrieve(job.ipfsJob);
11
+ let jobDefinition = await IpfsClientSingleton.retrieve(job.ipfsJob);
12
12
  if (jobDefinition && jobDefinition.logistics?.send?.type) {
13
13
  const strategySelector = new JobDefinitionStrategySelector();
14
14
  const strategy = strategySelector.selectStrategy(jobDefinition.logistics.send.type);
@@ -28,7 +28,7 @@ export class JobExternalUtil {
28
28
  }
29
29
  async resolveResult(id, job) {
30
30
  let result = this.repository.getFlowState(id);
31
- let jobDefinition = await this.sdk.ipfs.retrieve(job.ipfsJob);
31
+ let jobDefinition = await IpfsClientSingleton.retrieve(job.ipfsJob);
32
32
  if (result) {
33
33
  const orginalStatus = result.status;
34
34
  if (jobDefinition.logistics?.receive?.type) {
@@ -1,6 +1,8 @@
1
1
  import EventEmitter from 'events';
2
2
  import { PublicKey } from '@solana/web3.js';
3
3
  import { validateJobDefinition } from '@nosana/sdk';
4
+ import { ipfsHashToSolBytesArray } from '@nosana/ipfs';
5
+ import { IpfsClientSingleton } from '../../../ipfs/IpfsClient.js';
4
6
  import { applyLoggingProxyToClass, createLoggingProxy, } from '../../monitoring/proxy/loggingProxy.js';
5
7
  import { JobExternalUtil } from './jobExternalUtil.js';
6
8
  import { abortControllerSelector } from '../abort/abortControllerSelector.js';
@@ -15,7 +17,7 @@ export class JobHandler {
15
17
  this.provider = provider;
16
18
  this.repository = repository;
17
19
  this.finishing = false;
18
- this.jobExternalUtil = new JobExternalUtil(sdk, this.repository);
20
+ this.jobExternalUtil = new JobExternalUtil(this.repository);
19
21
  applyLoggingProxyToClass(this);
20
22
  this.eventEmitter = new EventEmitter();
21
23
  this.accountEmitter = new EventEmitter();
@@ -176,8 +178,8 @@ export class JobHandler {
176
178
  try {
177
179
  const jobId = this.jobId();
178
180
  let result = await this.jobExternalUtil.resolveResult(jobId, this.get());
179
- const ipfsResult = await this.sdk.ipfs.pin(result);
180
- const bytesArray = this.sdk.ipfs.IpfsHashToByteArray(ipfsResult);
181
+ const ipfsResult = await IpfsClientSingleton.pin(result);
182
+ const bytesArray = ipfsHashToSolBytesArray(ipfsResult);
181
183
  if (reason == StopReasons.STOPPED) {
182
184
  await this.sdk.jobs.complete(bytesArray, jobId);
183
185
  }
@@ -12,5 +12,4 @@ export declare class KeyHandler {
12
12
  setAccessKey(key?: PublicKey): void;
13
13
  doesMarketNeedAccessKey(market: Market): boolean;
14
14
  loadAccessKeyFromChain(market: Market): Promise<PublicKey | undefined>;
15
- join(): Promise<void>;
16
15
  }
@@ -1,7 +1,4 @@
1
- import { PublicKey } from '@solana/web3.js';
2
1
  import { EMPTY_ADDRESS } from '../../jobs/index.js';
3
- import { configs } from '../../configs/configs.js';
4
- import { isNodeOnboarded } from '../../utils/utils.js';
5
2
  export class KeyHandler {
6
3
  constructor(sdk) {
7
4
  this.sdk = sdk;
@@ -29,7 +26,7 @@ export class KeyHandler {
29
26
  try {
30
27
  this.key = await this.sdk.solana.getNftFromCollection(this.address, market.nodeAccessKey.toString());
31
28
  if (!this.key) {
32
- throw new Error('Could not find access key');
29
+ throw new Error("Could not find access key");
33
30
  }
34
31
  return this.key;
35
32
  }
@@ -37,38 +34,4 @@ export class KeyHandler {
37
34
  throw new Error(`error loading Access Key from chain: ${error}`);
38
35
  }
39
36
  }
40
- async join() {
41
- const signature = (await this.sdk.solana.signMessage(configs().signMessage));
42
- const base64Signature = Buffer.from(signature).toString('base64');
43
- // If we don't specify a market, try to get the correct market from the backend
44
- try {
45
- // Check if node is onboarded and has received access key
46
- // if not call onboard endpoint to create access key tx
47
- const response = await fetch(`${configs().backendUrl}/nodes/${this.address}`, {
48
- method: 'GET',
49
- headers: {
50
- Authorization: `${this.address}:${base64Signature}`,
51
- 'Content-Type': 'application/json',
52
- },
53
- });
54
- const result = await response.json();
55
- if (!result || (result && result.name === 'Error')) {
56
- throw new Error(result.message);
57
- }
58
- if (!isNodeOnboarded(result.status)) {
59
- throw new Error('Node not onboarded yet');
60
- }
61
- this.key = new PublicKey(result.accessKeyMint);
62
- this.market = new PublicKey(result.marketAddress);
63
- }
64
- catch (e) {
65
- if (e instanceof Error && e.message.includes('Node not onboarded yet')) {
66
- throw new Error('Node is still on the waitlist, wait until you are accepted.');
67
- }
68
- else if (e instanceof Error && e.message.includes('Node not found')) {
69
- throw new Error('Node is not registred yet. To register run the `node join` command.');
70
- }
71
- throw e;
72
- }
73
- }
74
37
  }
@@ -0,0 +1,15 @@
1
+ import { Client, JobDefinition } from "@nosana/sdk";
2
+ import { Provider } from "../../provider/Provider.js";
3
+ import { NodeRepository } from "../../repository/NodeRepository.js";
4
+ export declare class Benchmark {
5
+ private benchmarkId;
6
+ private jobDefinition;
7
+ private sdk;
8
+ private provider;
9
+ private repository;
10
+ constructor(benchmarkId: string, jobDefinition: JobDefinition, sdk: Client, provider: Provider, repository: NodeRepository);
11
+ run(): Promise<void>;
12
+ private executeBenchmark;
13
+ private submitResults;
14
+ private reportResults;
15
+ }
@@ -0,0 +1,84 @@
1
+ import chalk from 'chalk';
2
+ import { HostManager } from './hostManager.js';
3
+ import TaskManager, { OperationProgressStatuses } from "../task/TaskManager.js";
4
+ import { createLoggingProxy, logEmitter } from '../../monitoring/proxy/loggingProxy.js';
5
+ export class Benchmark {
6
+ constructor(benchmarkId, jobDefinition, sdk, provider, repository) {
7
+ this.benchmarkId = benchmarkId;
8
+ this.jobDefinition = jobDefinition;
9
+ this.sdk = sdk;
10
+ this.provider = provider;
11
+ this.repository = repository;
12
+ }
13
+ async run() {
14
+ await this.executeBenchmark();
15
+ const results = await this.submitResults();
16
+ await this.reportResults(results);
17
+ }
18
+ async executeBenchmark() {
19
+ for (const op of this.jobDefinition.ops) {
20
+ if (op.type === "container/run") {
21
+ op.args.env = {
22
+ ...(op.args.env || {}),
23
+ authorization: await this.sdk.authorization.generate(this.sdk.solana.provider.wallet.publicKey.toString()),
24
+ };
25
+ }
26
+ }
27
+ const task = createLoggingProxy(new TaskManager(this.provider, this.repository, this.benchmarkId, this.sdk.solana.wallet.publicKey.toString(), this.jobDefinition), 'BenchmarkTaskManager');
28
+ const total = this.jobDefinition.ops.length;
29
+ const events = task.getEventsEmitter();
30
+ const onUpdate = () => {
31
+ // Defer so we read status after the FINISHED setter (which runs
32
+ // after the relay listener that fires this event).
33
+ queueMicrotask(() => {
34
+ const statuses = task.getOperationsStatus();
35
+ const completed = Object.values(statuses).filter((s) => s === OperationProgressStatuses.FINISHED).length;
36
+ logEmitter.emit('log', {
37
+ class: 'BenchmarkTaskManager',
38
+ method: 'progress',
39
+ arguments: [],
40
+ timestamp: new Date().toISOString(),
41
+ type: 'call',
42
+ payload: { completed, total },
43
+ });
44
+ });
45
+ };
46
+ events.on('flow:updated', onUpdate);
47
+ try {
48
+ task.bootstrap();
49
+ await task.start();
50
+ }
51
+ catch (error) {
52
+ console.error('Error executing benchmark:', error);
53
+ }
54
+ finally {
55
+ events.off('flow:updated', onUpdate);
56
+ }
57
+ }
58
+ async submitResults() {
59
+ const flowResults = this.repository.getFlow(this.benchmarkId);
60
+ if (!flowResults)
61
+ throw new Error(`Cannot find results for flow with id ${this.benchmarkId}`);
62
+ this.repository.deleteflow(this.benchmarkId);
63
+ const response = await HostManager.submitBenchmarkResults(this.benchmarkId, flowResults.state);
64
+ return response.report?.metrics ?? [];
65
+ }
66
+ reportResults(metrics) {
67
+ const displayMetrics = metrics.filter((metric) => metric.measuredValue !== undefined);
68
+ const nameWidth = displayMetrics.length > 0
69
+ ? Math.max(20, ...displayMetrics.map((metric) => metric.metricKey.length))
70
+ : 20;
71
+ const width = nameWidth + 20;
72
+ console.log();
73
+ console.log(' ' + chalk.cyan('━'.repeat(width)));
74
+ console.log(' ' + chalk.bold.cyan('LATEST MEASUREMENTS'));
75
+ console.log(' ' + chalk.cyan('━'.repeat(width)));
76
+ console.log();
77
+ for (const metric of displayMetrics) {
78
+ const name = chalk.bold(metric.metricKey.padEnd(nameWidth));
79
+ const details = chalk.cyan(`measured: ${metric.measuredValue}`);
80
+ console.log(` ${chalk.cyan('•')} ${name} ${details}`);
81
+ }
82
+ console.log();
83
+ }
84
+ }
@@ -0,0 +1,114 @@
1
+ import { JobDefinition } from "@nosana/sdk";
2
+ import type { operations } from "../../clients/hostManager/schema.d.ts";
3
+ type RequestMarketResponse = operations["getNodesRequest-market"]["responses"][200]["content"]["application/json"];
4
+ export type SubmitBenchmarkBody = operations["postBenchmarksByIdSubmit-results"]["requestBody"]["content"]["application/json"];
5
+ type GetNodeResponse = operations["getNodesById"]["responses"][200]["content"]["application/json"];
6
+ type RequiredResourcesResponse = operations["getMarketsByIdRequired-resources"]["responses"][200]["content"]["application/json"];
7
+ type HeartbeatResponse = operations["postNodesHeartbeat"]["responses"][200]["content"]["application/json"];
8
+ type ErrorReportBody = operations["postErrorsReport"]["requestBody"]["content"]["application/json"];
9
+ export type FeedbackReport = NonNullable<RequestMarketResponse["feedbackReport"]>;
10
+ export type RequestMarketResult = Pick<RequestMarketResponse, "status" | "market" | "feedbackReport"> & {
11
+ notRegistered?: true;
12
+ jobDefinition?: JobDefinition & {
13
+ id: string;
14
+ };
15
+ benchmarkId?: string;
16
+ };
17
+ export declare class HostManager {
18
+ static register(email: string, discord?: string, twitter?: string): Promise<void>;
19
+ static requestMarket(market?: string): Promise<RequestMarketResult>;
20
+ static submitBenchmarkResults(benchmarkId: string, body: SubmitBenchmarkBody): Promise<import("openapi-typescript-helpers").SuccessResponse<{
21
+ 200: {
22
+ headers: {
23
+ [name: string]: unknown;
24
+ };
25
+ content: {
26
+ "application/json": {
27
+ status: "failed" | "success";
28
+ report?: {
29
+ marketAddress: string;
30
+ marketName?: string | undefined;
31
+ passed: boolean;
32
+ metrics: {
33
+ metricKey: string;
34
+ measuredValue?: string | number | boolean | undefined;
35
+ ruleDescription: string;
36
+ passed: boolean;
37
+ failureMessage?: string | undefined;
38
+ }[];
39
+ } | undefined;
40
+ };
41
+ "multipart/form-data": {
42
+ status: "failed" | "success";
43
+ report?: {
44
+ marketAddress: string;
45
+ marketName?: string | undefined;
46
+ passed: boolean;
47
+ metrics: {
48
+ metricKey: string;
49
+ measuredValue?: string | number | boolean | undefined;
50
+ ruleDescription: string;
51
+ passed: boolean;
52
+ failureMessage?: string | undefined;
53
+ }[];
54
+ } | undefined;
55
+ };
56
+ "text/plain": {
57
+ status: "failed" | "success";
58
+ report?: {
59
+ marketAddress: string;
60
+ marketName?: string | undefined;
61
+ passed: boolean;
62
+ metrics: {
63
+ metricKey: string;
64
+ measuredValue?: string | number | boolean | undefined;
65
+ ruleDescription: string;
66
+ passed: boolean;
67
+ failureMessage?: string | undefined;
68
+ }[];
69
+ } | undefined;
70
+ };
71
+ };
72
+ };
73
+ }, `${string}/${string}`>>;
74
+ static syncNodeAfterMint(address: string): Promise<void>;
75
+ static getNode(address: string): Promise<GetNodeResponse | null>;
76
+ static getMarketRequiredResources(marketId: string): Promise<RequiredResourcesResponse | null>;
77
+ static sendHeartbeat(): Promise<HeartbeatResponse>;
78
+ static getRpcUrl(): Promise<string | undefined>;
79
+ static reportError(body: ErrorReportBody): Promise<import("openapi-typescript-helpers").SuccessResponse<{
80
+ 200: {
81
+ headers: {
82
+ [name: string]: unknown;
83
+ };
84
+ content: {
85
+ "application/json": {
86
+ success: boolean;
87
+ };
88
+ "multipart/form-data": {
89
+ success: boolean;
90
+ };
91
+ "text/plain": {
92
+ success: boolean;
93
+ };
94
+ };
95
+ };
96
+ 401: {
97
+ headers: {
98
+ [name: string]: unknown;
99
+ };
100
+ content: {
101
+ "application/json": {
102
+ message: string;
103
+ };
104
+ "multipart/form-data": {
105
+ message: string;
106
+ };
107
+ "text/plain": {
108
+ message: string;
109
+ };
110
+ };
111
+ };
112
+ }, `${string}/${string}`>>;
113
+ }
114
+ export {};
@@ -0,0 +1,112 @@
1
+ import { hostManagerClientSelector } from "../../clients/hostManager/index.js";
2
+ export class HostManager {
3
+ static async register(email, discord, twitter) {
4
+ const { data, error } = await hostManagerClientSelector().POST("/nodes/register", {
5
+ body: {
6
+ email,
7
+ discord,
8
+ twitter
9
+ }
10
+ });
11
+ if (error || data?.status !== "registered") {
12
+ throw new Error(`Error registering node${error ? `: ${error}` : ""}`);
13
+ }
14
+ }
15
+ static async requestMarket(market) {
16
+ const { data, error, response } = await hostManagerClientSelector().GET("/nodes/request-market", {
17
+ params: {
18
+ query: {
19
+ market
20
+ }
21
+ }
22
+ });
23
+ if (error || !data) {
24
+ // HTTP 404 = node is not yet registered
25
+ if (response?.status === 404 || response?.status === 'NotFound') {
26
+ return { notRegistered: true };
27
+ }
28
+ throw new Error(`Error requesting market: ${error}`);
29
+ }
30
+ const result = {
31
+ status: data.status,
32
+ };
33
+ if (data.jobDefinition) {
34
+ result.jobDefinition = data.jobDefinition;
35
+ }
36
+ if (data.benchmarkId) {
37
+ result.benchmarkId = data.benchmarkId;
38
+ }
39
+ if (data.feedbackReport) {
40
+ result.feedbackReport = data.feedbackReport;
41
+ }
42
+ if (data.market) {
43
+ result.market = {
44
+ address: data.market.address,
45
+ sftTx: data.market.sftTx ?? undefined,
46
+ };
47
+ }
48
+ return result;
49
+ }
50
+ static async submitBenchmarkResults(benchmarkId, body) {
51
+ const { data, error } = await hostManagerClientSelector().POST("/benchmarks/{id}/submit-results", {
52
+ params: { path: { id: benchmarkId } },
53
+ body,
54
+ });
55
+ if (error && !data) {
56
+ throw new Error(`Error submitting benchmark results: ${JSON.stringify(error)}`);
57
+ }
58
+ return data;
59
+ }
60
+ static async syncNodeAfterMint(address) {
61
+ const { error } = await hostManagerClientSelector().POST("/nodes/sync-node", {
62
+ body: { address },
63
+ });
64
+ if (error) {
65
+ throw new Error(`Error syncing node after mint: ${error}`);
66
+ }
67
+ }
68
+ static async getNode(address) {
69
+ const { data, error, response } = await hostManagerClientSelector().GET("/nodes/{id}", {
70
+ params: { path: { id: address } },
71
+ });
72
+ if (error || !data) {
73
+ if (response?.status === 404 || response?.status === 'NotFound') {
74
+ return null;
75
+ }
76
+ throw new Error(`Error getting node: ${JSON.stringify(error)}`);
77
+ }
78
+ return data;
79
+ }
80
+ static async getMarketRequiredResources(marketId) {
81
+ const { data, error } = await hostManagerClientSelector().GET("/markets/{id}/required-resources", {
82
+ params: { path: { id: marketId } },
83
+ });
84
+ if (error || !data) {
85
+ return null;
86
+ }
87
+ return data;
88
+ }
89
+ static async sendHeartbeat() {
90
+ const { data, error } = await hostManagerClientSelector().POST("/nodes/heartbeat", {});
91
+ if (error || !data) {
92
+ throw new Error(`Error sending heartbeat: ${JSON.stringify(error)}`);
93
+ }
94
+ return data;
95
+ }
96
+ static async getRpcUrl() {
97
+ const { data, error } = await hostManagerClientSelector().GET("/rpc", {});
98
+ if (error || !data) {
99
+ throw new Error(`Error fetching host-manager RPC URL: ${JSON.stringify(error)}`);
100
+ }
101
+ return typeof data.url === "string" ? data.url : undefined;
102
+ }
103
+ static async reportError(body) {
104
+ const { data, error } = await hostManagerClientSelector().POST("/errors/report", {
105
+ body,
106
+ });
107
+ if (error || !data) {
108
+ throw new Error(`Error reporting node error: ${JSON.stringify(error)}`);
109
+ }
110
+ return data;
111
+ }
112
+ }
@@ -0,0 +1,8 @@
1
+ import { Client as SDK } from '@nosana/sdk';
2
+ export declare class MarketAccessHandler {
3
+ private sdk;
4
+ constructor(sdk: SDK);
5
+ mintAccessKey(sftTxBase64: string): Promise<string>;
6
+ private signAndSendTransaction;
7
+ private confirmTransaction;
8
+ }
@@ -0,0 +1,53 @@
1
+ import { VersionedTransaction, } from '@solana/web3.js';
2
+ import { getRawTransaction } from '../../sdk/index.js';
3
+ export class MarketAccessHandler {
4
+ constructor(sdk) {
5
+ this.sdk = sdk;
6
+ }
7
+ async mintAccessKey(sftTxBase64) {
8
+ // Incase of blockheight exceeded error, retry 3 times
9
+ for (let i = 0; i < 3; i++) {
10
+ try {
11
+ const txnSignature = await this.signAndSendTransaction(sftTxBase64);
12
+ await this.confirmTransaction(txnSignature);
13
+ if (!txnSignature) {
14
+ throw new Error("Transaction signature is undefined");
15
+ }
16
+ return txnSignature;
17
+ }
18
+ catch (error) {
19
+ if (i === 2 ||
20
+ !error?.message.includes("TransactionExpiredBlockheightExceededError")) {
21
+ throw error;
22
+ }
23
+ }
24
+ }
25
+ throw new Error("Failed to mint access key after 3 retries");
26
+ }
27
+ async signAndSendTransaction(txData) {
28
+ const feePayer = (this.sdk.solana.provider?.wallet).payer;
29
+ const recoveredTransaction = await getRawTransaction(Buffer.from(txData, 'base64'));
30
+ if (recoveredTransaction instanceof VersionedTransaction) {
31
+ recoveredTransaction.sign([feePayer]);
32
+ }
33
+ else {
34
+ recoveredTransaction.partialSign(feePayer);
35
+ }
36
+ const txnSignature = await this.sdk.solana.connection?.sendRawTransaction(recoveredTransaction.serialize());
37
+ return txnSignature;
38
+ }
39
+ async confirmTransaction(txnSignature) {
40
+ const latestBlockHash = await this.sdk.solana.connection?.getLatestBlockhash();
41
+ if (latestBlockHash && txnSignature) {
42
+ const confirmStrategy = {
43
+ blockhash: latestBlockHash.blockhash,
44
+ lastValidBlockHeight: latestBlockHash.lastValidBlockHeight,
45
+ signature: txnSignature,
46
+ };
47
+ await this.sdk.solana.connection?.confirmTransaction(confirmStrategy);
48
+ }
49
+ else {
50
+ throw new Error("Could not confirm minting transaction");
51
+ }
52
+ }
53
+ }
@@ -1,15 +1,22 @@
1
- import { Client as SDK, Market } from '@nosana/sdk';
2
- import { PublicKey } from '@solana/web3.js';
1
+ import { Client as SDK, Market } from "@nosana/sdk";
2
+ import { PublicKey } from "@solana/web3.js";
3
+ import { Provider } from "../../provider/Provider.js";
4
+ import { NodeRepository } from "../../repository/NodeRepository.js";
3
5
  export declare class MarketHandler {
4
6
  private sdk;
7
+ private provider;
8
+ private repository;
5
9
  private market;
6
10
  private address;
7
11
  private checkQueuedInterval?;
12
+ private marketAccessHandler;
8
13
  private inMarket;
9
- constructor(sdk: SDK);
14
+ constructor(sdk: SDK, provider: Provider, repository: NodeRepository);
10
15
  clear(): void;
11
16
  isInMarket(): boolean;
12
17
  setInMarket(): void;
18
+ request(requestedMarket?: string): Promise<Market>;
19
+ private logFeedbackReport;
13
20
  check(market: string): Promise<Market>;
14
21
  stopMarket(): Promise<boolean>;
15
22
  getJobMarket(market: string): Promise<Market>;